//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1999-2002. // // File: Schema.cpp // // Contents: DoSchemaDiagnosis and support methods // // //---------------------------------------------------------------------------- #include "stdafx.h" #include #include #include #include "ADUtils.h" #include "Schema.h" #include "SecDesc.h" // Function prototypes bool FindInGlobalList (const ACE_SAMNAME* pAceSAMNameToFind, const ACE_SAMNAME_LIST& defACLList); HRESULT GetSchemaDefaultSecurityDescriptor ( const wstring& strObjectDN, PADS_ATTR_INFO* ppAttrs, PSECURITY_DESCRIPTOR* ppSecurityDescriptor, wstring &objectClass); HRESULT GetObjectClass (const wstring& strObjectDN, wstring& ldapClassName); HRESULT GetClassSecurityDescriptor ( PADS_ATTR_INFO* ppAttrs, PSECURITY_DESCRIPTOR* ppSecurityDescriptor, CComPtr& spPathname, const wstring& ldapClassName); HRESULT GetADClassName ( CComPtr& spPathname, const wstring& ldapClassName, wstring& adClassName); // Functions HRESULT DoSchemaDiagnosis () { _TRACE (1, L"Entering DoSchemaDiagnosis\n"); HRESULT hr = S_OK; wstring str; size_t nDACLAcesFound = 0; bool bAllExplicit = true; bool bAllInherited = true; ACE_SAMNAME_LIST defDACLList; ACE_SAMNAME_LIST defSACLList; if ( !_Module.DoTabDelimitedOutput () ) { LoadFromResource (str, IDS_SCHEMA_DEFAULTS_DIAGNOSIS); MyWprintf (str.c_str ()); } PSECURITY_DESCRIPTOR pSecurityDescriptor = 0; PADS_ATTR_INFO pAttrs = NULL; wstring ldapClassName; hr = GetSchemaDefaultSecurityDescriptor (_Module.GetObjectDN (), &pAttrs, &pSecurityDescriptor, ldapClassName); if ( SUCCEEDED (hr) && pSecurityDescriptor ) { hr = EnumerateDacl (pSecurityDescriptor, defDACLList, false); if ( SUCCEEDED (hr) ) { ACE_SAMNAME* pAceSAMName = 0; // Compare the DACL for (ACE_SAMNAME_LIST::iterator itr = defDACLList.begin(); itr != defDACLList.end(); itr++) { pAceSAMName = *itr; if ( FindInGlobalList (pAceSAMName, _Module.m_DACLList) ) { if ( pAceSAMName->m_pAllowedAce->Header.AceFlags & INHERITED_ACE ) { switch ( pAceSAMName->m_AceType ) { case ACCESS_ALLOWED_OBJECT_ACE_TYPE: { wstring inheritedObjectClass; if ( SUCCEEDED (_Module.GetClassFromGUID ( pAceSAMName->m_pAllowedObjectAce->InheritedObjectType, inheritedObjectClass) ) ) { if ( !ldapClassName.compare (inheritedObjectClass) ) break; // matches } } continue; // not a match case ACCESS_DENIED_OBJECT_ACE_TYPE: { wstring inheritedObjectClass; if ( SUCCEEDED (_Module.GetClassFromGUID ( pAceSAMName->m_pDeniedObjectAce->InheritedObjectType, inheritedObjectClass) ) ) { if ( !ldapClassName.compare (inheritedObjectClass) ) break; // matches } } continue; // not a match default: break; // matches } bAllExplicit = false; } else bAllInherited = false; nDACLAcesFound++; } } } // Compare the SACL hr = EnumerateSacl (pSecurityDescriptor, defSACLList); if ( SUCCEEDED (hr) ) { ACE_SAMNAME* pAceSAMName = 0; for (ACE_SAMNAME_LIST::iterator itr = defSACLList.begin(); itr != defSACLList.end(); itr++) { pAceSAMName = *itr; if ( FindInGlobalList (pAceSAMName, _Module.m_SACLList) ) { if ( pAceSAMName->m_pAllowedAce->Header.AceFlags & INHERITED_ACE ) { switch ( pAceSAMName->m_AceType ) { case SYSTEM_AUDIT_OBJECT_ACE_TYPE: { wstring inheritedObjectClass; if ( SUCCEEDED (_Module.GetClassFromGUID ( pAceSAMName->m_pSystemAuditObjectAce->InheritedObjectType, inheritedObjectClass) ) ) { if ( !ldapClassName.compare (inheritedObjectClass) ) break; // matches } } continue; // not a match case SYSTEM_AUDIT_ACE_TYPE: default: break; // matches } bAllExplicit = false; } else bAllInherited = false; nDACLAcesFound++; } } } } wstring strDefaultState; bool bPresent = false; if ( !nDACLAcesFound ) { // absent LoadFromResource (strDefaultState, IDS_ABSENT); } else { if ( nDACLAcesFound == defDACLList.size () ) { if ( bAllExplicit | bAllInherited ) { // present LoadFromResource (strDefaultState, IDS_PRESENT); bPresent = true; } else { // partial LoadFromResource (strDefaultState, IDS_PARTIAL); } } else { // partial LoadFromResource (strDefaultState, IDS_PARTIAL); } } if ( _Module.DoTabDelimitedOutput () ) FormatMessage (str, IDS_SCHEMA_DEFAULTS_CDO, strDefaultState.c_str ()); else FormatMessage (str, IDS_SCHEMA_DEFAULTS, strDefaultState.c_str ()); MyWprintf (str.c_str ()); if ( bPresent ) { if ( bAllExplicit ) LoadFromResource (strDefaultState, IDS_AT_CREATION); else LoadFromResource (strDefaultState, IDS_BY_INHERITANCE); if ( _Module.DoTabDelimitedOutput () ) FormatMessage (str, IDS_OBTAINED_CDO, strDefaultState.c_str ()); else FormatMessage (str, IDS_OBTAINED, strDefaultState.c_str ()); MyWprintf (str.c_str ()); } else if ( _Module.DoTabDelimitedOutput () ) MyWprintf (L"\n\n"); _TRACE (-1, L"Leaving DoSchemaDiagnosis: 0x%x\n", hr); return hr; } HRESULT GetSchemaDefaultSecurityDescriptor ( const wstring& strObjectDN, PADS_ATTR_INFO* ppAttrs, PSECURITY_DESCRIPTOR* ppSecurityDescriptor, wstring &ldapClassName) { _TRACE (1, L"Entering GetSchemaDefaultSecurityDescriptor\n"); HRESULT hr = S_OK; if ( ppAttrs && ppSecurityDescriptor ) { hr = GetObjectClass (strObjectDN, ldapClassName); if ( SUCCEEDED (hr) ) { wstring strDC; size_t pos = strObjectDN.find (L"DC=", 0); if ( strObjectDN.npos != pos ) { strDC = strObjectDN.substr (pos); CComPtr spPathname; // // Constructing the directory paths // hr = CoCreateInstance( CLSID_Pathname, NULL, CLSCTX_ALL, IID_PPV_ARG (IADsPathname, &spPathname)); if ( SUCCEEDED (hr) ) { ASSERT (!!spPathname); hr = spPathname->Set (CComBSTR (ACLDIAG_LDAP), ADS_SETTYPE_PROVIDER); if ( SUCCEEDED (hr) ) { hr = spPathname->Set (CComBSTR (strDC.c_str ()), ADS_SETTYPE_DN); if ( SUCCEEDED (hr) ) { hr = spPathname->AddLeafElement (CComBSTR (L"CN=Configuration")); if ( SUCCEEDED (hr) ) { hr = spPathname->AddLeafElement (CComBSTR (L"CN=Schema")); if ( SUCCEEDED (hr) ) { hr = GetClassSecurityDescriptor ( ppAttrs, ppSecurityDescriptor, spPathname, ldapClassName); } } } else { _TRACE (0, L"IADsPathname->Set (%s): 0x%x\n", strDC.c_str (), hr); } } else { _TRACE (0, L"IADsPathname->Set (%s): 0x%x\n", ACLDIAG_LDAP, hr); } } else { _TRACE (0, L"CoCreateInstance(CLSID_Pathname): 0x%x\n", hr); } } } } else hr = E_POINTER; _TRACE (-1, L"Leaving GetSchemaDefaultSecurityDescriptor: 0x%x\n", hr); return hr; } HRESULT GetObjectClass (const wstring& strObjectDN, wstring& ldapClassName) { _TRACE (1, L"Entering GetObjectClass\n"); HRESULT hr = S_OK; CComPtr spPathname; // // Constructing the directory paths // hr = CoCreateInstance( CLSID_Pathname, NULL, CLSCTX_ALL, IID_PPV_ARG (IADsPathname, &spPathname)); if ( SUCCEEDED (hr) ) { ASSERT (!!spPathname); hr = spPathname->Set (CComBSTR (ACLDIAG_LDAP), ADS_SETTYPE_PROVIDER); if ( SUCCEEDED (hr) ) { hr = spPathname->Set (CComBSTR (strObjectDN.c_str ()), ADS_SETTYPE_DN); if ( SUCCEEDED (hr) ) { BSTR bstrFullPath = 0; hr = spPathname->Retrieve(ADS_FORMAT_X500, &bstrFullPath); if ( SUCCEEDED (hr) ) { CComPtr spDirObj; hr = ADsOpenObjectHelper (bstrFullPath, IID_IDirectoryObject, 0, (void**)&spDirObj); if ( SUCCEEDED (hr) ) { // // Get this object's object class. // const PWSTR wzObjectClass = L"objectClass"; DWORD cAttrs = 0; LPWSTR rgpwzAttrNames[] = {wzObjectClass}; PADS_ATTR_INFO pAttrs = NULL; hr = spDirObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs); if ( SUCCEEDED (hr) ) { if ( 1 <= cAttrs && pAttrs && pAttrs->pADsValues ) { if (!(pAttrs->pADsValues[pAttrs->dwNumValues-1].CaseIgnoreString) ) { _TRACE (0, L"IADS return bogus object class!\n"); hr = E_UNEXPECTED; } else { ldapClassName = pAttrs->pADsValues[pAttrs->dwNumValues-1].CaseIgnoreString; } FreeADsMem (pAttrs); } else hr = E_UNEXPECTED; } else { _TRACE (0, L"IDirectoryObject->GetObjectAttributes (): 0x%x\n", hr); } } else { _TRACE (0, L"ADsOpenObjectHelper (%s): 0x%x\n", bstrFullPath, hr); wstring strErr; FormatMessage (strErr, IDS_INVALID_OBJECT, _Module.GetObjectDN ().c_str (), GetSystemMessage (hr).c_str ()); MyWprintf (strErr.c_str ()); } } else { _TRACE (0, L"IADsPathname->Retrieve (): 0x%x\n", hr); } } else { _TRACE (0, L"IADsPathname->Set (%s): 0x%x\n", _Module.GetObjectDN ().c_str (), hr); } } else { _TRACE (0, L"IADsPathname->Set (%s): 0x%x\n", ACLDIAG_LDAP, hr); } } else { _TRACE (0, L"CoCreateInstance(CLSID_Pathname): 0x%x\n", hr); } _TRACE (-1, L"Leaving GetObjectClass: 0x%x\n", hr); return hr; } bool FindInGlobalList (const ACE_SAMNAME* pAceSAMNameToFind, const ACE_SAMNAME_LIST& defDACLList) { _TRACE (1, L"Entering FindInGlobalList\n"); bool bFound = false; ACE_SAMNAME* pAceSAMName = 0; for (ACE_SAMNAME_LIST::iterator itr = defDACLList.begin(); itr != defDACLList.end(); itr++) { pAceSAMName = *itr; // pAceSAMNameToFind must be on the left if ( *pAceSAMNameToFind == *pAceSAMName ) { bFound = true; break; } } _TRACE (-1, L"Leaving FindInGlobalList: %s\n", bFound ? L"found" : L"not found"); return bFound; } HRESULT GetClassSecurityDescriptor (PADS_ATTR_INFO* ppAttrs, PSECURITY_DESCRIPTOR* ppSecurityDescriptor, CComPtr& spPathname, const wstring& ldapClassName) { _TRACE (1, L"Entering GetClassSecurityDescriptor\n"); // ldapClassName must be converted from LDAP class to AD class wstring adClassName; HRESULT hr = GetADClassName (spPathname, ldapClassName, adClassName); if ( SUCCEEDED (hr) ) { wstring cnClassName (L"CN="); cnClassName += adClassName; hr = spPathname->AddLeafElement ( CComBSTR (cnClassName.c_str ())); if ( SUCCEEDED (hr) ) { BSTR bstrFullPath = 0; hr = spPathname->Retrieve(ADS_FORMAT_X500, &bstrFullPath); if ( SUCCEEDED (hr) ) { CComPtr spDirObj; hr = ADsOpenObjectHelper (bstrFullPath, IID_IDirectoryObject, 0, (void**)&spDirObj); if ( SUCCEEDED (hr) ) { hr = SetSecurityInfoMask (spDirObj, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION); // // Get this object's default Security Descriptor. // const PWSTR wzSecDescriptor = L"defaultSecurityDescriptor"; DWORD cAttrs = 0; LPWSTR rgpwzAttrNames[] = {wzSecDescriptor}; hr = spDirObj->GetObjectAttributes(rgpwzAttrNames, 1, ppAttrs, &cAttrs); if ( SUCCEEDED (hr) ) { if ( 1 == cAttrs && *ppAttrs && (*ppAttrs)->pADsValues ) { // Caller will delete the SD w/ LocalFree if ( !ConvertStringSecurityDescriptorToSecurityDescriptor( (*ppAttrs)->pADsValues->CaseIgnoreString, SDDL_REVISION, ppSecurityDescriptor, 0) ) { hr = HRESULT_FROM_WIN32(::GetLastError()); } if ( SUCCEEDED (hr) ) { ASSERT (*ppSecurityDescriptor); if ( !*ppSecurityDescriptor ) { _TRACE (0, L"Call to ConvertStringSecurityDescriptorToSecurityDescriptor () succeeded but returned null security descriptor.\n"); hr = E_FAIL; } } } else hr = E_UNEXPECTED; } else { _TRACE (0, L"IDirectoryObject->GetObjectAttributes (): 0x%x\n", hr); } } else { _TRACE (0, L"ADsOpenObjectHelper (%s): 0x%x\n", bstrFullPath, hr); wstring strErr; FormatMessage (strErr, IDS_INVALID_OBJECT, _Module.GetObjectDN ().c_str (), GetSystemMessage (hr).c_str ()); MyWprintf (strErr.c_str ()); } } else { _TRACE (0, L"IADsPathname->Retrieve (): 0x%x\n", hr); } } } _TRACE (-1, L"Leaving GetClassSecurityDescriptor: 0x%x\n", hr); return hr; } HRESULT GetADClassName ( CComPtr& spPathname, const wstring& ldapClassName, wstring& adClassName) { _TRACE (1, L"Entering GetADClassName\n"); HRESULT hr = S_OK; // Get the "CN=Schema,CN=Configuration,DC=..." object // Search for a child whose LDAP-Display-Name matches ldapClassName BSTR bstrFullPath = 0; hr = spPathname->Retrieve(ADS_FORMAT_X500, &bstrFullPath); if ( SUCCEEDED (hr) ) { CComPtr spDirObj; hr = ADsOpenObjectHelper (bstrFullPath, IID_IDirectoryObject, 0, (void**)&spDirObj); if ( SUCCEEDED (hr) ) { CComPtr spDsSearch; hr = spDirObj->QueryInterface (IID_PPV_ARG(IDirectorySearch, &spDsSearch)); if ( SUCCEEDED (hr) ) { ASSERT (!!spDsSearch); ADS_SEARCHPREF_INFO pSearchPref[2]; DWORD dwNumPref = 2; pSearchPref[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE; pSearchPref[0].vValue.dwType = ADSTYPE_INTEGER; pSearchPref[0].vValue.Integer = ADS_SCOPE_ONELEVEL; pSearchPref[1].dwSearchPref = ADS_SEARCHPREF_CHASE_REFERRALS; pSearchPref[1].vValue.dwType = ADSTYPE_INTEGER; pSearchPref[1].vValue.Integer = ADS_CHASE_REFERRALS_NEVER; hr = spDsSearch->SetSearchPreference( pSearchPref, dwNumPref ); if ( SUCCEEDED (hr) ) { PWSTR rgszAttrList[] = {L"cn"}; //Common-Name", NULL }; ADS_SEARCH_HANDLE hSearchHandle = 0; DWORD dwNumAttributes = 1; wstring strQuery; ADS_SEARCH_COLUMN Column; ::ZeroMemory (&Column, sizeof (ADS_SEARCH_COLUMN)); FormatMessage (strQuery, L"lDAPDisplayName=%1", //L"LDAP-Display-Name=%1", ldapClassName.c_str ()); hr = spDsSearch->ExecuteSearch( const_cast (strQuery.c_str ()), rgszAttrList, dwNumAttributes, &hSearchHandle ); if ( SUCCEEDED (hr) ) { hr = spDsSearch->GetFirstRow (hSearchHandle); if ( SUCCEEDED (hr) ) { while (hr != S_ADS_NOMORE_ROWS ) { // // Getting current row's information // hr = spDsSearch->GetColumn( hSearchHandle, rgszAttrList[0], &Column ); if ( SUCCEEDED (hr) ) { adClassName = Column.pADsValues->CaseIgnoreString; spDsSearch->FreeColumn (&Column); Column.pszAttrName = NULL; break; } else if ( hr != E_ADS_COLUMN_NOT_SET ) { break; } else { _TRACE (0, L"IDirectorySearch::GetColumn (): 0x%x\n", hr); } } } else { _TRACE (0, L"IDirectorySearch::GetFirstRow (): 0x%x\n", hr); } if (Column.pszAttrName) { spDsSearch->FreeColumn(&Column); } spDsSearch->CloseSearchHandle(hSearchHandle); } else { _TRACE (0, L"IDirectorySearch::ExecuteSearch (): 0x%x\n", hr); hr = S_OK; } } else { _TRACE (0, L"IDirectorySearch::SetSearchPreference (): 0x%x\n", hr); } } else { _TRACE (0, L"IDirectoryObject::QueryInterface (IDirectorySearch): 0x%x\n", hr); } } else { _TRACE (0, L"ADsOpenObjectHelper (%s): 0x%x\n", bstrFullPath, hr); } } else { _TRACE (0, L"IADsPathname->Retrieve (): 0x%x\n", hr); } _TRACE (-1, L"Leaving GetADClassName: 0x%x\n", hr); return hr; }