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

2573 lines
81 KiB

//+-------------------------------------------------------------------------
//
// 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 <dssec.h>
#include "exnc.h"
#include "ntsecapi.h"
TCHAR const c_szDomainClass[] = DOMAIN_CLASS_NAME; // adsnms.h
#define CLASS_COMPUTER L"computer"
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;
// 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);
// If this is a root object (i.e. domain), hide the ACL protect checkbox
// Note that there is more than one form of "domain", e.g. "domainDNS"
// so look for anything that starts with "domain".
// If this is a root object (i.e. domain), hide the ACL protect checkbox
if ((m_dwInitFlags & DSSI_IS_ROOT)
|| (m_strObjectClass &&
CSTR_EQUAL == CompareString(LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
m_strObjectClass,
ARRAYSIZE(c_szDomainClass) - 1,
c_szDomainClass,
ARRAYSIZE(c_szDomainClass) - 1)))
{
m_dwSIFlags |= SI_NO_ACL_PROTECT;
}
// 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;
m_pDsObject->GetObjectAttributes(&pszTemp,
1,
&pAttributeInfo,
&dwAttributesReturned);
if (pAttributeInfo)
{
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<LPSECURITYINFO>(this),
&IID_IEffectivePermission, static_cast<LPEFFECTIVEPERMISSION>(this),
&IID_ISecurityObjectTypeInfo, static_cast<LPSecurityObjectTypeInfo>(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
// <normal ACL>
//
// 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();
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<CDSSecurityInfo*>(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<CDSSecurityInfo*>(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(0,
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;
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");
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(SUCCEEDED(hr))
{
//FreeInheritedFromArray(pTempInherit, pACL->AceCount,NULL);
*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 <winldap.h>
#include <ntldap.h>
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:
// <GUID=278676f8d753d211a61ad7e2dfa25f11>;<SID=010400000000000515000000828ba6289b0bc11e67c2ef7f>;DC=colinbrdom1,DC=nttest,DC=microsoft,DC=com
// Parse through this to find the <SID=xxxxxx> 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"<SID=");
if ( pSidStart )
{
//
// find the end of this SID
//
pSidEnd = wcsstr(pSidStart, 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);
}