#include "stdafx.h" #include "macros.h" USE_HANDLE_MACROS("SCHMMGMT(aclpage.cpp)") #include "dataobj.h" #include "compdata.h" #include "cookie.h" #include "snapmgr.h" #include "schmutil.h" #include "cache.h" #include "relation.h" #include "attrpage.h" #include "advui.h" #include "aclpage.h" #include "ntsecapi.h" #include "sddlp.h" HRESULT GetDomainSid(LPCWSTR pszLdapPath, PSID *ppSid); // // CDynamicLibraryBase // This is the base class for CDSSecDll and CAclUiDll. // This was taken from the dnsmgr snap in. // class CDynamicLibraryBase { public: CDynamicLibraryBase() { m_lpszLibraryName = NULL; m_lpszFunctionName = NULL; m_hLibrary = NULL; m_pfFunction = NULL; } virtual ~CDynamicLibraryBase() { if ( m_hLibrary != NULL ) { ::FreeLibrary(m_hLibrary); m_hLibrary = NULL; } } // // Load a DLL and get a single entry point. // BOOL Load() { if (m_hLibrary != NULL) return TRUE; // already loaded ASSERT(m_lpszLibraryName != NULL); m_hLibrary = ::LoadLibrary(m_lpszLibraryName); if (NULL == m_hLibrary) { // The library is not present return FALSE; } ASSERT(m_lpszFunctionName != NULL); ASSERT(m_pfFunction == NULL); m_pfFunction = ::GetProcAddress(m_hLibrary, m_lpszFunctionName ); if ( NULL == m_pfFunction ) { // The library is present but does not have the entry point ::FreeLibrary( m_hLibrary ); m_hLibrary = NULL; return FALSE; } ASSERT(m_hLibrary != NULL); ASSERT(m_pfFunction != NULL); return TRUE; } protected: LPCSTR m_lpszFunctionName; LPCTSTR m_lpszLibraryName; FARPROC m_pfFunction; HMODULE m_hLibrary; }; // // CDsSecDLL - Holds the wrapper for the ACL editor wrapper. // class CDsSecDLL : public CDynamicLibraryBase { public: CDsSecDLL() { m_lpszLibraryName = _T("dssec.dll"); m_lpszFunctionName = "DSCreateISecurityInfoObject"; } HRESULT DSCreateISecurityInfoObject( LPCWSTR pwszObjectPath, // in LPCWSTR pwszObjectClass, // in DWORD dwFlags, // in LPSECURITYINFO* ppISecurityInfo, // out PFNREADOBJECTSECURITY pfnReadSd, // in PFNWRITEOBJECTSECURITY pfnWriteSd, // in LPARAM lpContext ); // in }; HRESULT CDsSecDLL::DSCreateISecurityInfoObject( LPCWSTR pwszObjectPath, // in LPCWSTR pwszObjectClass, // in DWORD dwFlags, // in LPSECURITYINFO* ppISecurityInfo, // out PFNREADOBJECTSECURITY pfnReadSd, // in PFNWRITEOBJECTSECURITY pfnWriteSd,// in LPARAM lpContext // in ) { // // Call the function of the same name. // ASSERT(m_hLibrary != NULL); ASSERT(m_pfFunction != NULL); return ((PFNDSCREATEISECINFO)m_pfFunction)( pwszObjectPath, pwszObjectClass, dwFlags, ppISecurityInfo, pfnReadSd, pfnWriteSd, lpContext ); } // // CAclUiDLL - Where the UI Actually Lives. // class CAclUiDLL : public CDynamicLibraryBase { public: CAclUiDLL() { m_lpszLibraryName = _T("aclui.dll"); m_lpszFunctionName = "CreateSecurityPage"; } HPROPSHEETPAGE CreateSecurityPage( LPSECURITYINFO psi ); }; HPROPSHEETPAGE CAclUiDLL::CreateSecurityPage( LPSECURITYINFO psi ) { ASSERT(m_hLibrary != NULL); ASSERT(m_pfFunction != NULL); return ((ACLUICREATESECURITYPAGEPROC)m_pfFunction) (psi); } // // CISecurityInformationWrapper - The wrapper for the routine that gets // sent to CreateSecurityPage(). // class CISecurityInformationWrapper : public ISecurityInformation { public: CISecurityInformationWrapper( CAclEditorPage* pAclEditorPage ) { m_dwRefCount = 0; ASSERT(pAclEditorPage != NULL); m_pAclEditorPage = pAclEditorPage; m_pISecInfo = NULL; } ~CISecurityInformationWrapper() { ASSERT(m_dwRefCount == 0); if (m_pISecInfo != NULL) m_pISecInfo->Release(); } public: // // *** IUnknown methods *** // Call through to the to actual SecurityInformation interface. // STDMETHOD(QueryInterface) (REFIID riid, LPVOID * ppvObj) { return m_pISecInfo->QueryInterface(riid, ppvObj); } STDMETHOD_(ULONG,AddRef) () { m_dwRefCount++; return m_pISecInfo->AddRef(); } STDMETHOD_(ULONG,Release) () { m_dwRefCount--; ISecurityInformation* pISecInfo = m_pISecInfo; return pISecInfo->Release(); } // // *** ISecurityInformation methods *** // These are also call through. // STDMETHOD(GetObjectInformation) (PSI_OBJECT_INFO pObjectInfo ) { HRESULT hr = m_pISecInfo->GetObjectInformation(pObjectInfo); if (m_szPageTitle.IsEmpty()) { m_szPageTitle.LoadString(IDS_DEFAULT_SECURITY); } pObjectInfo->dwFlags |= SI_PAGE_TITLE; pObjectInfo->pszPageTitle = (PWSTR)(PCWSTR)m_szPageTitle; return hr; } STDMETHOD(GetAccessRights) (const GUID* pguidObjectType, DWORD dwFlags, // SI_EDIT_AUDITS, SI_EDIT_PROPERTIES PSI_ACCESS *ppAccess, ULONG *pcAccesses, ULONG *piDefaultAccess ) { return m_pISecInfo->GetAccessRights(pguidObjectType, dwFlags, ppAccess, pcAccesses, piDefaultAccess); } STDMETHOD(MapGeneric) (const GUID *pguidObjectType, UCHAR *pAceFlags, ACCESS_MASK *pMask) { return m_pISecInfo->MapGeneric(pguidObjectType, pAceFlags, pMask); } STDMETHOD(GetInheritTypes) (PSI_INHERIT_TYPE *ppInheritTypes, ULONG *pcInheritTypes ) { return m_pISecInfo->GetInheritTypes(ppInheritTypes, pcInheritTypes); } STDMETHOD(PropertySheetPageCallback)(HWND hwnd, UINT uMsg, SI_PAGE_TYPE uPage ) { return m_pISecInfo->PropertySheetPageCallback(hwnd, uMsg, uPage); } STDMETHOD(GetSecurity) (SECURITY_INFORMATION RequestedInformation, PSECURITY_DESCRIPTOR *ppSecurityDescriptor, BOOL fDefault) { return m_pISecInfo->GetSecurity( RequestedInformation, ppSecurityDescriptor, fDefault ); } STDMETHOD(SetSecurity) (SECURITY_INFORMATION securityInformation, PSECURITY_DESCRIPTOR pSecurityDescriptor ) { return m_pISecInfo->SetSecurity( securityInformation, pSecurityDescriptor ); } private: DWORD m_dwRefCount; ISecurityInformation* m_pISecInfo; // interface pointer to the wrapped interface CAclEditorPage* m_pAclEditorPage; // back pointer CString m_szPageTitle; friend class CAclEditorPage; }; // // Static instances of the dynamically loaded DLLs. // CDsSecDLL g_DsSecDLL; CAclUiDLL g_AclUiDLL; // // CAclEditorPage Routines. // HRESULT CAclEditorPage::CreateInstance( CAclEditorPage ** ppAclPage, LPCTSTR lpszLDAPPath, LPCTSTR lpszObjectClass ) { HRESULT hr = S_OK; CAclEditorPage* pAclEditorPage = new CAclEditorPage; if (pAclEditorPage != NULL) { hr = pAclEditorPage->Initialize( lpszLDAPPath, lpszObjectClass ); if ( SUCCEEDED(hr) ) { *ppAclPage = pAclEditorPage; } else { delete pAclEditorPage; pAclEditorPage = NULL; } } else hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY ); return hr; } CAclEditorPage::CAclEditorPage() { m_pISecInfoWrap = new CISecurityInformationWrapper(this); } CAclEditorPage::~CAclEditorPage() { delete m_pISecInfoWrap; } HRESULT ReadSecurity( LPCWSTR lpszLdapPath, SECURITY_INFORMATION RequestedInformation, PSECURITY_DESCRIPTOR *ppSecDesc, LPARAM lpContext ); HRESULT WriteSecurity( LPCWSTR lpszLdapPath, SECURITY_INFORMATION securityInformation, PSECURITY_DESCRIPTOR pSecDesc, LPARAM lpContext ); HRESULT GetObjectSecurityDescriptor( LPCWSTR lpszLdapPath, PSECURITY_DESCRIPTOR * ppSecDesc, IADs ** ppIADsObject = NULL ); HRESULT CAclEditorPage::Initialize( LPCTSTR lpszLDAPPath, LPCTSTR lpszObjectClass ) { // // Get ISecurityInfo* for this object from DSSEC.DLL // if (!g_DsSecDLL.Load()) return E_INVALIDARG; ASSERT(m_pISecInfoWrap->m_pISecInfo == NULL); return g_DsSecDLL.DSCreateISecurityInfoObject( lpszLDAPPath, lpszObjectClass, DSSI_NO_ACCESS_CHECK | DSSI_NO_EDIT_OWNER | ( IsReadOnly(lpszLDAPPath) ? DSSI_READ_ONLY : 0 ), &(m_pISecInfoWrap->m_pISecInfo), ReadSecurity, WriteSecurity, 0 ); } HPROPSHEETPAGE CAclEditorPage::CreatePage() { ASSERT(m_pISecInfoWrap->m_pISecInfo != NULL); if (!g_AclUiDLL.Load()) return NULL; // // Call into ACLUI.DLL to create the page // passing the wrapper interface. // return g_AclUiDLL.CreateSecurityPage(m_pISecInfoWrap); } HRESULT ReadSecurity( LPCWSTR lpszLdapPath, SECURITY_INFORMATION /*RequestedInformation*/, // ignoring... PSECURITY_DESCRIPTOR* ppSecDesc, LPARAM /*lpContext*/) { return GetObjectSecurityDescriptor( lpszLdapPath, ppSecDesc ); } #define BREAK_ON_FAILED_BOOL(fResult) \ if ( !fResult ) \ { \ ASSERT( FALSE ); \ break; \ } const SECURITY_INFORMATION ALL_SECURITY_INFORMATION = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION; HRESULT WriteSecurity( LPCWSTR lpszLdapPath, SECURITY_INFORMATION securityInformation, PSECURITY_DESCRIPTOR pModificationDescriptor, LPARAM /*lpContext*/) { HRESULT hr = S_OK; BOOL fResult = TRUE; IADs * pIADsObject = NULL; PSECURITY_DESCRIPTOR pSecDesc = NULL; LPWSTR pstrSecDesc = NULL; const UINT cAbsoluteSecDescSize = 5; struct { PVOID pData; DWORD dwDataSize; } absSecDesc[cAbsoluteSecDescSize]; const PSECURITY_DESCRIPTOR & pAbsSecDesc = (PSECURITY_DESCRIPTOR) absSecDesc[0].pData; ZeroMemory( absSecDesc, sizeof(absSecDesc) ); // we only support changes in DACL & SACL ASSERT( securityInformation & (DACL_SECURITY_INFORMATION | SACL_SECURITY_INFORMATION) ); ASSERT( IsValidSecurityDescriptor(pModificationDescriptor) ); do { hr = GetObjectSecurityDescriptor( lpszLdapPath, &pSecDesc, &pIADsObject ); BREAK_ON_FAILED_HRESULT(hr); ASSERT(pIADsObject); fResult = MakeAbsoluteSD( pSecDesc, (PSECURITY_DESCRIPTOR) absSecDesc[0].pData, &absSecDesc[0].dwDataSize, (PACL) absSecDesc[1].pData, &absSecDesc[1].dwDataSize, (PACL) absSecDesc[2].pData, &absSecDesc[2].dwDataSize, (PSID) absSecDesc[3].pData, &absSecDesc[3].dwDataSize, (PSID) absSecDesc[4].pData, &absSecDesc[4].dwDataSize ); ASSERT( !fResult ); // the call must fail the first time hr = HRESULT_FROM_WIN32(::GetLastError()); if( ERROR_INSUFFICIENT_BUFFER != HRESULT_CODE(hr) ) BREAK_ON_FAILED_HRESULT(hr); fResult = TRUE; hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY ); // allocate memory for the security descriptor for( UINT i = 0; i < cAbsoluteSecDescSize; i++ ) if( absSecDesc[i].dwDataSize > 0 ) if( NULL == (absSecDesc[i].pData = new BYTE[ absSecDesc[i].dwDataSize ]) ) break;// NTRAID#NTBUG9-540268-2002/02/13-dantra Out of memory condition masked when building the SECURITY_DESCRIPTOR hr = S_OK; fResult = MakeAbsoluteSD( pSecDesc, (PSECURITY_DESCRIPTOR) absSecDesc[0].pData, &absSecDesc[0].dwDataSize, (PACL) absSecDesc[1].pData, &absSecDesc[1].dwDataSize, (PACL) absSecDesc[2].pData, &absSecDesc[2].dwDataSize, (PSID) absSecDesc[3].pData, &absSecDesc[3].dwDataSize, (PSID) absSecDesc[4].pData, &absSecDesc[4].dwDataSize ); BREAK_ON_FAILED_BOOL( fResult ); // for convinience, have another reference. ASSERT( absSecDesc[0].pData == pAbsSecDesc ); ASSERT( IsValidSecurityDescriptor(pAbsSecDesc) ); // Apply DACL changes if( securityInformation & DACL_SECURITY_INFORMATION ) { BOOL bDaclPresent = FALSE; PACL pDacl = NULL; BOOL bDaclDefaulted = FALSE; SECURITY_DESCRIPTOR_CONTROL control = 0; DWORD dwRevision = 0; fResult = GetSecurityDescriptorDacl( pModificationDescriptor, &bDaclPresent, &pDacl, &bDaclDefaulted ); BREAK_ON_FAILED_BOOL( fResult ); fResult = SetSecurityDescriptorDacl( pAbsSecDesc, bDaclPresent, pDacl, bDaclDefaulted ); BREAK_ON_FAILED_BOOL( fResult ); fResult = GetSecurityDescriptorControl( pModificationDescriptor, &control, &dwRevision ); BREAK_ON_FAILED_BOOL( fResult ); fResult = SetSecurityDescriptorControl( pAbsSecDesc, SE_DACL_PROTECTED, control & SE_DACL_PROTECTED ); BREAK_ON_FAILED_BOOL( fResult ); } // Apply SACL changes if( securityInformation & SACL_SECURITY_INFORMATION ) { BOOL bSaclPresent = FALSE; PACL pSacl = NULL; BOOL bSaclDefaulted = FALSE; SECURITY_DESCRIPTOR_CONTROL control = 0; DWORD dwRevision = 0; fResult = GetSecurityDescriptorSacl( pModificationDescriptor, &bSaclPresent, &pSacl, &bSaclDefaulted ); BREAK_ON_FAILED_BOOL( fResult ); fResult = SetSecurityDescriptorSacl( pAbsSecDesc, bSaclPresent, pSacl, bSaclDefaulted ); BREAK_ON_FAILED_BOOL( fResult ); fResult = GetSecurityDescriptorControl( pModificationDescriptor, &control, &dwRevision ); BREAK_ON_FAILED_BOOL( fResult ); fResult = SetSecurityDescriptorControl( pAbsSecDesc, SE_SACL_PROTECTED, control & SE_SACL_PROTECTED ); BREAK_ON_FAILED_BOOL( fResult ); } // Convert Security Descriptor to string format fResult = ConvertSecurityDescriptorToStringSecurityDescriptor( pAbsSecDesc, SDDL_REVISION, ALL_SECURITY_INFORMATION, &pstrSecDesc, NULL ); BREAK_ON_FAILED_BOOL( fResult ); ASSERT(pstrSecDesc); CComVariant v(pstrSecDesc); // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR hr = pIADsObject->Put( CComBSTR(g_DefaultAcl), v); BREAK_ON_FAILED_HRESULT(hr); hr = pIADsObject->SetInfo( ); } while (0); if( !fResult ) hr = HRESULT_FROM_WIN32(::GetLastError()); if( pIADsObject ) pIADsObject->Release(); if( pstrSecDesc ) LocalFree( pstrSecDesc ); for( UINT i = 0; i < cAbsoluteSecDescSize; i++ ) if( absSecDesc[i].pData ) delete absSecDesc[i].pData; return hr; } HRESULT GetObjectSecurityDescriptor( LPCWSTR lpszLdapPath, PSECURITY_DESCRIPTOR * ppSecDesc, IADs ** ppIADsObject /* = NULL */) // returns pIADsObject for future use. { HRESULT hr = S_OK; IADs * pIADsObject = NULL; CComVariant AdsResult; PSID pDomainSid = NULL; *ppSecDesc = NULL; do { hr = SchemaOpenObject( const_cast((LPCWSTR) lpszLdapPath), IID_IADs, (void **) &pIADsObject ); BREAK_ON_FAILED_HRESULT(hr); ASSERT(pIADsObject); // NTRAID#NTBUG9-540866-2002/02/13-dantra-Schema Manager: passing WCHAR * instead of BSTR to method requiring a BSTR hr = pIADsObject->Get( CComBSTR(g_DefaultAcl),&AdsResult); if (hr == E_ADS_PROPERTY_NOT_FOUND) { hr = S_OK; PSECURITY_DESCRIPTOR pSecDescTmp = (PSECURITY_DESCRIPTOR)LocalAlloc ( LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH ); if(pSecDescTmp==NULL) {hr=E_OUTOFMEMORY;break;} do { if(!InitializeSecurityDescriptor(pSecDescTmp, SECURITY_DESCRIPTOR_REVISION)) { hr = HRESULT_FROM_WIN32(::GetLastError()); ASSERT(FAILED(hr)); break; } DWORD size=0; BOOL fResult = MakeSelfRelativeSD(pSecDescTmp,*ppSecDesc,&size); ASSERT(!fResult); //ERROR_INSUFFICIENT_BUFFER is expected at first. hr = HRESULT_FROM_WIN32(::GetLastError()); if( HRESULT_CODE(hr) != ERROR_INSUFFICIENT_BUFFER) BREAK_ON_FAILED_HRESULT(hr); hr = S_OK; *ppSecDesc=(PSECURITY_DESCRIPTOR)LocalAlloc(LPTR,size); if(*ppSecDesc==NULL) {hr=E_OUTOFMEMORY;break;} fResult = MakeSelfRelativeSD(pSecDescTmp,*ppSecDesc,&size); if(!fResult) { hr = HRESULT_FROM_WIN32(::GetLastError()); ASSERT(FAILED(hr)); // NTRAID#NTBUG9-542445-2002/03/04-dantra-Memory Leak in Error path of aclpage.cpp::GetObjectSecurityDescriptor LocalFree(*ppSecDesc); *ppSecDesc=NULL; break; } } while(0); LocalFree(pSecDescTmp); BREAK_ON_FAILED_HRESULT(hr); } else if(FAILED(hr)) break; else { pDomainSid = NULL; GetDomainSid(lpszLdapPath, &pDomainSid); if(pDomainSid) { if(!ConvertStringSDToSDDomain(pDomainSid, NULL, V_BSTR(&AdsResult), SDDL_REVISION, ppSecDesc, NULL )) { ASSERT( FALSE ); hr = HRESULT_FROM_WIN32(::GetLastError()); break; } } else if( !ConvertStringSecurityDescriptorToSecurityDescriptor( V_BSTR(&AdsResult), SDDL_REVISION, ppSecDesc, NULL ) ) { ASSERT( FALSE ); hr = HRESULT_FROM_WIN32(::GetLastError()); break; } } ASSERT( IsValidSecurityDescriptor(*ppSecDesc) ); } while (0); if( pIADsObject ) { ASSERT( SUCCEEDED(hr) ); if( !ppIADsObject ) // if caller doesn't need pIADsObject { pIADsObject->Release(); // release it } else { *ppIADsObject = pIADsObject; // otherwise, return it } } if(pDomainSid) LocalFree(pDomainSid); return hr; } BOOL CAclEditorPage::IsReadOnly( LPCTSTR lpszLDAPPath ) { ASSERT( lpszLDAPPath ); HRESULT hr = S_OK; BOOL fFound = FALSE; CComPtr ipADs; CStringList strlist; do { // // Open the schema container. // hr = SchemaOpenObject( (LPWSTR)(LPCWSTR)lpszLDAPPath, IID_IADs, (void **)&ipADs ); BREAK_ON_FAILED_HRESULT(hr); // extract the list of allowed classes hr = GetStringListElement( ipADs, &g_allowedAttributesEffective, strlist ); BREAK_ON_FAILED_HRESULT(hr); // search for needed attributes for( POSITION pos = strlist.GetHeadPosition(); !fFound && pos != NULL; ) { CString * pstr = &strlist.GetNext( pos ); fFound = !pstr->CompareNoCase( g_DefaultAcl ); } } while( FALSE ); return !fFound; // in case something fails, make read-only. } LSA_HANDLE GetLSAConnection(LPCTSTR pszServer, DWORD dwAccessDesired) { LSA_HANDLE hPolicy = NULL; LSA_UNICODE_STRING uszServer = {0}; LSA_UNICODE_STRING *puszServer = NULL; LSA_OBJECT_ATTRIBUTES oa; SECURITY_QUALITY_OF_SERVICE sqos; sqos.Length = sizeof(sqos); sqos.ImpersonationLevel = SecurityImpersonation; sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; sqos.EffectiveOnly = FALSE; InitializeObjectAttributes(&oa, NULL, 0, NULL, NULL); oa.SecurityQualityOfService = &sqos; if (pszServer && *pszServer && RtlCreateUnicodeString(&uszServer, pszServer)) { puszServer = &uszServer; } LsaOpenPolicy(puszServer, &oa, dwAccessDesired, &hPolicy); if (puszServer) RtlFreeUnicodeString(puszServer); return hPolicy; } HRESULT GetDomainSid(LPCWSTR pszLdapPath, PSID *ppSid) { HRESULT hr = S_OK; NTSTATUS nts = STATUS_SUCCESS; PPOLICY_ACCOUNT_DOMAIN_INFO pDomainInfo = NULL; LSA_HANDLE hLSA = 0; if(!pszLdapPath || !ppSid) return E_INVALIDARG; *ppSid = NULL; IADsPathname *pPath = NULL; BSTR bstrServer = NULL; CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&pPath); if(pPath) { if(SUCCEEDED(pPath->Set((BSTR)pszLdapPath,ADS_SETTYPE_FULL))) { if(SUCCEEDED(pPath->Retrieve(ADS_FORMAT_SERVER, &bstrServer))) { hLSA = GetLSAConnection(bstrServer, POLICY_VIEW_LOCAL_INFORMATION); if (!hLSA) { hr = E_FAIL; goto exit_gracefully; } nts = LsaQueryInformationPolicy(hLSA, PolicyAccountDomainInformation, (PVOID*)&pDomainInfo); if(nts != STATUS_SUCCESS) { hr = E_FAIL; goto exit_gracefully; } if (pDomainInfo && pDomainInfo->DomainSid) { ULONG cbSid = GetLengthSid(pDomainInfo->DomainSid); *ppSid = (PSID) LocalAlloc(LPTR, cbSid); if (!*ppSid) { hr = E_OUTOFMEMORY; goto exit_gracefully; } // NOTE: Safe use of CopyMemory CopyMemory(*ppSid, pDomainInfo->DomainSid, cbSid); } } } } exit_gracefully: if(pDomainInfo) LsaFreeMemory(pDomainInfo); if(hLSA) LsaClose(hLSA); if(bstrServer) SysFreeString(bstrServer); if(pPath) pPath->Release(); return hr; }