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

7513 lines
250 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 <ADsOpenFlags.h> // GetADsOpenObjectFlags
  23. #include <lmaccess.h> // UF_* userAccountControl bits
  24. #include <ntsam.h> // GROUP_TYPE_*
  25. #include <Dsgetdc.h>
  26. #include <lmapibuf.h>
  27. // Internal use only helper functions
  28. BOOL TranslateNameXForest(LPCWSTR szDomain, LPCWSTR lpAccountName,
  29. DS_NAME_FORMAT AccountNameFormat,
  30. DS_NAME_FORMAT DesiredNameFormat,
  31. LPWSTR *lpTranslatedName);
  32. HRESULT IsBSTRInVariantArray(VARIANT& refvar, CComBSTR& strSearch,
  33. bool& bFound);
  34. HRESULT ValidatePartition(CDSCmdBasePathsInfo& basePathsInfo,
  35. LPCWSTR pszObjectDN);
  36. HRESULT GetAttrFromDN(PCWSTR pszDN,PWSTR pszAttribute,
  37. const CDSCmdBasePathsInfo& refBasePathsInfo,
  38. const CDSCmdCredentialObject& refCredentialObject,
  39. PADS_ATTR_INFO* ppAttrInfo);
  40. //+--------------------------------------------------------------------------
  41. //
  42. // Member: CDSCmdCredentialObject::CDSCmdCredentialObject
  43. //
  44. // Synopsis: Constructor for the credential management class
  45. //
  46. // Arguments:
  47. //
  48. // Returns:
  49. //
  50. // History: 06-Sep-2000 JeffJon Created
  51. //
  52. //---------------------------------------------------------------------------
  53. CDSCmdCredentialObject::CDSCmdCredentialObject()
  54. : m_bUsingCredentials(false)
  55. {
  56. ZeroMemory(&m_EncryptedPasswordDataBlob,sizeof(DATA_BLOB));
  57. }
  58. //+--------------------------------------------------------------------------
  59. //
  60. // Member: CDSCmdCredentialObject::~CDSCmdCredentialObject
  61. //
  62. // Synopsis: Destructor for the credential management class
  63. //
  64. // Arguments:
  65. //
  66. // Returns:
  67. //
  68. // History: 06-Sep-2000 JeffJon Created
  69. //
  70. //---------------------------------------------------------------------------
  71. CDSCmdCredentialObject::~CDSCmdCredentialObject()
  72. {
  73. if (m_EncryptedPasswordDataBlob.pbData)
  74. {
  75. LocalFree(m_EncryptedPasswordDataBlob.pbData);
  76. }
  77. }
  78. //+--------------------------------------------------------------------------
  79. //
  80. // Member: CDSCmdCredentialObject::SetUsername
  81. //
  82. // Synopsis: Encodes the passed in string and sets the m_pszPassword
  83. // member data.
  84. //
  85. // Arguments: [pszPassword] : unencoded password
  86. //
  87. // Returns: HRESULT : E_OUTOFMEMORY if we failed to allocate space
  88. // for the new password
  89. // E_POINTER if the string passed in is not valid
  90. // S_OK if we succeeded in setting the password
  91. //
  92. // History: 06-Sep-2000 JeffJon Created
  93. //
  94. //---------------------------------------------------------------------------
  95. HRESULT CDSCmdCredentialObject::SetUsername(PCWSTR pszUsername)
  96. {
  97. ENTER_FUNCTION_HR(FULL_LOGGING, CDSCmdCredentialObject::SetUsername, hr);
  98. do // false loop
  99. {
  100. //
  101. // Verify the input parameters
  102. //
  103. if (!pszUsername)
  104. {
  105. hr = E_POINTER;
  106. break;
  107. }
  108. //
  109. // Copy the new username
  110. //
  111. m_sbstrUsername = pszUsername;
  112. DEBUG_OUTPUT(FULL_LOGGING, L"Username = %s", pszUsername);
  113. } while (false);
  114. return hr;
  115. }
  116. //
  117. // A prime number used to seed the encoding and decoding
  118. //
  119. #define NW_ENCODE_SEED3 0x83
  120. //+--------------------------------------------------------------------------
  121. //
  122. // Member: CDSCmdCredentialObject::SetPassword
  123. //
  124. // Synopsis: Encodes the passed in string and sets the m_pszPassword
  125. // member data.
  126. //
  127. // Arguments: [pszPassword] : unencoded password
  128. //
  129. // Returns: HRESULT : E_OUTOFMEMORY if we failed to allocate space
  130. // for the new password
  131. // S_OK if we succeeded in setting the password
  132. //
  133. // History: 06-Sep-2000 JeffJon Created
  134. //
  135. //---------------------------------------------------------------------------
  136. HRESULT CDSCmdCredentialObject::SetPassword(PCWSTR pszPassword)
  137. {
  138. //Security Review:Rewrite this function as per
  139. //NTRAID#NTBUG9-571570-2000/11/13-hiteshr
  140. //Use CryptProtectMemory and SecureZeroMemory
  141. ENTER_FUNCTION_HR(FULL_LOGGING, CDSCmdCredentialObject::SetPassword, hr);
  142. if(!pszPassword)
  143. {
  144. return E_POINTER;
  145. }
  146. //
  147. // Free the previously allocated password if there was one
  148. //
  149. if (m_EncryptedPasswordDataBlob.pbData)
  150. {
  151. LocalFree(m_EncryptedPasswordDataBlob.pbData);
  152. ZeroMemory(&m_EncryptedPasswordDataBlob,sizeof(DATA_BLOB));
  153. }
  154. hr = EncryptPasswordString(pszPassword,&m_EncryptedPasswordDataBlob);
  155. return hr;
  156. }
  157. //+--------------------------------------------------------------------------
  158. //
  159. // Member: CDSCmdCredentialObject::SetEncryptedPassword
  160. //
  161. // Synopsis: Assign
  162. //
  163. // Arguments: [pszPassword] : unencoded password
  164. //
  165. // Returns: HRESULT : E_OUTOFMEMORY if we failed to allocate space
  166. // for the new password
  167. // S_OK if we succeeded in setting the password
  168. //
  169. // History: 06-Sep-2000 JeffJon Created
  170. //
  171. //---------------------------------------------------------------------------
  172. HRESULT CDSCmdCredentialObject::SetEncryptedPassword(DATA_BLOB* pEncryptedPasswordDataBlob)
  173. {
  174. //Security Review:Rewrite this function as per
  175. //NTRAID#NTBUG9-571570-2000/11/13-hiteshr
  176. //Use CryptProtectMemory and SecureZeroMemory
  177. ENTER_FUNCTION_HR(FULL_LOGGING, CDSCmdCredentialObject::SetEncryptedPassword, hr);
  178. if(!pEncryptedPasswordDataBlob)
  179. {
  180. return E_POINTER;
  181. }
  182. //
  183. // Free the previously allocated password if there was one
  184. //
  185. if (m_EncryptedPasswordDataBlob.pbData)
  186. {
  187. LocalFree(m_EncryptedPasswordDataBlob.pbData);
  188. ZeroMemory(&m_EncryptedPasswordDataBlob,sizeof(DATA_BLOB));
  189. }
  190. //Do a deep copy
  191. m_EncryptedPasswordDataBlob.pbData = (BYTE *)LocalAlloc(LPTR,pEncryptedPasswordDataBlob->cbData);
  192. if(!m_EncryptedPasswordDataBlob.pbData)
  193. {
  194. return E_OUTOFMEMORY;
  195. }
  196. m_EncryptedPasswordDataBlob.cbData = pEncryptedPasswordDataBlob->cbData;
  197. CopyMemory(m_EncryptedPasswordDataBlob.pbData,
  198. pEncryptedPasswordDataBlob->pbData,
  199. pEncryptedPasswordDataBlob->cbData);
  200. return S_OK;
  201. }
  202. //+--------------------------------------------------------------------------
  203. //
  204. // Member: CDSCmdCredentialObject::GetPassword
  205. //
  206. // Synopsis: Unencodes the password member and returns a string in the
  207. // provided buffer with the clear text password.
  208. //
  209. // Arguments: [ppszBuffer - IN] : Gets Cleartext password. Call
  210. // SecureZeroMemory on it when its nolonger required
  211. //
  212. // Returns: HRESULT : E_INVALIDARG if the buffer is not valid
  213. // E_OUTOFMEMORY if the buffer is too small
  214. // E_FAIL if the password has not been set
  215. // S_OK if we succeeded in getting the password
  216. //
  217. // History: 06-Sep-2000 JeffJon Created
  218. //
  219. //---------------------------------------------------------------------------
  220. HRESULT CDSCmdCredentialObject::GetPassword(PWSTR *ppszBuffer) const
  221. {
  222. ENTER_FUNCTION_HR(FULL_LOGGING, CDSCmdCredentialObject::GetPassword, hr);
  223. if (!ppszBuffer)
  224. {
  225. ASSERT(ppszBuffer);
  226. return E_INVALIDARG;
  227. }
  228. //
  229. // Verify there is a password to retrieve
  230. //
  231. if (!m_EncryptedPasswordDataBlob.pbData)
  232. {
  233. DEBUG_OUTPUT(FULL_LOGGING, L"No password has been set");
  234. return E_FAIL;
  235. }
  236. //UnEncode the password
  237. return DecryptPasswordString(&m_EncryptedPasswordDataBlob,ppszBuffer);
  238. }
  239. ////////////////////////////////////////////////////////////////////////////////
  240. //+--------------------------------------------------------------------------
  241. //
  242. // Member: CDSCmdBasePathsInfo::CDSCmdBasePathsInfo
  243. //
  244. // Synopsis: Constructor for the base paths management class
  245. //
  246. // Arguments:
  247. //
  248. // Returns:
  249. //
  250. // History: 06-Sep-2000 JeffJon Created
  251. //
  252. //---------------------------------------------------------------------------
  253. CDSCmdBasePathsInfo::CDSCmdBasePathsInfo()
  254. : m_bInitialized(false),
  255. m_bModeInitialized(false),
  256. m_bDomainMode(true)
  257. {
  258. }
  259. //+--------------------------------------------------------------------------
  260. //
  261. // Member: CDSCmdBasePathsInfo::~CDSCmdBasePathsInfo
  262. //
  263. // Synopsis: Destructor for the base paths management class
  264. //
  265. // Arguments:
  266. //
  267. // Returns:
  268. //
  269. // History: 06-Sep-2000 JeffJon Created
  270. //
  271. //---------------------------------------------------------------------------
  272. CDSCmdBasePathsInfo::~CDSCmdBasePathsInfo()
  273. {
  274. }
  275. //+--------------------------------------------------------------------------
  276. //
  277. // Member: CDSCmdBasePathsInfo::InitializeFromName
  278. //
  279. // Synopsis: Initializes all the member strings for the well known
  280. // naming contexts by connecting to the RootDSE of the server or
  281. // domain that is passed in.
  282. //
  283. // Arguments: [refCredentialObject - IN] : a reference to the credential manager
  284. // [pszServerOrDomain - IN] : a NULL terminated wide character string
  285. // that contains the name of the domain or
  286. // server to connect to
  287. // [bServerName - IN] : Specifies whether the name given by
  288. // pszServerOrDomain is a server name (true)
  289. // or a domain name (false)
  290. //
  291. // Returns: HRESULT : S_OK if everything succeeded
  292. // E_OUTOFMEMORY if an allocation for one of the strings
  293. // failed
  294. // Anything else is a failure code from an ADSI call
  295. //
  296. // History: 06-Sep-2000 JeffJon Created
  297. //
  298. //---------------------------------------------------------------------------
  299. HRESULT CDSCmdBasePathsInfo::InitializeFromName(const CDSCmdCredentialObject& refCredentialObject,
  300. PCWSTR pszServerOrDomain,
  301. bool bServerName)
  302. {
  303. ENTER_FUNCTION_HR(LEVEL5_LOGGING, CDSCmdBasePathsInfo::InitializeFromName, hr);
  304. do // false loop
  305. {
  306. //
  307. // Check to see if we are already initialized
  308. //
  309. if (IsInitialized())
  310. {
  311. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Base paths info already initialized");
  312. break;
  313. }
  314. //
  315. // Create the path to the RootDSE
  316. //
  317. CComBSTR sbstrRootDSE;
  318. sbstrRootDSE = g_bstrLDAPProvider;
  319. if (pszServerOrDomain)
  320. {
  321. sbstrRootDSE += pszServerOrDomain;
  322. sbstrRootDSE += L"/";
  323. }
  324. sbstrRootDSE += g_bstrRootDSE;
  325. //
  326. // Now sbstrRootDSE should either be in the form "LDAP://<serverOrDomain>/RootDSE"
  327. // or "LDAP://RootDSE"
  328. //
  329. //
  330. // Bind to the RootDSE
  331. //
  332. hr = DSCmdOpenObject(refCredentialObject,
  333. sbstrRootDSE,
  334. IID_IADs,
  335. (void**)&m_spRootDSE,
  336. false);
  337. if (FAILED(hr))
  338. {
  339. break;
  340. }
  341. if (bServerName)
  342. {
  343. m_sbstrServerName = pszServerOrDomain;
  344. }
  345. else
  346. {
  347. //
  348. // Get the configuration naming context
  349. //
  350. CComVariant var;
  351. hr = m_spRootDSE->Get(g_bstrConfigNCProperty, &var);
  352. if (FAILED(hr))
  353. {
  354. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to get the Configuration Naming Context: hr = 0x%x", hr);
  355. break;
  356. }
  357. if (var.vt != VT_BSTR)
  358. {
  359. DEBUG_OUTPUT(LEVEL5_LOGGING, L"The variant returned from Get(Config) isn't a VT_BSTR!");
  360. hr = E_FAIL;
  361. break;
  362. }
  363. m_sbstrConfigNamingContext = var.bstrVal;
  364. DEBUG_OUTPUT(LEVEL5_LOGGING, L"ConfigNC = %s", m_sbstrConfigNamingContext);
  365. //
  366. // Get the server name that we are connected to
  367. //
  368. //
  369. // Create the path to the config naming context
  370. //
  371. CComBSTR sbstrConfigPath;
  372. sbstrConfigPath = g_bstrLDAPProvider;
  373. if (pszServerOrDomain)
  374. {
  375. sbstrConfigPath += pszServerOrDomain;
  376. sbstrConfigPath += L"/";
  377. }
  378. sbstrConfigPath += m_sbstrConfigNamingContext;
  379. //
  380. // Bind to the configuration container
  381. //
  382. CComPtr<IADsObjectOptions> spIADsObjectOptions;
  383. hr = DSCmdOpenObject(refCredentialObject,
  384. sbstrConfigPath,
  385. IID_IADsObjectOptions,
  386. (void**)&spIADsObjectOptions,
  387. false);
  388. if (FAILED(hr))
  389. {
  390. break;
  391. }
  392. //
  393. // Retrieve the server name
  394. //
  395. var.Clear();
  396. hr = spIADsObjectOptions->GetOption(ADS_OPTION_SERVERNAME, &var);
  397. if (FAILED(hr))
  398. {
  399. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to get the server name: hr = 0x%x", hr);
  400. break;
  401. }
  402. if (var.vt != VT_BSTR)
  403. {
  404. DEBUG_OUTPUT(LEVEL5_LOGGING, L"The variant returned from GetOption isn't a VT_BSTR!");
  405. hr = E_FAIL;
  406. break;
  407. }
  408. //
  409. // Store the server name
  410. //
  411. m_sbstrServerName = V_BSTR(&var);
  412. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Server name = %s", m_sbstrServerName);
  413. }
  414. //
  415. // Create the provider plus server name string
  416. //
  417. m_sbstrProviderAndServerName = g_bstrLDAPProvider;
  418. m_sbstrProviderAndServerName += m_sbstrServerName;
  419. m_sbstrGCProvider = g_bstrGCProvider;
  420. // Derived by looking at code from \admin\dsadminlib\src\basePathsInfo.cpp
  421. ComposePathFromDN(L"Schema", m_sbstrAbstractSchemaPath);
  422. m_bInitialized = true;
  423. } while (false);
  424. return hr;
  425. }
  426. //+--------------------------------------------------------------------------
  427. //
  428. // Member: CDSCmdBasePathsInfo::GetConfigurationNamingContext
  429. //
  430. // Synopsis: Returns the the DN of the Configuration container
  431. //
  432. // Arguments:
  433. //
  434. // Returns: CComBSTR : A copy of the CComBSTR containing the string
  435. //
  436. // History: 06-Sep-2000 JeffJon Created
  437. //
  438. //---------------------------------------------------------------------------
  439. CComBSTR CDSCmdBasePathsInfo::GetConfigurationNamingContext() const
  440. {
  441. ENTER_FUNCTION(LEVEL5_LOGGING, CDSCmdBasePathsInfo::GetConfigurationNamingContext);
  442. if (IsInitialized() &&
  443. !m_sbstrConfigNamingContext.Length())
  444. {
  445. //
  446. // Get the configuration naming context
  447. //
  448. CComVariant var;
  449. HRESULT hr = m_spRootDSE->Get(g_bstrConfigNCProperty, &var);
  450. if (SUCCEEDED(hr) &&
  451. var.vt == VT_BSTR)
  452. {
  453. m_sbstrConfigNamingContext = var.bstrVal;
  454. DEBUG_OUTPUT(LEVEL5_LOGGING, L"ConfigNC = %s", m_sbstrConfigNamingContext);
  455. }
  456. else
  457. {
  458. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to retrieve the ConfigNC: hr = 0x%x", hr);
  459. }
  460. }
  461. return m_sbstrConfigNamingContext;
  462. }
  463. //+--------------------------------------------------------------------------
  464. //
  465. // Member: CDSCmdBasePathsInfo::GetSchemaNamingContext
  466. //
  467. // Synopsis: Returns the the DN of the Schema container
  468. //
  469. // Arguments:
  470. //
  471. // Returns: CComBSTR : A copy of the CComBSTR containing the string
  472. //
  473. // History: 06-Sep-2000 JeffJon Created
  474. //
  475. //---------------------------------------------------------------------------
  476. CComBSTR CDSCmdBasePathsInfo::GetSchemaNamingContext() const
  477. {
  478. ENTER_FUNCTION(LEVEL5_LOGGING, CDSCmdBasePathsInfo::GetSchemaNamingContext);
  479. if (IsInitialized() &&
  480. !m_sbstrSchemaNamingContext.Length())
  481. {
  482. //
  483. // Get the schema naming context
  484. //
  485. CComVariant var;
  486. HRESULT hr = m_spRootDSE->Get(g_bstrSchemaNCProperty, &var);
  487. if (SUCCEEDED(hr) &&
  488. var.vt == VT_BSTR)
  489. {
  490. m_sbstrSchemaNamingContext = var.bstrVal;
  491. DEBUG_OUTPUT(LEVEL5_LOGGING, L"SchemaNC = %s", m_sbstrConfigNamingContext);
  492. }
  493. else
  494. {
  495. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to retrieve the SchemaNC: hr = 0x%x", hr);
  496. }
  497. }
  498. return m_sbstrSchemaNamingContext;
  499. }
  500. //+--------------------------------------------------------------------------
  501. //
  502. // Member: CDSCmdBasePathsInfo::GetDefaultNamingContext
  503. //
  504. // Synopsis: Returns the the DN of the Domain
  505. //
  506. // Arguments:
  507. //
  508. // Returns: CComBSTR : A copy of the CComBSTR containing the string
  509. //
  510. // History: 06-Sep-2000 JeffJon Created
  511. //
  512. //---------------------------------------------------------------------------
  513. CComBSTR CDSCmdBasePathsInfo::GetDefaultNamingContext() const
  514. {
  515. ENTER_FUNCTION(LEVEL5_LOGGING, CDSCmdBasePathsInfo::GetDefaultNamingContext);
  516. if (IsInitialized() &&
  517. !m_sbstrDefaultNamingContext.Length())
  518. {
  519. //
  520. // Get the schema naming context
  521. //
  522. CComVariant var;
  523. HRESULT hr = m_spRootDSE->Get(g_bstrDefaultNCProperty, &var);
  524. if (SUCCEEDED(hr) &&
  525. var.vt == VT_BSTR)
  526. {
  527. m_sbstrDefaultNamingContext = var.bstrVal;
  528. DEBUG_OUTPUT(LEVEL5_LOGGING, L"DefaultNC = %s", m_sbstrDefaultNamingContext);
  529. }
  530. else
  531. {
  532. DEBUG_OUTPUT(LEVEL5_LOGGING, L"Failed to retrieve the DefaultNC: hr = 0x%x", hr);
  533. }
  534. }
  535. return m_sbstrDefaultNamingContext;
  536. }
  537. //+--------------------------------------------------------------------------
  538. //
  539. // Member: CDSCmdBasePathsInfo::GetDomainMode
  540. //
  541. // Synopsis: Figures out if the domain is in mixed or native mode
  542. //
  543. // Arguments: [refCredObject - IN] : reference to the credential manager
  544. // [bMixedMode - OUT] : Is the domain in mixed mode?
  545. //
  546. // Returns: HRESULT : S_OK if everything succeeded
  547. // Otherwise an ADSI error
  548. //
  549. // History: 24-Oct-2000 JeffJon Created
  550. //
  551. //---------------------------------------------------------------------------
  552. HRESULT CDSCmdBasePathsInfo::GetDomainMode(const CDSCmdCredentialObject& refCredObject,
  553. bool& bMixedMode) const
  554. {
  555. ENTER_FUNCTION_HR(LEVEL5_LOGGING, CDSCmdBasePathsInfo::GetDomainMode, hr);
  556. hr = S_OK;
  557. do // false loop
  558. {
  559. if (!m_bModeInitialized)
  560. {
  561. //
  562. // Get the path to the domainDNS node
  563. //
  564. CComBSTR sbstrDomainDN;
  565. sbstrDomainDN = GetDefaultNamingContext();
  566. CComBSTR sbstrDomainPath;
  567. ComposePathFromDN(sbstrDomainDN, sbstrDomainPath);
  568. //
  569. // Open the domainDNS node
  570. //
  571. CComPtr<IADs> spADs;
  572. hr = DSCmdOpenObject(refCredObject,
  573. sbstrDomainPath,
  574. IID_IADs,
  575. (void**)&spADs,
  576. true);
  577. if (FAILED(hr))
  578. {
  579. break;
  580. }
  581. CComVariant var;
  582. hr = spADs->Get(CComBSTR(L"nTMixedDomain"), &var);
  583. if (FAILED(hr))
  584. {
  585. DEBUG_OUTPUT(MINIMAL_LOGGING,
  586. L"Failed to retrieve the domain mode: hr = 0x%x",
  587. hr);
  588. break;
  589. }
  590. if (var.vt == VT_I4)
  591. {
  592. m_bDomainMode = (var.lVal != 0);
  593. m_bModeInitialized = true;
  594. }
  595. else
  596. {
  597. DEBUG_OUTPUT(MINIMAL_LOGGING,
  598. L"Variant not an VT_I4!");
  599. m_bDomainMode = true;
  600. m_bModeInitialized = true;
  601. }
  602. }
  603. bMixedMode = m_bDomainMode;
  604. } while (false);
  605. return hr;
  606. }
  607. //+--------------------------------------------------------------------------
  608. //
  609. // Member: CDSCmdBasePathsInfo::ComposePathFromDN
  610. //
  611. // Synopsis: Appends the DN to the provider and server name
  612. //
  613. // Arguments: [pszDN - IN] : pointer to a NULL terminated wide character string
  614. // that contains the DN of the object to make the ADSI
  615. // path to
  616. // [refsbstrPath - OUT] : reference to a CComBSTR that will take
  617. // the full ADSI path
  618. // [nProviderType - OPTIONAL IN] : Option to specify which provider
  619. // to compose the path with
  620. //
  621. // Returns:
  622. //
  623. // History: 06-Sep-2000 JeffJon Created
  624. //
  625. //---------------------------------------------------------------------------
  626. void CDSCmdBasePathsInfo::ComposePathFromDN(PCWSTR pszDN,
  627. CComBSTR& refsbstrPath,
  628. DSCMD_PROVIDER_TYPE nProviderType) const
  629. {
  630. refsbstrPath.Empty();
  631. switch (nProviderType)
  632. {
  633. case DSCMD_LDAP_PROVIDER :
  634. refsbstrPath = GetProviderAndServerName();
  635. break;
  636. case DSCMD_GC_PROVIDER :
  637. refsbstrPath = GetGCProvider();
  638. break;
  639. default :
  640. ASSERT(FALSE);
  641. break;
  642. }
  643. refsbstrPath += L"/";
  644. refsbstrPath += pszDN;
  645. }
  646. //+--------------------------------------------------------------------------
  647. //
  648. // Member: CPathCracker::CPathCracker
  649. //
  650. // Synopsis: Constructor for the path cracker IADsPathname wrapper
  651. //
  652. // Arguments:
  653. //
  654. // Returns:
  655. //
  656. // History: 06-Sep-2000 JeffJon Created
  657. //
  658. //---------------------------------------------------------------------------
  659. CPathCracker::CPathCracker()
  660. {
  661. m_hrCreate = Init();
  662. }
  663. //+--------------------------------------------------------------------------
  664. //
  665. // Member: CPathCracker::Init
  666. //
  667. // Synopsis: Called by the constructor to create the IADsPathname object
  668. // and store it in the m_spIADsPathname member
  669. //
  670. // Arguments:
  671. //
  672. // Returns: HRESULT : the value returned from the CoCreateInstance
  673. //
  674. // History: 06-Sep-2000 JeffJon Created
  675. //
  676. //---------------------------------------------------------------------------
  677. HRESULT CPathCracker::Init()
  678. {
  679. //Secuirty Review:Context is inproc, this is fine.
  680. HRESULT hr = ::CoCreateInstance(CLSID_Pathname,
  681. NULL,
  682. CLSCTX_INPROC_SERVER,
  683. IID_IADsPathname,
  684. (void**)&(m_spIADsPathname));
  685. return hr;
  686. }
  687. //+--------------------------------------------------------------------------
  688. //
  689. // Member: CPathCracker::GetParentDN
  690. //
  691. // Synopsis: Simply removes the leaf part of the DN
  692. //
  693. // Arguments: [pszDN - IN] : pointer to a NULL terminated wide string that
  694. // contains the DN of the child
  695. // [refsbstrDN - OUT] : reference to a CComBSTR that is to
  696. // receive the parent DN.
  697. //
  698. // Returns: HRESULT : S_OK if successful, otherwise the value returned
  699. // from the IADsPathname methods
  700. //
  701. // History: 06-Sep-2000 JeffJon Created
  702. //
  703. //---------------------------------------------------------------------------
  704. HRESULT CPathCracker::GetParentDN(PCWSTR pszDN,
  705. CComBSTR& refsbstrDN)
  706. {
  707. ENTER_FUNCTION_HR(FULL_LOGGING, CPathCracker::GetParentDN, hr);
  708. do // false loop
  709. {
  710. //
  711. // Verify parameters
  712. //
  713. if (!pszDN)
  714. {
  715. ASSERT(pszDN);
  716. hr = E_INVALIDARG;
  717. break;
  718. }
  719. refsbstrDN.Empty();
  720. CPathCracker pathCracker;
  721. hr = pathCracker.Set((BSTR)pszDN, ADS_SETTYPE_DN);
  722. if (FAILED(hr))
  723. {
  724. break;
  725. }
  726. hr = pathCracker.RemoveLeafElement();
  727. if (FAILED(hr))
  728. {
  729. break;
  730. }
  731. hr = pathCracker.Retrieve(ADS_FORMAT_X500_DN, &refsbstrDN);
  732. } while (false);
  733. return hr;
  734. }
  735. //+--------------------------------------------------------------------------
  736. //
  737. // Member: CPathCracker::GetObjectRDNFromDN
  738. //
  739. // Synopsis: Returns the leaf part of the DN
  740. //
  741. // Arguments: [pszDN - IN] : pointer to a NULL terminated wide string that
  742. // contains the DN of the child
  743. // [refsbstrRDN - OUT] : reference to a CComBSTR that is to
  744. // receive the leaf RDN.
  745. //
  746. // Returns: HRESULT : S_OK if successful, otherwise the value returned
  747. // from the IADsPathname methods
  748. //
  749. // History: 25-Sep-2000 JeffJon Created
  750. //
  751. //---------------------------------------------------------------------------
  752. HRESULT CPathCracker::GetObjectRDNFromDN(PCWSTR pszDN,
  753. CComBSTR& refsbstrRDN)
  754. {
  755. ENTER_FUNCTION_HR(FULL_LOGGING, CPathCracker::GetObjectRDNFromDN, hr);
  756. do // false loop
  757. {
  758. //
  759. // Verify parameters
  760. //
  761. if (!pszDN)
  762. {
  763. ASSERT(pszDN);
  764. hr = E_INVALIDARG;
  765. break;
  766. }
  767. refsbstrRDN.Empty();
  768. CPathCracker pathCracker;
  769. hr = pathCracker.Set((BSTR)pszDN, ADS_SETTYPE_DN);
  770. if (FAILED(hr))
  771. {
  772. break;
  773. }
  774. hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  775. if (FAILED(hr))
  776. {
  777. break;
  778. }
  779. hr = pathCracker.Retrieve(ADS_FORMAT_LEAF, &refsbstrRDN);
  780. } while (false);
  781. return hr;
  782. }
  783. //+--------------------------------------------------------------------------
  784. //
  785. // Member: CPathCracker::GetObjectNameFromDN
  786. //
  787. // Synopsis: Returns the value of the leaf part of the DN
  788. //
  789. // Arguments: [pszDN - IN] : pointer to a NULL terminated wide string that
  790. // contains the DN of the child
  791. // [refsbstrRDN - OUT] : reference to a CComBSTR that is to
  792. // receive the leaf Name.
  793. //
  794. // Returns: HRESULT : S_OK if successful, otherwise the value returned
  795. // from the IADsPathname methods
  796. //
  797. // History: 04-Oct-2000 JeffJon Created
  798. //
  799. //---------------------------------------------------------------------------
  800. HRESULT CPathCracker::GetObjectNameFromDN(PCWSTR pszDN,
  801. CComBSTR& refsbstrRDN)
  802. {
  803. ENTER_FUNCTION_HR(FULL_LOGGING, CPathCracker::GetObjectNameFromDN, hr);
  804. do // false loop
  805. {
  806. //
  807. // Verify parameters
  808. //
  809. if (!pszDN)
  810. {
  811. ASSERT(pszDN);
  812. hr = E_INVALIDARG;
  813. break;
  814. }
  815. refsbstrRDN.Empty();
  816. CPathCracker pathCracker;
  817. hr = pathCracker.Set((BSTR)pszDN, ADS_SETTYPE_DN);
  818. if (FAILED(hr))
  819. {
  820. break;
  821. }
  822. hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  823. if (FAILED(hr))
  824. {
  825. break;
  826. }
  827. hr = pathCracker.Retrieve(ADS_FORMAT_LEAF, &refsbstrRDN);
  828. } while (false);
  829. return hr;
  830. }
  831. //+--------------------------------------------------------------------------
  832. //
  833. // Member: CPathCracker::GetDNFromPath
  834. //
  835. // Synopsis: Returns the DN when given and ADSI path
  836. //
  837. // Arguments: [pszPath - IN] : pointer to a NULL terminated wide string that
  838. // contains the ADSI path of the object
  839. // [refsbstrDN - OUT] : reference to a CComBSTR that is to
  840. // receive the DN.
  841. //
  842. // Returns: HRESULT : S_OK if successful, otherwise the value returned
  843. // from the IADsPathname methods
  844. //
  845. // History: 24-Oct-2000 JeffJon Created
  846. //
  847. //---------------------------------------------------------------------------
  848. HRESULT CPathCracker::GetDNFromPath(PCWSTR pszPath,
  849. CComBSTR& refsbstrDN)
  850. {
  851. ENTER_FUNCTION_HR(FULL_LOGGING, CPathCracker::GetDNFromPath, hr);
  852. do // false loop
  853. {
  854. //
  855. // Verify parameters
  856. //
  857. if (!pszPath)
  858. {
  859. ASSERT(pszPath);
  860. hr = E_INVALIDARG;
  861. break;
  862. }
  863. refsbstrDN.Empty();
  864. CPathCracker pathCracker;
  865. hr = pathCracker.Set((BSTR)pszPath, ADS_SETTYPE_FULL);
  866. if (FAILED(hr))
  867. {
  868. break;
  869. }
  870. hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  871. if (FAILED(hr))
  872. {
  873. break;
  874. }
  875. hr = pathCracker.Retrieve(ADS_FORMAT_X500_DN, &refsbstrDN);
  876. } while (false);
  877. return hr;
  878. }
  879. ///////////////////////////////////////////////////////////////////////////////////
  880. //+--------------------------------------------------------------------------
  881. //
  882. // Function: DSCmdOpenObject
  883. //
  884. // Synopsis: A wrapper around ADsOpenObject
  885. //
  886. // Arguments: [refCredentialObject - IN] : a reference to a credential management object
  887. // [pszPath - IN] : a pointer to a NULL terminated wide character
  888. // string that contains the ADSI path of the
  889. // object to connect to
  890. // [refIID - IN] : the interface ID of the interface to return
  891. // [ppObject - OUT] : a pointer which is to receive the interface pointer
  892. // [bBindToServer - IN] : true if the path contains a server name,
  893. // false otherwise
  894. //
  895. // Returns: HRESULT : S_OK if everything succeeded
  896. // Anything else is a failure code from an ADSI call
  897. //
  898. // History: 06-Sep-2000 JeffJon Created
  899. // 01-Apr-2002 JeffJon Added call to GetADsOpenObjectFlags to
  900. // retrieve the additional security flags
  901. // to pass to ADsOpenObject for signing/sealing.
  902. //
  903. //---------------------------------------------------------------------------
  904. HRESULT DSCmdOpenObject(const CDSCmdCredentialObject& refCredentialObject,
  905. PCWSTR pszPath,
  906. REFIID refIID,
  907. void** ppObject,
  908. bool bBindToServer)
  909. {
  910. ENTER_FUNCTION_HR(FULL_LOGGING, DSCmdOpenObject, hr);
  911. do // false loop
  912. {
  913. static DWORD additionalFlags = GetADsOpenObjectFlags();
  914. DWORD dwFlags = ADS_SECURE_AUTHENTICATION | additionalFlags;
  915. if (!pszPath ||
  916. !ppObject)
  917. {
  918. ASSERT(pszPath);
  919. ASSERT(ppObject);
  920. hr = E_INVALIDARG;
  921. break;
  922. }
  923. if (bBindToServer)
  924. {
  925. //
  926. // If we know we are connecting to a specific server and not domain in general
  927. // then pass the ADS_SERVER_BIND flag to save ADSI the trouble of figuring it out
  928. //
  929. dwFlags |= ADS_SERVER_BIND;
  930. DEBUG_OUTPUT(FULL_LOGGING, L"Using ADS_SERVER_BIND flag");
  931. }
  932. if (refCredentialObject.UsingCredentials())
  933. {
  934. DEBUG_OUTPUT(FULL_LOGGING, L"Using credentials");
  935. LPWSTR pszPasswordBuffer=NULL;
  936. hr = refCredentialObject.GetPassword(&pszPasswordBuffer);
  937. if (FAILED(hr))
  938. {
  939. DEBUG_OUTPUT(FULL_LOGGING, L"GetPassword failed: hr = 0x%x", hr);
  940. DEBUG_OUTPUT(FULL_LOGGING, L"Using NULL password.");
  941. pszPasswordBuffer = 0;
  942. }
  943. DEBUG_OUTPUT(FULL_LOGGING, L"Calling ADsOpenObject()");
  944. DEBUG_OUTPUT(FULL_LOGGING, L" path = %s", pszPath);
  945. hr = ADsOpenObject((LPWSTR)pszPath,
  946. refCredentialObject.GetUsername(),
  947. pszPasswordBuffer,
  948. dwFlags,
  949. refIID,
  950. ppObject);
  951. //
  952. // If we failed with E_INVALIDARG and we were using ADS_SERVER_BIND
  953. // try calling again without the ADS_SERVER_BIND flag. W2K did not have
  954. // this flag available until SP1.
  955. //
  956. if (hr == E_INVALIDARG &&
  957. (dwFlags & ADS_SERVER_BIND))
  958. {
  959. DEBUG_OUTPUT(FULL_LOGGING, L"ADsOpenObject failed with E_INVALIDARG, trying again without ADS_SERVER_BIND");
  960. dwFlags &= ~ADS_SERVER_BIND;
  961. hr = ADsOpenObject((LPWSTR)pszPath,
  962. refCredentialObject.GetUsername(),
  963. pszPasswordBuffer,
  964. dwFlags,
  965. refIID,
  966. ppObject);
  967. }
  968. //
  969. // Make sure to zero out the password after it is used
  970. //
  971. //Security Review:Change with SecureZeroMemory
  972. //NTRAID#NTBUG9-553640-2002/03/08-hiteshr
  973. if(pszPasswordBuffer)
  974. {
  975. SecureZeroMemory(pszPasswordBuffer, wcslen(pszPasswordBuffer+1)*sizeof(WCHAR));
  976. LocalFree(pszPasswordBuffer);
  977. }
  978. }
  979. else
  980. {
  981. DEBUG_OUTPUT(FULL_LOGGING, L"Calling ADsOpenObject()");
  982. DEBUG_OUTPUT(FULL_LOGGING, L" path = %s", pszPath);
  983. hr = ADsOpenObject((LPWSTR)pszPath,
  984. NULL,
  985. NULL,
  986. dwFlags,
  987. refIID,
  988. ppObject);
  989. //
  990. // If we failed with E_INVALIDARG and we were using ADS_SERVER_BIND
  991. // try calling again without the ADS_SERVER_BIND flag. W2K did not have
  992. // this flag available until SP1.
  993. //
  994. if (hr == E_INVALIDARG &&
  995. (dwFlags & ADS_SERVER_BIND))
  996. {
  997. DEBUG_OUTPUT(FULL_LOGGING, L"ADsOpenObject failed with E_INVALIDARG, trying again without ADS_SERVER_BIND");
  998. dwFlags &= ~ADS_SERVER_BIND;
  999. hr = ADsOpenObject((LPWSTR)pszPath,
  1000. NULL,
  1001. NULL,
  1002. dwFlags,
  1003. refIID,
  1004. ppObject);
  1005. }
  1006. }
  1007. DEBUG_OUTPUT(FULL_LOGGING, L"ADsOpenObject() return hr = 0x%x", hr);
  1008. } while (false);
  1009. return hr;
  1010. }
  1011. //+--------------------------------------------------------------------------
  1012. // Function to be used in the attribute table for evaluating the command line
  1013. // strings
  1014. //---------------------------------------------------------------------------
  1015. //+--------------------------------------------------------------------------
  1016. //
  1017. // Function: FillAttrInfoFromObjectEntry
  1018. //
  1019. // Synopsis: Fills the ADS_ATTR_INFO from the attribute table associated
  1020. // with the object entry
  1021. //
  1022. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1023. // to the object being modified
  1024. // [refBasePathsInfo - IN] : reference to an instance of the
  1025. // CDSCmdBasePathsInfo class
  1026. // [refCredentialObject - IN] : reference to an instance of the
  1027. // CDSCmdCredentialObject class
  1028. // [pObjectEntry - IN] : pointer to an entry in the object table
  1029. // that defines the object we are modifying
  1030. // [argRecord - IN] : the argument record structure from the
  1031. // parser table that corresponds to this
  1032. // attribute
  1033. // [dwAttributeIdx - IN] : index into the attribute table for the
  1034. // object in which we are setting
  1035. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1036. // which this function will fill in
  1037. //
  1038. // Returns: HRESULT : S_OK if everything succeeded
  1039. // E_OUTOFMEMORY if we failed to allocate space for the value
  1040. // E_FAIL if we failed to format the value properly
  1041. //
  1042. // History: 06-Sep-2000 JeffJon Created
  1043. //
  1044. //---------------------------------------------------------------------------
  1045. HRESULT FillAttrInfoFromObjectEntry(PCWSTR /*pszDN*/,
  1046. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1047. const CDSCmdCredentialObject& refCredentialObject,
  1048. const PDSOBJECTTABLEENTRY pObjectEntry,
  1049. const ARG_RECORD& argRecord,
  1050. DWORD dwAttributeIdx,
  1051. PADS_ATTR_INFO* ppAttr)
  1052. {
  1053. ENTER_FUNCTION_HR(LEVEL3_LOGGING, FillAttrInfoFromObjectEntry, hr);
  1054. do // false loop
  1055. {
  1056. //
  1057. // Verify Parameters
  1058. //
  1059. if (!pObjectEntry ||
  1060. !ppAttr)
  1061. {
  1062. ASSERT(pObjectEntry);
  1063. ASSERT(ppAttr);
  1064. hr = E_INVALIDARG;
  1065. break;
  1066. }
  1067. switch (argRecord.fType)
  1068. {
  1069. case ARG_TYPE_INT :
  1070. DEBUG_OUTPUT(LEVEL3_LOGGING, L"argRecord.fType = ARG_TYPE_INT");
  1071. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1072. (*ppAttr)->pADsValues = new ADSVALUE[1];
  1073. if ((*ppAttr)->pADsValues)
  1074. {
  1075. (*ppAttr)->dwNumValues = 1;
  1076. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1077. (*ppAttr)->pADsValues->Integer = argRecord.nValue;
  1078. //
  1079. // Set the attribute dirty
  1080. //
  1081. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1082. }
  1083. break;
  1084. case ARG_TYPE_STR :
  1085. DEBUG_OUTPUT(LEVEL3_LOGGING, L"argRecord.fType = ARG_TYPE_STR");
  1086. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1087. if (argRecord.strValue && argRecord.strValue[0] != L'\0')
  1088. {
  1089. //
  1090. // REVIEW_JEFFJON : this is being leaked!
  1091. //
  1092. (*ppAttr)->pADsValues = new ADSVALUE[1];
  1093. if ((*ppAttr)->pADsValues)
  1094. {
  1095. (*ppAttr)->dwNumValues = 1;
  1096. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1097. switch ((*ppAttr)->dwADsType)
  1098. {
  1099. case ADSTYPE_DN_STRING :
  1100. {
  1101. //
  1102. // Lets bind to be sure the object exists
  1103. //
  1104. CComBSTR sbstrObjPath;
  1105. refBasePathsInfo.ComposePathFromDN(argRecord.strValue, sbstrObjPath);
  1106. CComPtr<IADs> spIADs;
  1107. hr = DSCmdOpenObject(refCredentialObject,
  1108. sbstrObjPath,
  1109. IID_IADs,
  1110. (void**)&spIADs,
  1111. true);
  1112. if (FAILED(hr))
  1113. {
  1114. DEBUG_OUTPUT(LEVEL3_LOGGING, L"DN object doesn't exist. %s", argRecord.strValue);
  1115. break;
  1116. }
  1117. (*ppAttr)->pADsValues->DNString = argRecord.strValue;
  1118. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_DN_STRING = %s", argRecord.strValue);
  1119. }
  1120. break;
  1121. case ADSTYPE_CASE_EXACT_STRING :
  1122. (*ppAttr)->pADsValues->CaseExactString = argRecord.strValue;
  1123. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_CASE_EXACT_STRING = %s", argRecord.strValue);
  1124. break;
  1125. case ADSTYPE_CASE_IGNORE_STRING :
  1126. (*ppAttr)->pADsValues->CaseIgnoreString = argRecord.strValue;
  1127. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_CASE_IGNORE_STRING = %s", argRecord.strValue);
  1128. break;
  1129. case ADSTYPE_PRINTABLE_STRING :
  1130. (*ppAttr)->pADsValues->PrintableString = argRecord.strValue;
  1131. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_PRINTABLE_STRING = %s", argRecord.strValue);
  1132. break;
  1133. default :
  1134. hr = E_INVALIDARG;
  1135. break;
  1136. }
  1137. //
  1138. // Set the attribute dirty
  1139. //
  1140. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1141. }
  1142. break;
  1143. }
  1144. else
  1145. {
  1146. DEBUG_OUTPUT(LEVEL3_LOGGING, L"No value present, changing control code to ADS_ATTR_CLEAR");
  1147. //
  1148. // Clear the attribute
  1149. //
  1150. (*ppAttr)->dwControlCode = ADS_ATTR_CLEAR;
  1151. (*ppAttr)->dwNumValues = 0;
  1152. //
  1153. // Set the attribute dirty
  1154. //
  1155. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1156. }
  1157. break;
  1158. default:
  1159. hr = E_INVALIDARG;
  1160. break;
  1161. }
  1162. } while (false);
  1163. return hr;
  1164. }
  1165. //+--------------------------------------------------------------------------
  1166. //
  1167. // Function: ResetObjectPassword
  1168. //
  1169. // Synopsis: Resets the password on any object that supports the IADsUser interface
  1170. //
  1171. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1172. // to the object being modified
  1173. // [refBasePathsInfo - IN] : reference to an instance of the
  1174. // CDSCmdBasePathsInfo class
  1175. // [refCredentialObject - IN] : reference to an instance of the
  1176. // CDSCmdCredentialObject class
  1177. // [pszNewPassword - IN] : pointer to the new password to set
  1178. //
  1179. // Returns: HRESULT : S_OK if everything succeeded
  1180. // Otherwise its an ADSI failure code
  1181. //
  1182. // History: 12-Sep-2000 JeffJon Created
  1183. //
  1184. //---------------------------------------------------------------------------
  1185. HRESULT ResetObjectPassword(PCWSTR pszDN,
  1186. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1187. const CDSCmdCredentialObject& refCredentialObject,
  1188. PCWSTR pszNewPassword)
  1189. {
  1190. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ResetObjectPassword, hr);
  1191. do // false loop
  1192. {
  1193. if (!pszDN ||
  1194. !pszNewPassword)
  1195. {
  1196. ASSERT(pszDN);
  1197. ASSERT(pszNewPassword);
  1198. hr = E_INVALIDARG;
  1199. break;
  1200. }
  1201. //
  1202. // Convert the DN to a path
  1203. //
  1204. CComBSTR sbstrPath;
  1205. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  1206. //
  1207. // Bind and obtain the IADsUser interface to the user object
  1208. //
  1209. CComPtr<IADsUser> spUser;
  1210. hr = DSCmdOpenObject(refCredentialObject,
  1211. sbstrPath,
  1212. IID_IADsUser,
  1213. (void**)&spUser,
  1214. true);
  1215. if (FAILED(hr))
  1216. {
  1217. break;
  1218. }
  1219. hr = spUser->SetPassword((BSTR)pszNewPassword);
  1220. } while (false);
  1221. return hr;
  1222. }
  1223. //+--------------------------------------------------------------------------
  1224. //
  1225. // Function: ResetUserPassword
  1226. //
  1227. // Synopsis: Resets the user's password
  1228. //
  1229. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1230. // to the object being modified
  1231. // [refBasePathsInfo - IN] : reference to an instance of the
  1232. // CDSCmdBasePathsInfo class
  1233. // [refCredentialObject - IN] : reference to an instance of the
  1234. // CDSCmdCredentialObject class
  1235. // [pObjectEntry - IN] : pointer to an entry in the object table
  1236. // that defines the object we are modifying
  1237. // [argRecord - IN] : the argument record structure from the
  1238. // parser table that corresponds to this
  1239. // attribute
  1240. // [dwAttributeIdx - IN] : index into the attribute table for the
  1241. // object in which we are setting
  1242. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1243. // which this function will fill in
  1244. //
  1245. // Returns: HRESULT : S_OK if everything succeeded
  1246. // E_FAIL if we failed to format the value properly
  1247. //
  1248. // History: 11-Sep-2000 JeffJon Created
  1249. //
  1250. //---------------------------------------------------------------------------
  1251. HRESULT ResetUserPassword(PCWSTR pszDN,
  1252. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1253. const CDSCmdCredentialObject& refCredentialObject,
  1254. const PDSOBJECTTABLEENTRY pObjectEntry,
  1255. const ARG_RECORD& argRecord,
  1256. DWORD /*dwAttributeIdx*/,
  1257. PADS_ATTR_INFO* ppAttr)
  1258. {
  1259. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ResetUserPassword, hr);
  1260. do // false loop
  1261. {
  1262. //
  1263. // Verify parameters
  1264. //
  1265. if (!pszDN ||
  1266. !pObjectEntry ||
  1267. !ppAttr ||
  1268. argRecord.fType != ARG_TYPE_PASSWORD)
  1269. {
  1270. ASSERT(pszDN);
  1271. ASSERT(pObjectEntry);
  1272. ASSERT(ppAttr);
  1273. ASSERT(argRecord.fType == ARG_TYPE_PASSWORD);
  1274. hr = E_INVALIDARG;
  1275. break;
  1276. }
  1277. //
  1278. // Don't create a new index in the array of ADS_ATTR_INFO
  1279. //
  1280. *ppAttr = NULL;
  1281. ASSERT(argRecord.bDefined && argRecord.encryptedDataBlob.pbData);
  1282. //Security Review: argRecord.strValue is encrypted and needs to be
  1283. //decrypted before it is passsed to ResetObjectPassword
  1284. //NTRAID#NTBUG9-571544-2002/03/08-hiteshr
  1285. //argRecord.strValue is encrypted password, decrypt it.
  1286. LPWSTR pszDecryptedPassword = NULL;
  1287. hr = DecryptPasswordString(&argRecord.encryptedDataBlob,&pszDecryptedPassword);
  1288. if(FAILED(hr))
  1289. {
  1290. break;
  1291. }
  1292. hr = ResetObjectPassword(pszDN,
  1293. refBasePathsInfo,
  1294. refCredentialObject,
  1295. pszDecryptedPassword);
  1296. SecureZeroMemory(pszDecryptedPassword,(wcslen(pszDecryptedPassword)+1)*sizeof(WCHAR));
  1297. LocalFree(pszDecryptedPassword);
  1298. if (FAILED(hr))
  1299. {
  1300. DisplayErrorMessage(g_pszDSCommandName,
  1301. pszDN,
  1302. hr,
  1303. IDS_FAILED_SET_PASSWORD);
  1304. hr = S_FALSE;
  1305. break;
  1306. }
  1307. } while (false);
  1308. return hr;
  1309. }
  1310. //+--------------------------------------------------------------------------
  1311. //
  1312. // Function: ResetComputerAccount
  1313. //
  1314. // Synopsis: Resets the computer account
  1315. //
  1316. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1317. // to the object being modified
  1318. // [refBasePathsInfo - IN] : reference to an instance of the
  1319. // CDSCmdBasePathsInfo class
  1320. // [refCredentialObject - IN] : reference to an instance of the
  1321. // CDSCmdCredentialObject class
  1322. // [pObjectEntry - IN] : pointer to an entry in the object table
  1323. // that defines the object we are modifying
  1324. // [argRecord - IN] : the argument record structure from the
  1325. // parser table that corresponds to this
  1326. // attribute
  1327. // [dwAttributeIdx - IN] : index into the attribute table for the
  1328. // object in which we are setting
  1329. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1330. // which this function will fill in
  1331. //
  1332. // Returns: HRESULT : S_OK if everything succeeded
  1333. // Otherwise an ADSI failure code
  1334. //
  1335. // History: 12-Sep-2000 JeffJon Created
  1336. //
  1337. //---------------------------------------------------------------------------
  1338. HRESULT ResetComputerAccount(PCWSTR pszDN,
  1339. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1340. const CDSCmdCredentialObject& refCredentialObject,
  1341. const PDSOBJECTTABLEENTRY pObjectEntry,
  1342. const ARG_RECORD& argRecord,
  1343. DWORD /*dwAttributeIdx*/,
  1344. PADS_ATTR_INFO* ppAttr)
  1345. {
  1346. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ResetComputerAccount, hr);
  1347. do // false loop
  1348. {
  1349. //
  1350. // Verify parameters
  1351. //
  1352. if (!pszDN ||
  1353. !pObjectEntry ||
  1354. !ppAttr)
  1355. {
  1356. ASSERT(pszDN);
  1357. ASSERT(pObjectEntry);
  1358. ASSERT(ppAttr);
  1359. hr = E_INVALIDARG;
  1360. break;
  1361. }
  1362. //
  1363. // Don't create a new entry in the ADS_ATTR_INFO array
  1364. //
  1365. *ppAttr = NULL;
  1366. ASSERT(argRecord.bDefined && argRecord.strValue);
  1367. //
  1368. // Retrieve the samAccountName from the computer object
  1369. //
  1370. //
  1371. // Convert the DN to a path
  1372. //
  1373. CComBSTR sbstrPath;
  1374. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  1375. //
  1376. // Bind and obtain the IADsUser interface to the user object
  1377. //
  1378. CComPtr<IADs> spADs;
  1379. hr = DSCmdOpenObject(refCredentialObject,
  1380. sbstrPath,
  1381. IID_IADs,
  1382. (void**)&spADs,
  1383. true);
  1384. if (FAILED(hr))
  1385. {
  1386. break;
  1387. }
  1388. CComVariant var;
  1389. hr = spADs->Get(CComBSTR(L"samAccountName"), &var);
  1390. if (FAILED(hr))
  1391. {
  1392. break;
  1393. }
  1394. ASSERT(var.vt == VT_BSTR);
  1395. //
  1396. // The new password for the computer account is the first
  1397. // 14 characters of the samAccountName minus the '$'.
  1398. //
  1399. WCHAR pszNewPassword[15];
  1400. //Security Review:This is fine.
  1401. memset(pszNewPassword, 0, sizeof(WCHAR) * 15);
  1402. //Security Review:This is fine if var.bstrval will never exceed 14chars, if it
  1403. //does wcsncpy won't null terminate. I suggest replacing with appropriate strsafe api.
  1404. //NTRAID#NTBUG9-571780-2002/03/08-hiteshr
  1405. wcsncpy(pszNewPassword, var.bstrVal, 14); //It is always null terminated. yanggao.
  1406. PWSTR pszDollar = wcschr(pszNewPassword, L'$');
  1407. if (pszDollar)
  1408. {
  1409. *pszDollar = L'\0';
  1410. }
  1411. hr = ResetObjectPassword(pszDN,
  1412. refBasePathsInfo,
  1413. refCredentialObject,
  1414. pszNewPassword);
  1415. if (FAILED(hr))
  1416. {
  1417. DisplayErrorMessage(g_pszDSCommandName,
  1418. pszDN,
  1419. hr,
  1420. IDS_FAILED_RESET_COMPUTER);
  1421. hr = S_FALSE;
  1422. break;
  1423. }
  1424. } while (false);
  1425. return hr;
  1426. }
  1427. //+--------------------------------------------------------------------------
  1428. //
  1429. // Function: ReadUserAccountControl
  1430. //
  1431. // Synopsis: Reads the userAccountControl attribute from the object specified
  1432. // by the DN
  1433. //
  1434. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1435. // to the object being modified
  1436. // [refBasePathsInfo - IN] : reference to an instance of the
  1437. // CDSCmdBasePathsInfo class
  1438. // [refCredentialObject - IN] : reference to an instance of the
  1439. // CDSCmdCredentialObject class
  1440. // [plBits - OUT] : returns the currect userAccountControl bits
  1441. //
  1442. // Returns: HRESULT : S_OK if everything succeeded
  1443. // Otherwise an ADSI failure code
  1444. //
  1445. // History: 12-Sep-2000 JeffJon Created
  1446. //
  1447. //---------------------------------------------------------------------------
  1448. HRESULT ReadUserAccountControl(PCWSTR pszDN,
  1449. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1450. const CDSCmdCredentialObject& refCredentialObject,
  1451. long* plBits)
  1452. {
  1453. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ReadUserAccountControl, hr);
  1454. do // false loop
  1455. {
  1456. if (!pszDN ||
  1457. !plBits)
  1458. {
  1459. ASSERT(pszDN);
  1460. ASSERT(plBits);
  1461. hr = E_INVALIDARG;
  1462. break;
  1463. }
  1464. //
  1465. // Convert the DN to a path
  1466. //
  1467. CComBSTR sbstrPath;
  1468. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  1469. //
  1470. // Bind and obtain the IADsUser interface to the user object
  1471. //
  1472. CComPtr<IADs> spADs;
  1473. hr = DSCmdOpenObject(refCredentialObject,
  1474. sbstrPath,
  1475. IID_IADs,
  1476. (void**)&spADs,
  1477. true);
  1478. if (FAILED(hr))
  1479. {
  1480. break;
  1481. }
  1482. CComVariant var;
  1483. hr = spADs->Get(CComBSTR(L"userAccountControl"), &var);
  1484. if (FAILED(hr))
  1485. {
  1486. break;
  1487. }
  1488. ASSERT(var.vt == VT_I4);
  1489. *plBits = var.lVal;
  1490. } while (false);
  1491. return hr;
  1492. }
  1493. //+--------------------------------------------------------------------------
  1494. //
  1495. // Function: PasswordNotRequired
  1496. //
  1497. // Synopsis: Adds/removes the UF_PASSWD_NOTREQD bit in the
  1498. // userAccountControl attribute
  1499. //
  1500. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1501. // to the object being modified
  1502. // [refBasePathsInfo - IN] : reference to an instance of the
  1503. // CDSCmdBasePathsInfo class
  1504. // [refCredentialObject - IN] : reference to an instance of the
  1505. // CDSCmdCredentialObject class
  1506. // [pObjectEntry - IN] : pointer to an entry in the object table
  1507. // that defines the object we are modifying
  1508. // [argRecord - IN] : the argument record structure from the
  1509. // parser table that corresponds to this
  1510. // attribute
  1511. // [dwAttributeIdx - IN] : index into the attribute table for the
  1512. // object in which we are setting
  1513. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1514. // which this function will fill in
  1515. //
  1516. // Returns: HRESULT : S_OK if everything succeeded
  1517. // Otherwise an ADSI failure code
  1518. //
  1519. // History: 10-Aug-2001 JeffJon Created
  1520. //
  1521. //---------------------------------------------------------------------------
  1522. HRESULT PasswordNotRequired(PCWSTR pszDN,
  1523. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1524. const CDSCmdCredentialObject& refCredentialObject,
  1525. const PDSOBJECTTABLEENTRY pObjectEntry,
  1526. const ARG_RECORD& argRecord,
  1527. DWORD dwAttributeIdx,
  1528. PADS_ATTR_INFO* ppAttr)
  1529. {
  1530. ENTER_FUNCTION_HR(LEVEL3_LOGGING, PasswordNotRequired, hr);
  1531. do // false loop
  1532. {
  1533. //
  1534. // Verify parameters
  1535. //
  1536. if (!pszDN ||
  1537. !pObjectEntry ||
  1538. !ppAttr)
  1539. {
  1540. ASSERT(pszDN);
  1541. ASSERT(pObjectEntry);
  1542. ASSERT(ppAttr);
  1543. hr = E_INVALIDARG;
  1544. break;
  1545. }
  1546. long lUserAccountControl = 0;
  1547. //
  1548. // If the userAccountControl hasn't already been read, do so now
  1549. //
  1550. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  1551. {
  1552. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Reading user account control from object");
  1553. hr = ReadUserAccountControl(pszDN,
  1554. refBasePathsInfo,
  1555. refCredentialObject,
  1556. &lUserAccountControl);
  1557. if (FAILED(hr))
  1558. {
  1559. break;
  1560. }
  1561. //
  1562. // Mark the table entry as read
  1563. //
  1564. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  1565. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1566. (*ppAttr)->pADsValues = new ADSVALUE;
  1567. if (!(*ppAttr)->pADsValues)
  1568. {
  1569. hr = E_OUTOFMEMORY;
  1570. break;
  1571. }
  1572. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1573. (*ppAttr)->dwNumValues = 1;
  1574. }
  1575. else
  1576. {
  1577. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Using existing userAccountControl from table.");
  1578. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  1579. {
  1580. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1581. hr = E_INVALIDARG;
  1582. break;
  1583. }
  1584. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  1585. //
  1586. // Don't create a new entry in the ADS_ATTR_INFO array
  1587. //
  1588. *ppAttr = NULL;
  1589. }
  1590. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1591. if (pObjectEntry->pAttributeTable[dwAttributeIdx]->nAttributeID != NULL &&
  1592. argRecord.bDefined && argRecord.bValue)
  1593. {
  1594. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding UF_PASSWD_NOTREQD to the userAccountControl");
  1595. lUserAccountControl |= UF_PASSWD_NOTREQD;
  1596. }
  1597. else
  1598. {
  1599. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Removing UF_PASSWD_NOTREQD from the userAccountControl");
  1600. lUserAccountControl &= ~UF_PASSWD_NOTREQD;
  1601. }
  1602. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  1603. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1604. } while (false);
  1605. return hr;
  1606. }
  1607. //+--------------------------------------------------------------------------
  1608. //
  1609. // Function: DisableAccount
  1610. //
  1611. // Synopsis: Disables/Enables the account using the UF_ACCOUNTDISABLE bit in the
  1612. // userAccountControl attribute
  1613. //
  1614. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1615. // to the object being modified
  1616. // [refBasePathsInfo - IN] : reference to an instance of the
  1617. // CDSCmdBasePathsInfo class
  1618. // [refCredentialObject - IN] : reference to an instance of the
  1619. // CDSCmdCredentialObject class
  1620. // [pObjectEntry - IN] : pointer to an entry in the object table
  1621. // that defines the object we are modifying
  1622. // [argRecord - IN] : the argument record structure from the
  1623. // parser table that corresponds to this
  1624. // attribute
  1625. // [dwAttributeIdx - IN] : index into the attribute table for the
  1626. // object in which we are setting
  1627. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1628. // which this function will fill in
  1629. //
  1630. // Returns: HRESULT : S_OK if everything succeeded
  1631. // Otherwise an ADSI failure code
  1632. //
  1633. // History: 12-Sep-2000 JeffJon Created
  1634. //
  1635. //---------------------------------------------------------------------------
  1636. HRESULT DisableAccount(PCWSTR pszDN,
  1637. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1638. const CDSCmdCredentialObject& refCredentialObject,
  1639. const PDSOBJECTTABLEENTRY pObjectEntry,
  1640. const ARG_RECORD& argRecord,
  1641. DWORD dwAttributeIdx,
  1642. PADS_ATTR_INFO* ppAttr)
  1643. {
  1644. ENTER_FUNCTION_HR(LEVEL3_LOGGING, DisableAccount, hr);
  1645. do // false loop
  1646. {
  1647. //
  1648. // Verify parameters
  1649. //
  1650. if (!pszDN ||
  1651. !pObjectEntry ||
  1652. !ppAttr)
  1653. {
  1654. ASSERT(pszDN);
  1655. ASSERT(pObjectEntry);
  1656. ASSERT(ppAttr);
  1657. hr = E_INVALIDARG;
  1658. break;
  1659. }
  1660. long lUserAccountControl = 0;
  1661. //
  1662. // If the userAccountControl hasn't already been read, do so now
  1663. //
  1664. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  1665. {
  1666. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Reading user account control from object");
  1667. hr = ReadUserAccountControl(pszDN,
  1668. refBasePathsInfo,
  1669. refCredentialObject,
  1670. &lUserAccountControl);
  1671. if (FAILED(hr))
  1672. {
  1673. break;
  1674. }
  1675. //
  1676. // Mark the table entry as read
  1677. //
  1678. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  1679. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1680. (*ppAttr)->pADsValues = new ADSVALUE;
  1681. if (!(*ppAttr)->pADsValues)
  1682. {
  1683. hr = E_OUTOFMEMORY;
  1684. break;
  1685. }
  1686. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1687. (*ppAttr)->dwNumValues = 1;
  1688. }
  1689. else
  1690. {
  1691. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Using existing userAccountControl from table.");
  1692. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  1693. {
  1694. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1695. hr = E_INVALIDARG;
  1696. break;
  1697. }
  1698. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  1699. //
  1700. // Don't create a new entry in the ADS_ATTR_INFO array
  1701. //
  1702. *ppAttr = NULL;
  1703. }
  1704. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1705. if (pObjectEntry->pAttributeTable[dwAttributeIdx]->nAttributeID != NULL &&
  1706. argRecord.bDefined && argRecord.bValue)
  1707. {
  1708. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding UF_ACCOUNTDISABLE to the userAccountControl");
  1709. lUserAccountControl |= UF_ACCOUNTDISABLE;
  1710. }
  1711. else
  1712. {
  1713. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Removing UF_ACCOUNTDISABLE from the userAccountControl");
  1714. lUserAccountControl &= ~UF_ACCOUNTDISABLE;
  1715. }
  1716. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  1717. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  1718. } while (false);
  1719. return hr;
  1720. }
  1721. //+--------------------------------------------------------------------------
  1722. //
  1723. // Function: SetMustChangePwd
  1724. //
  1725. // Synopsis: Sets the pwdLastSet attribute
  1726. //
  1727. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1728. // to the object being modified
  1729. // [refBasePathsInfo - IN] : reference to an instance of the
  1730. // CDSCmdBasePathsInfo class
  1731. // [refCredentialObject - IN] : reference to an instance of the
  1732. // CDSCmdCredentialObject class
  1733. // [pObjectEntry - IN] : pointer to an entry in the object table
  1734. // that defines the object we are modifying
  1735. // [argRecord - IN] : the argument record structure from the
  1736. // parser table that corresponds to this
  1737. // attribute
  1738. // [dwAttributeIdx - IN] : index into the attribute table for the
  1739. // object in which we are setting
  1740. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1741. // which this function will fill in
  1742. //
  1743. // Returns: HRESULT : S_OK if everything succeeded
  1744. // Otherwise an ADSI failure code
  1745. //
  1746. // History: 15-Sep-2000 JeffJon Created
  1747. //
  1748. //---------------------------------------------------------------------------
  1749. HRESULT SetMustChangePwd(PCWSTR pszDN,
  1750. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  1751. const CDSCmdCredentialObject& /*refCredentialObject*/,
  1752. const PDSOBJECTTABLEENTRY pObjectEntry,
  1753. const ARG_RECORD& argRecord,
  1754. DWORD dwAttributeIdx,
  1755. PADS_ATTR_INFO* ppAttr)
  1756. {
  1757. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetMustChangePwd, hr);
  1758. do // false loop
  1759. {
  1760. //
  1761. // Verify parameters
  1762. //
  1763. if (!pszDN ||
  1764. !pObjectEntry ||
  1765. !ppAttr)
  1766. {
  1767. ASSERT(pszDN);
  1768. ASSERT(pObjectEntry);
  1769. ASSERT(ppAttr);
  1770. hr = E_INVALIDARG;
  1771. break;
  1772. }
  1773. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1774. //
  1775. // REVIEW_JEFFJON : this is being leaked!
  1776. //
  1777. (*ppAttr)->pADsValues = new ADSVALUE;
  1778. if ((*ppAttr)->pADsValues)
  1779. {
  1780. (*ppAttr)->dwNumValues = 1;
  1781. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1782. if (argRecord.bValue)
  1783. {
  1784. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0;
  1785. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0;
  1786. }
  1787. else
  1788. {
  1789. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0xffffffff;
  1790. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0xffffffff;
  1791. }
  1792. }
  1793. } while (false);
  1794. return hr;
  1795. }
  1796. //+--------------------------------------------------------------------------
  1797. //
  1798. // Function: ChangeMustChangePwd
  1799. //
  1800. // Synopsis: Sets the pwdLastSet attribute
  1801. //
  1802. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1803. // to the object being modified
  1804. // [refBasePathsInfo - IN] : reference to an instance of the
  1805. // CDSCmdBasePathsInfo class
  1806. // [refCredentialObject - IN] : reference to an instance of the
  1807. // CDSCmdCredentialObject class
  1808. // [pObjectEntry - IN] : pointer to an entry in the object table
  1809. // that defines the object we are modifying
  1810. // [argRecord - IN] : the argument record structure from the
  1811. // parser table that corresponds to this
  1812. // attribute
  1813. // [dwAttributeIdx - IN] : index into the attribute table for the
  1814. // object in which we are setting
  1815. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1816. // which this function will fill in
  1817. //
  1818. // Returns: HRESULT : S_OK if everything succeeded
  1819. // Otherwise an ADSI failure code
  1820. //
  1821. // History: 15-Sep-2000 JeffJon Created
  1822. //
  1823. //---------------------------------------------------------------------------
  1824. HRESULT ChangeMustChangePwd(PCWSTR pszDN,
  1825. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1826. const CDSCmdCredentialObject& refCredentialObject,
  1827. const PDSOBJECTTABLEENTRY pObjectEntry,
  1828. const ARG_RECORD& argRecord,
  1829. DWORD dwAttributeIdx,
  1830. PADS_ATTR_INFO* ppAttr)
  1831. {
  1832. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ChangeMustChangePwd, hr);
  1833. do // false loop
  1834. {
  1835. //
  1836. // Verify parameters
  1837. //
  1838. if (!pszDN ||
  1839. !pObjectEntry ||
  1840. !ppAttr)
  1841. {
  1842. ASSERT(pszDN);
  1843. ASSERT(pObjectEntry);
  1844. ASSERT(ppAttr);
  1845. hr = E_INVALIDARG;
  1846. break;
  1847. }
  1848. //
  1849. // We will assume they can change their password unless we discover otherwise
  1850. //
  1851. bool bCanChangePassword = true;
  1852. hr = EvaluateCanChangePasswordAces(pszDN,
  1853. refBasePathsInfo,
  1854. refCredentialObject,
  1855. bCanChangePassword);
  1856. if (FAILED(hr))
  1857. {
  1858. DEBUG_OUTPUT(LEVEL5_LOGGING,
  1859. L"EvaluateCanChangePasswordAces failed: hr = 0x%x",
  1860. hr);
  1861. ASSERT(false);
  1862. }
  1863. if (!bCanChangePassword && argRecord.bValue)
  1864. {
  1865. DEBUG_OUTPUT(LEVEL5_LOGGING,
  1866. L"Cannot have must change password and cannot change password");
  1867. DisplayErrorMessage(g_pszDSCommandName, pszDN, S_OK, IDS_MUSTCHPWD_CANCHPWD_CONFLICT);
  1868. *ppAttr = NULL;
  1869. hr = S_FALSE;
  1870. break;
  1871. }
  1872. else
  1873. {
  1874. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1875. }
  1876. //
  1877. // REVIEW_JEFFJON : this is being leaked!
  1878. //
  1879. (*ppAttr)->pADsValues = new ADSVALUE;
  1880. if ((*ppAttr)->pADsValues)
  1881. {
  1882. (*ppAttr)->dwNumValues = 1;
  1883. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1884. if (argRecord.bValue)
  1885. {
  1886. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0;
  1887. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0;
  1888. }
  1889. else
  1890. {
  1891. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0xffffffff;
  1892. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0xffffffff;
  1893. }
  1894. }
  1895. } while (false);
  1896. return hr;
  1897. }
  1898. //+--------------------------------------------------------------------------
  1899. //
  1900. // Function: PwdNeverExpires
  1901. //
  1902. // Synopsis: Sets the UF_DONT_EXPIRE_PASSWD bit in the
  1903. // userAccountControl attribute
  1904. //
  1905. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  1906. // to the object being modified
  1907. // [refBasePathsInfo - IN] : reference to an instance of the
  1908. // CDSCmdBasePathsInfo class
  1909. // [refCredentialObject - IN] : reference to an instance of the
  1910. // CDSCmdCredentialObject class
  1911. // [pObjectEntry - IN] : pointer to an entry in the object table
  1912. // that defines the object we are modifying
  1913. // [argRecord - IN] : the argument record structure from the
  1914. // parser table that corresponds to this
  1915. // attribute
  1916. // [dwAttributeIdx - IN] : index into the attribute table for the
  1917. // object in which we are setting
  1918. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  1919. // which this function will fill in
  1920. //
  1921. // Returns: HRESULT : S_OK if everything succeeded
  1922. // Otherwise an ADSI failure code
  1923. //
  1924. // History: 15-Sep-2000 JeffJon Created
  1925. //
  1926. //---------------------------------------------------------------------------
  1927. HRESULT PwdNeverExpires(PCWSTR pszDN,
  1928. const CDSCmdBasePathsInfo& refBasePathsInfo,
  1929. const CDSCmdCredentialObject& refCredentialObject,
  1930. const PDSOBJECTTABLEENTRY pObjectEntry,
  1931. const ARG_RECORD& argRecord,
  1932. DWORD dwAttributeIdx,
  1933. PADS_ATTR_INFO* ppAttr)
  1934. {
  1935. ENTER_FUNCTION_HR(LEVEL3_LOGGING, PwdNeverExpires, hr);
  1936. do // false loop
  1937. {
  1938. //
  1939. // Verify parameters
  1940. //
  1941. if (!pszDN ||
  1942. !pObjectEntry ||
  1943. !ppAttr)
  1944. {
  1945. ASSERT(pszDN);
  1946. ASSERT(pObjectEntry);
  1947. ASSERT(ppAttr);
  1948. hr = E_INVALIDARG;
  1949. break;
  1950. }
  1951. long lUserAccountControl = 0;
  1952. //
  1953. // If the userAccountControl hasn't already been read, do so now
  1954. //
  1955. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  1956. {
  1957. hr = ReadUserAccountControl(pszDN,
  1958. refBasePathsInfo,
  1959. refCredentialObject,
  1960. &lUserAccountControl);
  1961. if (FAILED(hr))
  1962. {
  1963. break;
  1964. }
  1965. //
  1966. // Mark the table entry as read
  1967. //
  1968. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  1969. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  1970. (*ppAttr)->pADsValues = new ADSVALUE;
  1971. if (!(*ppAttr)->pADsValues)
  1972. {
  1973. hr = E_OUTOFMEMORY;
  1974. break;
  1975. }
  1976. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  1977. (*ppAttr)->dwNumValues = 1;
  1978. }
  1979. else
  1980. {
  1981. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  1982. {
  1983. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1984. hr = E_INVALIDARG;
  1985. break;
  1986. }
  1987. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  1988. //
  1989. // Don't create a new entry in the ADS_ATTR_INFO array
  1990. //
  1991. *ppAttr = NULL;
  1992. }
  1993. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  1994. if (argRecord.bValue)
  1995. {
  1996. lUserAccountControl |= UF_DONT_EXPIRE_PASSWD;
  1997. }
  1998. else
  1999. {
  2000. lUserAccountControl &= ~UF_DONT_EXPIRE_PASSWD;
  2001. }
  2002. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  2003. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  2004. } while (false);
  2005. return hr;
  2006. }
  2007. //+--------------------------------------------------------------------------
  2008. //
  2009. // Function: ReversiblePwd
  2010. //
  2011. // Synopsis: Sets the UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED bit in the
  2012. // userAccountControl attribute
  2013. //
  2014. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  2015. // to the object being modified
  2016. // [refBasePathsInfo - IN] : reference to an instance of the
  2017. // CDSCmdBasePathsInfo class
  2018. // [refCredentialObject - IN] : reference to an instance of the
  2019. // CDSCmdCredentialObject class
  2020. // [pObjectEntry - IN] : pointer to an entry in the object table
  2021. // that defines the object we are modifying
  2022. // [argRecord - IN] : the argument record structure from the
  2023. // parser table that corresponds to this
  2024. // attribute
  2025. // [dwAttributeIdx - IN] : index into the attribute table for the
  2026. // object in which we are setting
  2027. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  2028. // which this function will fill in
  2029. //
  2030. // Returns: HRESULT : S_OK if everything succeeded
  2031. // Otherwise an ADSI failure code
  2032. //
  2033. // History: 15-Sep-2000 JeffJon Created
  2034. //
  2035. //---------------------------------------------------------------------------
  2036. HRESULT ReversiblePwd(PCWSTR pszDN,
  2037. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2038. const CDSCmdCredentialObject& refCredentialObject,
  2039. const PDSOBJECTTABLEENTRY pObjectEntry,
  2040. const ARG_RECORD& argRecord,
  2041. DWORD dwAttributeIdx,
  2042. PADS_ATTR_INFO* ppAttr)
  2043. {
  2044. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ReversiblePwd, hr);
  2045. do // false loop
  2046. {
  2047. //
  2048. // Verify parameters
  2049. //
  2050. if (!pszDN ||
  2051. !pObjectEntry ||
  2052. !ppAttr)
  2053. {
  2054. ASSERT(pszDN);
  2055. ASSERT(pObjectEntry);
  2056. ASSERT(ppAttr);
  2057. hr = E_INVALIDARG;
  2058. break;
  2059. }
  2060. long lUserAccountControl = 0;
  2061. //
  2062. // If the userAccountControl hasn't already been read, do so now
  2063. //
  2064. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  2065. {
  2066. hr = ReadUserAccountControl(pszDN,
  2067. refBasePathsInfo,
  2068. refCredentialObject,
  2069. &lUserAccountControl);
  2070. if (FAILED(hr))
  2071. {
  2072. break;
  2073. }
  2074. //
  2075. // Mark the table entry as read
  2076. //
  2077. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  2078. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  2079. (*ppAttr)->pADsValues = new ADSVALUE;
  2080. if (!(*ppAttr)->pADsValues)
  2081. {
  2082. hr = E_OUTOFMEMORY;
  2083. break;
  2084. }
  2085. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  2086. (*ppAttr)->dwNumValues = 1;
  2087. }
  2088. else
  2089. {
  2090. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  2091. {
  2092. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  2093. hr = E_INVALIDARG;
  2094. break;
  2095. }
  2096. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  2097. //
  2098. // Don't create a new entry in the ADS_ATTR_INFO array
  2099. //
  2100. *ppAttr = NULL;
  2101. }
  2102. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  2103. if (argRecord.bValue)
  2104. {
  2105. lUserAccountControl |= UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED;
  2106. }
  2107. else
  2108. {
  2109. lUserAccountControl &= ~UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED;
  2110. }
  2111. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  2112. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  2113. } while (false);
  2114. return hr;
  2115. }
  2116. //+--------------------------------------------------------------------------
  2117. //
  2118. // Function: AccountExpires
  2119. //
  2120. // Synopsis: Sets in how many days the account will expire
  2121. //
  2122. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  2123. // to the object being modified
  2124. // [refBasePathsInfo - IN] : reference to an instance of the
  2125. // CDSCmdBasePathsInfo class
  2126. // [refCredentialObject - IN] : reference to an instance of the
  2127. // CDSCmdCredentialObject class
  2128. // [pObjectEntry - IN] : pointer to an entry in the object table
  2129. // that defines the object we are modifying
  2130. // [argRecord - IN] : the argument record structure from the
  2131. // parser table that corresponds to this
  2132. // attribute
  2133. // [dwAttributeIdx - IN] : index into the attribute table for the
  2134. // object in which we are setting
  2135. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  2136. // which this function will fill in
  2137. //
  2138. // Returns: HRESULT : S_OK if everything succeeded
  2139. // Otherwise an ADSI failure code
  2140. //
  2141. // History: 15-Sep-2000 JeffJon Created
  2142. //
  2143. //---------------------------------------------------------------------------
  2144. const unsigned long DSCMD_FILETIMES_PER_MILLISECOND = 10000;
  2145. const DWORD DSCMD_FILETIMES_PER_SECOND = 1000 * DSCMD_FILETIMES_PER_MILLISECOND;
  2146. const DWORD DSCMD_FILETIMES_PER_MINUTE = 60 * DSCMD_FILETIMES_PER_SECOND;
  2147. const __int64 DSCMD_FILETIMES_PER_HOUR = 60 * (__int64)DSCMD_FILETIMES_PER_MINUTE;
  2148. const __int64 DSCMD_FILETIMES_PER_DAY = 24 * DSCMD_FILETIMES_PER_HOUR;
  2149. const __int64 DSCMD_FILETIMES_PER_MONTH= 30 * DSCMD_FILETIMES_PER_DAY;
  2150. HRESULT AccountExpires(PCWSTR pszDN,
  2151. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  2152. const CDSCmdCredentialObject& /*refCredentialObject*/,
  2153. const PDSOBJECTTABLEENTRY pObjectEntry,
  2154. const ARG_RECORD& argRecord,
  2155. DWORD dwAttributeIdx,
  2156. PADS_ATTR_INFO* ppAttr)
  2157. {
  2158. ENTER_FUNCTION_HR(LEVEL3_LOGGING, AccountExpires, hr);
  2159. do // false loop
  2160. {
  2161. //
  2162. // Verify parameters
  2163. //
  2164. if (!pszDN ||
  2165. !pObjectEntry ||
  2166. !ppAttr)
  2167. {
  2168. ASSERT(pszDN);
  2169. ASSERT(pObjectEntry);
  2170. ASSERT(ppAttr);
  2171. hr = E_INVALIDARG;
  2172. break;
  2173. }
  2174. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  2175. //
  2176. // REVIEW_JEFFJON : this is being leaked
  2177. //
  2178. (*ppAttr)->pADsValues = new ADSVALUE;
  2179. if (!(*ppAttr)->pADsValues)
  2180. {
  2181. hr = E_OUTOFMEMORY;
  2182. break;
  2183. }
  2184. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  2185. (*ppAttr)->dwNumValues = 1;
  2186. //
  2187. // Note: the table entry for this attribute is ARG_TYPE_INTSTR but the parser
  2188. // will change it to ARG_TYPE_INT if the value starts with digits. If not then
  2189. // the parser will change the type to ARG_TYPE_STR
  2190. //
  2191. if (argRecord.fType == ARG_TYPE_INT)
  2192. {
  2193. //
  2194. // Get the system time and then add the number of days until the account expires
  2195. //
  2196. FILETIME currentFT = {0};
  2197. ::GetSystemTimeAsFileTime(&currentFT);
  2198. LARGE_INTEGER liExpires;
  2199. liExpires.LowPart = currentFT.dwLowDateTime;
  2200. liExpires.HighPart = currentFT.dwHighDateTime;
  2201. //
  2202. // If the value passed in is zero then add one to the day because it
  2203. // is really the start of the next day that the account gets
  2204. // disabled
  2205. //
  2206. __int64 days = argRecord.nValue;
  2207. if (argRecord.nValue == 0)
  2208. {
  2209. days = argRecord.nValue + 1;
  2210. }
  2211. __int64 nanosecs = days * DSCMD_FILETIMES_PER_DAY;
  2212. (*ppAttr)->pADsValues->LargeInteger.QuadPart = liExpires.QuadPart + nanosecs;
  2213. }
  2214. else if (argRecord.fType == ARG_TYPE_STR)
  2215. {
  2216. CComBSTR sbstrStrValue = argRecord.strValue;
  2217. sbstrStrValue.ToLower();
  2218. //Security Review:This is fine. Right string is fixed length.
  2219. if (0 == _wcsicmp(sbstrStrValue, g_bstrNever))
  2220. {
  2221. //
  2222. // Zero signifies that the account never expires
  2223. //
  2224. (*ppAttr)->pADsValues->LargeInteger.HighPart = 0;
  2225. (*ppAttr)->pADsValues->LargeInteger.LowPart = 0;
  2226. }
  2227. else
  2228. {
  2229. hr = E_INVALIDARG;
  2230. break;
  2231. }
  2232. }
  2233. //
  2234. // Mark the attribute as dirty
  2235. //
  2236. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  2237. } while (false);
  2238. return hr;
  2239. }
  2240. //+--------------------------------------------------------------------------
  2241. //
  2242. // Function: EvaluateMustChangePassword
  2243. //
  2244. // Synopsis: Determines whether the user must change their password at next logon
  2245. //
  2246. // Arguments: [pszDN - IN] : DN of the object to check
  2247. // [refBasePathsInfo - IN] : reference to the base paths info
  2248. // [refCredentialObject - IN] : reference to the credential manangement object
  2249. // [bMustChangePassword - OUT] : true if the user must change their
  2250. // password at next logon, false otherwise
  2251. //
  2252. // Returns: HRESULT : S_OK if everything succeeded
  2253. // Otherwise an ADSI failure code
  2254. //
  2255. // History: 27-Oct-2000 JeffJon Created
  2256. //
  2257. //---------------------------------------------------------------------------
  2258. HRESULT EvaluateMustChangePassword(PCWSTR pszDN,
  2259. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2260. const CDSCmdCredentialObject& refCredentialObject,
  2261. bool& bMustChangePassword)
  2262. {
  2263. ENTER_FUNCTION_HR(LEVEL5_LOGGING, EvaluateMustChangePassword, hr);
  2264. do // false loop
  2265. {
  2266. //
  2267. // Validate parameters
  2268. //
  2269. if (!pszDN)
  2270. {
  2271. ASSERT(pszDN);
  2272. hr = E_INVALIDARG;
  2273. break;
  2274. }
  2275. bMustChangePassword = false;
  2276. //
  2277. // Compose the path
  2278. //
  2279. CComBSTR sbstrPath;
  2280. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  2281. //
  2282. // Open the object
  2283. //
  2284. CComPtr<IDirectoryObject> spDirObject;
  2285. hr = DSCmdOpenObject(refCredentialObject,
  2286. sbstrPath,
  2287. IID_IDirectoryObject,
  2288. (void**)&spDirObject,
  2289. true);
  2290. if (FAILED(hr))
  2291. {
  2292. break;
  2293. }
  2294. static const DWORD dwAttrCount = 1;
  2295. PWSTR pszAttrs[] = { L"pwdLastSet" };
  2296. PADS_ATTR_INFO pAttrInfo = NULL;
  2297. DWORD dwAttrsReturned = 0;
  2298. hr = spDirObject->GetObjectAttributes(pszAttrs,
  2299. dwAttrCount,
  2300. &pAttrInfo,
  2301. &dwAttrsReturned);
  2302. if (FAILED(hr))
  2303. {
  2304. DEBUG_OUTPUT(MINIMAL_LOGGING,
  2305. L"GetObjectAttributes for pwdLastSet failed: hr = 0x%x",
  2306. hr);
  2307. break;
  2308. }
  2309. if (pAttrInfo && dwAttrsReturned && pAttrInfo->dwNumValues)
  2310. {
  2311. if (pAttrInfo->pADsValues->LargeInteger.HighPart == 0 &&
  2312. pAttrInfo->pADsValues->LargeInteger.LowPart == 0)
  2313. {
  2314. DEBUG_OUTPUT(LEVEL5_LOGGING, L"User must change password at next logon");
  2315. bMustChangePassword = true;
  2316. }
  2317. }
  2318. } while (false);
  2319. return hr;
  2320. }
  2321. //+--------------------------------------------------------------------------
  2322. //
  2323. // Function: EvaluateCanChangePasswordAces
  2324. //
  2325. // Synopsis: Looks for explicit entries in the ACL to see if the user can
  2326. // change their password
  2327. //
  2328. // Arguments: [pszDN - IN] : DN of the object to check
  2329. // [refBasePathsInfo - IN] : reference to the base paths info
  2330. // [refCredentialObject - IN] : reference to the credential manangement object
  2331. // [bCanChangePassword - OUT] : false if there are explicit entries
  2332. // that keep the user from changing their
  2333. // password. true otherwise.
  2334. //
  2335. // Returns: HRESULT : S_OK if everything succeeded
  2336. // Otherwise an ADSI failure code
  2337. //
  2338. // History: 27-Oct-2000 JeffJon Created
  2339. //
  2340. //---------------------------------------------------------------------------
  2341. HRESULT EvaluateCanChangePasswordAces(PCWSTR pszDN,
  2342. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2343. const CDSCmdCredentialObject& refCredentialObject,
  2344. bool& bCanChangePassword)
  2345. {
  2346. //SECURITY_REVIEW: Impelmentation of this function is not correct and can be improved
  2347. //using authz apis. NTRAID#NTBUG9-571799-2002/03/08-hiteshr
  2348. ENTER_FUNCTION_HR(LEVEL5_LOGGING, EvaluateCanChangePasswordAces, hr);
  2349. do // false loop
  2350. {
  2351. //
  2352. // Validate parameters
  2353. //
  2354. if (!pszDN)
  2355. {
  2356. ASSERT(pszDN);
  2357. hr = E_INVALIDARG;
  2358. break;
  2359. }
  2360. //
  2361. // Compose the path
  2362. //
  2363. CComBSTR sbstrPath;
  2364. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  2365. //
  2366. // Open the object
  2367. //
  2368. CComPtr<IDirectoryObject> spDirObject;
  2369. hr = DSCmdOpenObject(refCredentialObject,
  2370. sbstrPath,
  2371. IID_IDirectoryObject,
  2372. (void**)&spDirObject,
  2373. true);
  2374. if (FAILED(hr))
  2375. {
  2376. break;
  2377. }
  2378. SECURITY_DESCRIPTOR_CONTROL sdControl = {0};
  2379. CSimpleAclHolder Dacl;
  2380. hr = DSReadObjectSecurity(spDirObject,
  2381. &sdControl,
  2382. &(Dacl.m_pAcl));
  2383. if (FAILED(hr))
  2384. {
  2385. break;
  2386. }
  2387. //
  2388. // Create and Initialize the Self and World SIDs
  2389. //
  2390. CSidHolder selfSid;
  2391. CSidHolder worldSid;
  2392. PSID pSid = NULL;
  2393. SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY,
  2394. WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
  2395. //Security Review:this is fine.
  2396. if (!AllocateAndInitializeSid(&NtAuth,
  2397. 1,
  2398. SECURITY_PRINCIPAL_SELF_RID,
  2399. 0, 0, 0, 0, 0, 0, 0,
  2400. &pSid))
  2401. {
  2402. DWORD _dwErr = GetLastError();
  2403. hr = HRESULT_FROM_WIN32( _dwErr );
  2404. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate self SID: hr = 0x%x", hr);
  2405. break;
  2406. }
  2407. selfSid.Attach(pSid, false);
  2408. pSid = NULL;
  2409. //Security Review:Should Everyone be replaced by Authenticated Users?
  2410. if (!AllocateAndInitializeSid(&WorldAuth,
  2411. 1,
  2412. SECURITY_WORLD_RID,
  2413. 0, 0, 0, 0, 0, 0, 0,
  2414. &pSid))
  2415. {
  2416. DWORD _dwErr = GetLastError();
  2417. hr = HRESULT_FROM_WIN32(_dwErr);
  2418. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate world SID: hr = 0x%x", hr);
  2419. break;
  2420. }
  2421. worldSid.Attach(pSid, false);
  2422. pSid = NULL;
  2423. ULONG ulCount = 0, j = 0;
  2424. PEXPLICIT_ACCESS rgEntries = NULL;
  2425. DWORD dwErr = GetExplicitEntriesFromAcl(Dacl.m_pAcl, &ulCount, &rgEntries);
  2426. if (ERROR_SUCCESS != dwErr)
  2427. {
  2428. hr = HRESULT_FROM_WIN32(dwErr);
  2429. DEBUG_OUTPUT(LEVEL3_LOGGING, L"GetExplicitEntriesFromAcl failed: hr = 0x%x", hr);
  2430. break;
  2431. }
  2432. //
  2433. // Are these ACEs already present?
  2434. //
  2435. bool bSelfAllowPresent = false;
  2436. bool bWorldAllowPresent = false;
  2437. bool bSelfDenyPresent = false;
  2438. bool bWorldDenyPresent = false;
  2439. //
  2440. // Loop through looking for the can change password ACE for self and world
  2441. //
  2442. for (j = 0; j < ulCount; j++)
  2443. {
  2444. //
  2445. // Look for deny ACEs
  2446. //
  2447. if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  2448. (rgEntries[j].grfAccessMode == DENY_ACCESS))
  2449. {
  2450. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  2451. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  2452. //
  2453. // Look for the user can change password ACE
  2454. //
  2455. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  2456. GUID_CONTROL_UserChangePassword))
  2457. {
  2458. //
  2459. // See if it is for the self SID or the world SID
  2460. //
  2461. //Security Review:This is fine.RHS sid is coming from
  2462. //AllocateAndInitializeSid while LHS is coming from ACE
  2463. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  2464. {
  2465. //
  2466. // Deny self found
  2467. //
  2468. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Deny self found at rgEntries[%d]", j);
  2469. bSelfDenyPresent = true;
  2470. break;
  2471. }
  2472. //Security Review:This is fine.RHS sid is coming from
  2473. //AllocateAndInitializeSid while LHS is coming from ACE
  2474. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  2475. {
  2476. //
  2477. // Deny world found
  2478. //
  2479. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Deny world found at rgEntries[%d]", j);
  2480. bWorldDenyPresent = true;
  2481. break;
  2482. }
  2483. }
  2484. }
  2485. //
  2486. // Look for allow ACEs
  2487. //
  2488. else if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  2489. (rgEntries[j].grfAccessMode == GRANT_ACCESS))
  2490. {
  2491. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  2492. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  2493. //
  2494. // Look for the user can change password ACE
  2495. //
  2496. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  2497. GUID_CONTROL_UserChangePassword))
  2498. {
  2499. //
  2500. // See if it is for the self SID or the world SID
  2501. //
  2502. //Security Review:This is fine.RHS sid is coming from
  2503. //AllocateAndInitializeSid while LHS is coming from ACE
  2504. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  2505. {
  2506. //
  2507. // Allow self found
  2508. //
  2509. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Allow self found at rgEntries[%d]", j);
  2510. bSelfAllowPresent = true;
  2511. break;
  2512. }
  2513. //Security Review:This is fine.RHS sid is coming from
  2514. //AllocateAndInitializeSid while LHS is coming from ACE
  2515. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  2516. {
  2517. //
  2518. // Allow world found
  2519. //
  2520. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Allow world found at rgEntries[%d]", j);
  2521. bWorldAllowPresent = true;
  2522. break;
  2523. }
  2524. }
  2525. }
  2526. }
  2527. if (bSelfDenyPresent || bWorldDenyPresent)
  2528. {
  2529. //
  2530. // There is an explicit deny so we know that the user cannot change password
  2531. //
  2532. bCanChangePassword = false;
  2533. }
  2534. else if ((!bSelfDenyPresent && !bWorldDenyPresent) &&
  2535. (bSelfAllowPresent || bWorldAllowPresent))
  2536. {
  2537. //
  2538. // There is no explicit deny but there are explicit allows so we know that
  2539. // the user can change password
  2540. //
  2541. bCanChangePassword = true;
  2542. }
  2543. else
  2544. {
  2545. //
  2546. // We are not sure because the explicit entries are not telling us for
  2547. // certain so it all depends on inheritence. Most likely they will
  2548. // be able to change their password unless the admin has changed something
  2549. // higher up or through group membership
  2550. //
  2551. bCanChangePassword = true;
  2552. }
  2553. } while(false);
  2554. return hr;
  2555. }
  2556. //+--------------------------------------------------------------------------
  2557. //
  2558. // Function: ChangeCanChangePassword
  2559. //
  2560. // Synopsis: Sets or removes the Deny Ace on the can change password ACL
  2561. //
  2562. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  2563. // to the object being modified
  2564. // [refBasePathsInfo - IN] : reference to an instance of the
  2565. // CDSCmdBasePathsInfo class
  2566. // [refCredentialObject - IN] : reference to an instance of the
  2567. // CDSCmdCredentialObject class
  2568. // [pObjectEntry - IN] : pointer to an entry in the object table
  2569. // that defines the object we are modifying
  2570. // [argRecord - IN] : the argument record structure from the
  2571. // parser table that corresponds to this
  2572. // attribute
  2573. // [dwAttributeIdx - IN] : index into the attribute table for the
  2574. // object in which we are setting
  2575. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  2576. // which this function will fill in
  2577. //
  2578. // Returns: HRESULT : S_OK if everything succeeded
  2579. // Otherwise an ADSI failure code
  2580. //
  2581. // History: 15-Sep-2000 JeffJon Created
  2582. //
  2583. //---------------------------------------------------------------------------
  2584. HRESULT ChangeCanChangePassword(PCWSTR pszDN,
  2585. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2586. const CDSCmdCredentialObject& refCredentialObject,
  2587. const PDSOBJECTTABLEENTRY pObjectEntry,
  2588. const ARG_RECORD& argRecord,
  2589. DWORD dwAttributeIdx,
  2590. PADS_ATTR_INFO* ppAttr)
  2591. {
  2592. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ChangeCanChangePassword, hr);
  2593. do // false loop
  2594. {
  2595. //
  2596. // Verify parameters
  2597. //
  2598. if (!pszDN ||
  2599. !pObjectEntry ||
  2600. !ppAttr)
  2601. {
  2602. ASSERT(pszDN);
  2603. ASSERT(pObjectEntry);
  2604. ASSERT(ppAttr);
  2605. hr = E_INVALIDARG;
  2606. break;
  2607. }
  2608. *ppAttr = NULL;
  2609. //
  2610. // Read the userAccountControl to make sure we don't have
  2611. // the user must change password bit set
  2612. //
  2613. bool bMustChangePassword = false;
  2614. hr = EvaluateMustChangePassword(pszDN,
  2615. refBasePathsInfo,
  2616. refCredentialObject,
  2617. bMustChangePassword);
  2618. if (FAILED(hr))
  2619. {
  2620. //
  2621. // Lets log it but continue on as if everything was OK
  2622. //
  2623. DEBUG_OUTPUT(LEVEL5_LOGGING,
  2624. L"EvaluateMustChangePassword failed: hr = 0x%x",
  2625. hr);
  2626. }
  2627. if (bMustChangePassword && !argRecord.bValue)
  2628. {
  2629. DEBUG_OUTPUT(LEVEL5_LOGGING,
  2630. L"Cannot have must change password and cannot change password");
  2631. DisplayErrorMessage(g_pszDSCommandName, pszDN, S_OK, IDS_MUSTCHPWD_CANCHPWD_CONFLICT);
  2632. *ppAttr = NULL;
  2633. hr = S_FALSE;
  2634. break;
  2635. }
  2636. hr = SetCanChangePassword(pszDN,
  2637. refBasePathsInfo,
  2638. refCredentialObject,
  2639. pObjectEntry,
  2640. argRecord,
  2641. dwAttributeIdx,
  2642. ppAttr);
  2643. } while (false);
  2644. return hr;
  2645. }
  2646. //+--------------------------------------------------------------------------
  2647. //
  2648. // Function: SetCanChangePassword
  2649. //
  2650. // Synopsis: Sets or removes the Deny Ace on the can change password ACL
  2651. //
  2652. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  2653. // to the object being modified
  2654. // [refBasePathsInfo - IN] : reference to an instance of the
  2655. // CDSCmdBasePathsInfo class
  2656. // [refCredentialObject - IN] : reference to an instance of the
  2657. // CDSCmdCredentialObject class
  2658. // [pObjectEntry - IN] : pointer to an entry in the object table
  2659. // that defines the object we are modifying
  2660. // [argRecord - IN] : the argument record structure from the
  2661. // parser table that corresponds to this
  2662. // attribute
  2663. // [dwAttributeIdx - IN] : index into the attribute table for the
  2664. // object in which we are setting
  2665. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  2666. // which this function will fill in
  2667. //
  2668. // Returns: HRESULT : S_OK if everything succeeded
  2669. // Otherwise an ADSI failure code
  2670. //
  2671. // History: 15-Sep-2000 JeffJon Created
  2672. //
  2673. //---------------------------------------------------------------------------
  2674. HRESULT SetCanChangePassword(PCWSTR pszDN,
  2675. const CDSCmdBasePathsInfo& refBasePathsInfo,
  2676. const CDSCmdCredentialObject& refCredentialObject,
  2677. const PDSOBJECTTABLEENTRY pObjectEntry,
  2678. const ARG_RECORD& argRecord,
  2679. DWORD /*dwAttributeIdx*/,
  2680. PADS_ATTR_INFO* ppAttr)
  2681. {
  2682. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetCanChangePassword, hr);
  2683. //NTRAID#NTBUG9-571818-2000/11/13-hiteshr
  2684. //Why is the ace for everyone required? Isn't ace for Self sufficient.
  2685. //IF needed consider replacing Everyone with Authenticated User.
  2686. do // false loop
  2687. {
  2688. //
  2689. // Verify parameters
  2690. //
  2691. if (!pszDN ||
  2692. !pObjectEntry ||
  2693. !ppAttr)
  2694. {
  2695. ASSERT(pszDN);
  2696. ASSERT(pObjectEntry);
  2697. ASSERT(ppAttr);
  2698. hr = E_INVALIDARG;
  2699. break;
  2700. }
  2701. *ppAttr = NULL;
  2702. //
  2703. // Compose the path
  2704. //
  2705. CComBSTR sbstrPath;
  2706. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  2707. //
  2708. // Open the object
  2709. //
  2710. CComPtr<IDirectoryObject> spDirObject;
  2711. hr = DSCmdOpenObject(refCredentialObject,
  2712. sbstrPath,
  2713. IID_IDirectoryObject,
  2714. (void**)&spDirObject,
  2715. true);
  2716. if (FAILED(hr))
  2717. {
  2718. break;
  2719. }
  2720. SECURITY_DESCRIPTOR_CONTROL sdControl = {0};
  2721. CSimpleAclHolder Dacl;
  2722. hr = DSReadObjectSecurity(spDirObject,
  2723. &sdControl,
  2724. &(Dacl.m_pAcl));
  2725. if (FAILED(hr))
  2726. {
  2727. break;
  2728. }
  2729. //
  2730. // Create and Initialize the Self and World SIDs
  2731. //
  2732. CSidHolder selfSid;
  2733. CSidHolder worldSid;
  2734. PSID pSid = NULL;
  2735. //Security Review:Check if Everyone can be replaced with
  2736. //Authenticated User. NTRAID#NTBUG9-571818-2002/03/11-hiteshr
  2737. SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY,
  2738. WorldAuth = SECURITY_WORLD_SID_AUTHORITY;
  2739. //Security Review:This is fine.
  2740. if (!AllocateAndInitializeSid(&NtAuth,
  2741. 1,
  2742. SECURITY_PRINCIPAL_SELF_RID,
  2743. 0, 0, 0, 0, 0, 0, 0,
  2744. &pSid))
  2745. {
  2746. DWORD _dwErr = GetLastError();
  2747. hr = HRESULT_FROM_WIN32( _dwErr );
  2748. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate self SID: hr = 0x%x", hr);
  2749. break;
  2750. }
  2751. selfSid.Attach(pSid, false);
  2752. pSid = NULL;
  2753. //Security Review:This is fine.
  2754. if (!AllocateAndInitializeSid(&WorldAuth,
  2755. 1,
  2756. SECURITY_WORLD_RID,
  2757. 0, 0, 0, 0, 0, 0, 0,
  2758. &pSid))
  2759. {
  2760. DWORD _dwErr = GetLastError();
  2761. hr = HRESULT_FROM_WIN32( _dwErr );
  2762. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate world SID: hr = 0x%x", hr);
  2763. break;
  2764. }
  2765. worldSid.Attach(pSid, false);
  2766. pSid = NULL;
  2767. ULONG ulCount = 0, j = 0;
  2768. PEXPLICIT_ACCESS rgEntries = NULL;
  2769. DWORD dwErr = GetExplicitEntriesFromAcl(Dacl.m_pAcl, &ulCount, &rgEntries);
  2770. if (ERROR_SUCCESS != dwErr)
  2771. {
  2772. hr = HRESULT_FROM_WIN32(dwErr);
  2773. DEBUG_OUTPUT(LEVEL3_LOGGING, L"GetExplicitEntriesFromAcl failed: hr = 0x%x", hr);
  2774. break;
  2775. }
  2776. //
  2777. // At most we will be adding two ACEs hence the +2
  2778. //
  2779. PEXPLICIT_ACCESS rgNewEntries = (PEXPLICIT_ACCESS)LocalAlloc(LPTR, sizeof(EXPLICIT_ACCESS)*(ulCount + 2));
  2780. if (!rgNewEntries)
  2781. {
  2782. hr = E_OUTOFMEMORY;
  2783. break;
  2784. }
  2785. DEBUG_OUTPUT(FULL_LOGGING, L"GetExplicitEntriesFromAcl return %d entries", ulCount);
  2786. //
  2787. // Are these ACEs already present?
  2788. //
  2789. bool bSelfAllowPresent = false;
  2790. bool bWorldAllowPresent = false;
  2791. bool bSelfDenyPresent = false;
  2792. bool bWorldDenyPresent = false;
  2793. ULONG ulCurrentEntry = 0;
  2794. //
  2795. // If we are not granting them permission, then put the deny ACE at the top
  2796. //
  2797. OBJECTS_AND_SID rgObjectsAndSid[2]; //NTRAID#NTBUG9-572491-2002/05/24, fix, yanggao
  2798. memset(rgObjectsAndSid, 0, sizeof(rgObjectsAndSid));
  2799. if (!argRecord.bValue)
  2800. {
  2801. //
  2802. // initialize the new entries (DENY ACE's)
  2803. //
  2804. //Security Review:correct size is passed to memset
  2805. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding the deny self ACE at rgNewEntries[%d]", ulCurrentEntry);
  2806. rgNewEntries[ulCurrentEntry].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  2807. rgNewEntries[ulCurrentEntry].grfAccessMode = DENY_ACCESS;
  2808. rgNewEntries[ulCurrentEntry].grfInheritance = NO_INHERITANCE;
  2809. //
  2810. // build the trustee structs for change password
  2811. //
  2812. //Security Review:NTRAID#NTBUG9-572491-2002/03/11-hiteshr
  2813. //rgNewEntries[ulCurrentEntry].Trustee.pstrName is set to &(rgObjectsAndSid[0]
  2814. //which is local to if.
  2815. BuildTrusteeWithObjectsAndSid(&(rgNewEntries[ulCurrentEntry].Trustee),
  2816. &(rgObjectsAndSid[0]),
  2817. const_cast<GUID *>(&GUID_CONTROL_UserChangePassword),
  2818. NULL, // inherit guid
  2819. selfSid.Get());
  2820. ulCurrentEntry++;
  2821. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding the deny world ACE at rgNewEntries[%d]", ulCurrentEntry);
  2822. rgNewEntries[ulCurrentEntry].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  2823. rgNewEntries[ulCurrentEntry].grfAccessMode = DENY_ACCESS;
  2824. rgNewEntries[ulCurrentEntry].grfInheritance = NO_INHERITANCE;
  2825. //
  2826. // build the trustee structs for change password
  2827. //
  2828. //Security Review:NTRAID#NTBUG9-572491-2002/03/11-hiteshr
  2829. //rgNewEntries[ulCurrentEntry].Trustee.pstrName is set to &(rgObjectsAndSid[0]
  2830. //which is local to if.
  2831. BuildTrusteeWithObjectsAndSid(&(rgNewEntries[ulCurrentEntry].Trustee),
  2832. &(rgObjectsAndSid[1]),
  2833. const_cast<GUID *>(&GUID_CONTROL_UserChangePassword),
  2834. NULL, // inherit guid
  2835. worldSid.Get());
  2836. ulCurrentEntry++;
  2837. }
  2838. //
  2839. // Loop through all the ACEs and copy them over to the rgNewEntries unless it is
  2840. // an ACE that we want to remove
  2841. //
  2842. for (j = 0; j < ulCount; j++)
  2843. {
  2844. bool bCopyACE = true;
  2845. //
  2846. // Look for deny ACEs
  2847. //
  2848. if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  2849. (rgEntries[j].grfAccessMode == DENY_ACCESS))
  2850. {
  2851. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  2852. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  2853. //
  2854. // Look for the user can change password ACE
  2855. //
  2856. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  2857. GUID_CONTROL_UserChangePassword))
  2858. {
  2859. //
  2860. // See if it is for the self SID or the world SID
  2861. //
  2862. //Security Review:Both sids are fine.
  2863. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  2864. {
  2865. //
  2866. // Deny self found
  2867. //
  2868. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Deny self found at rgEntries[%d]", j);
  2869. bSelfDenyPresent = true;
  2870. //
  2871. // Never copy the deny ACE because we added it above for !argRecord.bValue
  2872. //
  2873. bCopyACE = false;
  2874. }
  2875. //Security Review:Both Sids are fine.
  2876. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  2877. {
  2878. //
  2879. // Deny world found
  2880. //
  2881. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Deny world found at rgEntries[%d]", j);
  2882. bWorldDenyPresent = true;
  2883. //
  2884. // Never copy the deny ACE because we added it above for !argRecord.bValue
  2885. //
  2886. bCopyACE = false;
  2887. }
  2888. }
  2889. }
  2890. //
  2891. // Look for allow ACEs
  2892. //
  2893. else if ((rgEntries[j].Trustee.TrusteeForm == TRUSTEE_IS_OBJECTS_AND_SID) &&
  2894. (rgEntries[j].grfAccessMode == GRANT_ACCESS))
  2895. {
  2896. OBJECTS_AND_SID* pObjectsAndSid = NULL;
  2897. pObjectsAndSid = (OBJECTS_AND_SID*)rgEntries[j].Trustee.ptstrName;
  2898. //
  2899. // Look for the user can change password ACE
  2900. //
  2901. if (IsEqualGUID(pObjectsAndSid->ObjectTypeGuid,
  2902. GUID_CONTROL_UserChangePassword))
  2903. {
  2904. //
  2905. // See if it is for the self SID or the world SID
  2906. //
  2907. //Security Review:Both sids are fine.
  2908. if (EqualSid(pObjectsAndSid->pSid, selfSid.Get()))
  2909. {
  2910. //
  2911. // Allow self found
  2912. //
  2913. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Allow self found at rgEntries[%d]", j);
  2914. bSelfAllowPresent = true;
  2915. if (!argRecord.bValue)
  2916. {
  2917. bCopyACE = false;
  2918. }
  2919. }
  2920. //Security Review:Both sids are fine.
  2921. else if (EqualSid(pObjectsAndSid->pSid, worldSid.Get()))
  2922. {
  2923. //
  2924. // Allow world found
  2925. //
  2926. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Allow world found at rgEntries[%d]", j);
  2927. bWorldAllowPresent = TRUE;
  2928. if (!argRecord.bValue)
  2929. {
  2930. bCopyACE = false;
  2931. }
  2932. }
  2933. }
  2934. }
  2935. if (bCopyACE)
  2936. {
  2937. DEBUG_OUTPUT(FULL_LOGGING,
  2938. L"Copying entry from rgEntries[%d] to rgNewEntries[%d]",
  2939. j,
  2940. ulCurrentEntry);
  2941. rgNewEntries[ulCurrentEntry] = rgEntries[j];
  2942. ulCurrentEntry++;
  2943. }
  2944. }
  2945. //
  2946. // Now add the allow ACEs if they were not present and we are granting user can change pwd
  2947. //
  2948. OBJECTS_AND_SID rgObjectsAndSid1 = {0}; //NTRAID#NTBUG9-572491-2002/05/24, fix, yanggao
  2949. OBJECTS_AND_SID rgObjectsAndSid2 = {0};
  2950. if (argRecord.bValue)
  2951. {
  2952. if (!bSelfAllowPresent)
  2953. {
  2954. //
  2955. // Need to add the grant self ACE
  2956. //
  2957. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding the grant self ACE at rgNewEntries[%d]", ulCurrentEntry);
  2958. rgNewEntries[ulCurrentEntry].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  2959. rgNewEntries[ulCurrentEntry].grfAccessMode = GRANT_ACCESS;
  2960. rgNewEntries[ulCurrentEntry].grfInheritance = NO_INHERITANCE;
  2961. //Security Review:NTRAID#NTBUG9-572491-2002/03/11-hiteshr
  2962. //rgNewEntries[ulCurrentEntry].Trustee.pstrName is set to &(rgObjectsAndSid)
  2963. //which is local to if statement.
  2964. BuildTrusteeWithObjectsAndSid(&(rgNewEntries[ulCurrentEntry].Trustee),
  2965. &(rgObjectsAndSid1),
  2966. const_cast<GUID*>(&GUID_CONTROL_UserChangePassword),
  2967. NULL, // inherit guid
  2968. selfSid.Get());
  2969. ulCurrentEntry++;
  2970. }
  2971. if (!bWorldAllowPresent)
  2972. {
  2973. //
  2974. // Need to add the grant world ACE
  2975. //
  2976. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Adding the grant world ACE at rgNewEntries[%d]", ulCurrentEntry);
  2977. rgNewEntries[ulCurrentEntry].grfAccessPermissions = ACTRL_DS_CONTROL_ACCESS;
  2978. rgNewEntries[ulCurrentEntry].grfAccessMode = GRANT_ACCESS;
  2979. rgNewEntries[ulCurrentEntry].grfInheritance = NO_INHERITANCE;
  2980. //Security Review:NTRAID#NTBUG9-572491-2002/03/11-hiteshr
  2981. //rgNewEntries[ulCurrentEntry].Trustee.pstrName is set to &(rgObjectsAndSid)
  2982. //which is local to if statement.
  2983. BuildTrusteeWithObjectsAndSid(&(rgNewEntries[ulCurrentEntry].Trustee),
  2984. &(rgObjectsAndSid2),
  2985. const_cast<GUID*>(&GUID_CONTROL_UserChangePassword),
  2986. NULL, // inherit guid
  2987. worldSid.Get());
  2988. ulCurrentEntry++;
  2989. }
  2990. }
  2991. //
  2992. // We should only have added two ACEs at most
  2993. //
  2994. ASSERT(ulCurrentEntry <= ulCount + 2);
  2995. //Security Review:NTRAID#NTBUG9-572465-2002/03/11-hiteshr
  2996. //Check should be if(ulCurrentEntry > ulCount + 2)
  2997. //if check fails, buffer overrun has occured and only thing safe to do is exit.
  2998. if (ulCurrentEntry > ulCount)
  2999. {
  3000. DEBUG_OUTPUT(MINIMAL_LOGGING,
  3001. L"We probably ran off the end of the array because ulCurrentEntry(%d) is > ulCount(%d)",
  3002. ulCurrentEntry,
  3003. ulCount);
  3004. }
  3005. //
  3006. // Now set the entries in the new ACL
  3007. //
  3008. CSimpleAclHolder NewDacl;
  3009. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Calling SetEntriesInAcl for %d entries", ulCurrentEntry);
  3010. //Security Review:NTRAID#NTBUG9-572491-2002/03/11-hiteshr
  3011. //See the comments above for rgNewEntries[ulCurrentEntry].
  3012. dwErr = SetEntriesInAcl(ulCurrentEntry, rgNewEntries, NULL, &(NewDacl.m_pAcl));
  3013. if (ERROR_SUCCESS != dwErr)
  3014. {
  3015. hr = HRESULT_FROM_WIN32(dwErr);
  3016. DEBUG_OUTPUT(LEVEL3_LOGGING, L"SetEntriesInAcl failed: hr = 0x%x", hr);
  3017. break;
  3018. }
  3019. ASSERT(IsValidAcl(NewDacl.m_pAcl));
  3020. //
  3021. // Free the entries
  3022. //
  3023. if (rgNewEntries)
  3024. {
  3025. LocalFree(rgNewEntries);
  3026. }
  3027. if (ulCount && rgEntries)
  3028. {
  3029. LocalFree(rgEntries);
  3030. }
  3031. //
  3032. // Write the new ACL back as a SecurityDescriptor
  3033. //
  3034. hr = DSWriteObjectSecurity(spDirObject,
  3035. sdControl,
  3036. NewDacl.m_pAcl);
  3037. if (FAILED(hr))
  3038. {
  3039. break;
  3040. }
  3041. } while (false);
  3042. return hr;
  3043. }
  3044. //+--------------------------------------------------------------------------
  3045. //
  3046. // Function: ReadGroupType
  3047. //
  3048. // Synopsis: Reads the group type from the group specified by the given DN
  3049. //
  3050. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3051. // to the object being modified
  3052. // [refBasePathsInfo - IN] : reference to an instance of the
  3053. // CDSCmdBasePathsInfo class
  3054. // [refCredentialObject - IN] : reference to an instance of the
  3055. // CDSCmdCredentialObject class
  3056. // [plType - OUT] : returns the currect group type
  3057. //
  3058. // Returns: HRESULT : S_OK if everything succeeded
  3059. // Otherwise an ADSI failure code
  3060. //
  3061. // History: 18-Sep-2000 JeffJon Created
  3062. //
  3063. //---------------------------------------------------------------------------
  3064. HRESULT ReadGroupType(PCWSTR pszDN,
  3065. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3066. const CDSCmdCredentialObject& refCredentialObject,
  3067. long* plType)
  3068. {
  3069. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ReadGroupType, hr);
  3070. do // false loop
  3071. {
  3072. if (!pszDN ||
  3073. !plType)
  3074. {
  3075. ASSERT(pszDN);
  3076. ASSERT(plType);
  3077. hr = E_INVALIDARG;
  3078. break;
  3079. }
  3080. //
  3081. // Convert the DN to a path
  3082. //
  3083. CComBSTR sbstrPath;
  3084. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  3085. //
  3086. // Bind and obtain the IADs interface to the user object
  3087. //
  3088. CComPtr<IADs> spADs;
  3089. hr = DSCmdOpenObject(refCredentialObject,
  3090. sbstrPath,
  3091. IID_IADs,
  3092. (void**)&spADs,
  3093. true);
  3094. if (FAILED(hr))
  3095. {
  3096. break;
  3097. }
  3098. CComVariant var;
  3099. hr = spADs->Get(CComBSTR(L"groupType"), &var);
  3100. if (FAILED(hr))
  3101. {
  3102. break;
  3103. }
  3104. ASSERT(var.vt == VT_I4);
  3105. *plType = var.lVal;
  3106. } while (false);
  3107. return hr;
  3108. }
  3109. //+--------------------------------------------------------------------------
  3110. //
  3111. // Function: SetGroupScope
  3112. //
  3113. // Synopsis: Sets the groupType attribute to local/universal/global
  3114. //
  3115. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3116. // to the object being modified
  3117. // [refBasePathsInfo - IN] : reference to an instance of the
  3118. // CDSCmdBasePathsInfo class
  3119. // [refCredentialObject - IN] : reference to an instance of the
  3120. // CDSCmdCredentialObject class
  3121. // [pObjectEntry - IN] : pointer to an entry in the object table
  3122. // that defines the object we are modifying
  3123. // [argRecord - IN] : the argument record structure from the
  3124. // parser table that corresponds to this
  3125. // attribute
  3126. // [dwAttributeIdx - IN] : index into the attribute table for the
  3127. // object in which we are setting
  3128. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3129. // which this function will fill in
  3130. //
  3131. // Returns: HRESULT : S_OK if everything succeeded
  3132. // Otherwise an ADSI failure code
  3133. //
  3134. // History: 18-Sep-2000 JeffJon Created
  3135. //
  3136. //---------------------------------------------------------------------------
  3137. HRESULT SetGroupScope(PCWSTR pszDN,
  3138. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3139. const CDSCmdCredentialObject& refCredentialObject,
  3140. const PDSOBJECTTABLEENTRY pObjectEntry,
  3141. const ARG_RECORD& argRecord,
  3142. DWORD dwAttributeIdx,
  3143. PADS_ATTR_INFO* ppAttr)
  3144. {
  3145. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetGroupScope, hr);
  3146. do // false loop
  3147. {
  3148. //
  3149. // Verify parameters
  3150. //
  3151. if (!pszDN ||
  3152. !pObjectEntry ||
  3153. !ppAttr)
  3154. {
  3155. ASSERT(pszDN);
  3156. ASSERT(pObjectEntry);
  3157. ASSERT(ppAttr);
  3158. hr = E_INVALIDARG;
  3159. break;
  3160. }
  3161. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  3162. //
  3163. // Read the current group type
  3164. //
  3165. bool bUseExistingAttrInfo = false;
  3166. long lGroupType = 0;
  3167. if (!(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ))
  3168. {
  3169. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Group type has not been read, try reading it now");
  3170. hr = ReadGroupType(pszDN,
  3171. refBasePathsInfo,
  3172. refCredentialObject,
  3173. &lGroupType);
  3174. if (FAILED(hr))
  3175. {
  3176. //
  3177. // Just continue on without knowing since we are trying to set it anyway
  3178. //
  3179. hr = S_OK;
  3180. lGroupType = 0;
  3181. }
  3182. //
  3183. // Mark the attribute as read and allocate space for the new value
  3184. //
  3185. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  3186. (*ppAttr)->pADsValues = new ADSVALUE;
  3187. if (!(*ppAttr)->pADsValues)
  3188. {
  3189. hr = E_OUTOFMEMORY;
  3190. break;
  3191. }
  3192. (*ppAttr)->dwNumValues = 1;
  3193. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  3194. }
  3195. else
  3196. {
  3197. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Group type has been read, just use that one");
  3198. //
  3199. // If the attribute hasn't been set yet create a new value for it
  3200. //
  3201. if (!(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_DIRTY))
  3202. {
  3203. (*ppAttr)->pADsValues = new ADSVALUE;
  3204. if (!(*ppAttr)->pADsValues)
  3205. {
  3206. hr = E_OUTOFMEMORY;
  3207. break;
  3208. }
  3209. (*ppAttr)->dwNumValues = 1;
  3210. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  3211. }
  3212. lGroupType = (*ppAttr)->pADsValues->Integer;
  3213. bUseExistingAttrInfo = true;
  3214. }
  3215. DEBUG_OUTPUT(LEVEL3_LOGGING, L"old grouptype = 0x%x", lGroupType);
  3216. //
  3217. // Remember the security bit
  3218. //
  3219. bool bIsSecurityEnabled = (lGroupType & GROUP_TYPE_SECURITY_ENABLED) != 0;
  3220. //
  3221. // Clear out the old value
  3222. //
  3223. lGroupType = 0;
  3224. //
  3225. // The parser should have already verified that the strValue contains
  3226. // either 'l', 'g', or 'u'
  3227. //
  3228. CComBSTR sbstrInput;
  3229. sbstrInput = argRecord.strValue;
  3230. sbstrInput.ToLower();
  3231. if (sbstrInput == g_bstrGroupScopeLocal)
  3232. {
  3233. //
  3234. // Local group
  3235. //
  3236. lGroupType = GROUP_TYPE_RESOURCE_GROUP;
  3237. }
  3238. else if (sbstrInput == g_bstrGroupScopeGlobal)
  3239. {
  3240. //
  3241. // Global group
  3242. //
  3243. lGroupType = GROUP_TYPE_ACCOUNT_GROUP;
  3244. }
  3245. else if (sbstrInput == g_bstrGroupScopeUniversal)
  3246. {
  3247. //
  3248. // Universal group
  3249. //
  3250. lGroupType = GROUP_TYPE_UNIVERSAL_GROUP;
  3251. }
  3252. else
  3253. {
  3254. *ppAttr = NULL;
  3255. hr = E_INVALIDARG;
  3256. break;
  3257. }
  3258. //
  3259. // Reset the security bit
  3260. //
  3261. if (bIsSecurityEnabled)
  3262. {
  3263. lGroupType |= GROUP_TYPE_SECURITY_ENABLED;
  3264. }
  3265. //
  3266. // Set the new value in the ADS_ATTR_INFO
  3267. //
  3268. (*ppAttr)->pADsValues->Integer = lGroupType;
  3269. DEBUG_OUTPUT(LEVEL3_LOGGING, L"new grouptype = 0x%x", lGroupType);
  3270. //
  3271. // Mark the attribute as dirty
  3272. //
  3273. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  3274. //
  3275. // if the attribute was previously read then we don't need to add another ADS_ATTR_INFO
  3276. //
  3277. if (bUseExistingAttrInfo)
  3278. {
  3279. *ppAttr = NULL;
  3280. }
  3281. } while (false);
  3282. return hr;
  3283. }
  3284. //+--------------------------------------------------------------------------
  3285. //
  3286. // Function: ChangeGroupScope
  3287. //
  3288. // Synopsis: Sets the groupType attribute to local/universal/global
  3289. //
  3290. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3291. // to the object being modified
  3292. // [refBasePathsInfo - IN] : reference to an instance of the
  3293. // CDSCmdBasePathsInfo class
  3294. // [refCredentialObject - IN] : reference to an instance of the
  3295. // CDSCmdCredentialObject class
  3296. // [pObjectEntry - IN] : pointer to an entry in the object table
  3297. // that defines the object we are modifying
  3298. // [argRecord - IN] : the argument record structure from the
  3299. // parser table that corresponds to this
  3300. // attribute
  3301. // [dwAttributeIdx - IN] : index into the attribute table for the
  3302. // object in which we are setting
  3303. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3304. // which this function will fill in
  3305. //
  3306. // Returns: HRESULT : S_OK if everything succeeded
  3307. // Otherwise an ADSI failure code
  3308. //
  3309. // History: 18-Sep-2000 JeffJon Created
  3310. //
  3311. //---------------------------------------------------------------------------
  3312. HRESULT ChangeGroupScope(PCWSTR pszDN,
  3313. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3314. const CDSCmdCredentialObject& refCredentialObject,
  3315. const PDSOBJECTTABLEENTRY pObjectEntry,
  3316. const ARG_RECORD& argRecord,
  3317. DWORD dwAttributeIdx,
  3318. PADS_ATTR_INFO* ppAttr)
  3319. {
  3320. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetGroupScope, hr);
  3321. do // false loop
  3322. {
  3323. //
  3324. // Verify parameters
  3325. //
  3326. if (!pszDN ||
  3327. !pObjectEntry ||
  3328. !ppAttr)
  3329. {
  3330. ASSERT(pszDN);
  3331. ASSERT(pObjectEntry);
  3332. ASSERT(ppAttr);
  3333. hr = E_INVALIDARG;
  3334. break;
  3335. }
  3336. //
  3337. // Check the domain mode
  3338. //
  3339. bool bMixedMode = false;
  3340. hr = refBasePathsInfo.GetDomainMode(refCredentialObject,
  3341. bMixedMode);
  3342. if (FAILED(hr))
  3343. {
  3344. *ppAttr = NULL;
  3345. break;
  3346. }
  3347. if (bMixedMode)
  3348. {
  3349. //
  3350. // We don't allow group type to be changed in Mixed Mode
  3351. //
  3352. DisplayErrorMessage(g_pszDSCommandName,
  3353. pszDN,
  3354. E_FAIL,
  3355. IDS_FAILED_CHANGE_GROUP_DOMAIN_VERSION);
  3356. hr = S_FALSE;
  3357. break;
  3358. }
  3359. hr = SetGroupScope(pszDN,
  3360. refBasePathsInfo,
  3361. refCredentialObject,
  3362. pObjectEntry,
  3363. argRecord,
  3364. dwAttributeIdx,
  3365. ppAttr);
  3366. } while (false);
  3367. return hr;
  3368. }
  3369. //+--------------------------------------------------------------------------
  3370. //
  3371. // Function: SetGroupSecurity
  3372. //
  3373. // Synopsis: Sets the groupType to be security enabled or disabled
  3374. //
  3375. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3376. // to the object being modified
  3377. // [refBasePathsInfo - IN] : reference to an instance of the
  3378. // CDSCmdBasePathsInfo class
  3379. // [refCredentialObject - IN] : reference to an instance of the
  3380. // CDSCmdCredentialObject class
  3381. // [pObjectEntry - IN] : pointer to an entry in the object table
  3382. // that defines the object we are modifying
  3383. // [argRecord - IN] : the argument record structure from the
  3384. // parser table that corresponds to this
  3385. // attribute
  3386. // [dwAttributeIdx - IN] : index into the attribute table for the
  3387. // object in which we are setting
  3388. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3389. // which this function will fill in
  3390. //
  3391. // Returns: HRESULT : S_OK if everything succeeded
  3392. // Otherwise an ADSI failure code
  3393. //
  3394. // History: 18-Sep-2000 JeffJon Created
  3395. //
  3396. //---------------------------------------------------------------------------
  3397. HRESULT SetGroupSecurity(PCWSTR pszDN,
  3398. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3399. const CDSCmdCredentialObject& refCredentialObject,
  3400. const PDSOBJECTTABLEENTRY pObjectEntry,
  3401. const ARG_RECORD& argRecord,
  3402. DWORD dwAttributeIdx,
  3403. PADS_ATTR_INFO* ppAttr)
  3404. {
  3405. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetGroupSecurity, hr);
  3406. do // false loop
  3407. {
  3408. //
  3409. // Verify parameters
  3410. //
  3411. if (!pszDN ||
  3412. !pObjectEntry ||
  3413. !ppAttr)
  3414. {
  3415. ASSERT(pszDN);
  3416. ASSERT(pObjectEntry);
  3417. ASSERT(ppAttr);
  3418. hr = E_INVALIDARG;
  3419. break;
  3420. }
  3421. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  3422. //
  3423. // Read the current group type
  3424. //
  3425. bool bUseExistingAttrInfo = false;
  3426. long lGroupType = 0;
  3427. if (!(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ))
  3428. {
  3429. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Group type has not been read, try reading it now");
  3430. hr = ReadGroupType(pszDN,
  3431. refBasePathsInfo,
  3432. refCredentialObject,
  3433. &lGroupType);
  3434. if (FAILED(hr))
  3435. {
  3436. //
  3437. // Continue on anyway since we are trying to set this attribute
  3438. //
  3439. hr = S_OK;
  3440. lGroupType = 0;
  3441. }
  3442. //
  3443. // Mark the attribute as read and allocate space for the new value
  3444. //
  3445. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  3446. (*ppAttr)->pADsValues = new ADSVALUE;
  3447. if (!(*ppAttr)->pADsValues)
  3448. {
  3449. hr = E_OUTOFMEMORY;
  3450. break;
  3451. }
  3452. (*ppAttr)->dwNumValues = 1;
  3453. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  3454. }
  3455. else
  3456. {
  3457. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Group type has been read, just use that one");
  3458. //
  3459. // if the attribute hasn't been marked dirty allocate space for the value
  3460. //
  3461. if (!(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags & DS_ATTRIBUTE_READ))
  3462. {
  3463. (*ppAttr)->pADsValues = new ADSVALUE;
  3464. if (!(*ppAttr)->pADsValues)
  3465. {
  3466. hr = E_OUTOFMEMORY;
  3467. break;
  3468. }
  3469. (*ppAttr)->dwNumValues = 1;
  3470. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  3471. }
  3472. lGroupType = (*ppAttr)->pADsValues->Integer;
  3473. bUseExistingAttrInfo = true;
  3474. }
  3475. DEBUG_OUTPUT(LEVEL3_LOGGING, L"old grouptype = 0x%x", lGroupType);
  3476. if (argRecord.bValue)
  3477. {
  3478. lGroupType |= GROUP_TYPE_SECURITY_ENABLED;
  3479. }
  3480. else
  3481. {
  3482. lGroupType &= ~(GROUP_TYPE_SECURITY_ENABLED);
  3483. }
  3484. //
  3485. // Set the new value in the ADS_ATTR_INFO
  3486. //
  3487. (*ppAttr)->pADsValues->Integer = lGroupType;
  3488. DEBUG_OUTPUT(LEVEL3_LOGGING, L"new grouptype = 0x%x", lGroupType);
  3489. //
  3490. // Mark the attribute as dirty
  3491. //
  3492. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  3493. //
  3494. // if we are just using the existing ADS_ATTR_INFO don't return a new one
  3495. //
  3496. if (bUseExistingAttrInfo)
  3497. {
  3498. *ppAttr = NULL;
  3499. }
  3500. } while (false);
  3501. return hr;
  3502. }
  3503. //+--------------------------------------------------------------------------
  3504. //
  3505. // Function: ChangeGroupSecurity
  3506. //
  3507. // Synopsis: Sets the groupType to be security enabled or disabled but
  3508. // checks if we are in native mode first
  3509. //
  3510. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3511. // to the object being modified
  3512. // [refBasePathsInfo - IN] : reference to an instance of the
  3513. // CDSCmdBasePathsInfo class
  3514. // [refCredentialObject - IN] : reference to an instance of the
  3515. // CDSCmdCredentialObject class
  3516. // [pObjectEntry - IN] : pointer to an entry in the object table
  3517. // that defines the object we are modifying
  3518. // [argRecord - IN] : the argument record structure from the
  3519. // parser table that corresponds to this
  3520. // attribute
  3521. // [dwAttributeIdx - IN] : index into the attribute table for the
  3522. // object in which we are setting
  3523. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3524. // which this function will fill in
  3525. //
  3526. // Returns: HRESULT : S_OK if everything succeeded
  3527. // Otherwise an ADSI failure code
  3528. //
  3529. // History: 18-Sep-2000 JeffJon Created
  3530. //
  3531. //---------------------------------------------------------------------------
  3532. HRESULT ChangeGroupSecurity(PCWSTR pszDN,
  3533. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3534. const CDSCmdCredentialObject& refCredentialObject,
  3535. const PDSOBJECTTABLEENTRY pObjectEntry,
  3536. const ARG_RECORD& argRecord,
  3537. DWORD dwAttributeIdx,
  3538. PADS_ATTR_INFO* ppAttr)
  3539. {
  3540. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetGroupSecurity, hr);
  3541. do // false loop
  3542. {
  3543. //
  3544. // Verify parameters
  3545. //
  3546. if (!pszDN ||
  3547. !pObjectEntry ||
  3548. !ppAttr)
  3549. {
  3550. ASSERT(pszDN);
  3551. ASSERT(pObjectEntry);
  3552. ASSERT(ppAttr);
  3553. hr = E_INVALIDARG;
  3554. break;
  3555. }
  3556. //
  3557. // Check the domain mode
  3558. //
  3559. bool bMixedMode = false;
  3560. hr = refBasePathsInfo.GetDomainMode(refCredentialObject,
  3561. bMixedMode);
  3562. if (FAILED(hr))
  3563. {
  3564. *ppAttr = NULL;
  3565. break;
  3566. }
  3567. if (bMixedMode)
  3568. {
  3569. //
  3570. // We don't allow group type to be changed in Mixed Mode
  3571. //
  3572. DisplayErrorMessage(g_pszDSCommandName,
  3573. pszDN,
  3574. E_FAIL,
  3575. IDS_FAILED_CHANGE_GROUP_DOMAIN_VERSION);
  3576. hr = S_FALSE;
  3577. break;
  3578. }
  3579. hr = SetGroupSecurity(pszDN,
  3580. refBasePathsInfo,
  3581. refCredentialObject,
  3582. pObjectEntry,
  3583. argRecord,
  3584. dwAttributeIdx,
  3585. ppAttr);
  3586. } while (false);
  3587. return hr;
  3588. }
  3589. //+--------------------------------------------------------------------------
  3590. //
  3591. // Function: ModifyGroupMembers
  3592. //
  3593. // Synopsis: Sets the groupType to be security enabled or disabled
  3594. //
  3595. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3596. // to the object being modified
  3597. // [refBasePathsInfo - IN] : reference to an instance of the
  3598. // CDSCmdBasePathsInfo class
  3599. // [refCredentialObject - IN] : reference to an instance of the
  3600. // CDSCmdCredentialObject class
  3601. // [pObjectEntry - IN] : pointer to an entry in the object table
  3602. // that defines the object we are modifying
  3603. // [argRecord - IN] : the argument record structure from the
  3604. // parser table that corresponds to this
  3605. // attribute
  3606. // [dwAttributeIdx - IN] : index into the attribute table for the
  3607. // object in which we are setting
  3608. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3609. // which this function will fill in
  3610. //
  3611. // Returns: HRESULT : S_OK if everything succeeded
  3612. // Otherwise an ADSI failure code
  3613. //
  3614. // History: 18-Sep-2000 JeffJon Created
  3615. //
  3616. //---------------------------------------------------------------------------
  3617. HRESULT ModifyGroupMembers(PCWSTR pszDN,
  3618. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  3619. const CDSCmdCredentialObject& /*refCredentialObject*/,
  3620. const PDSOBJECTTABLEENTRY pObjectEntry,
  3621. const ARG_RECORD& argRecord,
  3622. DWORD dwAttributeIdx,
  3623. PADS_ATTR_INFO* ppAttr)
  3624. {
  3625. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ModifyGroupMembers, hr);
  3626. do // false loop
  3627. {
  3628. //
  3629. // Verify parameters
  3630. //
  3631. if (!pObjectEntry ||
  3632. !ppAttr)
  3633. {
  3634. ASSERT(pObjectEntry);
  3635. ASSERT(ppAttr);
  3636. hr = E_INVALIDARG;
  3637. break;
  3638. }
  3639. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  3640. UINT nStrings = 0;
  3641. PWSTR* ppszArray = NULL;
  3642. ParseNullSeparatedString(argRecord.strValue,
  3643. &ppszArray,
  3644. &nStrings);
  3645. if (nStrings < 1 ||
  3646. !ppszArray)
  3647. {
  3648. *ppAttr = NULL;
  3649. hr = E_OUTOFMEMORY;
  3650. break;
  3651. }
  3652. (*ppAttr)->pADsValues = new ADSVALUE[nStrings];
  3653. if (!(*ppAttr)->pADsValues)
  3654. {
  3655. *ppAttr = NULL;
  3656. LocalFree(ppszArray);
  3657. hr = E_OUTOFMEMORY;
  3658. break;
  3659. }
  3660. (*ppAttr)->dwNumValues = nStrings;
  3661. for (UINT nIdx = 0; nIdx < nStrings; nIdx++)
  3662. {
  3663. if (_wcsicmp(ppszArray[nIdx], pszDN))
  3664. {
  3665. (*ppAttr)->pADsValues[nIdx].dwType = (*ppAttr)->dwADsType;
  3666. (*ppAttr)->pADsValues[nIdx].DNString = ppszArray[nIdx];
  3667. }
  3668. else
  3669. {
  3670. DEBUG_OUTPUT(
  3671. LEVEL3_LOGGING,
  3672. L"Can't make a group a member of itself!");
  3673. DisplayErrorMessage(
  3674. g_pszDSCommandName,
  3675. pszDN,
  3676. E_FAIL,
  3677. IDS_GROUP_MEMBER_ITSELF);
  3678. // return S_FALSE since we already presented the user with an error message
  3679. hr = S_FALSE;
  3680. break;
  3681. }
  3682. }
  3683. // Break out of the false loop if there was a failure in
  3684. // the for loop above
  3685. if (FAILED(hr))
  3686. {
  3687. break;
  3688. }
  3689. //
  3690. // Mark the attribute as dirty
  3691. //
  3692. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  3693. if (ppszArray)
  3694. {
  3695. LocalFree(ppszArray);
  3696. }
  3697. } while (false);
  3698. return hr;
  3699. }
  3700. //+--------------------------------------------------------------------------
  3701. //
  3702. // Function: ReadGroupMembership
  3703. //
  3704. // Synopsis: Reads the group members list
  3705. //
  3706. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3707. // to the object being modified
  3708. // [refBasePathsInfo - IN] : reference to an instance of the
  3709. // CDSCmdBasePathsInfo class
  3710. // [refCredentialObject - IN] : reference to an instance of the
  3711. // CDSCmdCredentialObject class
  3712. // [ppMembersAttr - OUT] : returns the currect group membership
  3713. // this value must be freed using FreeADsMem
  3714. //
  3715. // Returns: HRESULT : S_OK if everything succeeded
  3716. // Otherwise an ADSI failure code
  3717. //
  3718. // History: 18-Sep-2000 JeffJon Created
  3719. //
  3720. //---------------------------------------------------------------------------
  3721. HRESULT ReadGroupMembership(PCWSTR pszDN,
  3722. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3723. const CDSCmdCredentialObject& refCredentialObject,
  3724. PADS_ATTR_INFO* ppMembersAttr)
  3725. {
  3726. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ReadGroupMembership, hr);
  3727. do // false loop
  3728. {
  3729. if (!pszDN ||
  3730. !ppMembersAttr)
  3731. {
  3732. ASSERT(pszDN);
  3733. ASSERT(ppMembersAttr);
  3734. hr = E_INVALIDARG;
  3735. break;
  3736. }
  3737. //
  3738. // Convert the DN to a path
  3739. //
  3740. CComBSTR sbstrPath;
  3741. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  3742. //
  3743. // Bind and obtain the IADs interface to the user object
  3744. //
  3745. CComPtr<IDirectoryObject> spObject;
  3746. hr = DSCmdOpenObject(refCredentialObject,
  3747. sbstrPath,
  3748. IID_IDirectoryObject,
  3749. (void**)&spObject,
  3750. true);
  3751. if (FAILED(hr))
  3752. {
  3753. break;
  3754. }
  3755. DWORD dwNumReturned = 0;
  3756. PWSTR ppszAttrs[] = { L"member" };
  3757. hr = spObject->GetObjectAttributes(ppszAttrs,
  3758. sizeof(ppszAttrs)/sizeof(PWSTR),
  3759. ppMembersAttr,
  3760. &dwNumReturned);
  3761. if (FAILED(hr))
  3762. {
  3763. break;
  3764. }
  3765. } while (false);
  3766. return hr;
  3767. }
  3768. //+--------------------------------------------------------------------------
  3769. //
  3770. // Function: ShowRemoveFromGroupFailure
  3771. //
  3772. // Synopsis: Displays an error message as a result of failure to remove
  3773. // an object from a group
  3774. //
  3775. // Arguments: [hr - IN] : failure code
  3776. // [pszDN - IN] : DN of the group object
  3777. // [pszMember - IN] : DN of the member being removed
  3778. //
  3779. // Returns: HRESULT : S_OK if everything succeeded
  3780. // Otherwise an ADSI failure code
  3781. //
  3782. // History: 06-Dec-2000 JeffJon Created
  3783. //
  3784. //---------------------------------------------------------------------------
  3785. HRESULT ShowRemoveFromGroupFailure(HRESULT hrResult,
  3786. PCWSTR pszDN,
  3787. PCWSTR pszMember)
  3788. {
  3789. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ShowRemoveFromGroupFailure, hr);
  3790. do // false loop
  3791. {
  3792. //
  3793. // Verify parameters
  3794. //
  3795. if (!pszDN ||
  3796. !pszMember)
  3797. {
  3798. ASSERT(pszDN);
  3799. ASSERT(pszMember);
  3800. hr = E_INVALIDARG;
  3801. break;
  3802. }
  3803. bool bShowGenericMessage = true;
  3804. CComBSTR sbstrFormatter;
  3805. bool bLoadFormatString = sbstrFormatter.LoadString(::GetModuleHandle(NULL),
  3806. IDS_ERRMSG_REMOVE_FROM_GROUP);
  3807. if (bLoadFormatString)
  3808. {
  3809. //Security Review:this is fine.
  3810. size_t messageLength = wcslen(sbstrFormatter) + wcslen(pszMember);
  3811. PWSTR pszMessage = new WCHAR[messageLength + 1];
  3812. if (pszMessage)
  3813. {
  3814. //Security Review:Though care has been taken make sure enough buffer is allocated.
  3815. //I have filed a bug to replace wsprintf with strsafe api.
  3816. //NTRAID#NTBUG9-573053-2002/03/08-hiteshr
  3817. wsprintf(pszMessage, sbstrFormatter, pszMember);
  3818. DisplayErrorMessage(g_pszDSCommandName,
  3819. pszDN,
  3820. hrResult,
  3821. pszMessage);
  3822. bShowGenericMessage = false;
  3823. delete[] pszMessage;
  3824. pszMessage = 0;
  3825. }
  3826. }
  3827. if (bShowGenericMessage)
  3828. {
  3829. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to load the string IDS_ERRMSG_REMOVE_FROM_GROUP from the resource file");
  3830. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Using the default message instead");
  3831. DisplayErrorMessage(g_pszDSCommandName,
  3832. pszDN,
  3833. hrResult);
  3834. }
  3835. } while (false);
  3836. return hr;
  3837. }
  3838. //+--------------------------------------------------------------------------
  3839. //
  3840. // Function: RemoveGroupMembers
  3841. //
  3842. // Synopsis: Removes the specified members from the group
  3843. //
  3844. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3845. // to the object being modified
  3846. // [refBasePathsInfo - IN] : reference to an instance of the
  3847. // CDSCmdBasePathsInfo class
  3848. // [refCredentialObject - IN] : reference to an instance of the
  3849. // CDSCmdCredentialObject class
  3850. // [pObjectEntry - IN] : pointer to an entry in the object table
  3851. // that defines the object we are modifying
  3852. // [argRecord - IN] : the argument record structure from the
  3853. // parser table that corresponds to this
  3854. // attribute
  3855. // [dwAttributeIdx - IN] : index into the attribute table for the
  3856. // object in which we are setting
  3857. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3858. // which this function will fill in
  3859. //
  3860. // Returns: HRESULT : S_OK if everything succeeded
  3861. // Otherwise an ADSI failure code
  3862. //
  3863. // History: 18-Sep-2000 JeffJon Created
  3864. //
  3865. //---------------------------------------------------------------------------
  3866. HRESULT RemoveGroupMembers(PCWSTR pszDN,
  3867. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3868. const CDSCmdCredentialObject& refCredentialObject,
  3869. const PDSOBJECTTABLEENTRY pObjectEntry,
  3870. const ARG_RECORD& argRecord,
  3871. DWORD /*dwAttributeIdx*/,
  3872. PADS_ATTR_INFO* ppAttr)
  3873. {
  3874. ENTER_FUNCTION_HR(LEVEL3_LOGGING, RemoveGroupMembers, hr);
  3875. do // false loop
  3876. {
  3877. //
  3878. // Verify parameters
  3879. //
  3880. if (!pszDN ||
  3881. !pObjectEntry ||
  3882. !ppAttr)
  3883. {
  3884. ASSERT(pszDN);
  3885. ASSERT(pObjectEntry);
  3886. ASSERT(ppAttr);
  3887. hr = E_INVALIDARG;
  3888. break;
  3889. }
  3890. //
  3891. // We won't be returning an attribute
  3892. //
  3893. *ppAttr = 0;
  3894. //
  3895. // Parse the members to be removed
  3896. //
  3897. UINT nStrings = 0;
  3898. PWSTR* ppszArray = NULL;
  3899. ParseNullSeparatedString(argRecord.strValue,
  3900. &ppszArray,
  3901. &nStrings);
  3902. if (nStrings < 1 ||
  3903. !ppszArray)
  3904. {
  3905. *ppAttr = NULL;
  3906. hr = E_OUTOFMEMORY;
  3907. break;
  3908. }
  3909. //
  3910. // Convert the DN to a path
  3911. //
  3912. CComBSTR sbstrPath;
  3913. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  3914. //
  3915. // Bind and obtain the IADs interface to the user object
  3916. //
  3917. CComPtr<IADsGroup> spGroup;
  3918. hr = DSCmdOpenObject(refCredentialObject,
  3919. sbstrPath,
  3920. IID_IADsGroup,
  3921. (void**)&spGroup,
  3922. true);
  3923. if (FAILED(hr))
  3924. {
  3925. break;
  3926. }
  3927. //
  3928. // Remove each of the objects from the group
  3929. //
  3930. for (UINT nStringIdx = 0; nStringIdx < nStrings; nStringIdx++)
  3931. {
  3932. //
  3933. // Convert the member DN into a ADSI path
  3934. //
  3935. CComBSTR sbstrMemberPath;
  3936. refBasePathsInfo.ComposePathFromDN(ppszArray[nStringIdx], sbstrMemberPath);
  3937. //
  3938. // Remove the member
  3939. //
  3940. hr = spGroup->Remove(sbstrMemberPath);
  3941. if (FAILED(hr))
  3942. {
  3943. ShowRemoveFromGroupFailure(hr, pszDN, ppszArray[nStringIdx]);
  3944. hr = S_FALSE;
  3945. break;
  3946. }
  3947. }
  3948. if (ppszArray)
  3949. {
  3950. LocalFree(ppszArray);
  3951. }
  3952. } while (false);
  3953. return hr;
  3954. }
  3955. //+--------------------------------------------------------------------------
  3956. //
  3957. // Function: MakeMemberOf
  3958. //
  3959. // Synopsis: Makes the object specified by pszDN a member of the group
  3960. // specified in the argRecord.strValue
  3961. //
  3962. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  3963. // to the object being modified
  3964. // [refBasePathsInfo - IN] : reference to an instance of the
  3965. // CDSCmdBasePathsInfo class
  3966. // [refCredentialObject - IN] : reference to an instance of the
  3967. // CDSCmdCredentialObject class
  3968. // [pObjectEntry - IN] : pointer to an entry in the object table
  3969. // that defines the object we are modifying
  3970. // [argRecord - IN] : the argument record structure from the
  3971. // parser table that corresponds to this
  3972. // attribute
  3973. // [dwAttributeIdx - IN] : index into the attribute table for the
  3974. // object in which we are setting
  3975. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  3976. // which this function will fill in
  3977. //
  3978. // Returns: HRESULT : S_OK if everything succeeded
  3979. // Otherwise an ADSI failure code
  3980. //
  3981. // History: 25-Sep-2000 JeffJon Created
  3982. //
  3983. //---------------------------------------------------------------------------
  3984. HRESULT MakeMemberOf(PCWSTR pszDN,
  3985. const CDSCmdBasePathsInfo& refBasePathsInfo,
  3986. const CDSCmdCredentialObject& refCredentialObject,
  3987. const PDSOBJECTTABLEENTRY pObjectEntry,
  3988. const ARG_RECORD& argRecord,
  3989. DWORD dwAttributeIdx,
  3990. PADS_ATTR_INFO* ppAttr)
  3991. {
  3992. ENTER_FUNCTION_HR(LEVEL3_LOGGING, MakeMemberOf, hr);
  3993. PWSTR* ppszArray = NULL;
  3994. do // false loop
  3995. {
  3996. //
  3997. // Verify parameters
  3998. //
  3999. if (!pszDN ||
  4000. !pObjectEntry ||
  4001. !ppAttr)
  4002. {
  4003. ASSERT(pszDN);
  4004. ASSERT(pObjectEntry);
  4005. ASSERT(ppAttr);
  4006. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  4007. hr = E_INVALIDARG;
  4008. break;
  4009. }
  4010. //
  4011. // We are going to do all the work here so don't pass back the ADS_ATTR_INFO
  4012. //
  4013. *ppAttr = NULL;
  4014. ADS_ATTR_INFO* pMemberAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4015. UINT nStrings = 0;
  4016. ParseNullSeparatedString(argRecord.strValue,
  4017. &ppszArray,
  4018. &nStrings);
  4019. if (nStrings < 1 ||
  4020. !ppszArray)
  4021. {
  4022. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to parse null separated string list of groups");
  4023. hr = E_OUTOFMEMORY;
  4024. break;
  4025. }
  4026. //
  4027. // Create the value
  4028. //
  4029. ADSVALUE MemberValue = { ADSTYPE_DN_STRING, NULL };
  4030. pMemberAttr->pADsValues = &MemberValue;
  4031. pMemberAttr->dwNumValues = 1;
  4032. pMemberAttr->dwControlCode = ADS_ATTR_APPEND;
  4033. pMemberAttr->pADsValues->DNString = (PWSTR)pszDN;
  4034. //
  4035. // For each group in the list add the object to the group
  4036. //
  4037. for (UINT nIdx = 0; nIdx < nStrings; nIdx++)
  4038. {
  4039. PWSTR pszGroupDN = ppszArray[nIdx];
  4040. ASSERT(pszGroupDN);
  4041. CComBSTR sbstrGroupPath;
  4042. refBasePathsInfo.ComposePathFromDN(pszGroupDN, sbstrGroupPath);
  4043. CComPtr<IDirectoryObject> spDirObject;
  4044. hr = DSCmdOpenObject(refCredentialObject,
  4045. sbstrGroupPath,
  4046. IID_IDirectoryObject,
  4047. (void**)&spDirObject,
  4048. true);
  4049. if (FAILED(hr))
  4050. {
  4051. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to open group object: %s", sbstrGroupPath);
  4052. break;
  4053. }
  4054. DWORD dwNumAttrs = 0;
  4055. hr = spDirObject->SetObjectAttributes(pMemberAttr,
  4056. 1,
  4057. &dwNumAttrs);
  4058. if (FAILED(hr))
  4059. {
  4060. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to set object attributes on group object: %s", sbstrGroupPath);
  4061. break;
  4062. }
  4063. }
  4064. } while (false);
  4065. if (ppszArray)
  4066. {
  4067. LocalFree(ppszArray);
  4068. }
  4069. return hr;
  4070. }
  4071. //+--------------------------------------------------------------------------
  4072. //
  4073. // Function: SetIsGC
  4074. //
  4075. // Synopsis: Makes the server object specified by pszDN into a GC or not
  4076. // by modifying the NTDS Settings object contained within
  4077. //
  4078. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4079. // to the object being modified
  4080. // [refBasePathsInfo - IN] : reference to an instance of the
  4081. // CDSCmdBasePathsInfo class
  4082. // [refCredentialObject - IN] : reference to an instance of the
  4083. // CDSCmdCredentialObject class
  4084. // [pObjectEntry - IN] : pointer to an entry in the object table
  4085. // that defines the object we are modifying
  4086. // [argRecord - IN] : the argument record structure from the
  4087. // parser table that corresponds to this
  4088. // attribute
  4089. // [dwAttributeIdx - IN] : index into the attribute table for the
  4090. // object in which we are setting
  4091. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4092. // which this function will fill in
  4093. //
  4094. // Returns: HRESULT : S_OK if everything succeeded
  4095. // Otherwise an ADSI failure code
  4096. //
  4097. // History: 14-Apr-2001 JeffJon Created
  4098. //
  4099. //---------------------------------------------------------------------------
  4100. HRESULT SetIsGC(PCWSTR pszDN,
  4101. const CDSCmdBasePathsInfo& refBasePathsInfo,
  4102. const CDSCmdCredentialObject& refCredentialObject,
  4103. const PDSOBJECTTABLEENTRY pObjectEntry,
  4104. const ARG_RECORD& argRecord,
  4105. DWORD /*dwAttributeIdx*/,
  4106. PADS_ATTR_INFO* ppAttr)
  4107. {
  4108. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetIsGC, hr);
  4109. PADS_ATTR_INFO pAttrInfo = 0;
  4110. // To keep track of who allocated the memory for pAttrInfo
  4111. bool usingADSIMemory = true;
  4112. do // false loop
  4113. {
  4114. //
  4115. // Verify parameters
  4116. //
  4117. if (!pszDN ||
  4118. !pObjectEntry ||
  4119. !ppAttr)
  4120. {
  4121. ASSERT(pszDN);
  4122. ASSERT(pObjectEntry);
  4123. ASSERT(ppAttr);
  4124. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  4125. hr = E_INVALIDARG;
  4126. break;
  4127. }
  4128. //
  4129. // We are going to do all the work here so don't pass back the ADS_ATTR_INFO
  4130. //
  4131. *ppAttr = NULL;
  4132. //
  4133. // Get the NTDS Settings object that is contained within the server of the specified DN
  4134. //
  4135. CComBSTR sbstrSettingsDN = L"CN=NTDS Settings,";
  4136. sbstrSettingsDN += pszDN;
  4137. CComBSTR sbstrSettingsPath;
  4138. refBasePathsInfo.ComposePathFromDN(sbstrSettingsDN, sbstrSettingsPath);
  4139. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4140. L"NTDS Settings path = %s",
  4141. sbstrSettingsPath);
  4142. CComPtr<IDirectoryObject> spDirectoryObject;
  4143. hr = DSCmdOpenObject(refCredentialObject,
  4144. sbstrSettingsPath,
  4145. IID_IDirectoryObject,
  4146. (void**)&spDirectoryObject,
  4147. true);
  4148. if (FAILED(hr))
  4149. {
  4150. break;
  4151. }
  4152. PWSTR pszAttrs[] = { L"options" };
  4153. DWORD dwReturned = 0;
  4154. hr = spDirectoryObject->GetObjectAttributes(pszAttrs, 1, &pAttrInfo, &dwReturned);
  4155. if (FAILED(hr))
  4156. {
  4157. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4158. L"Failed to get old options: hr = 0x%x",
  4159. hr);
  4160. break;
  4161. }
  4162. if (dwReturned < 1 ||
  4163. !pAttrInfo)
  4164. {
  4165. // Since we were unable to get the options we have to constuct the
  4166. // value ourselves
  4167. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4168. L"Get options succeeded but no values were returned.");
  4169. // Make sure we know that we did the allocation ourselves so that the
  4170. // proper cleanup is done
  4171. usingADSIMemory = false;
  4172. pAttrInfo = new ADS_ATTR_INFO;
  4173. if (!pAttrInfo)
  4174. {
  4175. hr = E_OUTOFMEMORY;
  4176. break;
  4177. }
  4178. ZeroMemory(pAttrInfo, sizeof(ADS_ATTR_INFO));
  4179. pAttrInfo->pADsValues = new ADSVALUE;
  4180. if (!pAttrInfo->pADsValues)
  4181. {
  4182. hr = E_OUTOFMEMORY;
  4183. break;
  4184. }
  4185. ZeroMemory(pAttrInfo->pADsValues, sizeof(ADSVALUE));
  4186. pAttrInfo->dwADsType = ADSTYPE_INTEGER;
  4187. pAttrInfo->pszAttrName = pszAttrs[0];
  4188. pAttrInfo->dwNumValues = 1;
  4189. pAttrInfo->pADsValues->dwType = ADSTYPE_INTEGER;
  4190. }
  4191. if (argRecord.bDefined &&
  4192. argRecord.bValue)
  4193. {
  4194. pAttrInfo->pADsValues->Integer |= SERVER_IS_GC_BIT;
  4195. }
  4196. else
  4197. {
  4198. pAttrInfo->pADsValues->Integer &= ~(SERVER_IS_GC_BIT);
  4199. }
  4200. pAttrInfo->dwControlCode = ADS_ATTR_UPDATE;
  4201. dwReturned = 0;
  4202. hr = spDirectoryObject->SetObjectAttributes(pAttrInfo, 1, &dwReturned);
  4203. if (FAILED(hr))
  4204. {
  4205. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4206. L"Failed to set the new options: hr = 0x%x",
  4207. hr);
  4208. break;
  4209. }
  4210. ASSERT(dwReturned == 1);
  4211. } while (false);
  4212. if (pAttrInfo)
  4213. {
  4214. if (usingADSIMemory)
  4215. {
  4216. FreeADsMem(pAttrInfo);
  4217. }
  4218. else
  4219. {
  4220. if (pAttrInfo->pADsValues)
  4221. {
  4222. delete pAttrInfo->pADsValues;
  4223. }
  4224. delete pAttrInfo;
  4225. }
  4226. }
  4227. return hr;
  4228. }
  4229. //+--------------------------------------------------------------------------
  4230. //
  4231. // Function: BuildComputerSAMName
  4232. //
  4233. // Synopsis: If the -samname argument was defined use that, else compute
  4234. // the same name from the DN
  4235. //
  4236. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4237. // to the object being modified
  4238. // [refBasePathsInfo - IN] : reference to an instance of the
  4239. // CDSCmdBasePathsInfo class
  4240. // [refCredentialObject - IN] : reference to an instance of the
  4241. // CDSCmdCredentialObject class
  4242. // [pObjectEntry - IN] : pointer to an entry in the object table
  4243. // that defines the object we are modifying
  4244. // [argRecord - IN] : the argument record structure from the
  4245. // parser table that corresponds to this
  4246. // attribute
  4247. // [dwAttributeIdx - IN] : index into the attribute table for the
  4248. // object in which we are setting
  4249. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4250. // which this function will fill in
  4251. //
  4252. // Returns: HRESULT : S_OK if everything succeeded
  4253. // Otherwise an ADSI failure code
  4254. //
  4255. // History: 09-Oct-2000 JeffJon Created
  4256. //
  4257. //---------------------------------------------------------------------------
  4258. HRESULT BuildComputerSAMName(PCWSTR pszDN,
  4259. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  4260. const CDSCmdCredentialObject& /*refCredentialObject*/,
  4261. const PDSOBJECTTABLEENTRY pObjectEntry,
  4262. const ARG_RECORD& argRecord,
  4263. DWORD dwAttributeIdx,
  4264. PADS_ATTR_INFO* ppAttr)
  4265. {
  4266. ENTER_FUNCTION_HR(LEVEL3_LOGGING, BuildComputerSAMName, hr);
  4267. do // false loop
  4268. {
  4269. //
  4270. // Verify parameters
  4271. //
  4272. if (!pszDN ||
  4273. !pObjectEntry ||
  4274. !ppAttr)
  4275. {
  4276. ASSERT(pszDN);
  4277. ASSERT(pObjectEntry);
  4278. ASSERT(ppAttr);
  4279. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  4280. hr = E_INVALIDARG;
  4281. break;
  4282. }
  4283. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4284. (*ppAttr)->pADsValues = new ADSVALUE[1];
  4285. if (!(*ppAttr)->pADsValues)
  4286. {
  4287. hr = E_OUTOFMEMORY;
  4288. break;
  4289. }
  4290. (*ppAttr)->dwNumValues = 1;
  4291. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  4292. if (!argRecord.bDefined ||
  4293. !argRecord.strValue)
  4294. {
  4295. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Constructing sAMAccountName for computer");
  4296. //
  4297. // If the object type is group and the sAMAccountName
  4298. // was not specified, construct it from the DN or name
  4299. //
  4300. CONST DWORD computerNameLen = MAX_COMPUTERNAME_LENGTH + 1;
  4301. DWORD Len = computerNameLen;
  4302. WCHAR szDownLevel[computerNameLen];
  4303. //Security Review:This is fine.
  4304. ZeroMemory(szDownLevel, computerNameLen * sizeof(WCHAR));
  4305. CComBSTR sbstrName;
  4306. hr = CPathCracker::GetObjectNameFromDN(pszDN,
  4307. sbstrName);
  4308. if (SUCCEEDED(hr))
  4309. {
  4310. //
  4311. // run through the OEM conversion, just to
  4312. // behave the same way as typing in the OEM
  4313. // edit box
  4314. //
  4315. CComBSTR sbstrOemUnicode;
  4316. _UnicodeToOemConvert(sbstrName, sbstrOemUnicode);
  4317. DEBUG_OUTPUT(LEVEL8_LOGGING,
  4318. L"OemConverted name: %s",
  4319. sbstrOemUnicode);
  4320. // run through the DNS validation
  4321. if (!DnsHostnameToComputerName(sbstrOemUnicode, szDownLevel, &Len))
  4322. {
  4323. DWORD err = GetLastError();
  4324. hr = HRESULT_FROM_WIN32(err);
  4325. DEBUG_OUTPUT(LEVEL3_LOGGING,
  4326. L"Failed in DnsHostnameToComputerName: GLE = 0x%x",
  4327. err);
  4328. Len = 0;
  4329. }
  4330. if (Len > 0)
  4331. {
  4332. //
  4333. // Validate the SAM name
  4334. //
  4335. HRESULT hrValidate = ValidateAndModifySAMName(szDownLevel,
  4336. INVALID_NETBIOS_AND_ACCOUNT_NAME_CHARS_WITH_AT);
  4337. if (FAILED(hrValidate))
  4338. {
  4339. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Unable to validate the SamAccountName");
  4340. ASSERT(SUCCEEDED(hrValidate));
  4341. break;
  4342. }
  4343. //
  4344. // Change the last character to a $
  4345. //
  4346. if (Len == MAX_COMPUTERNAME_LENGTH)
  4347. {
  4348. szDownLevel[Len - 1] = L'$';
  4349. }
  4350. else
  4351. {
  4352. szDownLevel[Len] = L'$';
  4353. szDownLevel[Len+1] = L'\0';
  4354. }
  4355. //
  4356. // Allocate enough memory for the string in the command args structure
  4357. // REVIEW_JEFFJON : this is being leaked
  4358. //
  4359. //This is fine.
  4360. (*ppAttr)->pADsValues->CaseIgnoreString = (LPTSTR)LocalAlloc(LPTR, (wcslen(szDownLevel) + 1) * sizeof(WCHAR) );
  4361. if (!(*ppAttr)->pADsValues->CaseIgnoreString)
  4362. {
  4363. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Failed to allocate space for (*ppAttr)->pADsValues->CaseIgnoreString");
  4364. hr = E_OUTOFMEMORY;
  4365. break;
  4366. }
  4367. //
  4368. // Truncate the name if necessary but copy it to the command args structure
  4369. //
  4370. //Security Review:_tcsncpy won't null terminate. We are fine here since buffer is
  4371. //initialized to all zero. Filed a bug for making the code clear here.
  4372. //NTRAID#NTBUG9-573229-2002/03/08-hiteshr
  4373. _tcsncpy((*ppAttr)->pADsValues->CaseIgnoreString,
  4374. szDownLevel,
  4375. wcslen(szDownLevel));
  4376. }
  4377. }
  4378. }
  4379. else
  4380. {
  4381. (*ppAttr)->pADsValues->CaseIgnoreString = argRecord.strValue;
  4382. }
  4383. } while (false);
  4384. return hr;
  4385. }
  4386. //+--------------------------------------------------------------------------
  4387. //
  4388. // Function: BuildGroupSAMName
  4389. //
  4390. // Synopsis: If the -samname argument was defined use that, else compute
  4391. // the same name from the DN
  4392. //
  4393. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4394. // to the object being modified
  4395. // [refBasePathsInfo - IN] : reference to an instance of the
  4396. // CDSCmdBasePathsInfo class
  4397. // [refCredentialObject - IN] : reference to an instance of the
  4398. // CDSCmdCredentialObject class
  4399. // [pObjectEntry - IN] : pointer to an entry in the object table
  4400. // that defines the object we are modifying
  4401. // [argRecord - IN] : the argument record structure from the
  4402. // parser table that corresponds to this
  4403. // attribute
  4404. // [dwAttributeIdx - IN] : index into the attribute table for the
  4405. // object in which we are setting
  4406. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4407. // which this function will fill in
  4408. //
  4409. // Returns: HRESULT : S_OK if everything succeeded
  4410. // Otherwise an ADSI failure code
  4411. //
  4412. // History: 09-Oct-2000 JeffJon Created
  4413. //
  4414. //---------------------------------------------------------------------------
  4415. HRESULT BuildGroupSAMName(PCWSTR pszDN,
  4416. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  4417. const CDSCmdCredentialObject& /*refCredentialObject*/,
  4418. const PDSOBJECTTABLEENTRY pObjectEntry,
  4419. const ARG_RECORD& argRecord,
  4420. DWORD dwAttributeIdx,
  4421. PADS_ATTR_INFO* ppAttr)
  4422. {
  4423. ENTER_FUNCTION_HR(LEVEL3_LOGGING, BuildGroupSAMName, hr);
  4424. do // false loop
  4425. {
  4426. //
  4427. // Verify parameters
  4428. //
  4429. if (!pszDN ||
  4430. !pObjectEntry ||
  4431. !ppAttr)
  4432. {
  4433. ASSERT(pszDN);
  4434. ASSERT(pObjectEntry);
  4435. ASSERT(ppAttr);
  4436. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  4437. hr = E_INVALIDARG;
  4438. break;
  4439. }
  4440. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4441. (*ppAttr)->pADsValues = new ADSVALUE[1];
  4442. if (!(*ppAttr)->pADsValues)
  4443. {
  4444. hr = E_OUTOFMEMORY;
  4445. break;
  4446. }
  4447. (*ppAttr)->dwNumValues = 1;
  4448. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  4449. if (!argRecord.bDefined ||
  4450. !argRecord.strValue)
  4451. {
  4452. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Constructing sAMAccountName for group");
  4453. static const UINT nSamLength = 256;
  4454. //
  4455. // If the object type is group and the sAMAccountName
  4456. // was not specified, construct it from the DN or name
  4457. //
  4458. CComBSTR sbstrName;
  4459. hr = CPathCracker::GetObjectNameFromDN(pszDN,
  4460. sbstrName);
  4461. if (SUCCEEDED(hr))
  4462. {
  4463. UINT nNameLen = sbstrName.Length();
  4464. //
  4465. // Allocate enough memory for the string in the command args structure
  4466. // REVIEW_JEFFJON : this is being leaked
  4467. //
  4468. (*ppAttr)->pADsValues->CaseIgnoreString = (LPTSTR)LocalAlloc(LPTR, (nNameLen+2) * sizeof(WCHAR) );
  4469. if (!(*ppAttr)->pADsValues->CaseIgnoreString)
  4470. {
  4471. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Failed to allocate space for (*ppAttr)->pADsValues->CaseIgnoreString");
  4472. hr = E_OUTOFMEMORY;
  4473. break;
  4474. }
  4475. //
  4476. // Truncate the name if necessary but copy it to the command args structure
  4477. //
  4478. //Security Review:_tcsncpy won't null terminate. We are fine here since buffer is
  4479. //initialized to all zero. Filed a bug for making the code clear here.
  4480. //NTRAID#NTBUG9-573229-2002/03/08-hiteshr
  4481. _tcsncpy((*ppAttr)->pADsValues->CaseIgnoreString,
  4482. sbstrName,
  4483. (nNameLen > nSamLength) ? nSamLength : nNameLen);
  4484. }
  4485. }
  4486. else
  4487. {
  4488. (*ppAttr)->pADsValues->CaseIgnoreString = argRecord.strValue;
  4489. }
  4490. } while (false);
  4491. return hr;
  4492. }
  4493. //+--------------------------------------------------------------------------
  4494. //
  4495. // Function: BuildUserSAMName
  4496. //
  4497. // Synopsis: If the -samname argument was defined use that, else compute
  4498. // the same name from the DN
  4499. //
  4500. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4501. // to the object being modified
  4502. // [refBasePathsInfo - IN] : reference to an instance of the
  4503. // CDSCmdBasePathsInfo class
  4504. // [refCredentialObject - IN] : reference to an instance of the
  4505. // CDSCmdCredentialObject class
  4506. // [pObjectEntry - IN] : pointer to an entry in the object table
  4507. // that defines the object we are modifying
  4508. // [argRecord - IN] : the argument record structure from the
  4509. // parser table that corresponds to this
  4510. // attribute
  4511. // [dwAttributeIdx - IN] : index into the attribute table for the
  4512. // object in which we are setting
  4513. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4514. // which this function will fill in
  4515. //
  4516. // Returns: HRESULT : S_OK if everything succeeded
  4517. // Otherwise an ADSI failure code
  4518. //
  4519. // History: 09-Oct-2000 JeffJon Created
  4520. //
  4521. //---------------------------------------------------------------------------
  4522. HRESULT BuildUserSAMName(PCWSTR pszDN,
  4523. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  4524. const CDSCmdCredentialObject& /*refCredentialObject*/,
  4525. const PDSOBJECTTABLEENTRY pObjectEntry,
  4526. const ARG_RECORD& argRecord,
  4527. DWORD dwAttributeIdx,
  4528. PADS_ATTR_INFO* ppAttr)
  4529. {
  4530. ENTER_FUNCTION_HR(LEVEL3_LOGGING, BuildUserSAMName, hr);
  4531. do // false loop
  4532. {
  4533. //
  4534. // Verify parameters
  4535. //
  4536. if (!pszDN ||
  4537. !pObjectEntry ||
  4538. !ppAttr)
  4539. {
  4540. ASSERT(pszDN);
  4541. ASSERT(pObjectEntry);
  4542. ASSERT(ppAttr);
  4543. DEBUG_OUTPUT(LEVEL3_LOGGING, L"Invalid args");
  4544. hr = E_INVALIDARG;
  4545. break;
  4546. }
  4547. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4548. (*ppAttr)->pADsValues = new ADSVALUE[1];
  4549. if (!(*ppAttr)->pADsValues)
  4550. {
  4551. hr = E_OUTOFMEMORY;
  4552. break;
  4553. }
  4554. (*ppAttr)->dwNumValues = 1;
  4555. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  4556. if (!argRecord.bDefined ||
  4557. !argRecord.strValue)
  4558. {
  4559. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Constructing sAMAccountName for group");
  4560. static const UINT nSamLength = 20;
  4561. //
  4562. // If the object type is group and the sAMAccountName
  4563. // was not specified, construct it from the DN or name
  4564. //
  4565. CComBSTR sbstrName;
  4566. hr = CPathCracker::GetObjectNameFromDN(pszDN,
  4567. sbstrName);
  4568. if (SUCCEEDED(hr))
  4569. {
  4570. UINT nNameLen = sbstrName.Length();
  4571. //
  4572. // Allocate enough memory for the string in the command args structure
  4573. // REVIEW_JEFFJON : this is being leaked
  4574. //
  4575. (*ppAttr)->pADsValues->CaseIgnoreString = (LPTSTR)LocalAlloc(LPTR, (nNameLen+2) * sizeof(WCHAR) );
  4576. if (!(*ppAttr)->pADsValues->CaseIgnoreString)
  4577. {
  4578. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Failed to allocate space for (*ppAttr)->pADsValues->CaseIgnoreString");
  4579. hr = E_OUTOFMEMORY;
  4580. break;
  4581. }
  4582. //
  4583. // Truncate the name if necessary but copy it to the command args structure
  4584. //
  4585. //Security Review:_tcsncpy won't null terminate. We are fine here since buffer is
  4586. //initialized to all zero. Filed a bug for making the code clear here.
  4587. //NTRAID#NTBUG9-573229-2002/03/08-hiteshr
  4588. _tcsncpy((*ppAttr)->pADsValues->CaseIgnoreString,
  4589. sbstrName,
  4590. (nNameLen > nSamLength) ? nSamLength : nNameLen);
  4591. }
  4592. }
  4593. else
  4594. {
  4595. (*ppAttr)->pADsValues->CaseIgnoreString = argRecord.strValue;
  4596. }
  4597. } while (false);
  4598. return hr;
  4599. }
  4600. //+--------------------------------------------------------------------------
  4601. //
  4602. // Function: SetComputerAccountType
  4603. //
  4604. // Synopsis: Sets the userAccountControl to make the object a workstation
  4605. //
  4606. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  4607. // to the object being modified
  4608. // [refBasePathsInfo - IN] : reference to an instance of the
  4609. // CDSCmdBasePathsInfo class
  4610. // [refCredentialObject - IN] : reference to an instance of the
  4611. // CDSCmdCredentialObject class
  4612. // [pObjectEntry - IN] : pointer to an entry in the object table
  4613. // that defines the object we are modifying
  4614. // [argRecord - IN] : the argument record structure from the
  4615. // parser table that corresponds to this
  4616. // attribute
  4617. // [dwAttributeIdx - IN] : index into the attribute table for the
  4618. // object in which we are setting
  4619. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  4620. // which this function will fill in
  4621. //
  4622. // Returns: HRESULT : S_OK if everything succeeded
  4623. // Otherwise an ADSI failure code
  4624. //
  4625. // History: 05-Dec-2000 JeffJon Created
  4626. //
  4627. //---------------------------------------------------------------------------
  4628. HRESULT SetComputerAccountType(PCWSTR pszDN,
  4629. const CDSCmdBasePathsInfo& /*refBasePathsInfo*/,
  4630. const CDSCmdCredentialObject& /*refCredentialObject*/,
  4631. const PDSOBJECTTABLEENTRY pObjectEntry,
  4632. const ARG_RECORD& /*argRecord*/,
  4633. DWORD dwAttributeIdx,
  4634. PADS_ATTR_INFO* ppAttr)
  4635. {
  4636. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetComputerAccountType, hr);
  4637. do // false loop
  4638. {
  4639. //
  4640. // Verify parameters
  4641. //
  4642. if (!pszDN ||
  4643. !pObjectEntry ||
  4644. !ppAttr)
  4645. {
  4646. ASSERT(pszDN);
  4647. ASSERT(pObjectEntry);
  4648. ASSERT(ppAttr);
  4649. hr = E_INVALIDARG;
  4650. break;
  4651. }
  4652. long lUserAccountControl = 0;
  4653. //
  4654. // If the userAccountControl hasn't already been read, do so now
  4655. //
  4656. if (0 == (DS_ATTRIBUTE_READ & pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags))
  4657. {
  4658. //
  4659. // Mark the table entry as read
  4660. //
  4661. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  4662. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  4663. (*ppAttr)->pADsValues = new ADSVALUE;
  4664. if (!(*ppAttr)->pADsValues)
  4665. {
  4666. hr = E_OUTOFMEMORY;
  4667. break;
  4668. }
  4669. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  4670. (*ppAttr)->dwNumValues = 1;
  4671. }
  4672. else
  4673. {
  4674. if (!pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues)
  4675. {
  4676. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  4677. hr = E_INVALIDARG;
  4678. break;
  4679. }
  4680. lUserAccountControl = pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer;
  4681. //
  4682. // Don't create a new entry in the ADS_ATTR_INFO array
  4683. //
  4684. *ppAttr = NULL;
  4685. }
  4686. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  4687. //
  4688. // Add in the required workstation flags
  4689. //
  4690. lUserAccountControl |= UF_WORKSTATION_TRUST_ACCOUNT | UF_ACCOUNTDISABLE | UF_PASSWD_NOTREQD;
  4691. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->Integer = lUserAccountControl;
  4692. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  4693. } while (false);
  4694. return hr;
  4695. }
  4696. //+--------------------------------------------------------------------------
  4697. //
  4698. // Function: GetErrorMessage
  4699. //
  4700. // Synopsis: Retrieves the error message associated with the HRESULT by
  4701. // using FormatMessage
  4702. //
  4703. // Arguments: [hr - IN] : HRESULT for which the error
  4704. // message is to be retrieved
  4705. // [sbstrErrorMessage - OUT] : Receives the error message
  4706. //
  4707. // Returns: bool : true if the message was formatted properly
  4708. // false otherwise
  4709. //
  4710. // History: 11-Sep-2000 JeffJon Created
  4711. //
  4712. //---------------------------------------------------------------------------
  4713. bool GetErrorMessage(HRESULT hr, CComBSTR& sbstrErrorMessage)
  4714. {
  4715. ENTER_FUNCTION(MINIMAL_LOGGING, GetErrorMessage);
  4716. HRESULT hrGetLast = S_OK;
  4717. HRESULT hrADSI = S_OK;
  4718. DWORD status = 0;
  4719. PTSTR ptzSysMsg = NULL;
  4720. //
  4721. // first check if we have extended ADs errors
  4722. //
  4723. if (hr != S_OK)
  4724. {
  4725. WCHAR Buf1[256], Buf2[256];
  4726. hrGetLast = ::ADsGetLastError(&status, Buf1, 256, Buf2, 256);
  4727. if ((hrGetLast == S_OK) && (status != ERROR_INVALID_DATA) && (status != 0))
  4728. {
  4729. hrADSI = status;
  4730. DEBUG_OUTPUT(MINIMAL_LOGGING,
  4731. L"ADsGetLastError returned hr = 0x%x",
  4732. hrADSI);
  4733. if (HRESULT_CODE(hrADSI) == ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER)
  4734. {
  4735. DEBUG_OUTPUT(MINIMAL_LOGGING,
  4736. L"Displaying special error message for ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER");
  4737. bool bLoadedMessage = sbstrErrorMessage.LoadString(::GetModuleHandle(NULL),
  4738. IDS_ERRMSG_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER);
  4739. if (bLoadedMessage)
  4740. {
  4741. return true;
  4742. }
  4743. }
  4744. }
  4745. }
  4746. //
  4747. // try to get error message for ADSI HRESULT
  4748. //
  4749. int nChars = 0;
  4750. if(hrADSI != S_OK)
  4751. {
  4752. //
  4753. // try the system first
  4754. //
  4755. //Security Review:FORMAT_MESSAGE_ALLOCATE_BUFFER is used.API will allocate
  4756. //correct buffer.
  4757. nChars = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
  4758. | FORMAT_MESSAGE_FROM_SYSTEM,
  4759. NULL,
  4760. hrADSI,
  4761. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4762. (PTSTR)&ptzSysMsg,
  4763. 0,
  4764. NULL);
  4765. if (nChars == 0)
  4766. {
  4767. //
  4768. //try ads errors
  4769. //
  4770. static HMODULE g_adsMod = 0;
  4771. if (0 == g_adsMod)
  4772. {
  4773. g_adsMod = GetModuleHandle (L"activeds.dll");
  4774. }
  4775. //Security Review:FORMAT_MESSAGE_ALLOCATE_BUFFER is used.API will allocate
  4776. //correct buffer.
  4777. nChars = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
  4778. | FORMAT_MESSAGE_FROM_HMODULE,
  4779. g_adsMod,
  4780. hrADSI,
  4781. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4782. (PTSTR)&ptzSysMsg,
  4783. 0,
  4784. NULL);
  4785. }
  4786. }
  4787. //
  4788. // Try to get error message for hr
  4789. //
  4790. if(nChars == 0)
  4791. {
  4792. //Security Review:FORMAT_MESSAGE_ALLOCATE_BUFFER is used.API will allocate
  4793. //correct buffer.
  4794. nChars = ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
  4795. | FORMAT_MESSAGE_FROM_SYSTEM,
  4796. NULL,
  4797. hr,
  4798. MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
  4799. (PTSTR)&ptzSysMsg,
  4800. 0,
  4801. NULL);
  4802. }
  4803. if (nChars > 0)
  4804. {
  4805. //
  4806. // Strip off the newline if there is one
  4807. //
  4808. PTSTR ptzTemp = ptzSysMsg;
  4809. while (ptzTemp && *ptzTemp != _T('\0'))
  4810. {
  4811. if (*ptzTemp == _T('\n') || *ptzTemp == _T('\r'))
  4812. {
  4813. *ptzTemp = _T('\0');
  4814. }
  4815. ptzTemp++;
  4816. }
  4817. sbstrErrorMessage = ptzSysMsg;
  4818. ::LocalFree(ptzSysMsg);
  4819. }
  4820. return (nChars > 0);
  4821. }
  4822. //+--------------------------------------------------------------------------
  4823. //
  4824. // Function: DisplayErrorMessage
  4825. //
  4826. // Synopsis: Displays the error message retrieved from GetErrorMessage
  4827. // to stderr. If GetErrorMessage fails, it displays the error
  4828. // code of the HRESULT
  4829. //
  4830. // Arguments: [pszCommand - IN]: the name of the command line executable
  4831. // [pszName - IN] : the name passed in as the target of the operation
  4832. // [hr - IN] : HRESULT for which the error
  4833. // message is to be retrieved
  4834. // [pszMessage - IN]: string of an additional message to be displayed
  4835. // at the end
  4836. //
  4837. // Returns: bool : true if the message was formatted and displayed properly
  4838. // false otherwise
  4839. //
  4840. // History: 11-Sep-2000 JeffJon Created
  4841. // 10-May-2001 JonN 256583 output DSCMD-escaped DN
  4842. //
  4843. //---------------------------------------------------------------------------
  4844. bool DisplayErrorMessage(PCWSTR pszCommand,
  4845. PCWSTR pszName,
  4846. HRESULT hr,
  4847. PCWSTR pszMessage)
  4848. {
  4849. bool bRet = true;
  4850. CComBSTR sbstrError;
  4851. CComBSTR sbstrFailed;
  4852. bool bGetError = false;
  4853. if (FAILED(hr))
  4854. {
  4855. bGetError = GetErrorMessage(hr, sbstrError);
  4856. }
  4857. bool bLoadFailed = sbstrFailed.LoadString(::GetModuleHandle(NULL), IDS_FAILED);
  4858. // JonN 5/10/01 256583 output DSCMD-escaped DN
  4859. CComBSTR sbstrOutputDN;
  4860. if (pszName && *pszName)
  4861. {
  4862. HRESULT hrToo = GetOutputDN( &sbstrOutputDN, pszName );
  4863. if (FAILED(hrToo))
  4864. {
  4865. ASSERT(FALSE);
  4866. }
  4867. else
  4868. {
  4869. pszName = sbstrOutputDN;
  4870. }
  4871. }
  4872. if (bGetError && bLoadFailed && pszName && pszMessage)
  4873. {
  4874. WriteStandardError(L"%s %s:%s:%s:%s\r\n",
  4875. pszCommand,
  4876. sbstrFailed,
  4877. pszName,
  4878. sbstrError,
  4879. pszMessage);
  4880. }
  4881. else if (bGetError && bLoadFailed && pszName && !pszMessage)
  4882. {
  4883. WriteStandardError(L"%s %s:%s:%s\r\n",
  4884. pszCommand,
  4885. sbstrFailed,
  4886. pszName,
  4887. sbstrError);
  4888. }
  4889. else if (bGetError && bLoadFailed && !pszName && pszMessage)
  4890. {
  4891. WriteStandardError(L"%s %s:%s:%s\r\n",
  4892. pszCommand,
  4893. sbstrFailed,
  4894. sbstrError,
  4895. pszMessage);
  4896. }
  4897. else if (bGetError && bLoadFailed && !pszName && !pszMessage)
  4898. {
  4899. WriteStandardError(L"%s %s:%s\r\n",
  4900. pszCommand,
  4901. sbstrFailed,
  4902. sbstrError);
  4903. }
  4904. else if (!bGetError && bLoadFailed && !pszName && pszMessage)
  4905. {
  4906. WriteStandardError(L"%s %s:%s\r\n",
  4907. pszCommand,
  4908. sbstrFailed,
  4909. pszMessage);
  4910. }
  4911. else if (!bGetError && bLoadFailed && pszName && pszMessage)
  4912. {
  4913. WriteStandardError(L"%s %s:%s:%s\r\n",
  4914. pszCommand,
  4915. sbstrFailed,
  4916. pszName,
  4917. pszMessage);
  4918. }
  4919. else
  4920. {
  4921. WriteStandardError(L"Error code = 0x%x\r\n", hr);
  4922. bRet = FALSE;
  4923. }
  4924. DisplayUsageHelp(pszCommand);
  4925. return bRet;
  4926. }
  4927. //+--------------------------------------------------------------------------
  4928. //
  4929. // Function: DisplayErrorMessage
  4930. //
  4931. // Synopsis: Displays the error message retrieved from GetErrorMessage
  4932. // to stderr. If GetErrorMessage fails, it displays the error
  4933. // code of the HRESULT
  4934. //
  4935. // Arguments: [pszCommand - IN]: the name of the command line executable
  4936. // [pszName - IN] : the name passed in as the target of the operation
  4937. // [hr - IN] : HRESULT for which the error
  4938. // message is to be retrieved
  4939. // [nStringID - IN] : Resource ID an additional message to be displayed
  4940. // at the end
  4941. //
  4942. // Returns: bool : true if the message was formatted and displayed properly
  4943. // false otherwise
  4944. //
  4945. // History: 11-Sep-2000 JeffJon Created
  4946. //
  4947. //---------------------------------------------------------------------------
  4948. bool DisplayErrorMessage(PCWSTR pszCommand,
  4949. PCWSTR pszName,
  4950. HRESULT hr,
  4951. UINT nStringID)
  4952. {
  4953. CComBSTR sbstrMessage;
  4954. bool bLoadString = sbstrMessage.LoadString(::GetModuleHandle(NULL), nStringID);
  4955. if (bLoadString)
  4956. {
  4957. return DisplayErrorMessage(pszCommand, pszName, hr, sbstrMessage);
  4958. }
  4959. return DisplayErrorMessage(pszCommand, pszName, hr);
  4960. }
  4961. //+--------------------------------------------------------------------------
  4962. //
  4963. // Function: DisplaySuccessMessage
  4964. //
  4965. // Synopsis: Displays a success message for the command
  4966. //
  4967. // Arguments: [pszCommand - IN]: the name of the command line executable
  4968. // [pszName - IN] : the name passed in as the target of the operation
  4969. //
  4970. // Returns: bool : true if the message was formatted and displayed properly
  4971. // false otherwise
  4972. //
  4973. // History: 11-Sep-2000 JeffJon Created
  4974. // 10-May-2001 JonN 256583 output DSCMD-escaped DN
  4975. //
  4976. //---------------------------------------------------------------------------
  4977. bool DisplaySuccessMessage(PCWSTR pszCommand,
  4978. PCWSTR pszName)
  4979. {
  4980. //
  4981. // Verify parameters
  4982. //
  4983. if (!pszCommand)
  4984. {
  4985. ASSERT(pszCommand);
  4986. return false;
  4987. }
  4988. CComBSTR sbstrSuccess;
  4989. if (!sbstrSuccess.LoadString(::GetModuleHandle(NULL), IDS_SUCCESS))
  4990. {
  4991. return false;
  4992. }
  4993. CComBSTR sbstrOutputDN;
  4994. if (!pszName)
  4995. {
  4996. WriteStandardOut(L"%s %s\r\n", pszCommand, sbstrSuccess);
  4997. }
  4998. else
  4999. {
  5000. // JonN 5/10/01 256583 output DSCMD-escaped DN
  5001. if (*pszName)
  5002. {
  5003. HRESULT hr = GetOutputDN( &sbstrOutputDN, pszName );
  5004. if (FAILED(hr))
  5005. {
  5006. ASSERT(FALSE);
  5007. }
  5008. else
  5009. {
  5010. pszName = sbstrOutputDN;
  5011. }
  5012. }
  5013. WriteStandardOut(L"%s %s:%s\r\n", pszCommand, sbstrSuccess, pszName);
  5014. }
  5015. return true;
  5016. }
  5017. //+--------------------------------------------------------------------------
  5018. //
  5019. // Function: WriteStringIDToStandardOut
  5020. //
  5021. // Synopsis: Loads the String Resource and displays on Standardout
  5022. //
  5023. // Arguments: nStringID : Resource ID
  5024. // Returns: bool : true if the message was formatted and displayed properly
  5025. // false otherwise
  5026. //
  5027. // History: 11-Sep-2000 hiteshr Created
  5028. //
  5029. //---------------------------------------------------------------------------
  5030. bool WriteStringIDToStandardOut(UINT nStringID)
  5031. {
  5032. CComBSTR sbstrSuccess;
  5033. if (!sbstrSuccess.LoadString(::GetModuleHandle(NULL), nStringID))
  5034. {
  5035. return false;
  5036. }
  5037. WriteStandardOut(sbstrSuccess);
  5038. return true;
  5039. }
  5040. //+--------------------------------------------------------------------------
  5041. //
  5042. // Function: WriteStringIDToStandardErr
  5043. //
  5044. // Synopsis: Loads the String Resource and displays on StandardErr
  5045. //
  5046. // Arguments: nStringID : Resource ID
  5047. // Returns: bool : true if the message was formatted and displayed properly
  5048. // false otherwise
  5049. //
  5050. // History: 14-June-2001 hiteshr Created
  5051. //
  5052. //---------------------------------------------------------------------------
  5053. bool WriteStringIDToStandardErr(UINT nStringID)
  5054. {
  5055. CComBSTR sbstrSuccess;
  5056. if (!sbstrSuccess.LoadString(::GetModuleHandle(NULL), nStringID))
  5057. {
  5058. return false;
  5059. }
  5060. WriteStandardError(sbstrSuccess);
  5061. return true;
  5062. }
  5063. //+---------------------------------------------------------------------------
  5064. //
  5065. // Function: ExpandUsername
  5066. //
  5067. // Synopsis: If the value in pwzValue contains %username% it gets expanded
  5068. // to be the sAMAccountName
  5069. //
  5070. // Arguments: [pwzValue IN/OUT] : string that may contain %username%
  5071. // [pwzSamName IN] : the SAM name to substitute
  5072. // [fExpanded OUT] : whether the value needed to be expanded or not
  5073. //
  5074. // Return: bool : true if the function succeeded, false otherwise
  5075. //
  5076. // History 27-Oct-2000 JeffJon Created
  5077. //----------------------------------------------------------------------------
  5078. bool ExpandUsername(PWSTR& pwzValue, PWSTR pwzSamName, bool& fExpanded)
  5079. {
  5080. ENTER_FUNCTION(LEVEL5_LOGGING, ExpandUsername);
  5081. PCWSTR pszUserToken = L"$username$";
  5082. //Security Review:This is fine.
  5083. unsigned int TokenLength = static_cast<unsigned int>(wcslen(pszUserToken));
  5084. bool bRet = false;
  5085. do // false loop
  5086. {
  5087. if (!pwzValue)
  5088. {
  5089. ASSERT(pwzValue);
  5090. break;
  5091. }
  5092. //
  5093. // This determines if expansion is needed
  5094. //
  5095. PWSTR pwzTokenStart = wcschr(pwzValue, pszUserToken[0]);
  5096. if (pwzTokenStart)
  5097. {
  5098. //Security Review:This is fine.
  5099. if ((wcslen(pwzTokenStart) >= TokenLength) &&
  5100. (_wcsnicmp(pwzTokenStart, pszUserToken, TokenLength) == 0))
  5101. {
  5102. fExpanded = true;
  5103. }
  5104. else
  5105. {
  5106. fExpanded = false;
  5107. bRet = true;
  5108. break;
  5109. }
  5110. }
  5111. else
  5112. {
  5113. fExpanded = false;
  5114. bRet = true;
  5115. break;
  5116. }
  5117. //
  5118. // If the samName isn't given return without doing anything
  5119. // This is useful to just determine if expansion is needed or not
  5120. //
  5121. if (!pwzSamName)
  5122. {
  5123. bRet = false;
  5124. break;
  5125. }
  5126. CComBSTR sbstrValue;
  5127. CComBSTR sbstrAfterToken;
  5128. while (pwzTokenStart)
  5129. {
  5130. *pwzTokenStart = L'\0';
  5131. sbstrValue = pwzValue;
  5132. if ((L'\0' != *pwzValue) && !sbstrValue.Length())
  5133. {
  5134. bRet = false;
  5135. break;
  5136. }
  5137. PWSTR pwzAfterToken = pwzTokenStart + TokenLength;
  5138. sbstrAfterToken = pwzAfterToken;
  5139. if ((L'\0' != *pwzAfterToken) && !sbstrAfterToken.Length())
  5140. {
  5141. bRet = false;
  5142. break;
  5143. }
  5144. delete pwzValue;
  5145. sbstrValue += pwzSamName;
  5146. if (!sbstrValue.Length())
  5147. {
  5148. bRet = false;
  5149. break;
  5150. }
  5151. sbstrValue += sbstrAfterToken;
  5152. if (!sbstrValue.Length())
  5153. {
  5154. bRet = false;
  5155. break;
  5156. }
  5157. pwzValue = new WCHAR[sbstrValue.Length() + 1];
  5158. if (!pwzValue)
  5159. {
  5160. bRet = false;
  5161. break;
  5162. }
  5163. //Security Review:Buffer is correctly allocated above.
  5164. wcscpy(pwzValue, sbstrValue);
  5165. pwzTokenStart = wcschr(pwzValue, pszUserToken[0]);
  5166. //This is fine.
  5167. if (!(pwzTokenStart &&
  5168. (wcslen(pwzTokenStart) >= TokenLength) &&
  5169. (_wcsnicmp(pwzTokenStart, pszUserToken, TokenLength) == 0)))
  5170. {
  5171. bRet = true;
  5172. break;
  5173. }
  5174. } // while
  5175. } while (false);
  5176. return bRet;
  5177. }
  5178. //+--------------------------------------------------------------------------
  5179. //
  5180. // Function: FillAttrInfoFromObjectEntryExpandUsername
  5181. //
  5182. // Synopsis: Fills the ADS_ATTR_INFO from the attribute table associated
  5183. // with the object entry and expands values containing %username%
  5184. //
  5185. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  5186. // to the object being modified
  5187. // [refBasePathsInfo - IN] : reference to an instance of the
  5188. // CDSCmdBasePathsInfo class
  5189. // [refCredentialObject - IN] : reference to an instance of the
  5190. // CDSCmdCredentialObject class
  5191. // [pObjectEntry - IN] : pointer to an entry in the object table
  5192. // that defines the object we are modifying
  5193. // [argRecord - IN] : the argument record structure from the
  5194. // parser table that corresponds to this
  5195. // attribute
  5196. // [dwAttributeIdx - IN] : index into the attribute table for the
  5197. // object in which we are setting
  5198. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  5199. // which this function will fill in
  5200. //
  5201. // Returns: HRESULT : S_OK if everything succeeded
  5202. // E_OUTOFMEMORY if we failed to allocate space for the value
  5203. // E_FAIL if we failed to format the value properly
  5204. //
  5205. // History: 27-Oct-2000 JeffJon Created
  5206. //
  5207. //---------------------------------------------------------------------------
  5208. HRESULT FillAttrInfoFromObjectEntryExpandUsername(PCWSTR pszDN,
  5209. const CDSCmdBasePathsInfo& refBasePathsInfo,
  5210. const CDSCmdCredentialObject& refCredentialObject,
  5211. const PDSOBJECTTABLEENTRY pObjectEntry,
  5212. const ARG_RECORD& argRecord,
  5213. DWORD dwAttributeIdx,
  5214. PADS_ATTR_INFO* ppAttr)
  5215. {
  5216. ENTER_FUNCTION_HR(LEVEL3_LOGGING, FillAttrInfoFromObjectEntryExpandUsername, hr);
  5217. do // false loop
  5218. {
  5219. //
  5220. // Verify Parameters
  5221. //
  5222. if (!pObjectEntry ||
  5223. !ppAttr ||
  5224. !pszDN)
  5225. {
  5226. ASSERT(pObjectEntry);
  5227. ASSERT(ppAttr);
  5228. ASSERT(pszDN);
  5229. hr = E_INVALIDARG;
  5230. break;
  5231. }
  5232. if (argRecord.strValue && argRecord.strValue[0] != L'\0')
  5233. {
  5234. //
  5235. // REVIEW_JEFFJON : this is being leaked!!!
  5236. //
  5237. //Security Review:This is fine.
  5238. PWSTR pszValue = new WCHAR[wcslen(argRecord.strValue) + 1];
  5239. if (!pszValue)
  5240. {
  5241. hr = E_OUTOFMEMORY;
  5242. break;
  5243. }
  5244. //Security Review:Buffer is correctly allocated above.
  5245. wcscpy(pszValue, argRecord.strValue);
  5246. //
  5247. // First check to see if we need to expand %username%
  5248. //
  5249. CComBSTR sbstrSamName;
  5250. bool bExpandNeeded = false;
  5251. ExpandUsername(pszValue, NULL, bExpandNeeded);
  5252. if (bExpandNeeded)
  5253. {
  5254. DEBUG_OUTPUT(LEVEL5_LOGGING, L"%username% expansion required. Retrieving sAMAccountName...");
  5255. //
  5256. // Retrieve the sAMAccountName of the object and then expand the %username%
  5257. //
  5258. CComBSTR sbstrPath;
  5259. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  5260. CComPtr<IADs> spADs;
  5261. hr = DSCmdOpenObject(refCredentialObject,
  5262. sbstrPath,
  5263. IID_IADs,
  5264. (void**)&spADs,
  5265. true);
  5266. if (FAILED(hr))
  5267. {
  5268. break;
  5269. }
  5270. CComVariant var;
  5271. hr = spADs->Get(CComBSTR(L"sAMAccountName"), &var);
  5272. if (FAILED(hr))
  5273. {
  5274. DEBUG_OUTPUT(MINIMAL_LOGGING,
  5275. L"Failed to get sAMAccountName: hr = 0x%x",
  5276. hr);
  5277. break;
  5278. }
  5279. ASSERT(var.vt == VT_BSTR);
  5280. sbstrSamName = var.bstrVal;
  5281. DEBUG_OUTPUT(LEVEL5_LOGGING,
  5282. L"sAMAccountName = %w",
  5283. sbstrSamName);
  5284. //
  5285. // Now expand the username to the sAMAccountName
  5286. //
  5287. if (!ExpandUsername(pszValue, sbstrSamName, bExpandNeeded))
  5288. {
  5289. DEBUG_OUTPUT(MINIMAL_LOGGING, L"Failed to expand %username%");
  5290. hr = E_OUTOFMEMORY;
  5291. break;
  5292. }
  5293. }
  5294. switch (argRecord.fType)
  5295. {
  5296. case ARG_TYPE_STR :
  5297. DEBUG_OUTPUT(LEVEL3_LOGGING, L"argRecord.fType = ARG_TYPE_STR");
  5298. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  5299. //
  5300. // REVIEW_JEFFJON : this is being leaked!
  5301. //
  5302. (*ppAttr)->pADsValues = new ADSVALUE[1];
  5303. if ((*ppAttr)->pADsValues)
  5304. {
  5305. (*ppAttr)->dwNumValues = 1;
  5306. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  5307. switch ((*ppAttr)->dwADsType)
  5308. {
  5309. case ADSTYPE_DN_STRING :
  5310. {
  5311. //
  5312. // Lets bind to be sure the object exists
  5313. //
  5314. CComBSTR sbstrObjPath;
  5315. refBasePathsInfo.ComposePathFromDN(pszValue, sbstrObjPath);
  5316. CComPtr<IADs> spIADs;
  5317. hr = DSCmdOpenObject(refCredentialObject,
  5318. sbstrObjPath,
  5319. IID_IADs,
  5320. (void**)&spIADs,
  5321. true);
  5322. if (FAILED(hr))
  5323. {
  5324. DEBUG_OUTPUT(LEVEL3_LOGGING, L"DN object doesn't exist. %s", pszValue);
  5325. break;
  5326. }
  5327. (*ppAttr)->pADsValues->DNString = pszValue;
  5328. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_DN_STRING = %s", pszValue);
  5329. }
  5330. break;
  5331. case ADSTYPE_CASE_EXACT_STRING :
  5332. (*ppAttr)->pADsValues->CaseExactString = pszValue;
  5333. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_CASE_EXACT_STRING = %s", pszValue);
  5334. break;
  5335. case ADSTYPE_CASE_IGNORE_STRING :
  5336. (*ppAttr)->pADsValues->CaseIgnoreString = pszValue;
  5337. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_CASE_IGNORE_STRING = %s", pszValue);
  5338. break;
  5339. case ADSTYPE_PRINTABLE_STRING :
  5340. (*ppAttr)->pADsValues->PrintableString = pszValue;
  5341. DEBUG_OUTPUT(LEVEL3_LOGGING, L"ADSTYPE_PRINTABLE_STRING = %s", pszValue);
  5342. break;
  5343. default :
  5344. hr = E_INVALIDARG;
  5345. break;
  5346. }
  5347. //
  5348. // Set the attribute dirty
  5349. //
  5350. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  5351. }
  5352. break;
  5353. default:
  5354. hr = E_INVALIDARG;
  5355. break;
  5356. }
  5357. }
  5358. else
  5359. {
  5360. DEBUG_OUTPUT(LEVEL3_LOGGING, L"No value present, changing control code to ADS_ATTR_CLEAR");
  5361. //
  5362. // Clear the attribute
  5363. //
  5364. (*ppAttr)->dwControlCode = ADS_ATTR_CLEAR;
  5365. (*ppAttr)->dwNumValues = 0;
  5366. //
  5367. // Set the attribute dirty
  5368. //
  5369. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_DIRTY;
  5370. }
  5371. } while (false);
  5372. return hr;
  5373. }
  5374. //+--------------------------------------------------------------------------
  5375. //
  5376. // Function: BindToFSMOHolder
  5377. //
  5378. // Synopsis: Binds to the appropriate object which can be used to find a
  5379. // particular FSMO owner
  5380. //
  5381. // Arguments: [refBasePathsInfo - IN] : reference to the base paths info object
  5382. // [refCredObject - IN] : reference to the credential management object
  5383. // [fsmoType - IN] : type of the FSMO we are searching for
  5384. // [refspIADs - OUT] : interface to the object that will be
  5385. // used to start a search for the FSMO owner
  5386. //
  5387. // Returns: HRESULT : S_OK if everything succeeded
  5388. // E_INVALIDARG if an invalid FSMO type was passed
  5389. // Otherwise an ADSI failure code
  5390. //
  5391. // History: 13-Dec-2000 JeffJon Created
  5392. //
  5393. //---------------------------------------------------------------------------
  5394. HRESULT BindToFSMOHolder(IN const CDSCmdBasePathsInfo& refBasePathsInfo,
  5395. IN const CDSCmdCredentialObject& refCredObject,
  5396. IN FSMO_TYPE fsmoType,
  5397. OUT CComPtr<IADs>& refspIADs)
  5398. {
  5399. ENTER_FUNCTION_HR(LEVEL3_LOGGING, BindToFSMOHolder, hr);
  5400. refspIADs = 0;
  5401. CComBSTR sbstrDN;
  5402. switch (fsmoType)
  5403. {
  5404. case SCHEMA_FSMO:
  5405. {
  5406. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5407. L"FSMO_TYPE = SCHEMA_FSMO");
  5408. sbstrDN = refBasePathsInfo.GetSchemaNamingContext();
  5409. break;
  5410. }
  5411. case RID_POOL_FSMO:
  5412. {
  5413. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5414. L"FSMO_TYPE = RID_POOL_FSMO");
  5415. sbstrDN = refBasePathsInfo.GetDefaultNamingContext();
  5416. CComBSTR sbstrPath;
  5417. refBasePathsInfo.ComposePathFromDN(sbstrDN, sbstrPath);
  5418. CComPtr<IADs> spIADsDefault;
  5419. hr = DSCmdOpenObject(refCredObject,
  5420. sbstrPath,
  5421. IID_IADs,
  5422. (void**)&spIADsDefault,
  5423. true);
  5424. if (FAILED(hr))
  5425. {
  5426. break;
  5427. }
  5428. CComVariant var;
  5429. hr = spIADsDefault->Get(g_bstrIDManagerReference, &var);
  5430. if (FAILED(hr))
  5431. {
  5432. break;
  5433. }
  5434. ASSERT(var.vt == VT_BSTR);
  5435. sbstrDN = var.bstrVal;
  5436. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5437. L"rIDManagerReference = %s",
  5438. sbstrDN);
  5439. break;
  5440. }
  5441. case PDC_FSMO:
  5442. {
  5443. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5444. L"FSMO_TYPE = PDC_FSMO");
  5445. sbstrDN = refBasePathsInfo.GetDefaultNamingContext();
  5446. break;
  5447. }
  5448. case INFRASTUCTURE_FSMO:
  5449. {
  5450. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5451. L"FSMO_TYPE = INFRASTUCTURE_FSMO");
  5452. sbstrDN = refBasePathsInfo.GetDefaultNamingContext();
  5453. break;
  5454. }
  5455. case DOMAIN_NAMING_FSMO:
  5456. {
  5457. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5458. L"FSMO_TYPE = DOMAIN_NAMING_FSMO");
  5459. sbstrDN = L"CN=Partitions,";
  5460. sbstrDN += refBasePathsInfo.GetConfigurationNamingContext();
  5461. break;
  5462. }
  5463. default:
  5464. ASSERT(FALSE);
  5465. hr = E_INVALIDARG;
  5466. break;
  5467. }
  5468. if (SUCCEEDED(hr))
  5469. {
  5470. CComBSTR sbstrPath;
  5471. refBasePathsInfo.ComposePathFromDN(sbstrDN, sbstrPath);
  5472. hr = DSCmdOpenObject(refCredObject,
  5473. sbstrPath,
  5474. IID_IADs,
  5475. (void**)&refspIADs,
  5476. true);
  5477. }
  5478. return hr;
  5479. }
  5480. //+--------------------------------------------------------------------------
  5481. //
  5482. // Function: FindFSMOOwner
  5483. //
  5484. // Synopsis:
  5485. //
  5486. // Arguments: [refBasePathsInfo - IN] : reference to the base paths info object
  5487. // [refCredObject - IN] : reference to the credential management object
  5488. // [fsmoType - IN] : type of the FSMO we are searching for
  5489. // [refspIADs - OUT] : interface to the object that will be
  5490. // used to start a search for the FSMO owner
  5491. //
  5492. // Returns: HRESULT : S_OK if everything succeeded
  5493. // Otherwise an ADSI failure code
  5494. //
  5495. // History: 13-Dec-2000 JeffJon Created
  5496. //
  5497. //---------------------------------------------------------------------------
  5498. HRESULT FindFSMOOwner(IN const CDSCmdBasePathsInfo& refBasePathsInfo,
  5499. IN const CDSCmdCredentialObject& refCredObject,
  5500. IN FSMO_TYPE fsmoType,
  5501. OUT CComBSTR& refsbstrServerDN)
  5502. {
  5503. ENTER_FUNCTION_HR(LEVEL3_LOGGING, FindFSMOOwner, hr);
  5504. refsbstrServerDN.Empty();
  5505. static const int nMaxReferrals = 10;
  5506. int nIterations = 0;
  5507. //
  5508. // We will start the search with the current server
  5509. //
  5510. CComBSTR sbstrNextServer;
  5511. sbstrNextServer = refBasePathsInfo.GetServerName();
  5512. do
  5513. {
  5514. //
  5515. // Initialize a new base paths info object on each iteration
  5516. //
  5517. CDSCmdBasePathsInfo nextPathsInfo;
  5518. hr = nextPathsInfo.InitializeFromName(refCredObject,
  5519. sbstrNextServer,
  5520. true);
  5521. if (FAILED(hr))
  5522. {
  5523. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5524. L"Failed to initialize the base paths info for %s: hr = 0x%x",
  5525. sbstrNextServer,
  5526. hr);
  5527. break;
  5528. }
  5529. //
  5530. // Now bind to the fsmo holder for that server
  5531. //
  5532. CComPtr<IADs> spIADs;
  5533. hr = BindToFSMOHolder(nextPathsInfo,
  5534. refCredObject,
  5535. fsmoType,
  5536. spIADs);
  5537. if (FAILED(hr))
  5538. {
  5539. break;
  5540. }
  5541. //
  5542. // Get the fSMORoleOwner property
  5543. //
  5544. CComVariant fsmoRoleOwnerProperty;
  5545. hr = spIADs->Get(g_bstrFSMORoleOwner, &fsmoRoleOwnerProperty);
  5546. if (FAILED(hr))
  5547. {
  5548. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5549. L"Failed to get the fSMORoleOwner: hr = 0x%x",
  5550. hr);
  5551. break;
  5552. }
  5553. //
  5554. // The result here is in the form, "CN=NTDS Settings,CN=Machine,CN=..."
  5555. // we need to just have "CN=Machine,CN=..."
  5556. //
  5557. CComBSTR sbstrMachineOwner;
  5558. hr = CPathCracker::GetParentDN(fsmoRoleOwnerProperty.bstrVal, sbstrMachineOwner);
  5559. if (FAILED(hr))
  5560. {
  5561. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5562. L"Failed to get the parent DN of the FSMORoleOwner: hr = 0x%x",
  5563. hr);
  5564. break;
  5565. }
  5566. CComBSTR sbstrMachinePath;
  5567. nextPathsInfo.ComposePathFromDN(sbstrMachineOwner, sbstrMachinePath);
  5568. //
  5569. // Bind to the server object so we can get the dnsHostName to compare to the server name
  5570. //
  5571. CComPtr<IADs> spIADsServer;
  5572. hr = DSCmdOpenObject(refCredObject,
  5573. sbstrMachinePath,
  5574. IID_IADs,
  5575. (void**)&spIADsServer,
  5576. true);
  5577. if (FAILED(hr))
  5578. {
  5579. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5580. L"Failed to bind to server object: hr = 0x%x",
  5581. hr);
  5582. break;
  5583. }
  5584. //
  5585. // Get the DNS host name
  5586. //
  5587. CComVariant varServerName;
  5588. hr = spIADsServer->Get(g_bstrDNSHostName, &varServerName);
  5589. if (FAILED(hr))
  5590. {
  5591. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5592. L"Failed to get the dNSHostName: hr = 0x%x",
  5593. hr);
  5594. break;
  5595. }
  5596. ASSERT(varServerName.vt == VT_BSTR);
  5597. sbstrNextServer = varServerName.bstrVal;
  5598. //
  5599. // If the server name in the dNSHostName attribute matches the current
  5600. // base paths info, then we found the owner
  5601. //
  5602. //Security Review:This is fine.
  5603. if (0 == _wcsicmp(sbstrNextServer, nextPathsInfo.GetServerName()))
  5604. {
  5605. //
  5606. // We found it
  5607. //
  5608. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5609. L"The role owner is %s",
  5610. sbstrNextServer);
  5611. refsbstrServerDN = sbstrMachineOwner;
  5612. break;
  5613. }
  5614. ++nIterations;
  5615. } while (nIterations < nMaxReferrals);
  5616. return hr;
  5617. }
  5618. //+--------------------------------------------------------------------------
  5619. //
  5620. // Function: ValidateAndModifySAMName
  5621. //
  5622. // Synopsis: Looks for any illegal characters in the SamAccountName and
  5623. // converts them to the replacementChar
  5624. //
  5625. // Arguments: [pszSAMName - IN/OUT] : pointer to a string that contains the SamAccountName
  5626. // illegal characters will be replaced
  5627. // [pszInvalidChars - IN] : string containing the illegal characters
  5628. //
  5629. // Returns: HRESULT : S_OK if the name was valid and no characters had to be replaced
  5630. // S_FALSE if the name contained invalid characters that were replaced
  5631. // E_INVALIDARG
  5632. //
  5633. // History: 21-Feb-2001 JeffJon Created
  5634. //
  5635. //---------------------------------------------------------------------------
  5636. HRESULT ValidateAndModifySAMName(PWSTR pszSAMName,
  5637. PCWSTR pszInvalidChars)
  5638. {
  5639. ENTER_FUNCTION_HR(LEVEL3_LOGGING, ValidateAndModifySAMName, hr);
  5640. static const WCHAR replacementChar = L'_';
  5641. do
  5642. {
  5643. if (!pszSAMName ||
  5644. !pszInvalidChars)
  5645. {
  5646. ASSERT(pszSAMName);
  5647. ASSERT(pszInvalidChars);
  5648. hr = E_INVALIDARG;
  5649. break;
  5650. }
  5651. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5652. L"SAM name before: %s",
  5653. pszSAMName);
  5654. //Security Review:This is fine.pszInvalidChars is Null Terminated.
  5655. for (size_t idx = 0; idx < wcslen(pszInvalidChars); ++idx)
  5656. {
  5657. WCHAR* illegalChar = 0;
  5658. do
  5659. {
  5660. illegalChar = wcschr(pszSAMName, pszInvalidChars[idx]);
  5661. if (illegalChar)
  5662. {
  5663. *illegalChar = replacementChar;
  5664. hr = S_FALSE;
  5665. }
  5666. } while (illegalChar);
  5667. }
  5668. } while (false);
  5669. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5670. L"SAM name after: %s",
  5671. pszSAMName);
  5672. return hr;
  5673. }
  5674. //+--------------------------------------------------------------------------
  5675. //
  5676. // Class: GetEscapedElement
  5677. //
  5678. // Purpose: Calls IADsPathname::GetEscapedElement. Uses LocalAlloc.
  5679. //
  5680. // History: 28-Apr-2001 JonN Created
  5681. //
  5682. //---------------------------------------------------------------------------
  5683. HRESULT GetEscapedElement( OUT PWSTR* ppszOut, IN PCWSTR pszIn )
  5684. {
  5685. ENTER_FUNCTION_HR(LEVEL3_LOGGING, GetEscapedElement, hr);
  5686. CPathCracker pathCracker;
  5687. CComBSTR sbstrIn = pszIn;
  5688. CComBSTR sbstrEscaped;
  5689. if (sbstrIn.Length() > 0) // handle empty path component
  5690. {
  5691. hr = pathCracker.GetEscapedElement(0,
  5692. sbstrIn,
  5693. &sbstrEscaped);
  5694. if (FAILED(hr))
  5695. return hr;
  5696. else if (!sbstrEscaped)
  5697. return E_FAIL;
  5698. }
  5699. *ppszOut = (LPWSTR)LocalAlloc(LPTR, (sbstrEscaped.Length()+1) * sizeof(WCHAR) );
  5700. if (NULL == *ppszOut)
  5701. return E_OUTOFMEMORY;
  5702. //Security Review:This is fine. Memory is correctly allocted.
  5703. if (sbstrIn.Length() > 0) // handle empty path component
  5704. wcscpy( *ppszOut, sbstrEscaped );
  5705. return hr;
  5706. } // GetEscapedElement
  5707. //+--------------------------------------------------------------------------
  5708. //
  5709. // Class: GetOutputDN
  5710. //
  5711. // Purpose: Converts an ADSI-escaped DN to one with DSCMD input escaping.
  5712. // This way, the output DN can be piped as input to another
  5713. // DSCMD command.
  5714. //
  5715. // History: 08-May-2001 JonN Created
  5716. //
  5717. //---------------------------------------------------------------------------
  5718. HRESULT GetOutputDN( OUT BSTR* pbstrOut, IN PCWSTR pszIn )
  5719. {
  5720. ENTER_FUNCTION_HR(LEVEL3_LOGGING, GetOutputDN, hr);
  5721. if (NULL == pszIn || L'\0' == *pszIn)
  5722. {
  5723. *pbstrOut = SysAllocString(L"");
  5724. return (NULL == *pbstrOut) ? E_OUTOFMEMORY : S_OK;
  5725. }
  5726. CPathCracker pathCracker;
  5727. CComBSTR sbstrIn = pszIn;
  5728. hr = pathCracker.Set(sbstrIn, ADS_SETTYPE_DN);
  5729. if (FAILED(hr))
  5730. {
  5731. ASSERT(FALSE);
  5732. return hr;
  5733. }
  5734. long lnNumPathElements = 0;
  5735. hr = pathCracker.GetNumElements( &lnNumPathElements );
  5736. if (FAILED(hr))
  5737. {
  5738. ASSERT(FALSE);
  5739. return hr;
  5740. }
  5741. else if (0 >= lnNumPathElements)
  5742. {
  5743. ASSERT(FALSE);
  5744. return E_FAIL;
  5745. }
  5746. hr = pathCracker.put_EscapedMode( ADS_ESCAPEDMODE_OFF_EX );
  5747. if (FAILED(hr))
  5748. {
  5749. ASSERT(FALSE);
  5750. return hr;
  5751. }
  5752. CComBSTR sbstrOut;
  5753. CComBSTR sbstrComma( L"," );
  5754. for (long lnPathElement = 0;
  5755. lnPathElement < lnNumPathElements;
  5756. lnPathElement++)
  5757. {
  5758. CComBSTR sbstrElement;
  5759. hr = pathCracker.GetElement( lnPathElement, &sbstrElement );
  5760. if (FAILED(hr))
  5761. {
  5762. ASSERT(FALSE);
  5763. return hr;
  5764. }
  5765. // re-escape sbstrElement
  5766. // JonN 10/17/01 476225 0x000A -> "\0A"
  5767. //Security Review:This is fine.
  5768. CComBSTR sbstrEscapedElement( (sbstrElement.Length()+1) * 3 );
  5769. ::ZeroMemory( (BSTR)sbstrEscapedElement,
  5770. (sbstrElement.Length()+1) * 3 * sizeof(WCHAR) );
  5771. LPWSTR pszEscapedElement = sbstrEscapedElement;
  5772. for (LPWSTR pszElement = sbstrElement;
  5773. L'\0' != *pszElement;
  5774. pszElement++)
  5775. {
  5776. if (*pszElement < 0x0020)
  5777. {
  5778. // JonN 9/7/01 CRLF bug
  5779. // JonN 10/17/01 476225 0x000A -> "\0A"
  5780. *(pszEscapedElement++) = L'\\';
  5781. *(pszEscapedElement++) = (*pszElement >= 0x0010) ? L'1' : L'0';
  5782. *(pszEscapedElement++) = L"0123456789ABCDEF"[(*pszElement % 0x0010)];
  5783. }
  5784. else switch (*pszElement)
  5785. {
  5786. case L',':
  5787. case L'\\':
  5788. *(pszEscapedElement++) = L'\\';
  5789. // fall through
  5790. default:
  5791. *(pszEscapedElement++) = *pszElement;
  5792. break;
  5793. }
  5794. }
  5795. if (!!sbstrOut)
  5796. sbstrOut += sbstrComma;
  5797. // cast to avoid CComBSTR::operator+= "bug"
  5798. sbstrOut += (BSTR)sbstrEscapedElement;
  5799. }
  5800. *pbstrOut = sbstrOut.Detach();
  5801. return hr;
  5802. } // GetOutputDN
  5803. //+--------------------------------------------------------------------------
  5804. //
  5805. // Function: GetQuotedDN
  5806. //
  5807. // Purpose: Takes the give DN and surrounds it with quotes
  5808. //
  5809. // Returns: the quoted DN
  5810. //
  5811. // History: 10-Oct-2002 jeffjon Created
  5812. //
  5813. //---------------------------------------------------------------------------
  5814. CComBSTR GetQuotedDN(PWSTR pszDN)
  5815. {
  5816. ENTER_FUNCTION(LEVEL3_LOGGING, GetQuotedDN);
  5817. CComBSTR result = L"\"";
  5818. result += pszDN;
  5819. result += L"\"";
  5820. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5821. L"Quoted DN: %s",
  5822. (BSTR)result);
  5823. return result;
  5824. }
  5825. //+--------------------------------------------------------------------------
  5826. //
  5827. // Class: ValidateDNSyntax
  5828. //
  5829. // Purpose: Validates each string in the null separated list as having
  5830. // DN syntax
  5831. //
  5832. // Returns: The count of valid DNs in the list
  5833. //
  5834. // History: 12-Oct-2001 JeffJon Created
  5835. //
  5836. //---------------------------------------------------------------------------
  5837. UINT ValidateDNSyntax(IN PWSTR* ppszArray, UINT nStrings)
  5838. {
  5839. ENTER_FUNCTION(MINIMAL_LOGGING, ValidateDNSyntax);
  5840. if (!ppszArray ||
  5841. nStrings < 1)
  5842. {
  5843. ASSERT(ppszArray);
  5844. ASSERT(nStrings >= 1);
  5845. return 0;
  5846. }
  5847. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5848. L"nStrings = %d",
  5849. nStrings);
  5850. // Use a single path cracker for performance reasons
  5851. CPathCracker pathCracker;
  5852. UINT result = 0;
  5853. for (UINT idx = 0; idx < nStrings; ++idx)
  5854. {
  5855. if (ppszArray[idx])
  5856. {
  5857. HRESULT hr = pathCracker.Set(CComBSTR(ppszArray[idx]), ADS_SETTYPE_DN);
  5858. if (SUCCEEDED(hr))
  5859. {
  5860. ++result;
  5861. }
  5862. }
  5863. }
  5864. DEBUG_OUTPUT(LEVEL3_LOGGING,
  5865. L"result = %d",
  5866. result);
  5867. return result;
  5868. }
  5869. //+--------------------------------------------------------------------------
  5870. //
  5871. // Function: IsServerGC
  5872. //
  5873. // Purpose: Checks if server is Global Catalog
  5874. //
  5875. // Returns: TRUE if GC else flase
  5876. //
  5877. // History: 05-Jan-2002 hiteshr Created
  5878. //
  5879. //---------------------------------------------------------------------------
  5880. BOOL
  5881. IsServerGC(LPCWSTR pszServerName,
  5882. CDSCmdCredentialObject& refCredentialObject)
  5883. {
  5884. if(!pszServerName)
  5885. {
  5886. ASSERT(pszServerName);
  5887. return FALSE;
  5888. }
  5889. HRESULT hr = S_OK;
  5890. //Bind to RootDSE
  5891. CComPtr<IADs> m_spRootDSE;
  5892. CComBSTR bstrRootDSEPath = L"LDAP://";
  5893. bstrRootDSEPath += pszServerName;
  5894. bstrRootDSEPath += L"/RootDSE";
  5895. hr = DSCmdOpenObject(refCredentialObject,
  5896. bstrRootDSEPath,
  5897. IID_IADs,
  5898. (void**)&m_spRootDSE,
  5899. false);
  5900. if(FAILED(hr))
  5901. return FALSE;
  5902. //Read isGlobatCatalogReady attribute
  5903. VARIANT Default;
  5904. VariantInit(&Default);
  5905. hr = m_spRootDSE->Get (CComBSTR(L"isGlobalCatalogReady"), &Default);
  5906. if(FAILED(hr))
  5907. return FALSE;
  5908. BOOL bRet = FALSE;
  5909. ASSERT(Default.vt == VT_BSTR);
  5910. //Security Review:This is fine.
  5911. if(_wcsicmp(Default.bstrVal,L"TRUE") == 0)
  5912. bRet= TRUE;
  5913. ::VariantClear(&Default);
  5914. return bRet;
  5915. }
  5916. //+--------------------------------------------------------------------------
  5917. //
  5918. // Function: SetAccountEntry
  5919. //
  5920. // Synopsis: Sets acct to make it a SID
  5921. //
  5922. // Arguments: [pszDN - IN] : pointer to a string containing the DN
  5923. // to the object being modified
  5924. // [refBasePathsInfo - IN] : reference to an instance of the
  5925. // CDSCmdBasePathsInfo class
  5926. // [refCredentialObject - IN] : reference to an instance of the
  5927. // CDSCmdCredentialObject class
  5928. // [pObjectEntry - IN] : pointer to an entry in the object table
  5929. // that defines the object we are modifying
  5930. // [argRecord - IN] : the argument record structure from the
  5931. // parser table that corresponds to this
  5932. // attribute
  5933. // [dwAttributeIdx - IN] : index into the attribute table for the
  5934. // object in which we are setting
  5935. // [ppAttr - IN/OUT] : pointer to the ADS_ATTR_INFO structure
  5936. // which this function will fill in
  5937. //
  5938. // Returns: HRESULT : S_OK if everything succeeded
  5939. // Otherwise an ADSI failure code
  5940. //
  5941. // History: 02-Aug-2002 ronmart Created
  5942. //
  5943. //---------------------------------------------------------------------------
  5944. HRESULT SetAccountEntry(PCWSTR ,
  5945. const CDSCmdBasePathsInfo& refBasePathsInfo,
  5946. const CDSCmdCredentialObject& refCredentialObject,
  5947. const PDSOBJECTTABLEENTRY pObjectEntry,
  5948. const ARG_RECORD& argRecord,
  5949. DWORD dwAttributeIdx,
  5950. PADS_ATTR_INFO* ppAttr)
  5951. {
  5952. ENTER_FUNCTION_HR(LEVEL3_LOGGING, SetAccountEntry, hr);
  5953. LPWSTR lpszDN = NULL;
  5954. do // false loop
  5955. {
  5956. //
  5957. // Verify parameters
  5958. //
  5959. if (!pObjectEntry ||!ppAttr)
  5960. {
  5961. ASSERT(pObjectEntry);
  5962. ASSERT(ppAttr);
  5963. hr = E_INVALIDARG;
  5964. break;
  5965. }
  5966. // TODO: Need to provide the first param
  5967. hr = ConvertTrusteeToDN(NULL, argRecord.strValue, &lpszDN);
  5968. if(FAILED(hr))
  5969. {
  5970. break;
  5971. }
  5972. PSID pSid = NULL;
  5973. hr = GetDNSid(lpszDN, refBasePathsInfo,
  5974. refCredentialObject, &pSid);
  5975. if(FAILED(hr))
  5976. {
  5977. break;
  5978. }
  5979. //
  5980. // Mark the table entry as read
  5981. //
  5982. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->dwFlags |= DS_ATTRIBUTE_READ;
  5983. // Allocate the ADSVALUE to the attr array
  5984. *ppAttr = &(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo);
  5985. (*ppAttr)->pADsValues = new ADSVALUE;
  5986. if (!(*ppAttr)->pADsValues)
  5987. {
  5988. hr = E_OUTOFMEMORY;
  5989. break;
  5990. }
  5991. (*ppAttr)->pADsValues->dwType = (*ppAttr)->dwADsType;
  5992. (*ppAttr)->dwNumValues = 1;
  5993. ASSERT(pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues);
  5994. // Set the ADSVALUE fields
  5995. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->OctetString.dwLength = GetLengthSid(pSid);
  5996. pObjectEntry->pAttributeTable[dwAttributeIdx]->pAttrDesc->adsAttrInfo.pADsValues->OctetString.lpValue = (LPBYTE) pSid;
  5997. } while (false);
  5998. if(lpszDN)
  5999. LocalFree(lpszDN);
  6000. return hr;
  6001. }
  6002. //+--------------------------------------------------------------------------
  6003. //
  6004. // Function: TranslateNameXForest
  6005. //
  6006. // Synopsis: Takes an account name and returns its SID
  6007. //
  6008. // Arguments: [szDomain - IN] : Domain where the account lives or NULL
  6009. //
  6010. // [lpAccountName - IN] : Account you want to translate
  6011. //
  6012. // [AccountNameFormat - IN]: Name format (can be unknown)
  6013. //
  6014. // [DesiredNameFormat - IN]: New name format
  6015. //
  6016. // [lpTranslatedName - OUT]: return name buffer
  6017. // (free with LocalFree)
  6018. //
  6019. // Returns: BOOL : TRUE if successful, else FALSE. Call GetLastError for
  6020. // more error info
  6021. //
  6022. // History: 19-Aug-2002 ronmart Created from
  6023. // ds\security\gina\gpconsole\gprsop\rsopwizard.cpp
  6024. //
  6025. //---------------------------------------------------------------------------
  6026. BOOL TranslateNameXForest(LPCWSTR szDomain, LPCWSTR lpAccountName,
  6027. DS_NAME_FORMAT AccountNameFormat,
  6028. DS_NAME_FORMAT DesiredNameFormat,
  6029. LPWSTR *lpTranslatedName)
  6030. {
  6031. ENTER_FUNCTION_HR(LEVEL3_LOGGING, TranslateNameXForest, hr);
  6032. DWORD dwErr = 0;
  6033. PDOMAIN_CONTROLLER_INFO pDCInfo = NULL;
  6034. BOOL bRetry = FALSE;
  6035. HANDLE hDS = NULL;
  6036. PDS_NAME_RESULT pResult = NULL;
  6037. BOOL bRet = FALSE;
  6038. LPWSTR szTransName = NULL;
  6039. DEBUG_OUTPUT(FULL_LOGGING,
  6040. L"TranslateNameXForest: Resolving name <%s> at Domain <%s>",
  6041. lpAccountName, szDomain ? szDomain : L"");
  6042. //
  6043. // get a DC and bind to it. Make sure to force rediscover a DC if the bind fails
  6044. //
  6045. for (;;)
  6046. {
  6047. dwErr = DsGetDcName( NULL, szDomain ? szDomain : L"", NULL, NULL,
  6048. DS_GC_SERVER_REQUIRED |
  6049. DS_DIRECTORY_SERVICE_REQUIRED |
  6050. DS_RETURN_DNS_NAME |
  6051. (bRetry ? DS_FORCE_REDISCOVERY : 0) |
  6052. 0, &pDCInfo);
  6053. if (dwErr == NO_ERROR)
  6054. {
  6055. dwErr = DsBind(pDCInfo->DomainControllerName, NULL, &hDS);
  6056. if (dwErr == NO_ERROR)
  6057. {
  6058. break;
  6059. }
  6060. else
  6061. {
  6062. DEBUG_OUTPUT(FULL_LOGGING,
  6063. L"TranslateNameXForest: Failed to bind to DC <%s> with error %d",
  6064. pDCInfo->DomainControllerName, dwErr );
  6065. NetApiBufferFree(pDCInfo);
  6066. pDCInfo = NULL;
  6067. }
  6068. }
  6069. else
  6070. {
  6071. DEBUG_OUTPUT(FULL_LOGGING,
  6072. L"TranslateNameXForest: Failed to get DC for domain <%s> with error %d",
  6073. szDomain ? szDomain : L"", dwErr );
  6074. }
  6075. //
  6076. // Failed to bind to a DC. bail
  6077. //
  6078. if (bRetry)
  6079. {
  6080. goto Exit;
  6081. }
  6082. bRetry = TRUE;
  6083. }
  6084. DEBUG_OUTPUT(FULL_LOGGING, L"TranslateNameXForest: DC selected is <%s>",
  6085. pDCInfo->DomainControllerName );
  6086. //
  6087. // Now crack names with the DC that is bound
  6088. //
  6089. dwErr = DsCrackNames( hDS,
  6090. DS_NAME_FLAG_TRUST_REFERRAL,
  6091. AccountNameFormat,
  6092. DesiredNameFormat,
  6093. 1,
  6094. &lpAccountName,
  6095. &pResult);
  6096. if (dwErr != DS_NAME_NO_ERROR)
  6097. {
  6098. DEBUG_OUTPUT(FULL_LOGGING,
  6099. L"TranslateNameXForest: Failed to crack names with error %d", dwErr );
  6100. goto Exit;
  6101. }
  6102. if ( pResult->cItems == 0 )
  6103. {
  6104. DEBUG_OUTPUT(FULL_LOGGING,
  6105. L"TranslateNameXForest: Failed to return enough result items" );
  6106. dwErr = ERROR_INVALID_DATA;
  6107. goto Exit;
  6108. }
  6109. if ( pResult->rItems[0].status == DS_NAME_NO_ERROR )
  6110. {
  6111. //
  6112. // In case of no error, return the resolved name
  6113. //
  6114. DWORD dwTransNameLength = 1 + lstrlen(pResult->rItems[0].pName);
  6115. szTransName = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * ( dwTransNameLength ));
  6116. if (!szTransName) {
  6117. DEBUG_OUTPUT(FULL_LOGGING,
  6118. L"TranslateNameXForest: Failed to allocate memory for domain" );
  6119. dwErr = GetLastError();
  6120. goto Exit;
  6121. }
  6122. HRESULT hr = StringCchCopy(szTransName, dwTransNameLength, pResult->rItems[0].pName);
  6123. if(FAILED(hr))
  6124. {
  6125. if (szTransName)
  6126. LocalFree(szTransName);
  6127. dwErr = HRESULT_CODE(hr);
  6128. goto Exit;
  6129. }
  6130. *lpTranslatedName = szTransName;
  6131. szTransName = NULL;
  6132. }
  6133. else if( pResult->rItems[0].status == DS_NAME_ERROR_TRUST_REFERRAL)
  6134. {
  6135. return TranslateNameXForest(pResult->rItems[0].pDomain,
  6136. lpAccountName,
  6137. AccountNameFormat,
  6138. DesiredNameFormat,
  6139. lpTranslatedName);
  6140. }
  6141. else
  6142. {
  6143. //
  6144. // remap the error code to win32 error
  6145. //
  6146. DEBUG_OUTPUT(FULL_LOGGING,
  6147. L"TranslateNameXForest: DsCrackNames failed with error %d",
  6148. pResult->rItems[0].status );
  6149. // dwErr = MapDsNameError(pResult->rItems[0].status);
  6150. goto Exit;
  6151. }
  6152. bRet = TRUE;
  6153. Exit:
  6154. if (pDCInfo)
  6155. NetApiBufferFree(pDCInfo);
  6156. if (hDS)
  6157. DsUnBind(&hDS);
  6158. if (pResult)
  6159. DsFreeNameResult(pResult);
  6160. if(!bRet)
  6161. SetLastError(dwErr);
  6162. return bRet;
  6163. }
  6164. //+--------------------------------------------------------------------------
  6165. //
  6166. // Function: GetAttrFromDN
  6167. //
  6168. // Synopsis:
  6169. //
  6170. // Arguments: [pszDN - IN]: DN to query
  6171. // [pszAttribute - IN]: Attribute to retrieve
  6172. // [refBasePathsInfo - IN]: LDAP base path
  6173. // [refCredentialObject - IN]: Credentials to use for the query
  6174. // [ppAttrInfo- OUT]: Result returned. The caller must
  6175. // call FreeADsMem to free the array
  6176. //
  6177. // Returns: HRESULT : S_OK if everything succeeded
  6178. // E_INVALIDARG for invalid input
  6179. // Anything else is a failure code from an ADSI call
  6180. //
  6181. // History: 20-Aug-2002 RonMart Created
  6182. //
  6183. //---------------------------------------------------------------------------
  6184. HRESULT GetAttrFromDN(PCWSTR pszDN, PWSTR pszAttribute,
  6185. const CDSCmdBasePathsInfo& refBasePathsInfo,
  6186. const CDSCmdCredentialObject& refCredentialObject,
  6187. PADS_ATTR_INFO* ppAttrInfo)
  6188. {
  6189. ENTER_FUNCTION_HR(LEVEL5_LOGGING, GetAttrFromDN, hr);
  6190. do // false loop
  6191. {
  6192. //
  6193. // Validate parameters
  6194. //
  6195. if (!pszDN || !pszAttribute)
  6196. {
  6197. hr = E_INVALIDARG;
  6198. break;
  6199. }
  6200. //
  6201. // Compose the path
  6202. //
  6203. CComBSTR sbstrPath;
  6204. refBasePathsInfo.ComposePathFromDN(pszDN, sbstrPath);
  6205. //
  6206. // Open the object
  6207. //
  6208. CComPtr<IDirectoryObject> spDirObject;
  6209. hr = DSCmdOpenObject(refCredentialObject,
  6210. sbstrPath,
  6211. IID_IDirectoryObject,
  6212. (void**)&spDirObject,
  6213. true);
  6214. if (FAILED(hr))
  6215. {
  6216. break;
  6217. }
  6218. // Build an attribute array for the requested value
  6219. static const DWORD dwAttrCount = 1;
  6220. PWSTR pszAttrs[] = { pszAttribute };
  6221. DWORD dwAttrsReturned = 0;
  6222. hr = spDirObject->GetObjectAttributes(pszAttrs,
  6223. dwAttrCount,
  6224. ppAttrInfo,
  6225. &dwAttrsReturned);
  6226. if (FAILED(hr))
  6227. {
  6228. DEBUG_OUTPUT(MINIMAL_LOGGING,
  6229. L"GetObjectAttributes for %s failed: hr = 0x%x",
  6230. pszAttribute, hr);
  6231. break;
  6232. }
  6233. } while (false);
  6234. if(FAILED(hr) && *ppAttrInfo)
  6235. FreeADsMem(*ppAttrInfo);
  6236. return hr;
  6237. }
  6238. //+--------------------------------------------------------------------------
  6239. //
  6240. // Function: ConvertTrusteeToDN
  6241. //
  6242. // Synopsis: Get the DN for an -acct param
  6243. //
  6244. // Arguments: [lpszDomain - IN]: Domain to query or NULL for local
  6245. // [lpszTrustee - IN]: Acct to resolve
  6246. // [lpszDN - OUT]: Returns the DN. Use LocalFree when done
  6247. //
  6248. // Returns: HRESULT : S_OK if everything succeeded
  6249. // E_INVALIDARG for invalid input
  6250. // Anything else is a failure code from an ADSI call
  6251. //
  6252. // History: 20-Aug-2002 RonMart Created
  6253. //
  6254. //---------------------------------------------------------------------------
  6255. HRESULT ConvertTrusteeToDN(LPCWSTR lpszDomain, LPCWSTR lpszTrustee,
  6256. LPWSTR* lpszDN)
  6257. {
  6258. ENTER_FUNCTION_HR(LEVEL5_LOGGING, ConvertTrusteeToDN, hr);
  6259. do // false loop
  6260. {
  6261. //
  6262. // Validate parameters
  6263. //
  6264. if (!lpszTrustee || !lpszDN || *lpszDN != NULL)
  6265. {
  6266. hr = E_INVALIDARG;
  6267. break;
  6268. }
  6269. if(!TranslateNameXForest(lpszDomain,
  6270. lpszTrustee,
  6271. DS_UNKNOWN_NAME,
  6272. DS_FQDN_1779_NAME,
  6273. lpszDN))
  6274. {
  6275. hr = E_UNEXPECTED;
  6276. break;
  6277. }
  6278. } while (false);
  6279. return hr;
  6280. }
  6281. //+--------------------------------------------------------------------------
  6282. //
  6283. // Function: GetDNSid
  6284. //
  6285. // Synopsis: Given a DN the objectSid value will be retrieved and returned
  6286. // as a SID
  6287. //
  6288. // Arguments: [lpszDN - IN]: DN to query
  6289. // [refBasePathsInfo - IN]: LDAP settings
  6290. // [refCredentialObject - IN]: Credentials to use for the query
  6291. // [pSid - OUT]: A SID if successful. Call LocalFree
  6292. // when done.
  6293. //
  6294. // Returns: HRESULT : S_OK if everything succeeded
  6295. // E_INVALIDARG for invalid input
  6296. // Anything else is a failure code from an ADSI call
  6297. //
  6298. // History: 20-Aug-2002 RonMart Created
  6299. //
  6300. //---------------------------------------------------------------------------
  6301. HRESULT GetDNSid(LPCWSTR lpszDN,
  6302. const CDSCmdBasePathsInfo& refBasePathsInfo,
  6303. const CDSCmdCredentialObject& refCredentialObject,
  6304. PSID* pSid)
  6305. {
  6306. ENTER_FUNCTION_HR(LEVEL5_LOGGING, GetDNSid, hr);
  6307. do // false loop
  6308. {
  6309. //
  6310. // Validate parameters
  6311. //
  6312. if (!lpszDN)
  6313. {
  6314. hr = E_INVALIDARG;
  6315. break;
  6316. }
  6317. // Get the objectSid
  6318. PADS_ATTR_INFO pSidAttrInfo = NULL;
  6319. hr = GetAttrFromDN(lpszDN, L"objectSid",
  6320. refBasePathsInfo,
  6321. refCredentialObject,
  6322. &pSidAttrInfo);
  6323. if(FAILED(hr))
  6324. {
  6325. hr = E_UNEXPECTED;
  6326. break;
  6327. }
  6328. // Sids are stored in octet strings so validate the return value
  6329. if(!pSidAttrInfo || pSidAttrInfo->dwADsType != ADSTYPE_OCTET_STRING)
  6330. {
  6331. hr = E_UNEXPECTED;
  6332. break;
  6333. }
  6334. // Validate that we have a valid sid
  6335. if(!IsValidSid(pSidAttrInfo->pADsValues->OctetString.lpValue))
  6336. {
  6337. hr = E_UNEXPECTED;
  6338. break;
  6339. }
  6340. // Alloc the return buffer
  6341. SIZE_T size = GetLengthSid(pSidAttrInfo->pADsValues->OctetString.lpValue);
  6342. *pSid = (PSID) LocalAlloc(LPTR, size);
  6343. if(NULL == *pSid)
  6344. {
  6345. hr = E_OUTOFMEMORY;
  6346. break;
  6347. }
  6348. // Copy the sid to the return buffer
  6349. CopyMemory(*pSid, pSidAttrInfo->pADsValues->OctetString.lpValue, size);
  6350. // Confirm that the copy was successful
  6351. if(!IsValidSid(*pSid))
  6352. {
  6353. ASSERT(FALSE);
  6354. LocalFree(*pSid);
  6355. hr = E_UNEXPECTED;
  6356. break;
  6357. }
  6358. // Free the query result
  6359. FreeADsMem(pSidAttrInfo);
  6360. } while (false);
  6361. return hr;
  6362. }
  6363. //+--------------------------------------------------------------------------
  6364. //
  6365. // Function: IsBSTRInVariantArray
  6366. //
  6367. // Synopsis: Examines a BSTR variant or a variant array of BSTR's to see
  6368. // if bstrSearch is in refvar
  6369. //
  6370. // Arguments: [refvar - IN]: A variant that contains either a BSTR or
  6371. // an array of BSTR's
  6372. //
  6373. // [bstrSearch - IN]: String to look for in refvar
  6374. //
  6375. // [bFound - OUT]: true if bstrSearch is found in refvar,
  6376. // else false
  6377. //
  6378. // Returns: HRESULT : S_OK if everything succeeded
  6379. // E_UNEXPECTED in most failure cases
  6380. // Anything else is a failure code from call that
  6381. // returns a hr
  6382. //
  6383. // Note: This code was derrived from admin\snapin\adsiedit\common.cpp
  6384. //
  6385. // History: 05-Aug-2002 RonMart Created
  6386. //
  6387. //---------------------------------------------------------------------------
  6388. HRESULT IsBSTRInVariantArray(VARIANT& refvar, CComBSTR& bstrSearch,
  6389. bool& bFound)
  6390. {
  6391. ENTER_FUNCTION_HR(MINIMAL_LOGGING, IsBSTRInVariantArray, hr);
  6392. long start = 0;
  6393. long end = 0;
  6394. bFound = false;
  6395. // If a single value comes back
  6396. if ( !(V_VT(&refvar) & VT_ARRAY) )
  6397. {
  6398. // and it is not a BSTR then abort
  6399. if ( V_VT(&refvar) != VT_BSTR )
  6400. {
  6401. return E_UNEXPECTED;
  6402. }
  6403. // Is the search string the same as the variant value?
  6404. bFound = (lstrcmpi(bstrSearch, V_BSTR(&refvar)) == 0);
  6405. return hr;
  6406. }
  6407. // Otherwise it is a SafeArray so get the array
  6408. SAFEARRAY *saAttributes = V_ARRAY( &refvar );
  6409. // Verify array returned
  6410. if(NULL == saAttributes)
  6411. return E_UNEXPECTED;
  6412. // Figure out the dimensions of the array.
  6413. hr = SafeArrayGetLBound( saAttributes, 1, &start );
  6414. if( FAILED(hr) )
  6415. return hr;
  6416. hr = SafeArrayGetUBound( saAttributes, 1, &end );
  6417. if( FAILED(hr) )
  6418. return hr;
  6419. // Search the array elements and abort if a match is found
  6420. CComVariant SingleResult;
  6421. for ( long idx = start; (idx <= end) && !bFound; idx++ )
  6422. {
  6423. hr = SafeArrayGetElement( saAttributes, &idx, &SingleResult );
  6424. if( FAILED(hr) )
  6425. {
  6426. return hr;
  6427. }
  6428. if ( V_VT(&SingleResult) != VT_BSTR )
  6429. {
  6430. // If not BSTR then go to the next element
  6431. continue;
  6432. }
  6433. // Is this variant bstr value the same as the search string?
  6434. bFound = (lstrcmpi(bstrSearch, V_BSTR(&SingleResult)) == 0);
  6435. }
  6436. return S_OK;
  6437. }
  6438. //+--------------------------------------------------------------------------
  6439. //
  6440. // Function: ValidatePartition
  6441. //
  6442. // Synopsis: Confirms that the partion exists in the RootDSE namingContexts
  6443. //
  6444. // Arguments: [basePathsInfo - IN]: DSAdd's CDSCmdBasePathsInfo object
  6445. // for getting the RootDSE
  6446. // [pszObjectDN - IN]: Partion to examine
  6447. //
  6448. // Returns: HRESULT : S_OK if everything succeeded
  6449. // E_UNEXPECTED in most failure cases
  6450. // E_OUTOFMEMORY if a LocalAlloc fails
  6451. // Anything else is a failure code from an ADSI call
  6452. //
  6453. // History: 12-Aug-2002 RonMart Created
  6454. //
  6455. //---------------------------------------------------------------------------
  6456. HRESULT ValidatePartition(CDSCmdBasePathsInfo& basePathsInfo, LPCWSTR pszObjectDN)
  6457. {
  6458. ENTER_FUNCTION_HR(MINIMAL_LOGGING, ValidatePartition, hr);
  6459. do // false loop
  6460. {
  6461. //
  6462. // Verify parameters
  6463. //
  6464. if (!pszObjectDN || !basePathsInfo.IsInitialized())
  6465. {
  6466. ASSERT(pszObjectDN);
  6467. ASSERT(basePathsInfo.IsInitialized());
  6468. hr = E_INVALIDARG;
  6469. break;
  6470. }
  6471. CComVariant var;
  6472. CComPtr<IADs> spRootDSE = basePathsInfo.GetRootDSE();
  6473. hr = spRootDSE->Get(CComBSTR(L"namingContexts"), &var);
  6474. if (FAILED(hr))
  6475. {
  6476. DEBUG_OUTPUT(LEVEL5_LOGGING,
  6477. L"Failed to get the namingContexts from the RootDSE: hr = 0x%x",
  6478. hr);
  6479. hr = E_INVALIDARG;
  6480. break;
  6481. }
  6482. // Verify that the partition given really exists
  6483. bool bFound = false;
  6484. CComBSTR str(pszObjectDN);
  6485. hr = IsBSTRInVariantArray(var, str, bFound);
  6486. if(FAILED(hr) || (!bFound))
  6487. {
  6488. DEBUG_OUTPUT(LEVEL5_LOGGING,
  6489. L"IsBSTRInVariantArray didn't find the partion DN: hr = 0x%x",
  6490. hr);
  6491. hr = E_INVALIDARG;
  6492. break;
  6493. }
  6494. } while(false);
  6495. return hr;
  6496. }
  6497. //+--------------------------------------------------------------------------
  6498. //
  6499. // Function: GetQuotaContainerDN
  6500. //
  6501. // Synopsis: Takes the partition dn and merges it with the NTDS Quotas
  6502. // string (from wellKnownObjects GUID)
  6503. //
  6504. // Arguments: [basePathsInfo - IN]:
  6505. // [credentialObject - IN]: Creditials object used for
  6506. // binding to other objects
  6507. // [lpszPartitionDN - IN]: The partition to bind to
  6508. // [pszNewDN - OUT]: The munged quotas DN to return
  6509. //
  6510. // Returns: HRESULT : S_OK if everything succeeded
  6511. // E_UNEXPECTED in most failure cases
  6512. // E_OUTOFMEMORY if a LocalAlloc fails
  6513. // Anything else is a failure code from an ADSI call
  6514. //
  6515. // History: 05-Aug-2002 RonMart Created
  6516. //
  6517. //---------------------------------------------------------------------------
  6518. HRESULT GetQuotaContainerDN(IN CDSCmdBasePathsInfo& basePathsInfo,
  6519. IN const CDSCmdCredentialObject& credentialObject,
  6520. IN LPCWSTR lpszPartitionDN,
  6521. OUT PWSTR* pszNewDN)
  6522. {
  6523. ENTER_FUNCTION_HR(MINIMAL_LOGGING, GetQuotaContainerDN, hr);
  6524. LPWSTR pszNewObjectDN = NULL;
  6525. do // false loop
  6526. {
  6527. //
  6528. // Verify parameters
  6529. //
  6530. if (!lpszPartitionDN || !basePathsInfo.IsInitialized() || !pszNewDN)
  6531. {
  6532. hr = E_INVALIDARG;
  6533. break;
  6534. }
  6535. // Make sure the partition specified really exists
  6536. hr = ValidatePartition(basePathsInfo, lpszPartitionDN);
  6537. if(FAILED(hr))
  6538. {
  6539. break;
  6540. }
  6541. // Get the abstract schema path from the source domain
  6542. CComBSTR bstrSchemaPath = basePathsInfo.GetAbstractSchemaPath();
  6543. bstrSchemaPath += L"/msDS-QuotaControl";
  6544. // Bind to the schema definition of the quota control
  6545. CComPtr<IADsClass> spIADsItem;
  6546. hr = DSCmdOpenObject(credentialObject,
  6547. bstrSchemaPath,
  6548. IID_IADsClass,
  6549. (void**)&spIADsItem,
  6550. false);
  6551. if (FAILED(hr) || (spIADsItem.p == 0))
  6552. {
  6553. DEBUG_OUTPUT(MINIMAL_LOGGING,
  6554. L"DsCmdOpenObject failure - couldn't bind to msDS-QuotaControl: 0x%08x",
  6555. hr);
  6556. break;
  6557. }
  6558. // Build the DN to the GUID of "NTDS Quotas"
  6559. CPathCracker pathcracker;
  6560. hr = pathcracker.Set(CComBSTR(lpszPartitionDN), ADS_SETTYPE_DN );
  6561. if (FAILED(hr))
  6562. {
  6563. DEBUG_OUTPUT(MINIMAL_LOGGING,
  6564. L"pathcracker Set.failure: [%s] hr = 0x%08x",
  6565. lpszPartitionDN, hr);
  6566. hr = E_UNEXPECTED;
  6567. break;
  6568. }
  6569. // Use the wellKnownObject GUID string for NTDS Quotas
  6570. CComBSTR strNTDSQuotasContainer(L"WKGUID=");
  6571. strNTDSQuotasContainer += GUID_NTDS_QUOTAS_CONTAINER_W;
  6572. hr = pathcracker.AddLeafElement( strNTDSQuotasContainer );
  6573. if (FAILED(hr))
  6574. {
  6575. DEBUG_OUTPUT(MINIMAL_LOGGING,
  6576. L"pathcracker.AddLeafElement failure: [WKGUID] hr = 0x%08x",
  6577. hr);
  6578. hr = E_UNEXPECTED;
  6579. break;
  6580. }
  6581. CComBSTR bstrDN;
  6582. hr = pathcracker.Retrieve( ADS_FORMAT_X500_DN, &bstrDN );
  6583. if (FAILED(hr))
  6584. {
  6585. DEBUG_OUTPUT(MINIMAL_LOGGING,
  6586. L"pathcracker.Retrieve failure: hr = 0x%08x",
  6587. hr);
  6588. hr = E_UNEXPECTED;
  6589. break;
  6590. }
  6591. // Alloc a new string to hold bstrDN plus <>
  6592. SIZE_T cbBuf = SysStringByteLen(bstrDN) + (3 * sizeof(WCHAR));
  6593. pszNewObjectDN = (LPWSTR) LocalAlloc(LPTR, cbBuf);
  6594. if(NULL == pszNewObjectDN)
  6595. {
  6596. hr = E_OUTOFMEMORY;
  6597. break;
  6598. }
  6599. // Build a LDAP string to the well known quota object
  6600. hr = StringCbPrintfW(pszNewObjectDN, cbBuf, L"<%s>", bstrDN.m_str);
  6601. if(FAILED(hr))
  6602. {
  6603. ASSERT(FALSE);
  6604. break;
  6605. }
  6606. // Get a path that accounts for -domain or -server
  6607. CComBSTR sbstrObjectPath;
  6608. basePathsInfo.ComposePathFromDN(pszNewObjectDN, sbstrObjectPath,
  6609. DSCMD_LDAP_PROVIDER);
  6610. // Bind to it
  6611. CComPtr<IADs> spADs;
  6612. hr = DSCmdOpenObject(credentialObject,
  6613. sbstrObjectPath,
  6614. IID_IADs,
  6615. (void**)&spADs,
  6616. false);
  6617. if (FAILED(hr) || (spADs.p == 0))
  6618. {
  6619. ASSERT( !!spADs );
  6620. DEBUG_OUTPUT(MINIMAL_LOGGING,
  6621. L"DsCmdOpenObject failure: hr = 0x%08x, %s",
  6622. bstrDN, hr);
  6623. break;
  6624. }
  6625. // Resolve the GUID into a string (usually CN=NTDS Quotas,<DN>)
  6626. CComVariant var;
  6627. hr = spADs->Get(CComBSTR(L"distinguishedName"), &var);
  6628. if (FAILED(hr))
  6629. {
  6630. DEBUG_OUTPUT(MINIMAL_LOGGING,
  6631. L"Failed to retrieve the distinguishedName: hr = 0x%x",
  6632. hr);
  6633. hr = E_UNEXPECTED;
  6634. break;
  6635. }
  6636. // Convert the variant dn to a bstr
  6637. CComBSTR bstrNewDN(V_BSTR(&var));
  6638. // Alloc the return string to hold the munged name
  6639. *pszNewDN = (PWSTR) LocalAlloc(LPTR, SysStringByteLen(bstrNewDN)
  6640. + sizeof(WCHAR));
  6641. if(NULL == pszNewDN)
  6642. {
  6643. hr = E_OUTOFMEMORY;
  6644. break;
  6645. }
  6646. // Copy the resolved DN into the new objectDN string
  6647. lstrcpy(*pszNewDN, bstrNewDN);
  6648. } while (false);
  6649. // cleanup
  6650. if(pszNewObjectDN)
  6651. LocalFree(pszNewObjectDN);
  6652. return hr;
  6653. }
  6654. //+--------------------------------------------------------------------------
  6655. //
  6656. // Function: ConvertTrusteeToNT4Name
  6657. //
  6658. // Synopsis: Get the DN for an -acct param
  6659. //
  6660. // Arguments: [lpszDomain - IN]: Domain to query or NULL for local
  6661. // [lpszTrustee - IN]: Acct to resolve
  6662. // [lpszNT4 - OUT]: Returns the NT4 name.
  6663. // Use LocalFree when done
  6664. //
  6665. // Returns: HRESULT : S_OK if everything succeeded
  6666. // E_INVALIDARG for invalid input
  6667. // Anything else is a failure code from an ADSI call
  6668. //
  6669. // History: 20-Aug-2002 RonMart Created
  6670. //
  6671. //---------------------------------------------------------------------------
  6672. HRESULT ConvertTrusteeToNT4Name(LPCWSTR lpszDomain, LPCWSTR lpszTrustee,
  6673. LPWSTR* lpszNT4)
  6674. {
  6675. ENTER_FUNCTION_HR(LEVEL5_LOGGING, ConvertTrusteeToNT4Name, hr);
  6676. do // false loop
  6677. {
  6678. //
  6679. // Validate parameters
  6680. //
  6681. if (!lpszTrustee || !lpszNT4 || *lpszNT4 != NULL)
  6682. {
  6683. hr = E_INVALIDARG;
  6684. break;
  6685. }
  6686. if(!TranslateNameXForest(lpszDomain,
  6687. lpszTrustee,
  6688. DS_UNKNOWN_NAME,
  6689. DS_NT4_ACCOUNT_NAME,
  6690. lpszNT4))
  6691. {
  6692. hr = E_UNEXPECTED;
  6693. break;
  6694. }
  6695. // Replace any backslashes with an underscore
  6696. LPWSTR p = *lpszNT4;
  6697. while(*p++)
  6698. {
  6699. if(*p == L'\\')
  6700. *p = L'_';
  6701. }
  6702. } while (false);
  6703. return hr;
  6704. }