Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

660 lines
22 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1999.
  5. //
  6. // File: Schema.cpp
  7. //
  8. // Contents: DoSchemaDiagnosis and support methods
  9. //
  10. //
  11. //----------------------------------------------------------------------------
  12. #include "stdafx.h"
  13. #include <security.h>
  14. #include <seopaque.h>
  15. #include <sddl.h>
  16. #include "ADUtils.h"
  17. #include "Schema.h"
  18. #include "SecDesc.h"
  19. // Function prototypes
  20. bool FindInGlobalList (const ACE_SAMNAME* pAceSAMNameToFind, const ACE_SAMNAME_LIST& defACLList);
  21. HRESULT GetSchemaDefaultSecurityDescriptor (
  22. const wstring& strObjectDN,
  23. PADS_ATTR_INFO* ppAttrs,
  24. PSECURITY_DESCRIPTOR* ppSecurityDescriptor,
  25. wstring &objectClass);
  26. HRESULT GetObjectClass (const wstring& strObjectDN, wstring& ldapClassName);
  27. HRESULT GetClassSecurityDescriptor (
  28. PADS_ATTR_INFO* ppAttrs,
  29. PSECURITY_DESCRIPTOR* ppSecurityDescriptor,
  30. CComPtr<IADsPathname>& spPathname,
  31. const wstring& ldapClassName);
  32. HRESULT GetADClassName (
  33. CComPtr<IADsPathname>& spPathname,
  34. const wstring& ldapClassName,
  35. wstring& adClassName);
  36. // Functions
  37. HRESULT DoSchemaDiagnosis ()
  38. {
  39. _TRACE (1, L"Entering DoSchemaDiagnosis\n");
  40. HRESULT hr = S_OK;
  41. wstring str;
  42. size_t nDACLAcesFound = 0;
  43. bool bAllExplicit = true;
  44. bool bAllInherited = true;
  45. ACE_SAMNAME_LIST defDACLList;
  46. ACE_SAMNAME_LIST defSACLList;
  47. if ( !_Module.DoTabDelimitedOutput () )
  48. {
  49. LoadFromResource (str, IDS_SCHEMA_DEFAULTS_DIAGNOSIS);
  50. MyWprintf (str.c_str ());
  51. }
  52. PSECURITY_DESCRIPTOR pSecurityDescriptor = 0;
  53. PADS_ATTR_INFO pAttrs = NULL;
  54. wstring ldapClassName;
  55. hr = GetSchemaDefaultSecurityDescriptor (_Module.GetObjectDN (), &pAttrs,
  56. &pSecurityDescriptor, ldapClassName);
  57. if ( SUCCEEDED (hr) )
  58. {
  59. hr = EnumerateDacl (pSecurityDescriptor, defDACLList, false);
  60. if ( SUCCEEDED (hr) )
  61. {
  62. ACE_SAMNAME* pAceSAMName = 0;
  63. // Compare the DACL
  64. for (ACE_SAMNAME_LIST::iterator itr = defDACLList.begin();
  65. itr != defDACLList.end();
  66. itr++)
  67. {
  68. pAceSAMName = *itr;
  69. if ( FindInGlobalList (pAceSAMName, _Module.m_DACLList) )
  70. {
  71. if ( pAceSAMName->m_pAllowedAce->Header.AceFlags & INHERITED_ACE )
  72. {
  73. switch ( pAceSAMName->m_AceType )
  74. {
  75. case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
  76. {
  77. wstring inheritedObjectClass;
  78. if ( SUCCEEDED (_Module.GetClassFromGUID (
  79. pAceSAMName->m_pAllowedObjectAce->InheritedObjectType,
  80. inheritedObjectClass) ) )
  81. {
  82. if ( !ldapClassName.compare (inheritedObjectClass) )
  83. break; // matches
  84. }
  85. }
  86. continue; // not a match
  87. case ACCESS_DENIED_OBJECT_ACE_TYPE:
  88. {
  89. wstring inheritedObjectClass;
  90. if ( SUCCEEDED (_Module.GetClassFromGUID (
  91. pAceSAMName->m_pDeniedObjectAce->InheritedObjectType,
  92. inheritedObjectClass) ) )
  93. {
  94. if ( !ldapClassName.compare (inheritedObjectClass) )
  95. break; // matches
  96. }
  97. }
  98. continue; // not a match
  99. default:
  100. break; // matches
  101. }
  102. bAllExplicit = false;
  103. }
  104. else
  105. bAllInherited = false;
  106. nDACLAcesFound++;
  107. }
  108. }
  109. }
  110. // Compare the SACL
  111. hr = EnumerateSacl (pSecurityDescriptor, defSACLList);
  112. if ( SUCCEEDED (hr) )
  113. {
  114. ACE_SAMNAME* pAceSAMName = 0;
  115. for (ACE_SAMNAME_LIST::iterator itr = defSACLList.begin();
  116. itr != defSACLList.end();
  117. itr++)
  118. {
  119. pAceSAMName = *itr;
  120. if ( FindInGlobalList (pAceSAMName, _Module.m_SACLList) )
  121. {
  122. if ( pAceSAMName->m_pAllowedAce->Header.AceFlags & INHERITED_ACE )
  123. {
  124. switch ( pAceSAMName->m_AceType )
  125. {
  126. case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
  127. {
  128. wstring inheritedObjectClass;
  129. if ( SUCCEEDED (_Module.GetClassFromGUID (
  130. pAceSAMName->m_pSystemAuditObjectAce->InheritedObjectType,
  131. inheritedObjectClass) ) )
  132. {
  133. if ( !ldapClassName.compare (inheritedObjectClass) )
  134. break; // matches
  135. }
  136. }
  137. continue; // not a match
  138. case SYSTEM_AUDIT_ACE_TYPE:
  139. default:
  140. break; // matches
  141. }
  142. bAllExplicit = false;
  143. }
  144. else
  145. bAllInherited = false;
  146. nDACLAcesFound++;
  147. }
  148. }
  149. }
  150. }
  151. wstring strDefaultState;
  152. bool bPresent = false;
  153. if ( !nDACLAcesFound )
  154. {
  155. // absent
  156. LoadFromResource (strDefaultState, IDS_ABSENT);
  157. }
  158. else
  159. {
  160. if ( nDACLAcesFound == defDACLList.size () )
  161. {
  162. if ( bAllExplicit | bAllInherited )
  163. {
  164. // present
  165. LoadFromResource (strDefaultState, IDS_PRESENT);
  166. bPresent = true;
  167. }
  168. else
  169. {
  170. // partial
  171. LoadFromResource (strDefaultState, IDS_PARTIAL);
  172. }
  173. }
  174. else
  175. {
  176. // partial
  177. LoadFromResource (strDefaultState, IDS_PARTIAL);
  178. }
  179. }
  180. if ( _Module.DoTabDelimitedOutput () )
  181. FormatMessage (str, IDS_SCHEMA_DEFAULTS_CDO, strDefaultState.c_str ());
  182. else
  183. FormatMessage (str, IDS_SCHEMA_DEFAULTS, strDefaultState.c_str ());
  184. MyWprintf (str.c_str ());
  185. if ( bPresent )
  186. {
  187. if ( bAllExplicit )
  188. LoadFromResource (strDefaultState, IDS_AT_CREATION);
  189. else
  190. LoadFromResource (strDefaultState, IDS_BY_INHERITANCE);
  191. if ( _Module.DoTabDelimitedOutput () )
  192. FormatMessage (str, IDS_OBTAINED_CDO, strDefaultState.c_str ());
  193. else
  194. FormatMessage (str, IDS_OBTAINED, strDefaultState.c_str ());
  195. MyWprintf (str.c_str ());
  196. }
  197. else if ( _Module.DoTabDelimitedOutput () )
  198. MyWprintf (L"\n\n");
  199. _TRACE (-1, L"Leaving DoSchemaDiagnosis: 0x%x\n", hr);
  200. return hr;
  201. }
  202. HRESULT GetSchemaDefaultSecurityDescriptor (
  203. const wstring& strObjectDN,
  204. PADS_ATTR_INFO* ppAttrs,
  205. PSECURITY_DESCRIPTOR* ppSecurityDescriptor,
  206. wstring &ldapClassName)
  207. {
  208. _TRACE (1, L"Entering GetSchemaDefaultSecurityDescriptor\n");
  209. HRESULT hr = S_OK;
  210. if ( ppAttrs && ppSecurityDescriptor )
  211. {
  212. hr = GetObjectClass (strObjectDN, ldapClassName);
  213. if ( SUCCEEDED (hr) )
  214. {
  215. wstring strDC;
  216. size_t pos = strObjectDN.find (L"DC=", 0);
  217. if ( strObjectDN.npos != pos )
  218. {
  219. strDC = strObjectDN.substr (pos);
  220. CComPtr<IADsPathname> spPathname;
  221. //
  222. // Constructing the directory paths
  223. //
  224. hr = CoCreateInstance(
  225. CLSID_Pathname,
  226. NULL,
  227. CLSCTX_ALL,
  228. IID_PPV_ARG (IADsPathname, &spPathname));
  229. if ( SUCCEEDED (hr) )
  230. {
  231. ASSERT (!!spPathname);
  232. hr = spPathname->Set (const_cast <PWSTR> (ACLDIAG_LDAP),
  233. ADS_SETTYPE_PROVIDER);
  234. if ( SUCCEEDED (hr) )
  235. {
  236. hr = spPathname->Set (
  237. const_cast <PWSTR> (strDC.c_str ()),
  238. ADS_SETTYPE_DN);
  239. if ( SUCCEEDED (hr) )
  240. {
  241. hr = spPathname->AddLeafElement (L"CN=Configuration");
  242. if ( SUCCEEDED (hr) )
  243. {
  244. hr = spPathname->AddLeafElement (L"CN=Schema");
  245. if ( SUCCEEDED (hr) )
  246. {
  247. hr = GetClassSecurityDescriptor (
  248. ppAttrs,
  249. ppSecurityDescriptor,
  250. spPathname,
  251. ldapClassName);
  252. }
  253. }
  254. }
  255. else
  256. {
  257. _TRACE (0, L"IADsPathname->Set (%s): 0x%x\n",
  258. strDC.c_str (), hr);
  259. }
  260. }
  261. else
  262. {
  263. _TRACE (0, L"IADsPathname->Set (%s): 0x%x\n", ACLDIAG_LDAP, hr);
  264. }
  265. }
  266. else
  267. {
  268. _TRACE (0, L"CoCreateInstance(CLSID_Pathname): 0x%x\n", hr);
  269. }
  270. }
  271. }
  272. }
  273. else
  274. hr = E_POINTER;
  275. _TRACE (-1, L"Leaving GetSchemaDefaultSecurityDescriptor: 0x%x\n", hr);
  276. return hr;
  277. }
  278. HRESULT GetObjectClass (const wstring& strObjectDN, wstring& ldapClassName)
  279. {
  280. _TRACE (1, L"Entering GetObjectClass\n");
  281. HRESULT hr = S_OK;
  282. CComPtr<IADsPathname> spPathname;
  283. //
  284. // Constructing the directory paths
  285. //
  286. hr = CoCreateInstance(
  287. CLSID_Pathname,
  288. NULL,
  289. CLSCTX_ALL,
  290. IID_PPV_ARG (IADsPathname, &spPathname));
  291. if ( SUCCEEDED (hr) )
  292. {
  293. ASSERT (!!spPathname);
  294. hr = spPathname->Set (const_cast <PWSTR> (ACLDIAG_LDAP),
  295. ADS_SETTYPE_PROVIDER);
  296. if ( SUCCEEDED (hr) )
  297. {
  298. hr = spPathname->Set (
  299. const_cast <PWSTR> (strObjectDN.c_str ()),
  300. ADS_SETTYPE_DN);
  301. if ( SUCCEEDED (hr) )
  302. {
  303. BSTR bstrFullPath = 0;
  304. hr = spPathname->Retrieve(ADS_FORMAT_X500, &bstrFullPath);
  305. if ( SUCCEEDED (hr) )
  306. {
  307. CComPtr<IDirectoryObject> spDirObj;
  308. hr = ADsGetObject (
  309. bstrFullPath,
  310. IID_PPV_ARG (IDirectoryObject, &spDirObj));
  311. if ( SUCCEEDED (hr) )
  312. {
  313. //
  314. // Get this object's object class.
  315. //
  316. const PWSTR wzObjectClass = L"objectClass";
  317. DWORD cAttrs = 0;
  318. LPWSTR rgpwzAttrNames[] = {wzObjectClass};
  319. PADS_ATTR_INFO pAttrs = NULL;
  320. hr = spDirObj->GetObjectAttributes(rgpwzAttrNames, 1,
  321. &pAttrs, &cAttrs);
  322. if ( SUCCEEDED (hr) )
  323. {
  324. if ( 1 <= cAttrs && pAttrs && pAttrs->pADsValues )
  325. {
  326. if (!(pAttrs->pADsValues[pAttrs->dwNumValues-1].CaseIgnoreString) )
  327. {
  328. _TRACE (0, L"IADS return bogus object class!\n");
  329. hr = E_UNEXPECTED;
  330. }
  331. else
  332. {
  333. ldapClassName =
  334. pAttrs->pADsValues[pAttrs->dwNumValues-1].CaseIgnoreString;
  335. }
  336. FreeADsMem (pAttrs);
  337. }
  338. else
  339. hr = E_UNEXPECTED;
  340. }
  341. else
  342. {
  343. _TRACE (0, L"IDirectoryObject->GetObjectAttributes (): 0x%x\n", hr);
  344. }
  345. }
  346. else
  347. {
  348. _TRACE (0, L"ADsGetObject (%s): 0x%x\n", bstrFullPath, hr);
  349. wstring strErr;
  350. FormatMessage (strErr, IDS_INVALID_OBJECT,
  351. _Module.GetObjectDN ().c_str (),
  352. GetSystemMessage (hr).c_str ());
  353. MyWprintf (strErr.c_str ());
  354. }
  355. }
  356. else
  357. {
  358. _TRACE (0, L"IADsPathname->Retrieve (): 0x%x\n", hr);
  359. }
  360. }
  361. else
  362. {
  363. _TRACE (0, L"IADsPathname->Set (%s): 0x%x\n",
  364. _Module.GetObjectDN ().c_str (), hr);
  365. }
  366. }
  367. else
  368. {
  369. _TRACE (0, L"IADsPathname->Set (%s): 0x%x\n", ACLDIAG_LDAP, hr);
  370. }
  371. }
  372. else
  373. {
  374. _TRACE (0, L"CoCreateInstance(CLSID_Pathname): 0x%x\n", hr);
  375. }
  376. _TRACE (-1, L"Leaving GetObjectClass: 0x%x\n", hr);
  377. return hr;
  378. }
  379. bool FindInGlobalList (const ACE_SAMNAME* pAceSAMNameToFind, const ACE_SAMNAME_LIST& defDACLList)
  380. {
  381. _TRACE (1, L"Entering FindInGlobalList\n");
  382. bool bFound = false;
  383. ACE_SAMNAME* pAceSAMName = 0;
  384. for (ACE_SAMNAME_LIST::iterator itr = defDACLList.begin();
  385. itr != defDACLList.end();
  386. itr++)
  387. {
  388. pAceSAMName = *itr;
  389. // pAceSAMNameToFind must be on the left
  390. if ( *pAceSAMNameToFind == *pAceSAMName )
  391. {
  392. bFound = true;
  393. break;
  394. }
  395. }
  396. _TRACE (-1, L"Leaving FindInGlobalList: %s\n", bFound ? L"found" : L"not found");
  397. return bFound;
  398. }
  399. HRESULT GetClassSecurityDescriptor (PADS_ATTR_INFO* ppAttrs,
  400. PSECURITY_DESCRIPTOR* ppSecurityDescriptor,
  401. CComPtr<IADsPathname>& spPathname,
  402. const wstring& ldapClassName)
  403. {
  404. _TRACE (1, L"Entering GetClassSecurityDescriptor\n");
  405. // ldapClassName must be converted from LDAP class to AD class
  406. wstring adClassName;
  407. HRESULT hr = GetADClassName (spPathname, ldapClassName, adClassName);
  408. if ( SUCCEEDED (hr) )
  409. {
  410. wstring cnClassName (L"CN=");
  411. cnClassName += adClassName;
  412. hr = spPathname->AddLeafElement (
  413. const_cast<BSTR>(cnClassName.c_str ()));
  414. if ( SUCCEEDED (hr) )
  415. {
  416. BSTR bstrFullPath = 0;
  417. hr = spPathname->Retrieve(ADS_FORMAT_X500, &bstrFullPath);
  418. if ( SUCCEEDED (hr) )
  419. {
  420. CComPtr<IDirectoryObject> spDirObj;
  421. hr = ADsGetObject (
  422. bstrFullPath,
  423. IID_PPV_ARG (IDirectoryObject, &spDirObj));
  424. if ( SUCCEEDED (hr) )
  425. {
  426. hr = SetSecurityInfoMask (spDirObj,
  427. OWNER_SECURITY_INFORMATION |
  428. GROUP_SECURITY_INFORMATION |
  429. DACL_SECURITY_INFORMATION |
  430. SACL_SECURITY_INFORMATION);
  431. //
  432. // Get this object's default Security Descriptor.
  433. //
  434. const PWSTR wzSecDescriptor = L"defaultSecurityDescriptor";
  435. DWORD cAttrs = 0;
  436. LPWSTR rgpwzAttrNames[] = {wzSecDescriptor};
  437. hr = spDirObj->GetObjectAttributes(rgpwzAttrNames, 1, ppAttrs, &cAttrs);
  438. if ( SUCCEEDED (hr) )
  439. {
  440. if ( 1 == cAttrs && *ppAttrs && (*ppAttrs)->pADsValues )
  441. {
  442. // Caller will delete the SD w/ LocalFree
  443. if ( !ConvertStringSecurityDescriptorToSecurityDescriptor(
  444. (*ppAttrs)->pADsValues->CaseIgnoreString,
  445. SDDL_REVISION,
  446. ppSecurityDescriptor,
  447. 0) )
  448. {
  449. hr = HRESULT_FROM_WIN32(::GetLastError());
  450. }
  451. }
  452. else
  453. hr = E_UNEXPECTED;
  454. }
  455. else
  456. {
  457. _TRACE (0, L"IDirectoryObject->GetObjectAttributes (): 0x%x\n", hr);
  458. }
  459. }
  460. else
  461. {
  462. _TRACE (0, L"ADsGetObject (%s): 0x%x\n", bstrFullPath, hr);
  463. wstring strErr;
  464. FormatMessage (strErr, IDS_INVALID_OBJECT,
  465. _Module.GetObjectDN ().c_str (),
  466. GetSystemMessage (hr).c_str ());
  467. MyWprintf (strErr.c_str ());
  468. }
  469. }
  470. else
  471. {
  472. _TRACE (0, L"IADsPathname->Retrieve (): 0x%x\n", hr);
  473. }
  474. }
  475. }
  476. _TRACE (-1, L"Leaving GetClassSecurityDescriptor: 0x%x\n", hr);
  477. return hr;
  478. }
  479. HRESULT GetADClassName (
  480. CComPtr<IADsPathname>& spPathname,
  481. const wstring& ldapClassName,
  482. wstring& adClassName)
  483. {
  484. _TRACE (1, L"Entering GetADClassName\n");
  485. HRESULT hr = S_OK;
  486. // Get the "CN=Schema,CN=Configuration,DC=..." object
  487. // Search for a child whose LDAP-Display-Name matches ldapClassName
  488. BSTR bstrFullPath = 0;
  489. hr = spPathname->Retrieve(ADS_FORMAT_X500, &bstrFullPath);
  490. if ( SUCCEEDED (hr) )
  491. {
  492. CComPtr<IDirectoryObject> spDirObj;
  493. hr = ADsGetObject (
  494. bstrFullPath,
  495. IID_PPV_ARG (IDirectoryObject, &spDirObj));
  496. if ( SUCCEEDED (hr) )
  497. {
  498. CComPtr<IDirectorySearch> spDsSearch;
  499. hr = spDirObj->QueryInterface (IID_PPV_ARG(IDirectorySearch, &spDsSearch));
  500. if ( SUCCEEDED (hr) )
  501. {
  502. ASSERT (!!spDsSearch);
  503. ADS_SEARCHPREF_INFO pSearchPref[2];
  504. DWORD dwNumPref = 2;
  505. pSearchPref[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
  506. pSearchPref[0].vValue.dwType = ADSTYPE_INTEGER;
  507. pSearchPref[0].vValue.Integer = ADS_SCOPE_ONELEVEL;
  508. pSearchPref[1].dwSearchPref = ADS_SEARCHPREF_CHASE_REFERRALS;
  509. pSearchPref[1].vValue.dwType = ADSTYPE_INTEGER;
  510. pSearchPref[1].vValue.Integer = ADS_CHASE_REFERRALS_NEVER;
  511. hr = spDsSearch->SetSearchPreference(
  512. pSearchPref,
  513. dwNumPref
  514. );
  515. if ( SUCCEEDED (hr) )
  516. {
  517. PWSTR rgszAttrList[] = {L"cn"}; //Common-Name", NULL };
  518. ADS_SEARCH_HANDLE hSearchHandle = 0;
  519. DWORD dwNumAttributes = 1;
  520. wstring strQuery;
  521. ADS_SEARCH_COLUMN Column;
  522. ::ZeroMemory (&Column, sizeof (ADS_SEARCH_COLUMN));
  523. FormatMessage (strQuery,
  524. L"lDAPDisplayName=%1", //L"LDAP-Display-Name=%1",
  525. ldapClassName.c_str ());
  526. hr = spDsSearch->ExecuteSearch(
  527. const_cast <LPWSTR>(strQuery.c_str ()),
  528. rgszAttrList,
  529. dwNumAttributes,
  530. &hSearchHandle
  531. );
  532. if ( SUCCEEDED (hr) )
  533. {
  534. hr = spDsSearch->GetFirstRow (hSearchHandle);
  535. if ( SUCCEEDED (hr) )
  536. {
  537. while (hr != S_ADS_NOMORE_ROWS )
  538. {
  539. //
  540. // Getting current row's information
  541. //
  542. hr = spDsSearch->GetColumn(
  543. hSearchHandle,
  544. rgszAttrList[0],
  545. &Column
  546. );
  547. if ( SUCCEEDED (hr) )
  548. {
  549. adClassName = Column.pADsValues->CaseIgnoreString;
  550. spDsSearch->FreeColumn (&Column);
  551. Column.pszAttrName = NULL;
  552. break;
  553. }
  554. else if ( hr != E_ADS_COLUMN_NOT_SET )
  555. {
  556. break;
  557. }
  558. else
  559. {
  560. _TRACE (0, L"IDirectorySearch::GetColumn (): 0x%x\n", hr);
  561. }
  562. }
  563. }
  564. else
  565. {
  566. _TRACE (0, L"IDirectorySearch::GetFirstRow (): 0x%x\n", hr);
  567. }
  568. if (Column.pszAttrName)
  569. {
  570. spDsSearch->FreeColumn(&Column);
  571. }
  572. spDsSearch->CloseSearchHandle(hSearchHandle);
  573. }
  574. else
  575. {
  576. _TRACE (0, L"IDirectorySearch::ExecuteSearch (): 0x%x\n", hr);
  577. hr = S_OK;
  578. }
  579. }
  580. else
  581. {
  582. _TRACE (0, L"IDirectorySearch::SetSearchPreference (): 0x%x\n", hr);
  583. }
  584. }
  585. else
  586. {
  587. _TRACE (0, L"IDirectoryObject::QueryInterface (IDirectorySearch): 0x%x\n", hr);
  588. }
  589. }
  590. else
  591. {
  592. _TRACE (0, L"ADsGetObject (%s): 0x%x\n", bstrFullPath, hr);
  593. }
  594. }
  595. else
  596. {
  597. _TRACE (0, L"IADsPathname->Retrieve (): 0x%x\n", hr);
  598. }
  599. _TRACE (-1, L"Leaving GetADClassName: 0x%x\n", hr);
  600. return hr;
  601. }