//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: dssi.cpp // // This file contains the implementation of the CDSSecurityInfo object, // which provides the ISecurityInformation interface for invoking // the ACL Editor. // //-------------------------------------------------------------------------- #include "pch.h" #include #include "exnc.h" #include "ntsecapi.h" TCHAR const c_szDomainClass[] = DOMAIN_CLASS_NAME; // adsnms.h #define CLASS_COMPUTER L"computer" BOOL IsRootObject(IDirectoryObject * pDirObject); GENERIC_MAPPING g_DSMap = { DS_GENERIC_READ, DS_GENERIC_WRITE, DS_GENERIC_EXECUTE, DS_GENERIC_ALL }; #define DSSI_LOCAL_NO_CREATE_DELETE 0x00000001 // //Function Declarations // HRESULT GetDomainSid(LPCWSTR pszServer, PSID *ppSid); HRESULT GetRootDomainSid(LPCWSTR pszServer, PSID *ppSid); // // CDSSecurityInfo (ISecurityInformation) class definition // class CDSSecurityInfo : public ISecurityInformation, IEffectivePermission, ISecurityObjectTypeInfo, CUnknown { protected: GUID m_guidObjectType; BSTR m_strServerName; BSTR m_strObjectPath; BSTR m_strObjectClass; BSTR m_strDisplayName; BSTR m_strSchemaRootPath; AUTHZ_RESOURCE_MANAGER_HANDLE m_ResourceManager; // //List of Aux Clasess Attached to the object // HDPA m_hAuxClasses; IDirectoryObject *m_pDsObject; PSECURITY_DESCRIPTOR m_pSD; PSID m_pDomainSid; PSID m_pRootDomainSid; PSECURITY_DESCRIPTOR m_pDefaultSD; DWORD m_dwSIFlags; DWORD m_dwInitFlags; // DSSI_* DWORD m_dwLocalFlags; //DSSI_LOCAL_* HANDLE m_hInitThread; HANDLE m_hLoadLibWaitEvent; volatile BOOL m_bThreadAbort; PFNREADOBJECTSECURITY m_pfnReadSD; PFNWRITEOBJECTSECURITY m_pfnWriteSD; LPARAM m_lpReadContext; LPARAM m_lpWriteContext; // //Access Information // PACCESS_INFO m_pAIGeneral; //For First Page and Object Page on Advanced PACCESS_INFO m_pAIProperty; //For Property Page on Advanced PACCESS_INFO m_pAIEffective; //For Effective Page on Advanced // //Object Type List Info // POBJECT_TYPE_LIST m_pOTL; ULONG m_cCountOTL; public: virtual ~CDSSecurityInfo(); STDMETHODIMP Init(LPCWSTR pszObjectPath, LPCWSTR pszObjectClass, LPCWSTR pszServer, LPCWSTR pszUserName, LPCWSTR pszPassword, DWORD dwFlags, PFNREADOBJECTSECURITY pfnReadSD, PFNWRITEOBJECTSECURITY pfnWriteSD, LPARAM lpContext); // IUnknown STDMETHODIMP QueryInterface(REFIID, LPVOID *); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // ISecurityInformation STDMETHODIMP GetObjectInformation(PSI_OBJECT_INFO pObjectInfo); STDMETHODIMP GetSecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR *ppSD, BOOL fDefault); STDMETHODIMP SetSecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD); STDMETHODIMP GetAccessRights(const GUID* pguidObjectType, DWORD dwFlags, PSI_ACCESS *ppAccess, ULONG *pcAccesses, ULONG *piDefaultAccess); STDMETHODIMP MapGeneric(const GUID *pguidObjectType, UCHAR *pAceFlags, ACCESS_MASK *pmask); STDMETHODIMP GetInheritTypes(PSI_INHERIT_TYPE *ppInheritTypes, ULONG *pcInheritTypes); STDMETHODIMP PropertySheetPageCallback(HWND hwnd, UINT uMsg, SI_PAGE_TYPE uPage); //IEffectivePermission STDMETHODIMP GetEffectivePermission(const GUID* pguidObjectType, PSID pUserSid, LPCWSTR pszServerName, PSECURITY_DESCRIPTOR pSD, POBJECT_TYPE_LIST *ppObjectTypeList, ULONG *pcObjectTypeListLength, PACCESS_MASK *ppGrantedAccessList, ULONG *pcGrantedAccessListLength); //ISecurityObjectTypeInfo STDMETHOD(GetInheritSource)(SECURITY_INFORMATION si, PACL pACL, PINHERITED_FROM *ppInheritArray); private: HRESULT Init2(LPCWSTR pszUserName, LPCWSTR pszPassword); HRESULT Init3(); HRESULT GetAuxClassList(); DWORD CheckObjectAccess(); void WaitOnInitThread(void) { WaitOnThread(&m_hInitThread); } static DWORD WINAPI InitThread(LPVOID pvThreadData); static HRESULT WINAPI DSReadObjectSecurity(LPCWSTR pszObjectPath, SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR *ppSD, LPARAM lpContext); static HRESULT WINAPI DSWriteObjectSecurity(LPCWSTR pszObjectPath, SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD, LPARAM lpContext); }; // // CDSSecurityInfo (ISecurityInformation) implementation // CDSSecurityInfo::~CDSSecurityInfo() { TraceEnter(TRACE_DSSI, "CDSSecurityInfo::~CDSSecurityInfo"); m_bThreadAbort = TRUE; if (m_hInitThread != NULL) { WaitForSingleObject(m_hInitThread, INFINITE); CloseHandle(m_hInitThread); } DoRelease(m_pDsObject); SysFreeString(m_strServerName); SysFreeString(m_strObjectPath); SysFreeString(m_strObjectClass); SysFreeString(m_strDisplayName); SysFreeString(m_strSchemaRootPath); if (m_pSD != NULL) LocalFree(m_pSD); if( m_pDefaultSD != NULL ) LocalFree(m_pDefaultSD); if(m_pDomainSid) LocalFree(m_pDomainSid); if(m_pRootDomainSid) LocalFree(m_pRootDomainSid); DestroyDPA(m_hAuxClasses); if(m_pAIGeneral && m_pAIGeneral->bLocalFree) { LocalFree(m_pAIGeneral->pAccess); LocalFree(m_pAIGeneral); } if(m_pAIProperty && m_pAIProperty->bLocalFree) { LocalFree(m_pAIProperty->pAccess); LocalFree(m_pAIProperty); } if(m_pAIEffective && m_pAIEffective->bLocalFree) { LocalFree(m_pAIEffective->pAccess); LocalFree(m_pAIEffective); } if(m_pOTL) LocalFree(m_pOTL); if(m_ResourceManager) AuthzFreeResourceManager(m_ResourceManager); TraceLeaveVoid(); } //+-------------------------------------------------------------------------- // // Function: DeleteParents // // Synopsis: Delete the parent of pszClassName from the list. // And recursively calls the function to delete the // parent of parent of pszClassName from the list. // History: 06-22-2000 DavidMun Created // //--------------------------------------------------------------------------- HRESULT DeleteParents(HDPA hListAux, LPWSTR pszClassName, LPWSTR pszSchemaRootPath) { TraceEnter(TRACE_DSSI, "DeleteParents"); if(!hListAux || !pszSchemaRootPath) return E_INVALIDARG; HRESULT hr = S_OK; IADsClass *pDsClass = NULL; VARIANT varDerivedFrom; int cCount = DPA_GetPtrCount(hListAux); if(cCount > 1) { hr = Schema_BindToObject(pszSchemaRootPath, pszClassName, IID_IADsClass, (LPVOID*)&pDsClass); FailGracefully(hr, "Schema_BindToObject failed"); // //Find out the parent // hr = pDsClass->get_DerivedFrom(&varDerivedFrom); if(hr == E_ADS_PROPERTY_NOT_FOUND) { // //This error will come for TOP which doesn't //have any parent // hr = S_OK; goto exit_gracefully; } FailGracefully(hr, "IADsClass get_DerivedFrom failed"); LPWSTR pszParent= NULL; LPWSTR pszTemp = NULL; if( V_VT(&varDerivedFrom) == VT_BSTR) { pszParent = V_BSTR(&varDerivedFrom); int i; // //Remove all the pszParent entry from the //hListAux // for(i = 0; i < cCount; ++i) { pszTemp = (LPWSTR)DPA_FastGetPtr(hListAux,i); if(wcscmp(pszTemp, pszParent) == 0) { DPA_DeletePtr(hListAux,i); --cCount; --i; } } } VariantClear(&varDerivedFrom); } exit_gracefully: if(pDsClass) DoRelease(pDsClass); return hr; } HRESULT CDSSecurityInfo::GetAuxClassList() { TraceEnter(TRACE_DSSI, "GetAuxClassList"); if(!m_pDsObject || !m_strSchemaRootPath) { return S_FALSE; } HRESULT hr = S_OK; PADS_ATTR_INFO pAtrrInfoObject = NULL; DWORD dwAttrCountObject = 0; PADS_ATTR_INFO pAttrInfoStruct = NULL; DWORD dwAttrCountStruct= 0; HDPA hListAux = NULL; HDPA hListCopy = NULL; //ObjectClass is list of "class hierarchy of StructuralClass" and "class hierarchy of AuxClass" //for the object. //So ObjectClass MINUS StructurcalClass is the list of AuxClass. //This list after subtraction may conatin the inheritance hierarchy. //We only want the mostsignificant classes for the purpose of aclui. // //Get the ObjectClass Attribute // LPWSTR pszTemp = (LPWSTR)c_szObjectClass; hr = m_pDsObject->GetObjectAttributes(&pszTemp, 1, &pAtrrInfoObject, &dwAttrCountObject); FailGracefully(hr, "Failed to get ObjectClass Attribute"); if(!pAtrrInfoObject || !dwAttrCountObject) ExitGracefully(hr, S_OK, "Couldn't get ObjectClass, Assume no AuxClass"); // //Get the StructuralObjectClass Attribute // pszTemp = (LPWSTR)c_szStructuralObjectClass; hr = m_pDsObject->GetObjectAttributes(&pszTemp, 1, &pAttrInfoStruct, &dwAttrCountStruct); FailGracefully(hr, "Failed to get StructuralObjectClass Attribute"); if(!pAttrInfoStruct || !dwAttrCountStruct) ExitGracefully(hr, S_OK, "Couldn't get Structural Object Class Attribute, Assume no Aux Class"); if(pAtrrInfoObject->dwNumValues == pAttrInfoStruct->dwNumValues) { Trace((L"No Auxillary Class Attached to this object\n")); goto exit_gracefully; } hListAux = DPA_Create(4); UINT i,j; BOOL bAuxClass; for(i = 0; i < pAtrrInfoObject->dwNumValues; ++i) { bAuxClass = TRUE; for(j = 0; j < pAttrInfoStruct->dwNumValues; ++j) { if( wcscmp(pAtrrInfoObject->pADsValues[i].CaseIgnoreString, pAttrInfoStruct->pADsValues[j].CaseExactString) == 0 ) { bAuxClass = FALSE; break; } } if(bAuxClass) { DPA_AppendPtr(hListAux,pAtrrInfoObject->pADsValues[i].CaseExactString); } } UINT cCount; cCount = DPA_GetPtrCount(hListAux); if(cCount) { if(cCount > 1) { // //Make a copy of hListAux // HDPA hListCopy2 = DPA_Create(cCount); for(i = 0; i < cCount; ++i) DPA_AppendPtr(hListCopy2,DPA_FastGetPtr(hListAux, i)); // //For each item in hListCopy2 remove its parent from //hListAux // for(i = 0; i < cCount; ++i) { hr = DeleteParents(hListAux, (LPWSTR)DPA_FastGetPtr(hListCopy2, i), m_strSchemaRootPath); FailGracefully(hr, "DeleteParents Failed"); // //if only one item is left we are done. // if( 1 == DPA_GetPtrCount(hListAux)) break; } } // // What we have left is list of mostsignificant AuxClass[es] // LPWSTR pszItem; cCount = DPA_GetPtrCount(hListAux); TraceAssert(cCount); if(!m_hAuxClasses) { m_hAuxClasses = DPA_Create(cCount); } // //Copy AuxClasses into class member // while(cCount) { pszItem = (LPWSTR)DPA_FastGetPtr(hListAux,--cCount); PAUX_INFO pAI = (PAUX_INFO)LocalAlloc(LPTR,sizeof(AUX_INFO) + StringByteSize(pszItem)); if(!pAI) ExitGracefully(hr, E_OUTOFMEMORY, "Out of memory"); wcscpy(pAI->pszClassName,pszItem); pAI->guid = GUID_NULL; DPA_AppendPtr(m_hAuxClasses, pAI); } } exit_gracefully: if(hListAux) DPA_Destroy(hListAux); if(hListCopy) DPA_Destroy(hListCopy); if(pAttrInfoStruct) FreeADsMem(pAttrInfoStruct); if(pAtrrInfoObject) FreeADsMem(pAtrrInfoObject); return S_OK; } STDMETHODIMP CDSSecurityInfo::Init(LPCWSTR pszObjectPath, LPCWSTR pszObjectClass, LPCWSTR pszServer, LPCWSTR pszUserName, LPCWSTR pszPassword, DWORD dwFlags, PFNREADOBJECTSECURITY pfnReadSD, PFNWRITEOBJECTSECURITY pfnWriteSD, LPARAM lpContext) { HRESULT hr = S_OK; DWORD dwThreadID; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::Init"); TraceAssert(pszObjectPath != NULL); TraceAssert(m_strObjectPath == NULL); // only initialize once m_dwInitFlags = dwFlags; m_ResourceManager = NULL; m_pfnReadSD = DSReadObjectSecurity; m_pfnWriteSD = DSWriteObjectSecurity; m_lpReadContext = (LPARAM)this; m_lpWriteContext = (LPARAM)this; m_hLoadLibWaitEvent = NULL; m_hAuxClasses = NULL; m_pAIGeneral = NULL; //For First Page and Object Page on Advanced m_pAIProperty = NULL; //For Property Page on Advanced m_pAIEffective = NULL; //For Effective Page on Advanced m_pOTL = NULL; m_cCountOTL = 0; m_pDomainSid = NULL; m_pRootDomainSid = NULL; if (pfnReadSD) { m_pfnReadSD = pfnReadSD; m_lpReadContext = lpContext; } if (pfnWriteSD) { m_pfnWriteSD = pfnWriteSD; m_lpWriteContext = lpContext; } m_pDefaultSD = NULL; m_pSD = NULL; m_strObjectPath = SysAllocString(pszObjectPath); if (m_strObjectPath == NULL) ExitGracefully(hr, E_OUTOFMEMORY, "Unable to copy the object path"); if (pszObjectClass && *pszObjectClass) m_strObjectClass = SysAllocString(pszObjectClass); if (pszServer) { // Skip any preceding backslashes while (L'\\' == *pszServer) pszServer++; if (*pszServer) m_strServerName = SysAllocString(pszServer); } // Init2 cracks the path, binds to the object, checks access to // the object and gets the schema path. This used to be done on // the other thread below, but is faster now than it used to be. // // It's preferable to do it here where we can fail and prevent the // page from being created if, for example, the user has no access // to the object's security descriptor. This is better than always // creating the Security page and having it show a message // when initialization fails. hr = Init2(pszUserName, pszPassword); if (SUCCEEDED(hr)) { // //Get the domain sid // GetDomainSid(m_strServerName, &m_pDomainSid); GetRootDomainSid(m_strServerName,&m_pRootDomainSid); if( !m_strObjectClass || !m_strSchemaRootPath ) { // We evidently don't have read_property access to the object, // so just assume it's not a container, so we don't have to deal // with inherit types. // // We need to struggle on as best as we can. If someone removes // all access to an object, this is the only way an admin can // restore it. // m_guidObjectType = GUID_NULL; //don't show effective permission tab m_dwSIFlags &= (~SI_EDIT_EFFECTIVE); } else { // //Get the list of Dynamic Auxillary Classes attached to this Object. // hr = GetAuxClassList(); } //Create event to make sure load library is called by InitThread before //function returns m_hLoadLibWaitEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); if( m_hLoadLibWaitEvent != NULL ) { m_hInitThread = CreateThread(NULL, 0, InitThread, this, 0, &dwThreadID); WaitForSingleObject( m_hLoadLibWaitEvent, INFINITE ); } } exit_gracefully: if( m_hLoadLibWaitEvent ) CloseHandle( m_hLoadLibWaitEvent ); TraceLeaveResult(hr); } char const c_szDsGetDcNameProc[] = "DsGetDcNameW"; char const c_szNetApiBufferFreeProc[] = "NetApiBufferFree"; typedef DWORD (WINAPI *PFN_DSGETDCNAME)(LPCWSTR, LPCWSTR, GUID*, LPCWSTR, ULONG, PDOMAIN_CONTROLLER_INFOW*); typedef DWORD (WINAPI *PFN_NETAPIFREE)(LPVOID); HRESULT GetDsDcAddress(BSTR *pbstrDcAddress) { HRESULT hr = E_FAIL; HMODULE hNetApi32 = LoadLibrary(c_szNetApi32); if (hNetApi32) { PFN_DSGETDCNAME pfnDsGetDcName = (PFN_DSGETDCNAME)GetProcAddress(hNetApi32, c_szDsGetDcNameProc); PFN_NETAPIFREE pfnNetApiFree = (PFN_NETAPIFREE)GetProcAddress(hNetApi32, c_szNetApiBufferFreeProc); if (pfnDsGetDcName && pfnNetApiFree) { PDOMAIN_CONTROLLER_INFOW pDCI; DWORD dwErr = (*pfnDsGetDcName)(NULL, NULL, NULL, NULL, DS_DIRECTORY_SERVICE_REQUIRED | DS_IP_REQUIRED, &pDCI); hr = HRESULT_FROM_WIN32(dwErr); if (SUCCEEDED(hr)) { LPCWSTR pszAddress = pDCI->DomainControllerAddress; // Skip any preceding backslashes while (L'\\' == *pszAddress) pszAddress++; *pbstrDcAddress = SysAllocString(pszAddress); if (NULL == *pbstrDcAddress) hr = E_OUTOFMEMORY; (*pfnNetApiFree)(pDCI); } } FreeLibrary(hNetApi32); } return hr; } HRESULT CDSSecurityInfo::Init2(LPCWSTR pszUserName, LPCWSTR pszPassword) { HRESULT hr = S_OK; DWORD dwAccessGranted; PADS_OBJECT_INFO pObjectInfo = NULL; IADsPathname *pPath = NULL; LPWSTR pszTemp; DWORD dwPrivs[] = { SE_SECURITY_PRIVILEGE, SE_TAKE_OWNERSHIP_PRIVILEGE }; HANDLE hToken = INVALID_HANDLE_VALUE; PADS_ATTR_INFO pAttributeInfo = NULL; DWORD dwAttributesReturned; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::Init2"); TraceAssert(m_strObjectPath != NULL); TraceAssert(m_pDsObject == NULL); // only do this one time // // Create an ADsPathname object to parse the path and get the // leaf name (for display) and server name (if necessary) // hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&pPath); if (pPath) { if (FAILED(pPath->Set(m_strObjectPath, ADS_SETTYPE_FULL))) DoRelease(pPath); // sets pPath to NULL } if (NULL == m_strServerName) { // The path may or may not specify a server. If not, call DsGetDcName if (pPath) hr = pPath->Retrieve(ADS_FORMAT_SERVER, &m_strServerName); if (!pPath || FAILED(hr)) hr = GetDsDcAddress(&m_strServerName); FailGracefully(hr, "Unable to get server name"); } Trace((TEXT("Server \"%s\""), m_strServerName)); // Enable privileges before binding so CheckObjectAccess // and DSRead/WriteObjectSecurity work correctly. hToken = EnablePrivileges(dwPrivs, ARRAYSIZE(dwPrivs)); // Bind to the object and get the schema path, etc. Trace((TEXT("Calling OpenDSObject(%s)"), m_strObjectPath)); hr = OpenDSObject(m_strObjectPath, (LPWSTR)pszUserName, (LPWSTR)pszPassword, ADS_SECURE_AUTHENTICATION | ADS_FAST_BIND, IID_IDirectoryObject, (LPVOID*)&m_pDsObject); FailGracefully(hr, "Failed to get the DS object"); // Assume certain access by default if (m_dwInitFlags & DSSI_READ_ONLY) dwAccessGranted = READ_CONTROL; else dwAccessGranted = READ_CONTROL | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY; if (!(m_dwInitFlags & DSSI_NO_ACCESS_CHECK)) { // Check whether the user has permission to do anything to // the security descriptor on this object. dwAccessGranted = CheckObjectAccess(); Trace((TEXT("AccessGranted = 0x%08x"), dwAccessGranted)); if (!(dwAccessGranted & (READ_CONTROL | WRITE_DAC | WRITE_OWNER | ACCESS_SYSTEM_SECURITY | SI_MAY_WRITE))) ExitGracefully(hr, E_ACCESSDENIED, "No access"); } // Translate the access into SI_* flags, starting with this: m_dwSIFlags = SI_EDIT_ALL | SI_ADVANCED | SI_EDIT_PROPERTIES | SI_SERVER_IS_DC |SI_EDIT_EFFECTIVE; if (!(dwAccessGranted & WRITE_DAC)) { if( !(dwAccessGranted & SI_MAY_WRITE) ) m_dwSIFlags |= SI_READONLY; else m_dwSIFlags |= SI_MAY_WRITE; } if (!(dwAccessGranted & WRITE_OWNER)) { if (!(dwAccessGranted & READ_CONTROL)) m_dwSIFlags &= ~SI_EDIT_OWNER; else m_dwSIFlags |= SI_OWNER_READONLY; } if (!(dwAccessGranted & ACCESS_SYSTEM_SECURITY) || (m_dwInitFlags & DSSI_NO_EDIT_SACL)) m_dwSIFlags &= ~SI_EDIT_AUDITS; if (m_dwInitFlags & DSSI_NO_EDIT_OWNER) m_dwSIFlags &= ~SI_EDIT_OWNER; // If this is a root object (ex. domain), hide the ACL protect checkbox if ((m_dwInitFlags & DSSI_IS_ROOT) || IsRootObject(m_pDsObject)) { m_dwSIFlags |= SI_NO_ACL_PROTECT; } // Get the class name and schema path m_pDsObject->GetObjectInformation(&pObjectInfo); if (pObjectInfo) { // // Note that m_strObjectClass, if provided, can be different // than pObjectInfo->pszClassName. This is true when editing default // ACLs on schema class objects, for example, in which case // pObjectInfo->pszClassName will be "attributeSchema" but m_strObjectClass // will be something else such as "computer" or "user". Be // careful to only use pObjectInfo->pszClassName for getting the path of // the schema root, and use m_strObjectClass for everything else. // // If m_strObjectClass is not provided, use pObjectInfo->pszClassName. // if (m_strObjectClass == NULL) m_strObjectClass = SysAllocString(pObjectInfo->pszClassName); // Get the the path of the schema root int nClassLen; nClassLen = lstrlenW(pObjectInfo->pszClassName); pszTemp = pObjectInfo->pszSchemaDN + lstrlenW(pObjectInfo->pszSchemaDN) - nClassLen; if (CSTR_EQUAL == CompareString(LOCALE_SYSTEM_DEFAULT, NORM_IGNORECASE, pszTemp, nClassLen, pObjectInfo->pszClassName, nClassLen)) { *pszTemp = L'\0'; } // Save the schema root path m_strSchemaRootPath = SysAllocString(pObjectInfo->pszSchemaDN); } //For computer objects only use CN as display name doesn't get updated //when name of computer is changed which results in displaying old name. //see bug 104186 if(!m_strObjectClass || lstrcmpi(m_strObjectClass,CLASS_COMPUTER)) { // // Get the displayName property // pszTemp = (LPWSTR)c_szDisplayName; HRESULT hr1 = m_pDsObject->GetObjectAttributes(&pszTemp, 1, &pAttributeInfo, &dwAttributesReturned); //NTRAID#NTBUG9-68903-2002/08/07-hiteshr //Under certain conditions where underlying AD is host, and ADSI fails to //to get any schema information from AD, this can fail or return information //in provider specific format. if (SUCCEEDED(hr1) && pAttributeInfo && pAttributeInfo->pADsValues->dwType == ADSTYPE_CASE_EXACT_STRING ) { m_strDisplayName = SysAllocString(pAttributeInfo->pADsValues->CaseExactString); FreeADsMem(pAttributeInfo); pAttributeInfo = NULL; } } // If that failed, try the leaf name. if (!m_strDisplayName && pPath) { // Retrieve the display name pPath->SetDisplayType(ADS_DISPLAY_VALUE_ONLY); pPath->Retrieve(ADS_FORMAT_LEAF, &m_strDisplayName); pPath->SetDisplayType(ADS_DISPLAY_FULL); } // If we still don't have a display name, just copy the RDN. // Ugly, but better than nothing. if (!m_strDisplayName && pObjectInfo) m_strDisplayName = SysAllocString(pObjectInfo->pszRDN); exit_gracefully: if (pObjectInfo) FreeADsMem(pObjectInfo); DoRelease(pPath); ReleasePrivileges(hToken); TraceLeaveResult(hr); } HRESULT CDSSecurityInfo::Init3() { HRESULT hr = S_OK; IADsClass *pDsClass = NULL; VARIANT var = {0}; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::Init3"); TraceAssert(m_strSchemaRootPath != NULL); TraceAssert(m_strObjectClass != NULL); if (m_bThreadAbort) goto exit_gracefully; // Create the schema cache // Sandwich this SchemaCache_Create call with refcounting. In the event that // the threads created therein take a long time to return, this allows the // release on the CDSSecurityInfo object in the invoking application to simply // return instead of waiting potentially a very long time for the schema cache // creation threads to complete. This is because the last release on the // CDSSecurityInfo calls its destructor (duh!) and the destructor waits on the // threads. AddRef (); hr = SchemaCache_Create(m_strServerName); Release(); FailGracefully(hr, "Unable to create schema cache"); if( m_strSchemaRootPath && m_strObjectClass ) { // Bind to the schema class object hr = Schema_BindToObject(m_strSchemaRootPath, m_strObjectClass, IID_IADsClass, (LPVOID*)&pDsClass); FailGracefully(hr, "Failed to get the Schema class object"); // Get the class GUID Schema_GetObjectID(pDsClass, &m_guidObjectType); if (m_bThreadAbort) goto exit_gracefully; // See if this object is a container, by getting the list of possible // child classes. If this fails, treat it as a non-container. pDsClass->get_Containment(&var); //set m_dwLocalFlags to DSSI_LOCAL_NO_CREATE_DELETE if object is not // a container. If this flag is set, CREATE_DELETE permission which are // inherited from parents but meaning less for leaf object will not be shown. // In most cases presence of this flag is same as absence of SI_CONTAINER in m_dwSIFlags, // however in some cases its not possible to determine if the object is container or not. // there object is treated as non-container but we still must show all the aces. if (V_VT(&var) == (VT_ARRAY | VT_VARIANT)) { LPSAFEARRAY psa = V_ARRAY(&var); TraceAssert(psa && psa->cDims == 1); if (psa->rgsabound[0].cElements > 0) { m_dwSIFlags |= SI_CONTAINER; } else m_dwLocalFlags |= DSSI_LOCAL_NO_CREATE_DELETE; } else if (V_VT(&var) == VT_BSTR) // single entry { TraceAssert(V_BSTR(&var)); m_dwSIFlags |= SI_CONTAINER; } else m_dwLocalFlags |= DSSI_LOCAL_NO_CREATE_DELETE; if( !IsEqualGUID( m_guidObjectType, GUID_NULL ) ) { hr = Schema_GetDefaultSD( &m_guidObjectType, m_pDomainSid, m_pRootDomainSid, &m_pDefaultSD ); FailGracefully(hr, "Failed to get the Schema class object"); m_dwSIFlags |= SI_RESET_DACL; } } exit_gracefully: VariantClear(&var); DoRelease(pDsClass); TraceLeaveResult(hr); } /////////////////////////////////////////////////////////// // // IUnknown methods // /////////////////////////////////////////////////////////// #undef CLASS_NAME #define CLASS_NAME CDSSecurityInfo #include "unknown.inc" STDMETHODIMP CDSSecurityInfo::QueryInterface(REFIID riid, LPVOID FAR* ppv) { INTERFACES iface[] = { &IID_ISecurityInformation, static_cast(this), &IID_IEffectivePermission, static_cast(this), &IID_ISecurityObjectTypeInfo, static_cast(this), }; return HandleQueryInterface(riid, ppv, iface, ARRAYSIZE(iface)); } /////////////////////////////////////////////////////////// // // ISecurityInformation methods // /////////////////////////////////////////////////////////// STDMETHODIMP CDSSecurityInfo::GetObjectInformation(PSI_OBJECT_INFO pObjectInfo) { TraceEnter(TRACE_DSSI, "CDSSecurityInfo::GetObjectInformation"); TraceAssert(pObjectInfo != NULL && !IsBadWritePtr(pObjectInfo, SIZEOF(*pObjectInfo))); pObjectInfo->hInstance = GLOBAL_HINSTANCE; pObjectInfo->dwFlags = m_dwSIFlags ; pObjectInfo->pszServerName = m_strServerName; pObjectInfo->pszObjectName = m_strDisplayName ? m_strDisplayName : m_strObjectPath; if (!IsEqualGUID(m_guidObjectType, GUID_NULL)) { pObjectInfo->dwFlags |= SI_OBJECT_GUID; pObjectInfo->guidObjectType = m_guidObjectType; } TraceLeaveResult(S_OK); } //IF object is leaf object which cannot have create/detele object //permissions, this function removes those aces from SD, so that //they are not displayed. It only removes inherited aces, if ace is //explicit, it better get displayed so that user can remove it. //This is done to fix bug 14793 DWORD RemoveRedundantPermissions( PSECURITY_DESCRIPTOR *ppSD, GUID *pGuidObjectType ) { PACL pAcl = NULL; PACE_HEADER pAce= NULL; UINT cAces = 0; BOOL *pBoolArray = NULL; TraceEnter(TRACE_DSSI, "RemoveRedundantPermissions"); if ( NULL == ppSD || NULL == *ppSD ) TraceLeaveResult(ERROR_SUCCESS); // Nothing to do BOOL bDefaulted; BOOL bPresent; GetSecurityDescriptorDacl(*ppSD, &bPresent, &pAcl, &bDefaulted); if (NULL != pAcl) { if(pAcl->AceCount) { //pBoolArray is initialzied to FALSE pBoolArray = (PBOOL)LocalAlloc(LPTR,sizeof(BOOL)*pAcl->AceCount); if(!pBoolArray) return ERROR_NOT_ENOUGH_MEMORY; } for (cAces = 0, pAce = (PACE_HEADER)FirstAce(pAcl); cAces < pAcl->AceCount; ++cAces, pAce = (PACE_HEADER)NextAce(pAce)) { if( pAce->AceFlags & INHERITED_ACE ) { //If Only Create Child/Delete Child don't show it if((((ACCESS_ALLOWED_ACE*)pAce)->Mask & (~(ACTRL_DS_CREATE_CHILD |ACTRL_DS_DELETE_CHILD))) == 0 ) { pBoolArray[cAces] = TRUE; continue; } //If the ace is inherited and inherit only and inherited object type //is not same as this object type bug 22559 if( (((ACCESS_ALLOWED_ACE*)pAce)->Header.AceFlags & INHERIT_ONLY_ACE ) && IsObjectAceType(pAce) ) { GUID *pGuid = RtlObjectAceInheritedObjectType(pAce); if(pGuid && pGuidObjectType && !IsEqualGUID(*pGuid,*pGuidObjectType)) { pBoolArray[cAces] = TRUE; continue; } } } } //Now Delete the Aces UINT cAceCount = pAcl->AceCount; UINT cAdjust = 0; for( cAces = 0; cAces < cAceCount; ++cAces) { if(pBoolArray[cAces]) { DeleteAce(pAcl, cAces - cAdjust); cAdjust++; } } LocalFree(pBoolArray); } TraceLeaveResult(ERROR_SUCCESS); } STDMETHODIMP CDSSecurityInfo::GetSecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR *ppSD, BOOL fDefault) { HRESULT hr = S_OK; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::GetSecurity"); TraceAssert(si != 0); TraceAssert(ppSD != NULL); *ppSD = NULL; if (fDefault) { if( m_pDefaultSD ) { ULONG nLength = GetSecurityDescriptorLength(m_pDefaultSD); *ppSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, nLength); if (*ppSD != NULL) CopyMemory(*ppSD, m_pDefaultSD, nLength); else hr = E_OUTOFMEMORY; } else hr = E_NOTIMPL; } else if (!(si & SACL_SECURITY_INFORMATION) && m_pSD != NULL) { ULONG nLength = GetSecurityDescriptorLength(m_pSD); *ppSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, nLength); if (*ppSD != NULL) CopyMemory(*ppSD, m_pSD, nLength); else hr = E_OUTOFMEMORY; } else { TraceAssert(m_strObjectPath != NULL); TraceAssert(m_pfnReadSD != NULL) hr = (*m_pfnReadSD)(m_strObjectPath, si, ppSD, m_lpReadContext); } if( si & DACL_SECURITY_INFORMATION && (m_dwLocalFlags & DSSI_LOCAL_NO_CREATE_DELETE ) ) RemoveRedundantPermissions(ppSD, &m_guidObjectType); TraceLeaveResult(hr); } STDMETHODIMP CDSSecurityInfo::SetSecurity(SECURITY_INFORMATION si, PSECURITY_DESCRIPTOR pSD) { HRESULT hr = S_OK; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::SetSecurity"); TraceAssert(si != 0); TraceAssert(pSD != NULL); TraceAssert(m_strObjectPath != NULL); TraceAssert(m_pfnWriteSD != NULL) hr = (*m_pfnWriteSD)(m_strObjectPath, si, pSD, m_lpWriteContext); if (SUCCEEDED(hr) && m_pSD != NULL && (si != SACL_SECURITY_INFORMATION)) { // The initial security descriptor is no longer valid. LocalFree(m_pSD); m_pSD = NULL; } TraceLeaveResult(hr); } STDMETHODIMP CDSSecurityInfo::GetAccessRights(const GUID* pguidObjectType, DWORD dwFlags, PSI_ACCESS *ppAccesses, ULONG *pcAccesses, ULONG *piDefaultAccess) { HRESULT hr = S_OK; LPCTSTR pszClassName = NULL; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::GetAccessRights"); TraceAssert(ppAccesses != NULL); TraceAssert(pcAccesses != NULL); TraceAssert(piDefaultAccess != NULL); *ppAccesses = NULL; *pcAccesses = 0; *piDefaultAccess = 0; WaitOnInitThread(); // //If we are getting the access rights for GUID which is selected in the //Apply onto combobox of permission page, we don't need to worry about //aux class. Only get the permission for the object type // BOOL bInheritGuid = TRUE; if (pguidObjectType == NULL || IsEqualGUID(*pguidObjectType, GUID_NULL)) { bInheritGuid = FALSE; pguidObjectType = &m_guidObjectType; pszClassName = m_strObjectClass; } if (m_dwInitFlags & DSSI_NO_FILTER) dwFlags |= SCHEMA_NO_FILTER; // No schema path means we don't have read_property access to the object. // This limits what we can do. if (NULL == m_strSchemaRootPath) dwFlags |= SCHEMA_COMMON_PERM; PACCESS_INFO *ppAI = NULL; PACCESS_INFO pAI = NULL; if(!bInheritGuid) { if(dwFlags & SI_EDIT_PROPERTIES) ppAI = &m_pAIProperty; else if(dwFlags & SI_EDIT_EFFECTIVE) ppAI = &m_pAIEffective; else ppAI = &m_pAIGeneral; } else { ppAI = &pAI; } if(!*ppAI) { hr = SchemaCache_GetAccessRights(pguidObjectType, pszClassName, !bInheritGuid ? m_hAuxClasses : NULL, m_strSchemaRootPath, dwFlags, ppAI); if(FAILED(hr)) { return hr; } } if(*ppAI) { *ppAccesses = (*ppAI)->pAccess; *pcAccesses = (*ppAI)->cAccesses; *piDefaultAccess = (*ppAI)->iDefaultAccess; } TraceLeaveResult(hr); } STDMETHODIMP CDSSecurityInfo::MapGeneric(const GUID* /*pguidObjectType*/, UCHAR *pAceFlags, ACCESS_MASK *pmask) { TraceEnter(TRACE_DSSI, "CDSSecurityInfo::MapGeneric"); TraceAssert(pAceFlags != NULL); TraceAssert(pmask != NULL); // Only CONTAINER_INHERIT_ACE has meaning for DS *pAceFlags &= ~OBJECT_INHERIT_ACE; MapGenericMask(pmask, &g_DSMap); // We don't expose SYNCHRONIZE, so don't pass it through // to the UI. 192389 *pmask &= ~SYNCHRONIZE; TraceLeaveResult(S_OK); } STDMETHODIMP CDSSecurityInfo::GetInheritTypes(PSI_INHERIT_TYPE *ppInheritTypes, ULONG *pcInheritTypes) { HRESULT hr; DWORD dwFlags = 0; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::GetInheritTypes"); if (m_dwInitFlags & DSSI_NO_FILTER) dwFlags |= SCHEMA_NO_FILTER; hr = SchemaCache_GetInheritTypes(&m_guidObjectType, dwFlags, ppInheritTypes, pcInheritTypes); TraceLeaveResult(hr); } STDMETHODIMP CDSSecurityInfo::PropertySheetPageCallback(HWND hwnd, UINT uMsg, SI_PAGE_TYPE uPage) { if (PSPCB_SI_INITDIALOG == uMsg && uPage == SI_PAGE_PERM) { WaitOnInitThread(); // // HACK ALERT!!! // // Exchange Platinum is required to hide membership of some groups // (distribution lists) for legal reasons. The only way they found // to do this is with non-canonical ACLs which look roughly like // Allow Admins access // Deny Everyone access // // // Since ACLUI always generates ACLs in NT Canonical Order, we can't // allow these funky ACLs to be modified. If we did, the DL's would // either become visible or Admins would get locked out. // if (!(SI_READONLY & m_dwSIFlags)) { DWORD dwAclType = IsSpecificNonCanonicalSD(m_pSD); if (ENC_RESULT_NOT_PRESENT != dwAclType) { // It's a funky ACL so don't allow changes m_dwSIFlags |= SI_READONLY; // Tell the user what's going on MsgPopup(hwnd, MAKEINTRESOURCE(IDS_SPECIAL_ACL_WARNING), MAKEINTRESOURCE(IDS_SPECIAL_SECURITY_TITLE), MB_OK | MB_ICONWARNING | MB_SETFOREGROUND, g_hInstance, m_strDisplayName); // S_FALSE suppresses further popups from aclui ("The ACL // is not ordered correctly, etc.") return S_FALSE; } } if( (SI_READONLY & m_dwSIFlags) && (DSSI_NO_READONLY_MESSAGE & m_dwSIFlags) ) return S_FALSE; } return S_OK; } DWORD WINAPI CDSSecurityInfo::InitThread(LPVOID pvThreadData) { CDSSecurityInfo *psi; HINSTANCE hInstThisDll = LoadLibrary(c_szDllName); psi = (CDSSecurityInfo*)pvThreadData; SetEvent(psi->m_hLoadLibWaitEvent); InterlockedIncrement(&GLOBAL_REFCOUNT); TraceEnter(TRACE_DSSI, "CDSSecurityInfo::InitThread"); TraceAssert(psi != NULL); #if DBG DWORD dwTime = GetTickCount(); #endif ThreadCoInitialize(); psi->Init3(); ThreadCoUninitialize(); #if DBG Trace((TEXT("InitThread complete, elapsed time: %d ms"), GetTickCount() - dwTime)); #endif TraceLeave(); ASSERT( 0 != GLOBAL_REFCOUNT ); InterlockedDecrement(&GLOBAL_REFCOUNT); FreeLibraryAndExitThread(hInstThisDll, 0); } HRESULT SetSecurityInfoMask(LPUNKNOWN punk, SECURITY_INFORMATION si) { HRESULT hr = E_INVALIDARG; if (punk) { IADsObjectOptions *pOptions; hr = punk->QueryInterface(IID_IADsObjectOptions, (void**)&pOptions); if (SUCCEEDED(hr)) { VARIANT var; VariantInit(&var); V_VT(&var) = VT_I4; V_I4(&var) = si; hr = pOptions->SetOption(ADS_OPTION_SECURITY_MASK, var); pOptions->Release(); } } return hr; } //+--------------------------------------------------------------------------- // // Function: CDSSecurityInfo::DSReadObjectSecurity // // Synopsis: Reads the security descriptor from the specied DS object // // Arguments: [IN pszObjectPath] -- ADS path of DS object // [IN SeInfo] -- Security descriptor parts requested // [OUT ppSD] -- Security descriptor returned here // [IN lpContext] -- CDSSecurityInfo* // // Notes: The returned security descriptor must be freed with LocalFree // //---------------------------------------------------------------------------- HRESULT WINAPI CDSSecurityInfo::DSReadObjectSecurity(LPCWSTR /*pszObjectPath*/, SECURITY_INFORMATION SeInfo, PSECURITY_DESCRIPTOR *ppSD, LPARAM lpContext) { HRESULT hr = S_OK; LPWSTR pszSDProperty = (LPWSTR)c_szSDProperty; PADS_ATTR_INFO pSDAttributeInfo = NULL; DWORD dwAttributesReturned; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::DSReadObjectSecurity"); TraceAssert(SeInfo != 0); TraceAssert(ppSD != NULL); TraceAssert(lpContext != 0); *ppSD = NULL; CDSSecurityInfo *pThis = reinterpret_cast(lpContext); TraceAssert(pThis != NULL); TraceAssert(pThis->m_pDsObject != NULL); // Set the SECURITY_INFORMATION mask hr = SetSecurityInfoMask(pThis->m_pDsObject, SeInfo); FailGracefully(hr, "Unable to set ADS_OPTION_SECURITY_MASK"); // Read the security descriptor hr = pThis->m_pDsObject->GetObjectAttributes(&pszSDProperty, 1, &pSDAttributeInfo, &dwAttributesReturned); if (SUCCEEDED(hr) && !pSDAttributeInfo) hr = E_ACCESSDENIED; // This happens for SACL if no SecurityPrivilege FailGracefully(hr, "Unable to read nTSecurityDescriptor attribute"); TraceAssert(ADSTYPE_NT_SECURITY_DESCRIPTOR == pSDAttributeInfo->dwADsType); TraceAssert(ADSTYPE_NT_SECURITY_DESCRIPTOR == pSDAttributeInfo->pADsValues->dwType); *ppSD = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, pSDAttributeInfo->pADsValues->SecurityDescriptor.dwLength); if (!*ppSD) ExitGracefully(hr, E_OUTOFMEMORY, "LocalAlloc failed"); CopyMemory(*ppSD, pSDAttributeInfo->pADsValues->SecurityDescriptor.lpValue, pSDAttributeInfo->pADsValues->SecurityDescriptor.dwLength); exit_gracefully: if (pSDAttributeInfo) FreeADsMem(pSDAttributeInfo); TraceLeaveResult(hr); } //+--------------------------------------------------------------------------- // // Function: CDSSecurityInfo::DSWriteObjectSecurity // // Synopsis: Writes the security descriptor to the specied DS object // // Arguments: [IN pszObjectPath] -- ADS path of DS object // [IN SeInfo] -- Security descriptor parts provided // [IN pSD] -- The new security descriptor // [IN lpContext] -- CDSSecurityInfo* // //---------------------------------------------------------------------------- HRESULT WINAPI CDSSecurityInfo::DSWriteObjectSecurity(LPCWSTR /*pszObjectPath*/, SECURITY_INFORMATION SeInfo, PSECURITY_DESCRIPTOR pSD, LPARAM lpContext) { HRESULT hr = S_OK; ADSVALUE attributeValue; ADS_ATTR_INFO attributeInfo; DWORD dwAttributesModified; DWORD dwSDLength; PSECURITY_DESCRIPTOR psd = NULL; SECURITY_DESCRIPTOR_CONTROL sdControl = 0; DWORD dwRevision; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::DSWriteObjectSecurity"); TraceAssert(pSD && IsValidSecurityDescriptor(pSD)); TraceAssert(SeInfo != 0); TraceAssert(lpContext != 0); CDSSecurityInfo *pThis = reinterpret_cast(lpContext); TraceAssert(pThis != NULL); TraceAssert(pThis->m_pDsObject != NULL); // Set the SECURITY_INFORMATION mask hr = SetSecurityInfoMask(pThis->m_pDsObject, SeInfo); FailGracefully(hr, "Unable to set ADS_OPTION_SECURITY_MASK"); // Need the total size dwSDLength = GetSecurityDescriptorLength(pSD); // // If necessary, make a self-relative copy of the security descriptor // GetSecurityDescriptorControl(pSD, &sdControl, &dwRevision); if (!(sdControl & SE_SELF_RELATIVE)) { psd = (PSECURITY_DESCRIPTOR)LocalAlloc(LPTR, dwSDLength); if (psd == NULL || !MakeSelfRelativeSD(pSD, psd, &dwSDLength)) { DWORD dwErr = GetLastError(); ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "Unable to make self-relative SD copy"); } // Point to the self-relative copy pSD = psd; } attributeValue.dwType = ADSTYPE_NT_SECURITY_DESCRIPTOR; attributeValue.SecurityDescriptor.dwLength = dwSDLength; attributeValue.SecurityDescriptor.lpValue = (LPBYTE)pSD; attributeInfo.pszAttrName = (LPWSTR)c_szSDProperty; attributeInfo.dwControlCode = ADS_ATTR_UPDATE; attributeInfo.dwADsType = ADSTYPE_NT_SECURITY_DESCRIPTOR; attributeInfo.pADsValues = &attributeValue; attributeInfo.dwNumValues = 1; // Write the security descriptor hr = pThis->m_pDsObject->SetObjectAttributes(&attributeInfo, 1, &dwAttributesModified); if (HRESULT_FROM_WIN32(ERROR_DS_CONSTRAINT_VIOLATION) == hr && OWNER_SECURITY_INFORMATION == SeInfo) { hr = HRESULT_FROM_WIN32(ERROR_INVALID_OWNER); } exit_gracefully: if (psd != NULL) LocalFree(psd); TraceLeaveResult(hr); } //+--------------------------------------------------------------------------- // // Function: CheckObjectAccess // // Synopsis: Checks access to the security descriptor of a DS object. // In particular, determines whether the user has READ_CONTROL, // WRITE_DAC, WRITE_OWNER, and/or ACCESS_SYSTEM_SECURITY access. // If it cannot determine for sure about WRITE_DAC permission, // it returns SI_MAY_WRITE which helps aclui to put a better warning // Arguments: none // // Return: DWORD (Access Mask) // // Notes: The check for READ_CONTROL involves reading the Owner, // Group, and DACL. This security descriptor is saved // in m_pSD. // // The checks for WRITE_DAC, WRITE_OWNER, and // ACCESS_SYSTEM_SECURITY involve getting sDRightsEffective // from the object. // //---------------------------------------------------------------------------- DWORD CDSSecurityInfo::CheckObjectAccess() { DWORD dwAccessGranted = 0; HRESULT hr; SECURITY_INFORMATION si = 0; LPWSTR pProp = (LPWSTR)c_szSDRightsProp; PADS_ATTR_INFO pSDRightsInfo = NULL; DWORD dwAttributesReturned; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::CheckObjectAccess"); #ifdef DSSEC_PRIVATE_DEBUG // FOR DEBUGGING ONLY // Turn this on to prevent the dialogs from being read-only. This is // useful for testing the object picker against NTDEV (for example). TraceMsg("Returning all access for debugging"); dwAccessGranted = (READ_CONTROL | WRITE_OWNER | WRITE_DAC | ACCESS_SYSTEM_SECURITY); #endif // Test for READ_CONTROL by trying to read the Owner, Group, and DACL TraceAssert(NULL == m_pSD); // shouldn't get here twice TraceAssert(m_strObjectPath != NULL); TraceAssert(m_pfnReadSD != NULL); hr = (*m_pfnReadSD)(m_strObjectPath, OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, &m_pSD, m_lpReadContext); if (SUCCEEDED(hr)) { TraceAssert(NULL != m_pSD); dwAccessGranted |= READ_CONTROL; } // If we're in read-only mode, there's no need to check anything else. if (m_dwInitFlags & DSSI_READ_ONLY) TraceLeaveValue(dwAccessGranted); // Read the sDRightsEffective property to determine writability m_pDsObject->GetObjectAttributes(&pProp, 1, &pSDRightsInfo, &dwAttributesReturned); if (pSDRightsInfo) { TraceAssert(ADSTYPE_INTEGER == pSDRightsInfo->dwADsType); si = pSDRightsInfo->pADsValues->Integer; FreeADsMem(pSDRightsInfo); } else { // // Note that GetObjectAttributes commonly returns S_OK even when // it fails, so the HRESULT is basically useless here. // // This can fail if we don't have read_property access, which can // happen when an admin is trying to restore access to an object // that has had all access removed or denied // // Assume we can write the Owner and DACL. If not, the worst that // happens is the user gets an "Access Denied" message when trying // to save changes. // //Instead of add SI_MAY_WRITE to dwAccessGranted . This helps //ACLUI to putup a better error message. bug 411843 TraceMsg("GetObjectAttributes failed to read sDRightsEffective"); dwAccessGranted |= SI_MAY_WRITE; si = OWNER_SECURITY_INFORMATION ; } // The resulting SECURITY_INFORMATION mask indicates the // security descriptor parts that may be modified by the user. Trace((TEXT("sDRightsEffective = 0x%08x"), si)); if (OWNER_SECURITY_INFORMATION & si) dwAccessGranted |= WRITE_OWNER; if (DACL_SECURITY_INFORMATION & si) dwAccessGranted |= WRITE_DAC; if (SACL_SECURITY_INFORMATION & si) dwAccessGranted |= ACCESS_SYSTEM_SECURITY; TraceLeaveValue(dwAccessGranted); } BOOL SkipLocalGroup(LPCWSTR pszServerName, PSID psid) { SID_NAME_USE use; WCHAR szAccountName[MAX_PATH]; WCHAR szDomainName[MAX_PATH]; DWORD dwAccountLen = MAX_PATH; DWORD dwDomainLen = MAX_PATH; if(LookupAccountSid(pszServerName, psid, szAccountName, &dwAccountLen, szDomainName, &dwDomainLen, &use)) { if(use == SidTypeWellKnownGroup) return TRUE; } // //Built In sids have first subauthority of 32 ( s-1-5-32 ) // if((*(GetSidSubAuthorityCount(psid)) >= 1 ) && (*(GetSidSubAuthority(psid,0)) == 32)) return TRUE; return FALSE; } STDMETHODIMP CDSSecurityInfo::GetEffectivePermission(const GUID* pguidObjectType, PSID pUserSid, LPCWSTR pszServerName, PSECURITY_DESCRIPTOR pSD, POBJECT_TYPE_LIST *ppObjectTypeList, ULONG *pcObjectTypeListLength, PACCESS_MASK *ppGrantedAccessList, ULONG *pcGrantedAccessListLength) { AUTHZ_CLIENT_CONTEXT_HANDLE CC = NULL; LUID luid = {0xdead,0xbeef}; AUTHZ_ACCESS_REQUEST AReq; AUTHZ_ACCESS_REPLY AReply; HRESULT hr = S_OK; DWORD dwFlags = 0; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::GetEffectivePermission"); TraceAssert(pUserSid && IsValidSecurityDescriptor(pSD)); TraceAssert(ppObjectTypeList != NULL); TraceAssert(pcObjectTypeListLength != NULL); TraceAssert(ppGrantedAccessList != NULL); TraceAssert(pcGrantedAccessListLength != NULL); AReply.GrantedAccessMask = NULL; AReply.Error = NULL; AReq.ObjectTypeList = NULL; AReq.ObjectTypeListLength = 0; if( m_ResourceManager == NULL ) { //Initilize RM for Access check AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT, NULL, NULL, NULL, L"Dummy", &m_ResourceManager ); if( m_ResourceManager == NULL ) ExitGracefully(hr, E_UNEXPECTED, "Could Not Get Resource Manager"); } //Initialize the client context BOOL bSkipLocalGroup = SkipLocalGroup(pszServerName, pUserSid); if( !AuthzInitializeContextFromSid(bSkipLocalGroup?AUTHZ_SKIP_TOKEN_GROUPS:0 , pUserSid, m_ResourceManager, NULL, luid, NULL, &CC) ) { DWORD dwErr = GetLastError(); ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "AuthzInitializeContextFromSid Failed"); } if (NULL == m_strSchemaRootPath) dwFlags = SCHEMA_COMMON_PERM; if(!m_pOTL) { //Get ObjectTypeList hr = Schema_GetObjectTypeList((LPGUID)pguidObjectType, m_hAuxClasses, m_strSchemaRootPath, dwFlags, &(AReq.ObjectTypeList), &(AReq.ObjectTypeListLength)); FailGracefully( hr, "Schema_GetObjectTypeList Failed"); m_pOTL = AReq.ObjectTypeList; m_cCountOTL = AReq.ObjectTypeListLength; } else { AReq.ObjectTypeList = m_pOTL; AReq.ObjectTypeListLength = m_cCountOTL; } //Do the Access Check AReq.DesiredAccess = MAXIMUM_ALLOWED; AReq.PrincipalSelfSid = NULL; AReq.OptionalArguments = NULL; AReply.ResultListLength = AReq.ObjectTypeListLength; AReply.SaclEvaluationResults = NULL; if( (AReply.GrantedAccessMask = (PACCESS_MASK)LocalAlloc(LPTR, sizeof(ACCESS_MASK)*AReply.ResultListLength) ) == NULL ) ExitGracefully(hr, E_OUTOFMEMORY, "Unable to LocalAlloc"); if( (AReply.Error = (PDWORD)LocalAlloc(LPTR, sizeof(DWORD)*AReply.ResultListLength)) == NULL ) ExitGracefully(hr, E_OUTOFMEMORY, "Unable to LocalAlloc"); if( !AuthzAccessCheck(0, CC, &AReq, NULL, pSD, NULL, 0, &AReply, NULL) ) { DWORD dwErr = GetLastError(); ExitGracefully(hr, HRESULT_FROM_WIN32(dwErr), "AuthzAccessCheck Failed"); } exit_gracefully: if(CC) AuthzFreeContext(CC); if(!SUCCEEDED(hr)) { if(AReply.GrantedAccessMask) LocalFree(AReply.GrantedAccessMask); if(AReply.Error) LocalFree(AReply.Error); AReply.Error = NULL; AReply.GrantedAccessMask = NULL; } else { *ppObjectTypeList = AReq.ObjectTypeList; *pcObjectTypeListLength = AReq.ObjectTypeListLength; *ppGrantedAccessList = AReply.GrantedAccessMask; *pcGrantedAccessListLength = AReq.ObjectTypeListLength; } TraceLeaveResult(hr); } STDMETHODIMP CDSSecurityInfo::GetInheritSource(SECURITY_INFORMATION si, PACL pACL, PINHERITED_FROM *ppInheritArray) { HRESULT hr = S_OK; DWORD dwErr = ERROR_SUCCESS; PINHERITED_FROM pTempInherit = NULL; PINHERITED_FROM pTempInherit2 = NULL; LPWSTR pStrTemp = NULL; IADsPathname *pPath = NULL; BSTR strObjectPath = NULL; BSTR strServerName = NULL; BSTR strParentPath = NULL; LPGUID *ppGuid = NULL; BOOL bFreeInheritFromArray = FALSE; TraceEnter(TRACE_DSSI, "CDSSecurityInfo::GetInheritSource"); TraceAssert(pACL != 0); TraceAssert(ppInheritArray != NULL); // // Create an ADsPathname object to parse the path and get the // the objectname in ADS_FORMAT_X500_DN // hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&pPath); if (pPath) { if (SUCCEEDED(pPath->Set(m_strObjectPath, ADS_SETTYPE_FULL))) { hr = pPath->Retrieve(ADS_FORMAT_SERVER, &strServerName); if(!strServerName) { pPath->Set(m_strServerName,ADS_SETTYPE_SERVER ); hr = pPath->Retrieve(ADS_FORMAT_X500 ,&strObjectPath); } else strObjectPath = m_strObjectPath; } } if(strObjectPath == NULL) strObjectPath = m_strObjectPath; if( pACL == NULL || ppInheritArray == NULL ) ExitGracefully(hr, E_POINTER, "CDSSecurityInfo::GetInheritSource Invalid Parameters"); pTempInherit = (PINHERITED_FROM)LocalAlloc( LPTR, sizeof(INHERITED_FROM)*pACL->AceCount); if(pTempInherit == NULL) ExitGracefully(hr, E_OUTOFMEMORY,"OUT of Memory"); UINT cGuidCount; cGuidCount = 1; if(m_hAuxClasses) cGuidCount += DPA_GetPtrCount(m_hAuxClasses); ppGuid = (LPGUID*)LocalAlloc(LPTR,sizeof(LPGUID)*cGuidCount); if(!ppGuid) ExitGracefully(hr, E_OUTOFMEMORY,"OUT of Memory"); ppGuid[0] = &m_guidObjectType; for(UINT i = 1; i < cGuidCount ; ++i) { PAUX_INFO pAI = (PAUX_INFO)DPA_FastGetPtr(m_hAuxClasses, i-1); if(IsEqualGUID(pAI->guid, GUID_NULL)) { hr = Schema_GetObjectTypeGuid(pAI->pszClassName,&pAI->guid); FailGracefully( hr, "Schema_GetObjectTypeGuid Failed"); } ppGuid[i] = &pAI->guid; } dwErr = GetInheritanceSource(strObjectPath, SE_DS_OBJECT_ALL, si, //m_dwSIFlags & SI_CONTAINER, TRUE, ppGuid, cGuidCount, pACL, NULL, &g_DSMap, pTempInherit); hr = HRESULT_FROM_WIN32(dwErr); FailGracefully( hr, "GetInheritanceSource Failed"); bFreeInheritFromArray = TRUE; DWORD nSize; nSize = sizeof(INHERITED_FROM)*pACL->AceCount; for(UINT i = 0; i < pACL->AceCount; ++i) { if(pTempInherit[i].AncestorName) nSize += StringByteSize(pTempInherit[i].AncestorName); } pTempInherit2 = (PINHERITED_FROM)LocalAlloc( LPTR, nSize ); if(pTempInherit2 == NULL) ExitGracefully(hr, E_OUTOFMEMORY,"OUT of Memory"); pStrTemp = (LPWSTR)(pTempInherit2 + pACL->AceCount); for(i = 0; i < pACL->AceCount; ++i) { pTempInherit2[i].GenerationGap = pTempInherit[i].GenerationGap; if(pTempInherit[i].AncestorName) { if (SUCCEEDED(pPath->Set(pTempInherit[i].AncestorName, ADS_SETTYPE_FULL))) { hr = pPath->Retrieve(ADS_FORMAT_X500_DN, &strParentPath); } pTempInherit2[i].AncestorName = pStrTemp; if(strParentPath) { wcscpy(pStrTemp,strParentPath); pStrTemp += (wcslen(pStrTemp)+1); SysFreeString(strParentPath); strParentPath = NULL; } else { wcscpy(pStrTemp,pTempInherit[i].AncestorName); pStrTemp += (wcslen(pTempInherit[i].AncestorName)+1); } } } exit_gracefully: if(bFreeInheritFromArray) FreeInheritedFromArray(pTempInherit, pACL->AceCount,NULL); if(SUCCEEDED(hr)) { *ppInheritArray = pTempInherit2; } if(pTempInherit) LocalFree(pTempInherit); if(ppGuid) LocalFree(ppGuid); DoRelease(pPath); if(strObjectPath != m_strObjectPath) SysFreeString(strObjectPath); if(strServerName) SysFreeString(strServerName); TraceLeaveResult(hr); } //+--------------------------------------------------------------------------- // // Function: DSCreateISecurityInfoObjectEx // // Synopsis: Instantiates an ISecurityInfo interface for a DS object // // Arguments: [IN pwszObjectPath] -- Full ADS path of DS object // [IN pwszObjectClass] -- Class of the object (optional) // [IN pwszServer] -- Name/address of DS DC (optional) // [IN pwszUserName] -- User name for validation (optional) // [IN pwszPassword] -- Password for validation (optional) // [IN dwFlags] -- Combination of DSSI_* flags // [OUT ppSI] -- Interface pointer returned here // [IN pfnReadSD] -- Optional function for reading SD // [IN pfnWriteSD] -- Optional function for writing SD // [IN lpContext] -- Passed to pfnReadSD/pfnWriteSD // // Return: HRESULT // //---------------------------------------------------------------------------- STDAPI DSCreateISecurityInfoObjectEx(LPCWSTR pwszObjectPath, LPCWSTR pwszObjectClass, LPCWSTR pwszServer, LPCWSTR pwszUserName, LPCWSTR pwszPassword, DWORD dwFlags, LPSECURITYINFO *ppSI, PFNREADOBJECTSECURITY pfnReadSD, PFNWRITEOBJECTSECURITY pfnWriteSD, LPARAM lpContext) { HRESULT hr; CDSSecurityInfo* pSI = NULL; TraceEnter(TRACE_SECURITY, "DSCreateISecurityInfoObjectEx"); if (pwszObjectPath == NULL || ppSI == NULL) TraceLeaveResult(E_INVALIDARG); *ppSI = NULL; // // Create and initialize the ISecurityInformation object. // pSI = new CDSSecurityInfo(); // ref == 0 if (!pSI) ExitGracefully(hr, E_OUTOFMEMORY, "Unable to create CDSSecurityInfo object"); pSI->AddRef(); // ref == 1 hr = pSI->Init(pwszObjectPath, pwszObjectClass, pwszServer, pwszUserName, pwszPassword, dwFlags, pfnReadSD, pfnWriteSD, lpContext); if (FAILED(hr)) { DoRelease(pSI); } *ppSI = (LPSECURITYINFO)pSI; exit_gracefully: TraceLeaveResult(hr); } STDAPI DSCreateISecurityInfoObject(LPCWSTR pwszObjectPath, LPCWSTR pwszObjectClass, DWORD dwFlags, LPSECURITYINFO *ppSI, PFNREADOBJECTSECURITY pfnReadSD, PFNWRITEOBJECTSECURITY pfnWriteSD, LPARAM lpContext) { return DSCreateISecurityInfoObjectEx(pwszObjectPath, pwszObjectClass, NULL, //pwszServer, NULL, //pwszUserName, NULL, //pwszPassword, dwFlags, ppSI, pfnReadSD, pfnWriteSD, lpContext); } //+--------------------------------------------------------------------------- // // Function: DSCreateSecurityPage // // Synopsis: Creates a Security property page for a DS object // // Arguments: [IN pwszObjectPath] -- Full ADS path of DS object // [IN pwszObjectClass] -- Class of the object (optional) // [IN dwFlags] -- Combination of DSSI_* flags // [OUT phPage] -- HPROPSHEETPAGE returned here // [IN pfnReadSD] -- Optional function for reading SD // [IN pfnWriteSD] -- Optional function for writing SD // [IN lpContext] -- Passed to pfnReadSD/pfnWriteSD // // Return: HRESULT // //---------------------------------------------------------------------------- STDAPI DSCreateSecurityPage(LPCWSTR pwszObjectPath, LPCWSTR pwszObjectClass, DWORD dwFlags, HPROPSHEETPAGE *phPage, PFNREADOBJECTSECURITY pfnReadSD, PFNWRITEOBJECTSECURITY pfnWriteSD, LPARAM lpContext) { HRESULT hr; LPSECURITYINFO pSI = NULL; TraceEnter(TRACE_SECURITY, "DSCreateSecurityPage"); if (NULL == phPage || NULL == pwszObjectPath || !*pwszObjectPath) TraceLeaveResult(E_INVALIDARG); *phPage = NULL; hr = DSCreateISecurityInfoObject(pwszObjectPath, pwszObjectClass, dwFlags, &pSI, pfnReadSD, pfnWriteSD, lpContext); if (SUCCEEDED(hr)) { hr = _CreateSecurityPage(pSI, phPage); DoRelease(pSI); } TraceLeaveResult(hr); } //+--------------------------------------------------------------------------- // // Function: DSEditSecurity // // Synopsis: Displays a modal dialog for editing security on a DS object // // Arguments: [IN hwndOwner] -- Dialog owner window // [IN pwszObjectPath] -- Full ADS path of DS object // [IN pwszObjectClass] -- Class of the object (optional) // [IN dwFlags] -- Combination of DSSI_* flags // [IN pwszCaption -- Optional dialog caption // [IN pfnReadSD] -- Optional function for reading SD // [IN pfnWriteSD] -- Optional function for writing SD // [IN lpContext] -- Passed to pfnReadSD/pfnWriteSD // // Return: HRESULT // //---------------------------------------------------------------------------- STDAPI DSEditSecurity(HWND hwndOwner, LPCWSTR pwszObjectPath, LPCWSTR pwszObjectClass, DWORD dwFlags, LPCWSTR pwszCaption, PFNREADOBJECTSECURITY pfnReadSD, PFNWRITEOBJECTSECURITY pfnWriteSD, LPARAM lpContext) { HRESULT hr; LPSECURITYINFO pSI = NULL; TraceEnter(TRACE_SECURITY, "DSCreateSecurityPage"); if (NULL == pwszObjectPath || !*pwszObjectPath) TraceLeaveResult(E_INVALIDARG); if (pwszCaption && *pwszCaption) { // Use the provided caption HPROPSHEETPAGE hPage = NULL; hr = DSCreateSecurityPage(pwszObjectPath, pwszObjectClass, dwFlags, &hPage, pfnReadSD, pfnWriteSD, lpContext); if (SUCCEEDED(hr)) { PROPSHEETHEADERW psh; psh.dwSize = SIZEOF(psh); psh.dwFlags = PSH_DEFAULT; psh.hwndParent = hwndOwner; psh.hInstance = GLOBAL_HINSTANCE; psh.pszCaption = pwszCaption; psh.nPages = 1; psh.nStartPage = 0; psh.phpage = &hPage; PropertySheetW(&psh); } } else { // This method creates a caption like "Permissions for Foo" hr = DSCreateISecurityInfoObject(pwszObjectPath, pwszObjectClass, dwFlags, &pSI, pfnReadSD, pfnWriteSD, lpContext); if (SUCCEEDED(hr)) { hr = _EditSecurity(hwndOwner, pSI); DoRelease(pSI); } } TraceLeaveResult(hr); } /******************************************************************* NAME: GetLSAConnection SYNOPSIS: Wrapper for LsaOpenPolicy ENTRY: pszServer - the server on which to make the connection EXIT: RETURNS: LSA_HANDLE if successful, NULL otherwise NOTES: HISTORY: JeffreyS 08-Oct-1996 Created ********************************************************************/ 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 pszServer, PSID *ppSid) { HRESULT hr = S_OK; NTSTATUS nts = STATUS_SUCCESS; PPOLICY_ACCOUNT_DOMAIN_INFO pDomainInfo = NULL; if(!pszServer || !ppSid) return E_INVALIDARG; *ppSid = NULL; LSA_HANDLE hLSA = GetLSAConnection(pszServer, 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; } CopyMemory(*ppSid, pDomainInfo->DomainSid, cbSid); } exit_gracefully: if(pDomainInfo) LsaFreeMemory(pDomainInfo); if(hLSA) LsaClose(hLSA); return hr; } // // include and defines for ldap calls // #include #include typedef LDAP * (LDAPAPI *PFN_LDAP_OPEN)( PWCHAR, ULONG ); typedef ULONG (LDAPAPI *PFN_LDAP_UNBIND)( LDAP * ); typedef ULONG (LDAPAPI *PFN_LDAP_SEARCH)(LDAP *, PWCHAR, ULONG, PWCHAR, PWCHAR *, ULONG,PLDAPControlA *, PLDAPControlA *, struct l_timeval *, ULONG, LDAPMessage **); typedef LDAPMessage * (LDAPAPI *PFN_LDAP_FIRST_ENTRY)( LDAP *, LDAPMessage * ); typedef PWCHAR * (LDAPAPI *PFN_LDAP_GET_VALUE)(LDAP *, LDAPMessage *, PWCHAR ); typedef ULONG (LDAPAPI *PFN_LDAP_MSGFREE)( LDAPMessage * ); typedef ULONG (LDAPAPI *PFN_LDAP_VALUE_FREE)( PWCHAR * ); typedef ULONG (LDAPAPI *PFN_LDAP_MAP_ERROR)( ULONG ); HRESULT GetRootDomainSid(LPCWSTR pszServer, PSID *ppSid) { // // get root domain sid, save it in RootDomSidBuf (global) // this function is called within the critical section // // 1) ldap_open to the DC of interest. // 2) you do not need to ldap_connect - the following step works anonymously // 3) read the operational attribute rootDomainNamingContext and provide the // operational control LDAP_SERVER_EXTENDED_DN_OID as defined in sdk\inc\ntldap.h. DWORD Win32rc=NO_ERROR; HINSTANCE hLdapDll=NULL; PFN_LDAP_OPEN pfnLdapOpen=NULL; PFN_LDAP_UNBIND pfnLdapUnbind=NULL; PFN_LDAP_SEARCH pfnLdapSearch=NULL; PFN_LDAP_FIRST_ENTRY pfnLdapFirstEntry=NULL; PFN_LDAP_GET_VALUE pfnLdapGetValue=NULL; PFN_LDAP_MSGFREE pfnLdapMsgFree=NULL; PFN_LDAP_VALUE_FREE pfnLdapValueFree=NULL; PFN_LDAP_MAP_ERROR pfnLdapMapError=NULL; PLDAP phLdap=NULL; LDAPControlW serverControls = { LDAP_SERVER_EXTENDED_DN_OID_W, { 0, NULL }, TRUE }; LPWSTR Attribs[] = { LDAP_OPATT_ROOT_DOMAIN_NAMING_CONTEXT_W, NULL }; PLDAPControlW rServerControls[] = { &serverControls, NULL }; PLDAPMessage pMessage = NULL; LDAPMessage *pEntry = NULL; PWCHAR *ppszValues=NULL; LPWSTR pSidStart, pSidEnd, pParse; BYTE *pDest = NULL; BYTE OneByte; DWORD RootDomSidBuf[sizeof(SID)/sizeof(DWORD)+5]; hLdapDll = LoadLibraryA("wldap32.dll"); if ( hLdapDll) { pfnLdapOpen = (PFN_LDAP_OPEN)GetProcAddress(hLdapDll, "ldap_openW"); pfnLdapUnbind = (PFN_LDAP_UNBIND)GetProcAddress(hLdapDll, "ldap_unbind"); pfnLdapSearch = (PFN_LDAP_SEARCH)GetProcAddress(hLdapDll, "ldap_search_ext_sW"); pfnLdapFirstEntry = (PFN_LDAP_FIRST_ENTRY)GetProcAddress(hLdapDll, "ldap_first_entry"); pfnLdapGetValue = (PFN_LDAP_GET_VALUE)GetProcAddress(hLdapDll, "ldap_get_valuesW"); pfnLdapMsgFree = (PFN_LDAP_MSGFREE)GetProcAddress(hLdapDll, "ldap_msgfree"); pfnLdapValueFree = (PFN_LDAP_VALUE_FREE)GetProcAddress(hLdapDll, "ldap_value_freeW"); pfnLdapMapError = (PFN_LDAP_MAP_ERROR)GetProcAddress(hLdapDll, "LdapMapErrorToWin32"); } if ( pfnLdapOpen == NULL || pfnLdapUnbind == NULL || pfnLdapSearch == NULL || pfnLdapFirstEntry == NULL || pfnLdapGetValue == NULL || pfnLdapMsgFree == NULL || pfnLdapValueFree == NULL || pfnLdapMapError == NULL ) { Win32rc = ERROR_PROC_NOT_FOUND; } else { // // bind to ldap // phLdap = (*pfnLdapOpen)((PWCHAR)pszServer, LDAP_PORT); if ( phLdap == NULL ) Win32rc = ERROR_FILE_NOT_FOUND; } if ( NO_ERROR == Win32rc ) { // // now get the ldap handle, // Win32rc = (*pfnLdapSearch)( phLdap, L"", LDAP_SCOPE_BASE, L"(objectClass=*)", Attribs, 0, (PLDAPControlA *)&rServerControls, NULL, NULL, 10000, &pMessage); if( Win32rc == NO_ERROR && pMessage ) { Win32rc = ERROR_SUCCESS; pEntry = (*pfnLdapFirstEntry)(phLdap, pMessage); if(pEntry == NULL) { Win32rc = (*pfnLdapMapError)( phLdap->ld_errno ); } else { // // Now, we'll have to get the values // ppszValues = (*pfnLdapGetValue)(phLdap, pEntry, Attribs[0]); if( ppszValues == NULL) { Win32rc = (*pfnLdapMapError)( phLdap->ld_errno ); } else if ( ppszValues[0] && ppszValues[0][0] != '\0' ) { // // ppszValues[0] is the value to parse. // The data will be returned as something like: // ;;DC=colinbrdom1,DC=nttest,DC=microsoft,DC=com // Parse through this to find the part. Note that it may be missing, but the GUID= and trailer should not be. // The xxxxx represents the hex nibbles of the SID. Translate to the binary form and case to a SID. pSidStart = wcsstr(ppszValues[0], L""); if ( pSidEnd ) { pParse = pSidStart + 5; pDest = (BYTE *)RootDomSidBuf; while ( pParse < pSidEnd-1 ) { if ( *pParse >= '0' && *pParse <= '9' ) { OneByte = (BYTE) ((*pParse - '0') * 16); } else { OneByte = (BYTE) ( (tolower(*pParse) - 'a' + 10) * 16 ); } if ( *(pParse+1) >= '0' && *(pParse+1) <= '9' ) { OneByte = OneByte + (BYTE) ( (*(pParse+1)) - '0' ) ; } else { OneByte = OneByte + (BYTE) ( tolower(*(pParse+1)) - 'a' + 10 ) ; } *pDest = OneByte; pDest++; pParse += 2; } ULONG cbSid = GetLengthSid((PSID)RootDomSidBuf); *ppSid = (PSID) LocalAlloc(LPTR, cbSid); if (!*ppSid) { Win32rc = ERROR_NOT_ENOUGH_MEMORY; } CopyMemory(*ppSid, (PSID)RootDomSidBuf, cbSid); ASSERT(IsValidSid(*ppSid)); } else { Win32rc = ERROR_OBJECT_NOT_FOUND; } } else { Win32rc = ERROR_OBJECT_NOT_FOUND; } (*pfnLdapValueFree)(ppszValues); } else { Win32rc = ERROR_OBJECT_NOT_FOUND; } } (*pfnLdapMsgFree)(pMessage); } } // // even though it's not binded, use unbind to close // if ( phLdap != NULL && pfnLdapUnbind ) (*pfnLdapUnbind)(phLdap); if ( hLdapDll ) { FreeLibrary(hLdapDll); } return HRESULT_FROM_WIN32(Win32rc); } BOOL IsRootObject(IDirectoryObject * pDirObject) { if(!pDirObject) { ASSERT(pDirObject); return FALSE; } LPWSTR pAttrNames[]={L"instanceType"}; DWORD dwNumAttr= ARRAYSIZE(pAttrNames); ADS_ATTR_INFO *pAttrInfo=NULL; DWORD dwReturn = 0; HRESULT hr = pDirObject->GetObjectAttributes( pAttrNames, dwNumAttr, &pAttrInfo, &dwReturn ); if(FAILED(hr) || dwReturn != 1 ) return FALSE; ASSERT(ADSTYPE_INTEGER == pAttrInfo->dwADsType); int InstanceType = pAttrInfo[0].pADsValues->Integer; FreeADsMem( pAttrInfo ); if(InstanceType & DS_INSTANCETYPE_IS_NC_HEAD) return TRUE; else return FALSE; }