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.
 
 
 
 
 
 

3405 lines
86 KiB

/*
File adsi.cpp
Com interaction with adsi
Paul Mayfield, 4/14/98
*/
#include "dsrights.h"
#include "sddl.h"
#include "mprapip.h"
#include "dsgetdc.h"
// Definition for convenience
//
#define DSR_ADS_RIGHT_GENERIC_READ (ADS_RIGHT_READ_CONTROL | \
ADS_RIGHT_DS_LIST_OBJECT | \
ADS_RIGHT_DS_READ_PROP | \
ADS_RIGHT_ACTRL_DS_LIST )
#define DSR_ADS_ACE_INHERITED (ADS_ACEFLAG_INHERIT_ONLY_ACE | \
ADS_ACEFLAG_INHERIT_ACE)
//
// Describes an Access control entry
//
typedef struct _DSR_ACE_DESCRIPTOR
{
LONG dwAccessMask;
LONG dwAceType;
LONG dwAceFlags;
LONG dwFlags;
BSTR bstrTrustee;
BSTR bstrObjectType;
BSTR bstrInheritedObjectType;
} DSR_ACE_DESCRIPTOR;
//
// Structure maps a domain object to the ACES that should be
// added or removed from it in order to enable/disable NT4
// ras servers in the domain
//
typedef struct _DSR_ACE_APPLICATION
{
IADs* pObject;
DSR_ACE_DESCRIPTOR Ace;
} DSR_ACE_APPLICATION;
//
// Parameters used to generate a DSR_ACE_APPLICATION
//
typedef struct _DSR_ACE_APPLICATION_DESC
{
PWCHAR pszObjectCN; // NULL means domain root
PWCHAR pszObjectClass;
DSR_ACE_DESCRIPTOR Ace;
} DSR_ACE_APPLICATION_DESC;
//
// Structure contains the information needed to have
// ACL's in the AD of a given domain adjusted such that
// the various modes (MPR_DOMAIN_*) of access are granted.
//
typedef struct _DSR_DOMAIN_ACCESS_INFO
{
// The name of a DC in the target domain
//
PWCHAR pszDC;
// Aces derived from the default user SD
// These are added in all modes but never removed.
//
DSR_ACE_APPLICATION* pAcesUser;
DWORD dwAceCountUser;
// Aces for MPRFLAG_DOMAIN_NT4_SERVERS mode
//
DSR_ACE_APPLICATION* pAcesNt4;
DWORD dwAceCountNt4;
// Aces for MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS mode
//
DSR_ACE_APPLICATION* pAcesW2k;
DWORD dwAceCountW2k;
// Stored here for convenience, pointers
// to common ds objects
//
IADs* pDomain;
IADs* pRootDse;
IADs* pUserClass;
} DSR_DOMAIN_ACCESS_INFO;
//
// Strings used in DS queries
//
static const WCHAR pszLdapPrefix[] = L"LDAP://";
static const WCHAR pszLdap[] = L"LDAP:";
static const WCHAR pszCN[] = L"CN=";
static const WCHAR pszGCPrefix[] = L"GC://";
static const WCHAR pszGC[] = L"GC:";
static const WCHAR pszRootDse[] = L"RootDSE";
static const WCHAR pszSecurityDesc[] = L"ntSecurityDescriptor";
static const WCHAR pszDefSecurityDesc[] = L"defaultSecurityDescriptor";
static const WCHAR pszDn[] = L"distinguishedName";
static const WCHAR pszSid[] = L"objectSid";
static const WCHAR pszEveryone[] = L"S-1-1-0";
static const WCHAR pszDefaultNamingContext[] = L"defaultNamingContext";
static const WCHAR pszSchemaNamingCtx[] = L"schemaNamingContext";
static const WCHAR pszBecomeSchemaMaster[] = L"becomeSchemaMaster";
static const WCHAR pszUpdateSchemaCache[] = L"schemaUpdateNow";
static const WCHAR pszRegValSchemaLock[] = L"Schema Update Allowed";
static const WCHAR pszRegKeySchemaLock[]
= L"System\\CurrentControlSet\\Services\\NTDS\\Parameters";
static const WCHAR pszSystemClass[] = L"Container";
static const WCHAR pszSystemCN[] = L"CN=System";
static const WCHAR pszBuiltinClass[] = L"builtinDomain";
static const WCHAR pszBuiltinCN[] = L"CN=Builtin";
static const WCHAR pszSamSvrClass[] = L"samServer";
static const WCHAR pszSamSvrCN[] = L"CN=Server,CN=System";
static const WCHAR pszUserClass[] = L"classSchema";
static const WCHAR pszUserCN[] = L"CN=user";
static const WCHAR pszAccessChkClass[] = L"Container";
static const WCHAR pszAccessChkCN[] =
L"CN=RAS and IAS Servers Access Check,CN=System";
static const WCHAR pszGuidUserParms[] =
L"{BF967A6D-0DE6-11D0-A285-00AA003049E2}";
static const WCHAR pszGuidUserClass[] =
L"{BF967ABA-0DE6-11D0-A285-00aa003049E2}";
//
// This GUID is the property set of the following
// attributes needed for w2k level access.
//
// Token-Groups
// msNPAllowDialin
// msNPCallingStationID
// msRADIUSCallbackNumber
// msRADIUSFramedIPAddress
// msRADIUSFramedRoute
// msRADIUSServiceType
//
static const WCHAR pszGuidRasPropSet1[] =
L"{037088F8-0AE1-11D2-B422-00A0C968F939}";
//
// This GUID is the property set of the following
// attributes needed for w2k level access
//
// User-Account-Control
// Account-Expires
//
static const WCHAR pszGuidRasPropSet2[] =
L"{4C164200-20C0-11D0-A768-00AA006E0529}";
//
// This GUID is the property of the following
// attribute needed for w2k level access
//
// Logon-Hours
//
static const WCHAR pszGuidLogonHours[] =
L"{BF9679AB-0DE6-11D0-A285-00AA003049E2}";
//
// This GUID is the value of the samAccountName
// attribute needed for w2k level access.
//
// samAccountName
//
static const WCHAR pszGuidSamAccountName[] =
L"{3E0ABFD0-126A-11D0-A060-00AA006C33ED}";
// The optimal means for searching for a computer
// in a domain is to lookup its sam account name which
// is indexed. The optimal means for searching for a
// group of a given sid is to lookup its SID which is indexed.
//
const WCHAR pszCompFilterFmt[] = L"(samaccountname=%s$)";
const WCHAR pszGroupFilterFmt[] = L"(objectSid=%s)";
const WCHAR pszUserClassFmt[] =
L"(&(objectClass=user)(!(objectClass=computer)))";
//
// The table of aces to be applied for MPRFLAG_DOMAIN_NT4_SERVERS
//
DSR_ACE_APPLICATION_DESC g_pAcesNt4[] =
{
// Grant list options to everyone for the root domain
// object
//
{
NULL, // Object (NULL = root)
NULL, // Object class
{
ADS_RIGHT_ACTRL_DS_LIST, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED, // dwAceType
0, // dwAceFlags
0, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
NULL, // bstrObjectType
NULL // bstrInheritedObjectType
}
},
// Grant list contents to everyone for the builtin
// object
//
{
(PWCHAR)pszBuiltinCN, // Object
(PWCHAR)pszBuiltinClass, // Object class
{
ADS_RIGHT_ACTRL_DS_LIST, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED, // dwAceType
0, // dwAceFlags
0, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
NULL, // bstrObjectType
NULL // bstrInheritedObjectType
}
},
// Grant generic read to everyone on the sam server
// object
//
{
(PWCHAR)pszSamSvrCN, // Object
(PWCHAR)pszSamSvrClass, // Object class
{
DSR_ADS_RIGHT_GENERIC_READ, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED, // dwAceType
0, // dwAceFlags
0, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
NULL, // bstrObjectType
NULL // bstrInheritedObjectType
}
},
// Allow everyone to read the userparms property of the
// user class by enabling this inheritable ACE to the
// root domain object
{
NULL, // Object (NULL = root)
NULL, // Object class
{
ADS_RIGHT_DS_READ_PROP, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, // dwAceType
DSR_ADS_ACE_INHERITED, // dwAceFlags
ADS_FLAG_OBJECT_TYPE_PRESENT |
ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
(PWCHAR)pszGuidUserParms, // bstrObjectType
(PWCHAR)pszGuidUserClass // bstrInheritedObjectType
}
}
};
//
// The table of aces to be applied for
// MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS
//
DSR_ACE_APPLICATION_DESC g_pAcesW2k[] =
{
// Grant list contents to Everyone for the System
// container
//
{
(PWCHAR)pszSystemCN, // Object
(PWCHAR)pszSystemClass, // Object class
{
ADS_RIGHT_ACTRL_DS_LIST, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED, // dwAceType
0, // dwAceFlags
0, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
NULL, // bstrObjectType
NULL // bstrInheritedObjectType
}
},
// Grant generic read to Everyone for the 'RAS and IAS Servers
// Access Check' container
//
{
(PWCHAR)pszAccessChkCN, // Object
(PWCHAR)pszAccessChkClass, // Object class
{
DSR_ADS_RIGHT_GENERIC_READ, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED, // dwAceType
0, // dwAceFlags
0, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
NULL, // bstrObjectType
NULL // bstrInheritedObjectType
}
},
// Users should expose their RAS properties
//
{
NULL, // Object (NULL = root)
NULL, // Object class
{
ADS_RIGHT_DS_READ_PROP, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, // dwAceType
DSR_ADS_ACE_INHERITED, // dwAceFlags
ADS_FLAG_OBJECT_TYPE_PRESENT |
ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
(PWCHAR)pszGuidRasPropSet1, // bstrObjectType
(PWCHAR)pszGuidUserClass // bstrInheritedObjectType
}
},
// Users should expose their RAS properties
//
{
NULL, // Object (NULL = root)
NULL, // Object class
{
ADS_RIGHT_DS_READ_PROP, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, // dwAceType
DSR_ADS_ACE_INHERITED, // dwAceFlags
ADS_FLAG_OBJECT_TYPE_PRESENT |
ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
(PWCHAR)pszGuidRasPropSet2, // bstrObjectType
(PWCHAR)pszGuidUserClass // bstrInheritedObjectType
}
},
// Users should expose their logon hours property
//
{
NULL, // Object (NULL = root)
NULL, // Object class
{
ADS_RIGHT_DS_READ_PROP, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, // dwAceType
DSR_ADS_ACE_INHERITED, // dwAceFlags
ADS_FLAG_OBJECT_TYPE_PRESENT |
ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
(PWCHAR)pszGuidLogonHours, // bstrObjectType
(PWCHAR)pszGuidUserClass // bstrInheritedObjectType
}
},
// Grant list contents to everything in the domain.
//
//{
// NULL, // Object
// NULL, // Object class
// {
// ADS_RIGHT_ACTRL_DS_LIST, // dwAccessMask
// ADS_ACETYPE_ACCESS_ALLOWED, // dwAceType
// DSR_ADS_ACE_INHERITED, // dwAceFlags
// 0, // dwFlags
// (PWCHAR)pszEveryone, // bstrTrustee
// NULL, // bstrObjectType
// NULL // bstrInheritedObjectType
// }
//},
// Users should expose their samAccountName
//
{
NULL, // Object (NULL = root)
NULL, // Object class
{
ADS_RIGHT_DS_READ_PROP, // dwAccessMask
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT, // dwAceType
DSR_ADS_ACE_INHERITED, // dwAceFlags
ADS_FLAG_OBJECT_TYPE_PRESENT |
ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT, // dwFlags
(PWCHAR)pszEveryone, // bstrTrustee
(PWCHAR)pszGuidSamAccountName, // bstrObjectType
(PWCHAR)pszGuidUserClass // bstrInheritedObjectType
}
}
};
DWORD
DsrAccessInfoCleanup(
IN DSR_DOMAIN_ACCESS_INFO* pSecurityInfo);
DWORD
DsrAceDescClear(
IN DSR_ACE_DESCRIPTOR* pParams);
HRESULT
DsrAceDescCopy(
OUT DSR_ACE_DESCRIPTOR* pDst,
IN DSR_ACE_DESCRIPTOR* pSrc);
VOID
DsrAceDescTrace(
IN IADs* pIads,
IN DSR_ACE_DESCRIPTOR* pA);
HRESULT
DsrAceAdd(
IN PWCHAR pszDC,
IN IADs* pIads,
IN DSR_ACE_DESCRIPTOR * pAceParams);
HRESULT
DsrAceCreate(
IN DSR_ACE_DESCRIPTOR * pAceParams,
OUT IDispatch** ppAce);
HRESULT
DsrAceFind(
IN PWCHAR pszDC,
IN IADs* pObject,
IN DSR_ACE_DESCRIPTOR* pAceParams,
OUT VARIANT* pVarSD,
OUT IADsSecurityDescriptor** ppSD,
OUT IADsAccessControlList** ppAcl,
OUT IDispatch** ppAce);
HRESULT
DsrAceFindInAcl(
IN PWCHAR pszDC,
IN IADsAccessControlList* pAcl,
IN DSR_ACE_DESCRIPTOR* pAceDesc,
OUT IDispatch** ppAce);
HRESULT
DsrAceRemove(
IN PWCHAR pszDC,
IN IADs* pIads,
IN DSR_ACE_DESCRIPTOR * pAceParams);
HRESULT
DsrDomainQueryAccessEx(
IN PWCHAR pszDomain,
OUT LPDWORD lpdwAccessFlags,
OUT DSR_DOMAIN_ACCESS_INFO** ppInfo);
//
// Allocates memory for use with dsr functions
//
PVOID DsrAlloc(DWORD dwSize, BOOL bZero) {
return GlobalAlloc (bZero ? GPTR : GMEM_FIXED, dwSize);
}
//
// Free memory used by dsr functions
//
DWORD DsrFree(PVOID pvBuf) {
GlobalFree(pvBuf);
return NO_ERROR;
}
//
// Compares to optional strings
//
INT
DsrStrCompare(
IN BSTR bstrS1,
IN BSTR bstrS2)
{
if ((!!bstrS1) != (!!bstrS2))
{
return -1;
}
if (bstrS1 == NULL)
{
return 0;
}
return lstrcmpi(bstrS1, bstrS2);
}
//
// Adds or removes a substring from the given string
//
HRESULT
DsrStrAddRemoveSubstring(
IN BSTR bstrSrc,
IN PWCHAR pszSubString,
IN BOOL bAdd,
OUT BSTR* pbstrResult)
{
HRESULT hr = S_OK;
PWCHAR pszBuffer = NULL, pszStart = NULL, pszEnd = NULL;
PWCHAR pszSrc, pszDst;
DWORD dwSize = 0, dwLen = 0;
// Find out if the sub string is already in the
// string
pszStart = wcsstr(bstrSrc, pszSubString);
// The substring already exists in the string
//
if (pszStart)
{
// No need to add it since it's already there.
if (bAdd)
{
*pbstrResult = SysAllocString(bstrSrc);
}
// Remove the substring
else
{
dwLen = wcslen(pszSubString);
pszEnd = pszStart + dwLen;
dwSize = (DWORD)(pszStart - bstrSrc) + wcslen(pszEnd) + 1;
dwSize *= sizeof(WCHAR);
pszBuffer = (PWCHAR) DsrAlloc(dwSize, FALSE);
if (pszBuffer == NULL)
{
return E_OUTOFMEMORY;
}
// Copy everything up to the substring
//
for (pszSrc = bstrSrc, pszDst = pszBuffer;
pszSrc != pszStart;
pszSrc++, pszDst++)
{
*pszDst = *pszSrc;
}
// Copy everything after the substring
for (pszSrc = pszEnd; *pszSrc; pszSrc++, pszDst++)
{
*pszDst = *pszSrc;
}
// Null terminate
*pszDst = L'\0';
*pbstrResult = SysAllocString(pszBuffer);
DsrFree(pszBuffer);
}
}
// The substring does not already exist in the
// string
else
{
// Append the string
//
if (bAdd)
{
dwSize = wcslen(bstrSrc) + wcslen(pszSubString) + 1;
dwSize *= sizeof(WCHAR);
pszBuffer = (PWCHAR) DsrAlloc(dwSize, FALSE);
if (pszBuffer == NULL)
{
return E_OUTOFMEMORY;
}
wcscpy(pszBuffer, bstrSrc);
wcscat(pszBuffer, pszSubString);
*pbstrResult = SysAllocString(pszBuffer);
DsrFree(pszBuffer);
}
// Or nothing to do since the substring was
// already removed
else
{
*pbstrResult = SysAllocString(bstrSrc);
}
}
return (*pbstrResult) ? S_OK : E_OUTOFMEMORY;
}
//
// Converts a SID into a buffer
//
DWORD
DsrStrFromSID(
IN PSID pSid,
OUT PWCHAR pszString,
IN DWORD dwSize)
{
NTSTATUS nStatus = STATUS_SUCCESS;
UNICODE_STRING UnicodeString;
// Initialize the unicode string
//
RtlInitUnicodeString(&UnicodeString, NULL);
do
{
// Convert the string
//
nStatus = RtlConvertSidToUnicodeString(
&UnicodeString,
pSid,
TRUE);
if (! NT_SUCCESS(nStatus))
{
break;
}
// Validate the result
//
if (UnicodeString.Buffer == NULL)
{
nStatus = ERROR_CAN_NOT_COMPLETE;
break;
}
if (UnicodeString.Length > dwSize)
{
nStatus = STATUS_BUFFER_OVERFLOW;
break;
}
// Copy the result
//
wcscpy(pszString, UnicodeString.Buffer);
nStatus = STATUS_SUCCESS;
} while (FALSE);
// Cleanup
{
if (UnicodeString.Buffer != NULL)
{
RtlFreeUnicodeString(&UnicodeString);
}
}
return RtlNtStatusToDosError(nStatus);
}
//
// Generates an LDAP path based on a domain and a
// distinguished name
//
// Form of value returned: LDAP://<domain or dc>/dn
HRESULT
DsrDomainGenLdapPath(
IN PWCHAR pszDomain,
IN PWCHAR pszDN,
OUT PWCHAR* ppszObject)
{
DWORD dwSize;
// Calculate the size needed
//
dwSize = (wcslen(pszLdapPrefix) + wcslen(pszDN) + 1) * sizeof(WCHAR);
if (pszDomain)
{
dwSize += (wcslen(pszDomain) + 1) * sizeof(WCHAR); // +1 for '/'
}
// Allocate the return value
//
*ppszObject = (PWCHAR) DsrAlloc(dwSize, FALSE);
if (*ppszObject == NULL)
{
return E_OUTOFMEMORY;
}
// Format the return value
if (pszDomain == NULL)
{
wsprintfW(*ppszObject, L"%s%s", pszLdapPrefix, pszDN);
}
else
{
wsprintfW(*ppszObject, L"%s%s/%s", pszLdapPrefix, pszDomain, pszDN);
}
return S_OK;
}
//
// Returns a reference to rootDse of the given
// domain
//
HRESULT
DsrDomainGetRootDse(
IN PWCHAR pszDomain,
OUT IADs** ppRootDse)
{
HRESULT hr = S_OK;
PWCHAR pszPath = NULL;
DWORD dwSize = 0;
do
{
// Get the object path
//
hr = DsrDomainGenLdapPath(pszDomain, (PWCHAR)pszRootDse, &pszPath);
DSR_BREAK_ON_FAILED_HR(hr);
// Get RootDSE
//
hr = ADsGetObject(pszPath, IID_IADs, (VOID**)ppRootDse);
DSR_BREAK_ON_FAILED_HR( hr );
} while (FALSE);
// Cleanup
{
DSR_FREE(pszPath);
if (FAILED (hr))
{
DSR_RELEASE(*ppRootDse);
}
}
return hr;
}
//
// Returns a reference to the root domain object
//
HRESULT
DsrDomainGetContainers(
IN PWCHAR pszDomain,
OUT IADs** ppRootDse,
OUT IADsContainer** ppDomain,
OUT IADsContainer** ppSchema)
{
PWCHAR pszDomainObj = NULL, pszSchemaObj = NULL;
HRESULT hr = S_OK;
DWORD dwSize = 0;
VARIANT var;
// Iniatialize
//
{
*ppRootDse = NULL;
*ppDomain = NULL;
*ppSchema = NULL;
VariantInit(&var);
}
do
{
// Get RootDSE
//
hr = DsrDomainGetRootDse(pszDomain, ppRootDse);
DSR_BREAK_ON_FAILED_HR(hr);
// Use RootDSE to figure out the name of the domain object
// to query
hr = (*ppRootDse)->Get((PWCHAR)pszDefaultNamingContext, &var);
DSR_BREAK_ON_FAILED_HR( hr );
// Compute the distinguished name of the root domain object
//
hr = DsrDomainGenLdapPath(pszDomain, V_BSTR(&var), &pszDomainObj);
DSR_BREAK_ON_FAILED_HR(hr);
// Use RootDSE to figure out the name of the schema context
//
VariantClear(&var);
hr = (*ppRootDse)->Get((PWCHAR)pszSchemaNamingCtx, &var);
DSR_BREAK_ON_FAILED_HR( hr );
// Compute the distinguished name of the root schema object
//
hr = DsrDomainGenLdapPath(pszDomain, V_BSTR(&var), &pszSchemaObj);
DSR_BREAK_ON_FAILED_HR(hr);
// Get the objects
//
hr = ADsGetObject(pszDomainObj, IID_IADsContainer, (VOID**)ppDomain);
DSR_BREAK_ON_FAILED_HR( hr );
hr = ADsGetObject(pszSchemaObj, IID_IADsContainer, (VOID**)ppSchema);
DSR_BREAK_ON_FAILED_HR( hr );
} while (FALSE);
// Cleanup
//
{
if (FAILED( hr ))
{
DSR_RELEASE(*ppRootDse);
DSR_RELEASE(*ppDomain);
DSR_RELEASE(*ppSchema);
*ppRootDse = NULL;
*ppDomain = NULL;
*ppSchema = NULL;
}
DSR_FREE(pszDomainObj);
DSR_FREE(pszSchemaObj);
VariantClear(&var);
}
return hr;
}
//
// Initializes COM
//
HRESULT
DsrComIntialize()
{
HRESULT hr;
hr = CoInitializeEx (NULL, COINIT_APARTMENTTHREADED);
if (hr == RPC_E_CHANGED_MODE)
{
hr = CoInitializeEx (NULL, COINIT_MULTITHREADED);
}
if ((hr != S_FALSE) && (FAILED(hr)))
{
return hr;
}
return NO_ERROR;
}
//
// Unitializes COM
//
VOID
DsrComUninitialize()
{
CoUninitialize();
}
//
// Creates a SID based on the array of bytes
// stored in a variant.
//
DWORD
DsrSidInit (
IN VARIANT * pVar,
OUT PBYTE* ppbSid)
{
SAFEARRAY * pArray = V_ARRAY(pVar);
DWORD dwSize, dwLow, dwHigh, i;
HRESULT hr;
BYTE* pbRet = NULL;
VARIANT var;
DsrTraceEx (0, "DsrSidInit: entered.");
// Get the array of bytes
i = 0;
hr = SafeArrayGetElement(pArray, (LONG*)&i, (VOID*)&var);
if (FAILED (hr))
return hr;
// Initialize the return buffer accordingly
pArray = V_ARRAY(&var);
dwSize = SafeArrayGetDim(pArray);
hr = SafeArrayGetLBound(pArray, 1, (LONG*)&dwLow);
if (FAILED (hr))
return DsrTraceEx(hr, "DsrSidInit: %x unable to get lbound", hr);
hr = SafeArrayGetUBound(pArray, 1, (LONG*)&dwHigh);
if (FAILED (hr))
return DsrTraceEx(hr, "DsrSidInit: %x unable to get ubound", hr);
DsrTraceEx (
0,
"DsrSidInit: Dim=%d, Low=%d, High=%d",
dwSize,
dwLow,
dwHigh);
// Allocate the sid
if ((pbRet = (BYTE*)DsrAlloc((dwHigh - dwLow) + 2, TRUE)) == NULL) {
return DsrTraceEx (
ERROR_NOT_ENOUGH_MEMORY,
"DsrSidInit: Unable to alloc");
}
// Copy in the bytes of the SID
i = dwLow;
while (TRUE) {
hr = SafeArrayGetElement(pArray, (LONG*)&i, (VOID*)(&(pbRet[i])));
if (FAILED (hr))
break;
i++;
}
DsrTraceEx(0, "DsrSidInit: copied %d bytes", i);
*ppbSid = pbRet;
{
PUCHAR puSA;
DsrTraceEx (0, "DsrSidInit: Sid Length: %d", GetLengthSid(pbRet));
puSA = GetSidSubAuthorityCount(pbRet);
if (puSA)
DsrTraceEx (0, "DsrSidInit: Sid SA Count: %d", *puSA);
}
return NO_ERROR;
}
//
// Generates the ascii equivalent (suitable for submission as part of
// a query against the DS) of a SID based on a base SID and a sub authority
// to be appeneded.
//
HRESULT
DsrSidInitAscii(
IN LPBYTE pBaseSid,
IN DWORD dwSubAuthority,
OUT PWCHAR* ppszSid)
{
DWORD dwLen, dwSidLen, i;
WCHAR* pszRet = NULL;
PUCHAR puCount;
LPBYTE pByte;
// Calculate the length of the returned buffer
dwSidLen = GetLengthSid(pBaseSid);
dwLen = (dwSidLen * 2) + sizeof(DWORD) + 1;
dwLen *= sizeof (WCHAR);
// we put '\' before each byte, so double the size
dwLen *= 2;
// Allocate the return buffer
pszRet = (PWCHAR) DsrAlloc(dwLen, TRUE);
if (pszRet == NULL)
return E_OUTOFMEMORY;
// Increment the sub authority count
puCount = GetSidSubAuthorityCount(pBaseSid);
*puCount = *puCount + 1;
// Copy the bytes
for (i = 0; i < dwSidLen; i++) {
pszRet[i*3] = L'\\';
wsprintfW(&(pszRet[i*3+1]), L"%02x", (DWORD)pBaseSid[i]);
}
// Append the bytes for the new sub authority
pByte = (LPBYTE)&(dwSubAuthority);
for (; i < dwSidLen + sizeof(DWORD); i++) {
pszRet[i*3] = L'\\';
wsprintfW(&(pszRet[i*3+1]), L"%02x", (DWORD)pByte[i-dwSidLen]);
}
// Decrement the sub authority count -- restoring the
// base sid.
*puCount = *puCount - 1;
*ppszSid = pszRet;
return NO_ERROR;
}
//
// Searches given domain for a computer account
// with the given name and returns its ADsPath
// if found.
//
DWORD
DsrFindDomainComputer (
IN PWCHAR pszDomain,
IN PWCHAR pszComputer,
OUT PWCHAR* ppszADsPath)
{
HRESULT hr = S_OK;
DWORD dwLen, dwSrchAttribCount;
IDirectorySearch * pSearch = NULL;
PWCHAR pszDomainPath = NULL, pszFilter = NULL;
PWCHAR pszBase, pszPrefix;
ADS_SEARCH_HANDLE hSearch = NULL;
ADS_SEARCH_COLUMN adsColumn;
PWCHAR ppszSrchAttribs[] =
{
(PWCHAR)pszDn,
NULL
};
BOOL bSearchGC = FALSE;
do {
// Validate parameters
if (!pszDomain || !pszComputer || !ppszADsPath) {
hr = ERROR_INVALID_PARAMETER;
break;
}
// Decide whether to search the GC or the domain
// object
if (bSearchGC) {
pszBase = (PWCHAR)pszGC;
pszPrefix = (PWCHAR)pszGCPrefix;
}
else {
pszBase = (PWCHAR)pszLdap;
pszPrefix = (PWCHAR)pszLdapPrefix;
}
// Allocate the domain path
dwLen = (pszDomain) ? wcslen(pszDomain) : 0;
dwLen += wcslen(pszPrefix) + 1;
dwLen *= sizeof(WCHAR);
pszDomainPath = (PWCHAR) DsrAlloc(dwLen, FALSE);
if (pszDomainPath == NULL) {
hr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Format the domain path
if (pszDomain) {
wcscpy(pszDomainPath, pszPrefix);
wcscat(pszDomainPath, pszDomain);
}
else
wcscpy(pszDomainPath, pszBase);
// Get a reference to the object to search
// (either domain object or GC)
hr = ADsGetObject (
pszDomainPath,
IID_IDirectorySearch,
(VOID**)&pSearch);
if (FAILED (hr))
break;
// Prepare the search filter
//
dwLen = wcslen(pszCompFilterFmt) + wcslen(pszComputer) + 1;
dwLen *= sizeof(WCHAR);
pszFilter = (PWCHAR) DsrAlloc(dwLen, FALSE);
if (pszFilter == NULL) {
hr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
wsprintfW(pszFilter, pszCompFilterFmt, pszComputer);
// Count the number of attributes we're searching
// for
if (ppszSrchAttribs == NULL)
dwSrchAttribCount = (DWORD)-1;
else {
for (dwSrchAttribCount = 0;
ppszSrchAttribs[dwSrchAttribCount];
dwSrchAttribCount++);
}
// Search the DS
hr = pSearch->ExecuteSearch(
pszFilter,
ppszSrchAttribs,
dwSrchAttribCount,
&hSearch);
if (FAILED (hr))
break;
// Get the first result
hr = pSearch->GetNextRow(hSearch);
if (hr == S_ADS_NOMORE_ROWS) {
hr = ERROR_NOT_FOUND;
break;
}
// Get the attribute we're interested in
hr = pSearch->GetColumn(hSearch, (PWCHAR)pszDn, &adsColumn);
if (SUCCEEDED (hr)) {
dwLen = wcslen(adsColumn.pADsValues[0].PrintableString) +
wcslen(pszLdapPrefix) +
1;
dwLen *= 2;
*ppszADsPath = (PWCHAR) DsrAlloc(dwLen, FALSE);
if (*ppszADsPath == NULL)
{
pSearch->FreeColumn(&adsColumn);
hr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
wcscpy(*ppszADsPath, pszLdapPrefix);
wcscat(*ppszADsPath, adsColumn.pADsValues[0].PrintableString);
pSearch->FreeColumn (&adsColumn);
hr = NO_ERROR;
}
} while (FALSE);
// Cleanup
{
if (hSearch)
pSearch->CloseSearchHandle(hSearch);
DSR_FREE (pszDomainPath);
DSR_FREE (pszFilter);
DSR_RELEASE (pSearch);
}
return DSR_ERROR(hr);
}
//
// Searches given domain for the well known
// "RAS and IAS Servers" group and returns
// its ADsPath if found.
//
DWORD
DsrFindRasServersGroup (
IN PWCHAR pszDomain,
OUT PWCHAR* ppszADsPath)
{
HRESULT hr = S_OK;
DWORD dwLen, dwSrchAttribCount, dwErr;
IDirectorySearch * pSearch = NULL;
IADs * pIads = NULL;
PWCHAR pszDomainPath = NULL, pszFilter = NULL;
PWCHAR pszBase, pszPrefix, pszGroupSid = NULL;
ADS_SEARCH_HANDLE hSearch = NULL;
ADS_SEARCH_COLUMN adsColumn;
PWCHAR ppszSrchAttribs[] =
{
(PWCHAR)pszDn,
NULL
};
BOOL bSearchGC = FALSE;
VARIANT var;
LPBYTE pDomainSid = NULL;
BSTR bstrSid = NULL;
do {
// Validate parameters
if (!pszDomain || !ppszADsPath) {
hr = ERROR_INVALID_PARAMETER;
break;
}
// Decide whether to search the GC or the domain
// object
if (bSearchGC) {
pszBase = (PWCHAR)pszGC;
pszPrefix = (PWCHAR)pszGCPrefix;
}
else {
pszBase = (PWCHAR)pszLdap;
pszPrefix = (PWCHAR)pszLdapPrefix;
}
// Allocate the domain path
dwLen = wcslen(pszDomain) + wcslen(pszPrefix) + 1;
dwLen *= sizeof(WCHAR);
pszDomainPath = (PWCHAR) DsrAlloc(dwLen, FALSE);
if (pszDomainPath == NULL) {
hr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
// Format the domain path
wcscpy(pszDomainPath, pszPrefix);
wcscat(pszDomainPath, pszDomain);
// Get a reference to the object to search
// (either domain object or GC)
hr = ADsGetObject (
pszDomainPath,
IID_IDirectorySearch,
(VOID**)&pSearch);
if (FAILED (hr))
break;
// Get IADs reference to domain object
hr = pSearch->QueryInterface(IID_IADs, (VOID**)&pIads);
if (FAILED (hr))
break;
// Get the SID of the domain object
VariantInit(&var);
bstrSid = SysAllocString(pszSid);
if (bstrSid == NULL)
{
hr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
hr = pIads->GetEx(bstrSid, &var);
if (FAILED (hr))
{
break;
}
dwErr = DsrSidInit(&var, &pDomainSid);
if (dwErr != NO_ERROR) {
hr = dwErr;
break;
}
VariantClear(&var);
// Prepare the ascii version of the "RAS and IAS Servers" SID
// for use in querying the DC
hr = DsrSidInitAscii(
pDomainSid,
DOMAIN_ALIAS_RID_RAS_SERVERS,
&pszGroupSid);
if (FAILED (hr))
break;
DsrTraceEx(0, "GroupSid = %ls", pszGroupSid);
// Prepare the search filter
//
dwLen = (wcslen(pszGroupFilterFmt) + wcslen(pszGroupSid) + 1);
dwLen *= sizeof(WCHAR);
pszFilter = (PWCHAR) DsrAlloc(dwLen, FALSE);
if (pszFilter == NULL) {
hr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
wsprintfW(pszFilter, pszGroupFilterFmt, pszGroupSid);
// Count the number of attributes we're searching
// for
if (ppszSrchAttribs == NULL)
dwSrchAttribCount = (DWORD)-1;
else
{
for (dwSrchAttribCount = 0;
ppszSrchAttribs[dwSrchAttribCount];
dwSrchAttribCount++);
}
// Search the DS
hr = pSearch->ExecuteSearch(
pszFilter,
ppszSrchAttribs,
dwSrchAttribCount,
&hSearch);
if (FAILED (hr))
break;
// Get the first result
hr = pSearch->GetNextRow(hSearch);
if (hr == S_ADS_NOMORE_ROWS) {
hr = ERROR_NOT_FOUND;
break;
}
// Get the attribute we're interested in
hr = pSearch->GetColumn(hSearch, (PWCHAR)pszDn, &adsColumn);
if (SUCCEEDED (hr))
{
dwLen = wcslen(adsColumn.pADsValues[0].PrintableString) +
wcslen(pszLdapPrefix) +
1;
dwLen *= sizeof(WCHAR);
*ppszADsPath = (PWCHAR) DsrAlloc(dwLen, FALSE);
if (*ppszADsPath == NULL)
{
pSearch->FreeColumn(&adsColumn);
hr = ERROR_NOT_ENOUGH_MEMORY;
break;
}
wsprintfW(
*ppszADsPath,
L"%s%s",
pszLdapPrefix,
adsColumn.pADsValues[0].PrintableString);
pSearch->FreeColumn(&adsColumn);
hr = NO_ERROR;
}
} while (FALSE);
// Cleanup
{
if (hSearch)
pSearch->CloseSearchHandle(hSearch);
DSR_FREE (pszDomainPath);
DSR_FREE (pszFilter);
DSR_FREE (pDomainSid);
DSR_FREE (pszGroupSid);
DSR_RELEASE (pSearch);
DSR_RELEASE (pIads);
if (bstrSid)
SysFreeString(bstrSid);
}
return DSR_ERROR(hr);
}
//
// Adds or removes a given object from a given group.
//
DWORD
DsrGroupAddRemoveMember(
IN PWCHAR pszGroupDN,
IN PWCHAR pszNewMemberDN,
IN BOOL bAdd)
{
VARIANT_BOOL vbIsMember = VARIANT_FALSE;
IADsGroup* pGroup = NULL;
HRESULT hr = S_OK;
DsrTraceEx (
0,
"DsrGroupAddRemoveMember entered for [%S] [%S]",
pszGroupDN,
pszNewMemberDN);
do
{
// Get a reference to the group
hr = ADsGetObject (pszGroupDN, IID_IADsGroup, (VOID**)&pGroup);
if (FAILED (hr))
{
DsrTraceEx(
hr,
"DsrGroupAddRemoveMember: %x from ADsGetObject(%S)",
hr,
pszGroupDN);
break;
}
// Find out if the given new member is in the group
hr = pGroup->IsMember (pszNewMemberDN, &vbIsMember);
if (FAILED (hr))
{
DsrTraceEx (
hr,
"DsrGroupAddRemoveMember: %x from IsMember\n",
hr);
break;
}
// Add the object to the group and flush the cache
if (bAdd)
{
if (vbIsMember == VARIANT_FALSE)
{
hr = pGroup->Add (pszNewMemberDN);
}
}
else
{
if (vbIsMember == VARIANT_TRUE)
{
hr = pGroup->Remove (pszNewMemberDN);
}
}
// If the new member is already in the group, the error code
// is ERROR_DS_CONSTRAINT_VIOLATION. I suspect this may change.
//
if (hr == ERROR_DS_CONSTRAINT_VIOLATION)
{
hr = ERROR_ALREADY_EXISTS;
break;
}
if (FAILED (hr))
{
DsrTraceEx(
hr,
"DsrGroupAddRemoveMember: %x from Add/Remove",
hr);
break;
}
} while (FALSE);
// Cleanup
{
DSR_RELEASE(pGroup);
}
return DSR_ERROR(hr);
}
//
// Returns whether the given object is a member of
// the given group.
//
DWORD
DsrGroupIsMember(
IN PWCHAR pszGroupDN,
IN PWCHAR pszObjectDN,
OUT PBOOL pbIsMember)
{
IADsGroup * pGroup = NULL;
HRESULT hr = S_OK;
VARIANT_BOOL vbIsMember = VARIANT_FALSE;
DsrTraceEx (
0,
"DsrGroupIsMember: entered [%S] [%S].",
pszGroupDN,
pszObjectDN);
do
{
// Get a reference to the group
hr = ADsGetObject (pszGroupDN, IID_IADsGroup, (VOID**)&pGroup);
if (FAILED (hr))
{
DsrTraceEx (
hr,
"DsrGroupIsMember: %x returned when opening %S",
hr,
pszGroupDN);
*pbIsMember = FALSE;
hr = NO_ERROR;
break;
}
// Find out if the object is a member
hr = pGroup->IsMember (pszObjectDN, &vbIsMember);
if (FAILED (hr))
{
DsrTraceEx (hr, "DsrGroupIsMember: %x from IsMember\n", hr);
break;
}
*pbIsMember = (vbIsMember == VARIANT_TRUE) ? TRUE : FALSE;
} while (FALSE);
// Cleanup
{
DSR_RELEASE(pGroup);
}
return DSR_ERROR(hr);
}
//
// Applies the aces in the given access settings to the
// appropriate domain.
//
HRESULT
DsrAceAppAdd(
IN PWCHAR pszDC,
IN DSR_ACE_APPLICATION* pAces,
IN DWORD dwCount)
{
HRESULT hr = S_OK;
DSR_ACE_APPLICATION* pAceApp = NULL;
DWORD i;
// Output the aces that we'll set
//
DsrTraceEx(0, "Adding %d aces...", dwCount);
do
{
// Add the ACES to the domain objects
//
for (i = 0, pAceApp = pAces; i < dwCount; i++, pAceApp++)
{
hr = DsrAceAdd(
pszDC,
pAceApp->pObject,
&(pAceApp->Ace));
DSR_BREAK_ON_FAILED_HR( hr );
}
DSR_BREAK_ON_FAILED_HR( hr );
// Commit the ACE's to the domain objects.
//
for (i = 0, pAceApp = pAces; i < dwCount; i++, pAceApp++)
{
hr = pAceApp->pObject->SetInfo();
DSR_BREAK_ON_FAILED_HR( hr );
}
DSR_BREAK_ON_FAILED_HR( hr );
} while (FALSE);
// Cleanup
{
}
return hr;
}
//
// Releases the resources held by an ace application
//
HRESULT
DsrAceAppCleanup(
IN DSR_ACE_APPLICATION* pAces,
IN DWORD dwCount)
{
DSR_ACE_APPLICATION* pAceApp = NULL;
DWORD i;
if (pAces)
{
for (i = 0, pAceApp = pAces; i < dwCount; i++, pAceApp++)
{
DSR_RELEASE(pAceApp->pObject);
DsrAceDescClear(&(pAceApp->Ace));
}
DSR_FREE(pAces);
}
return NO_ERROR;
}
//
// Generates a list of ace applications based on a list
// of ace application descriptions
//
HRESULT
DsrAceAppFromAppDesc(
IN DSR_ACE_APPLICATION_DESC* pDesc,
IN DWORD dwCount,
IN IADsContainer* pContainer,
IN IADs* pDefault,
OUT DSR_ACE_APPLICATION** ppAceApp,
OUT LPDWORD lpdwCount)
{
DSR_ACE_APPLICATION* pAceApp = NULL, *pCurApp = NULL;
DSR_ACE_APPLICATION_DESC* pAceAppDesc = NULL;
IDispatch* pDispatch = NULL;
HRESULT hr = S_OK;
DWORD i;
do
{
// Allocate and zero the ACE list
//
pAceApp = (DSR_ACE_APPLICATION*)
DsrAlloc(sizeof(DSR_ACE_APPLICATION) * dwCount, TRUE);
if (pAceApp == NULL)
{
DSR_BREAK_ON_FAILED_HR(hr = E_OUTOFMEMORY);
}
// Set up the ACE applications
//
for (i = 0, pAceAppDesc = pDesc, pCurApp = pAceApp;
i < dwCount;
i++, pAceAppDesc++, pCurApp++)
{
// Get the desired object in the DS
//
if (pAceAppDesc->pszObjectCN)
{
hr = pContainer->GetObject(
pAceAppDesc->pszObjectClass,
pAceAppDesc->pszObjectCN,
&pDispatch);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pDispatch->QueryInterface(
IID_IADs,
(VOID**)&(pCurApp->pObject));
DSR_BREAK_ON_FAILED_HR( hr );
pDispatch->Release();
pDispatch = NULL;
}
else
{
pCurApp->pObject = pDefault;
pCurApp->pObject->AddRef();
}
// Copy over the ACE information
hr = DsrAceDescCopy(
&(pCurApp->Ace),
&(pAceAppDesc->Ace));
DSR_BREAK_ON_FAILED_HR( hr );
}
DSR_BREAK_ON_FAILED_HR( hr );
// Assign the return values
*ppAceApp = pAceApp;
*lpdwCount = dwCount;
} while (FALSE);
// Cleanup
{
if (FAILED(hr))
{
DsrAceAppCleanup(pAceApp, i);
}
}
return hr;
}
//
// Discovers whether a set of aces is present in the given
// domain.
//
HRESULT
DsrAceAppQueryPresence(
IN PWCHAR pszDC,
IN DSR_ACE_APPLICATION* pAces,
IN DWORD dwCount,
OUT PBOOL pbPresent)
{
DSR_ACE_APPLICATION* pAceApp = NULL;
IADsSecurityDescriptor* pSD = NULL;
IADsAccessControlList* pAcl = NULL;
IDispatch* pAce = NULL;
VARIANT varSD;
HRESULT hr = S_OK;
BOOL bEnabled = FALSE, bOk = TRUE;
DWORD i;
do
{
// Initialize
*pbPresent = FALSE;
VariantInit(&varSD);
// Find out if the ACES are set
//
for (i = 0, pAceApp = pAces; i < dwCount; i++, pAceApp++)
{
hr = DsrAceFind(
pszDC,
pAceApp->pObject,
&(pAceApp->Ace),
&varSD,
&pSD,
&pAcl,
&pAce);
DSR_BREAK_ON_FAILED_HR( hr );
// We're enabled so long as we don't find
// a missing ACE
//
bOk = (pAce != NULL);
// Cleanup
//
DSR_RELEASE( pAce );
DSR_RELEASE( pAcl );
DSR_RELEASE( pSD );
VariantClear(&varSD);
pAce = NULL;
pAcl = NULL;
pSD = NULL;
// Break if we find out we're not enabled
//
if (bOk == FALSE)
{
break;
}
}
DSR_BREAK_ON_FAILED_HR( hr );
*pbPresent = bOk;
} while (FALSE);
// Cleanup
{
}
return hr;
}
//
// Applies the aces in the given access settings to the
// appropriate domain.
//
HRESULT
DsrAceAppRemove(
IN PWCHAR pszDC,
IN DSR_ACE_APPLICATION* pAces,
IN DWORD dwCount)
{
HRESULT hr = S_OK;
DSR_ACE_APPLICATION* pAceApp = NULL;
DWORD i;
// Output the aces that we'll set
//
DsrTraceEx(0, "Removing %d aces...", dwCount);
do
{
// Add/Del the ACES to the domain objects
//
for (i = 0, pAceApp = pAces; i < dwCount; i++, pAceApp++)
{
hr = DsrAceRemove(
pszDC,
pAceApp->pObject,
&(pAceApp->Ace));
DSR_BREAK_ON_FAILED_HR( hr );
}
DSR_BREAK_ON_FAILED_HR( hr );
// Commit the ACE's to the domain objects.
//
for (i = 0, pAceApp = pAces; i < dwCount; i++, pAceApp++)
{
hr = pAceApp->pObject->SetInfo();
DSR_BREAK_ON_FAILED_HR( hr );
}
DSR_BREAK_ON_FAILED_HR( hr );
} while (FALSE);
// Cleanup
{
}
return hr;
}
//
// Clear the dsr ace parameters
//
DWORD
DsrAceDescClear(
IN DSR_ACE_DESCRIPTOR* pParams)
{
if (pParams)
{
if (pParams->bstrTrustee)
{
SysFreeString(pParams->bstrTrustee);
}
if (pParams->bstrObjectType)
{
SysFreeString(pParams->bstrObjectType);
}
if (pParams->bstrInheritedObjectType)
{
SysFreeString(pParams->bstrInheritedObjectType);
}
ZeroMemory(pParams, sizeof(DSR_ACE_DESCRIPTOR));
}
return NO_ERROR;
}
//
// Returns 0 if ACE descriptors are describing the same ACE.
// FALSE, otherwise.
//
HRESULT
DsrAceDescCompare(
IN DSR_ACE_DESCRIPTOR* pAce1,
IN DSR_ACE_DESCRIPTOR* pAce2)
{
DWORD dw1, dw2;
// Compare the non-string fields so that we can rule things
// out w/o string compares if possible
//
if (
(pAce1->dwAccessMask != pAce2->dwAccessMask) ||
(pAce1->dwAceFlags != pAce2->dwAceFlags) ||
(pAce1->dwAceType != pAce2->dwAceType) ||
(pAce1->dwFlags != pAce2->dwFlags)
)
{
return 1;
}
// Compare the strings
//
if ((DsrStrCompare(pAce1->bstrTrustee, pAce2->bstrTrustee)) ||
(DsrStrCompare(pAce1->bstrObjectType, pAce2->bstrObjectType)) ||
(DsrStrCompare(pAce1->bstrInheritedObjectType,
pAce2->bstrInheritedObjectType))
)
{
return 1;
}
// Return success
//
return 0;
}
//
// Copy over the ACE information
//
HRESULT
DsrAceDescCopy(
OUT DSR_ACE_DESCRIPTOR* pDst,
IN DSR_ACE_DESCRIPTOR* pSrc)
{
HRESULT hr = S_OK;
do
{
// Initialize the ACE parameters
*pDst = *pSrc;
if (pSrc->bstrTrustee)
{
pDst->bstrTrustee =
SysAllocString(pSrc->bstrTrustee);
if (pDst->bstrTrustee == NULL)
{
DSR_BREAK_ON_FAILED_HR(hr = E_OUTOFMEMORY);
}
}
if (pSrc->bstrObjectType)
{
pDst->bstrObjectType =
SysAllocString(pSrc->bstrObjectType);
if (pDst->bstrObjectType == NULL)
{
DSR_BREAK_ON_FAILED_HR(hr = E_OUTOFMEMORY);
}
}
if (pSrc->bstrInheritedObjectType)
{
pDst->bstrInheritedObjectType =
SysAllocString(pSrc->bstrInheritedObjectType);
if (pDst->bstrInheritedObjectType == NULL)
{
DSR_BREAK_ON_FAILED_HR(hr = E_OUTOFMEMORY);
}
}
} while (FALSE);
// Cleanup
{
if (FAILED( hr ))
{
if (pDst->bstrTrustee)
{
SysFreeString(pDst->bstrTrustee);
}
if (pDst->bstrObjectType)
{
SysFreeString(pDst->bstrObjectType);
}
if (pDst->bstrInheritedObjectType)
{
SysFreeString(pDst->bstrInheritedObjectType);
}
}
}
return hr;
}
//
// Populates the given ACE descriptor with the values from
// the given ACE.
//
HRESULT
DsrAceDescFromIadsAce(
IN PWCHAR pszDC,
IN IADsAccessControlEntry* pAce,
IN DSR_ACE_DESCRIPTOR* pAceParams)
{
HRESULT hr = S_OK;
BSTR bstrTrustee = NULL;
WCHAR pszSid[1024], pszDomain[1024];
BYTE pbSid[1024];
DWORD dwSidSize, dwDomainSize;
BOOL bOk;
SID_NAME_USE SidNameUse;
do
{
hr = pAce->get_AccessMask(&(pAceParams->dwAccessMask));
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->get_AceType(&(pAceParams->dwAceType));
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->get_AceFlags(&(pAceParams->dwAceFlags));
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->get_Flags(&(pAceParams->dwFlags));
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->get_ObjectType(&(pAceParams->bstrObjectType));
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->get_InheritedObjectType(
&(pAceParams->bstrInheritedObjectType));
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->get_Trustee(&bstrTrustee);
DSR_BREAK_ON_FAILED_HR( hr );
// Get the SID of the trustee
//
dwSidSize = sizeof(pbSid);
dwDomainSize = sizeof(pszDomain) / sizeof(WCHAR);
bOk = LookupAccountName(
pszDC,
bstrTrustee,
(PSID)pbSid,
&dwSidSize,
pszDomain,
&dwDomainSize,
&SidNameUse);
if (bOk == FALSE)
{
hr = GetLastError();
break;
}
// Convert the sid to a string
//
hr = DsrStrFromSID((PSID)pbSid, pszSid, sizeof(pszSid));
if (hr != NO_ERROR)
{
break;
}
// Create the trustee accordingly
//
pAceParams->bstrTrustee = SysAllocString(pszSid);
if (pAceParams->bstrTrustee == NULL)
{
hr = E_OUTOFMEMORY;
break;
}
} while (FALSE);
// Cleanup
{
if (bstrTrustee)
{
SysFreeString(bstrTrustee);
}
if (FAILED(hr))
{
DsrAceDescClear(pAceParams);
}
}
return hr;
}
//
// Initialize an ace descriptor from a W2K Ace
//
HRESULT
DsrAceDescFromW2KAce(
IN PWCHAR pszDC,
IN PVOID pvAce,
OUT DSR_ACE_DESCRIPTOR* pAceDesc)
{
PACCESS_ALLOWED_ACE pAaAce = NULL;
PACCESS_DENIED_ACE pAdAce = NULL;
PACCESS_ALLOWED_OBJECT_ACE pAaoAce = NULL;
PACCESS_DENIED_OBJECT_ACE pAdoAce = NULL;
PSID pSID = NULL;
DWORD dwFlags = 0, dwNameSize, dwDomainSize, dwAccessMask;
BYTE bAceType, bAceFlags;
SID_NAME_USE SidNameUse;
WCHAR pszGuid[64], pszName[512], pszDomain[512], pszTrustee[1024];
HRESULT hr = S_OK;
GUID* pgObj = NULL, *pgInhObj = NULL;
BOOL bOk = TRUE;
// Read in the ace values
//
bAceType = ((ACE_HEADER *)pvAce)->AceType;
bAceFlags = ((ACE_HEADER *)pvAce)->AceFlags;
switch (bAceType)
{
case ACCESS_ALLOWED_ACE_TYPE:
pAaAce = (PACCESS_ALLOWED_ACE)pvAce;
dwAccessMask = pAaAce->Mask;
pSID = (PSID)&(pAaAce->SidStart);
break;
case ACCESS_DENIED_ACE_TYPE:
pAdAce = (PACCESS_DENIED_ACE)pvAce;
dwAccessMask = pAdAce->Mask;
pSID = (PSID)&(pAdAce->SidStart);
break;
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
pAaoAce = (PACCESS_ALLOWED_OBJECT_ACE)pvAce;
dwAccessMask = pAaoAce->Mask;
dwFlags = pAaoAce->Flags;
// Determine the location of the guids
// and SIDs. They are arranged such that they
// take up as little memory as possible
//
if (dwFlags & ACE_OBJECT_TYPE_PRESENT)
{
pgObj = (GUID*)&(pAaoAce->ObjectType);
pSID = (PSID)&(pAaoAce->InheritedObjectType);
if (dwFlags & ACE_INHERITED_OBJECT_TYPE_PRESENT)
{
pgInhObj = (GUID*)&(pAaoAce->InheritedObjectType);
pSID = (PSID)&(pAaoAce->SidStart);
}
}
else
{
pSID = (PSID)&(pAaoAce->ObjectType);
if (dwFlags & ACE_INHERITED_OBJECT_TYPE_PRESENT)
{
pgInhObj = (GUID*)&(pAaoAce->ObjectType);
pSID = (PSID)&(pAaoAce->InheritedObjectType);
}
}
break;
case ACCESS_DENIED_OBJECT_ACE_TYPE:
pAdoAce = (PACCESS_DENIED_OBJECT_ACE)pvAce;
dwAccessMask = pAdoAce->Mask;
dwFlags = pAdoAce->Flags;
// Determine the location of the guids
// and SIDs. They are arranged such that they
// take up as little memory as possible
//
if (dwFlags & ACE_OBJECT_TYPE_PRESENT)
{
pgObj = (GUID*)&(pAdoAce->ObjectType);
pSID = (PSID)&(pAdoAce->InheritedObjectType);
if (dwFlags & ACE_INHERITED_OBJECT_TYPE_PRESENT)
{
pgInhObj = (GUID*)&(pAdoAce->InheritedObjectType);
pSID = (PSID)&(pAdoAce->SidStart);
}
}
else
{
pSID = (PSID)&(pAdoAce->ObjectType);
if (dwFlags & ACE_INHERITED_OBJECT_TYPE_PRESENT)
{
pgInhObj = (GUID*)&(pAdoAce->ObjectType);
pSID = (PSID)&(pAdoAce->InheritedObjectType);
}
}
break;
default:
DsrTraceEx(0, "Unknown ACE TYPE %x", bAceType);
bOk = FALSE;
break;
}
if (bOk == FALSE)
{
return E_FAIL;
}
// Lookup the account name of the sid
//
hr = DsrStrFromSID(pSID, pszTrustee, sizeof(pszTrustee));
if (hr != NO_ERROR)
{
return HRESULT_FROM_WIN32(hr);
}
// Fill in the ACE fields
pAceDesc->dwAceType = (LONG)bAceType;
pAceDesc->dwAceFlags = (LONG)bAceFlags;
pAceDesc->dwAccessMask = (LONG)dwAccessMask;
pAceDesc->dwFlags = (LONG)dwFlags;
pAceDesc->bstrTrustee = SysAllocString(pszTrustee);
if (pgObj)
{
StringFromGUID2(
*pgObj,
pszGuid,
sizeof(pszGuid)/sizeof(WCHAR));
pAceDesc->bstrObjectType = SysAllocString(pszGuid);
}
if (pgInhObj)
{
StringFromGUID2(
*pgInhObj,
pszGuid,
sizeof(pszGuid)/sizeof(WCHAR));
pAceDesc->bstrInheritedObjectType = SysAllocString(pszGuid);
}
return hr;
}
//
// Generates a list of ace descriptors based on a stringized
// SD
//
HRESULT
DsrAceDescListFromString(
IN PWCHAR pszDC,
IN PWCHAR pszSD,
OUT DSR_ACE_DESCRIPTOR** ppAceList,
OUT LPDWORD lpdwAceCount)
{
BOOL bOk = TRUE, bPresent = FALSE, bDefaulted = FALSE;
DSR_ACE_DESCRIPTOR* pAceList = NULL, *pCurAce = NULL;
PSECURITY_DESCRIPTOR pSD = NULL;
PVOID pvAce = NULL;
ULONG ulSize = 0;
PACL pDacl = NULL;
HRESULT hr = S_OK;
DWORD i;
do
{
// First, convert the stringized security descriptor to a
// plain old vanilla security descriptor. ADSI doesn't
// support this for W2K, so we have to do it with SDDL
// api's
//
bOk = ConvertStringSecurityDescriptorToSecurityDescriptorW(
pszSD,
SDDL_REVISION_1,
&pSD,
&ulSize);
if (bOk == FALSE)
{
hr = E_FAIL;
break;
}
// Get the DACL from the SD.
//
bOk = GetSecurityDescriptorDacl(pSD, &bPresent, &pDacl, &bDefaulted);
if (bOk == FALSE)
{
hr = E_FAIL;
break;
}
// If there are no aces, then there's nothing to do
//
if (pDacl->AceCount == 0)
{
break;
}
// Allocate the list that we'll return if everything goes well.
//
pAceList = (DSR_ACE_DESCRIPTOR*)
DsrAlloc(pDacl->AceCount * sizeof(DSR_ACE_DESCRIPTOR), TRUE);
if (pAceList == NULL)
{
hr = E_OUTOFMEMORY;
break;
}
// Initialize the list of aces
//
for (i = 0, pCurAce = pAceList; i < pDacl->AceCount; i++, pCurAce++)
{
// Get a reference to the current
// ACE
//
if (! GetAce(pDacl, i, &pvAce))
{
continue;
}
// Initialize the ACE descriptor accordingly
//
hr = DsrAceDescFromW2KAce(pszDC, pvAce, pCurAce);
DSR_BREAK_ON_FAILED_HR(hr);
//DsrAceDescTrace(pCurAce);
}
DSR_BREAK_ON_FAILED_HR(hr);
// Set the return values. Clear pAceList so it doesn't
// get cleaned up.
//
*ppAceList = pAceList;
*lpdwAceCount = pDacl->AceCount;
pAceList = NULL;
} while (FALSE);
// Cleanup
{
if (pSD)
{
LocalFree(pSD);
}
if (pAceList)
{
for (i = 0; i < pDacl->AceCount; i++)
{
DsrAceDescClear(&(pAceList[i]));
}
DsrFree(pAceList);
}
}
return hr;
}
PWCHAR
DsrAceAttrToString(
IN PWCHAR pszObjectType)
{
if (pszObjectType == NULL)
{
return L"All";
}
else if (lstrcmpi(pszObjectType, pszGuidUserParms) == 0)
{
return L"UserParms (BF967A6D-0DE6-11D0-A285-00AA003049E2)";
}
else if (lstrcmpi(pszObjectType, pszGuidRasPropSet1) == 0)
{
return L"Ras user properties (037088F8-0AE1-11D2-B422-00A0C968F939)";
}
else if (lstrcmpi(pszObjectType, pszGuidRasPropSet2) == 0)
{
return L"Misc user properties (4C164200-20C0-11D0-A768-00AA006E0529)";
}
else if (lstrcmpi(pszObjectType, pszGuidLogonHours) == 0)
{
return L"Logon-Hours (BF9679AB-0DE6-11D0-A285-00AA003049E2)";
}
else if (lstrcmpi(pszObjectType, pszGuidSamAccountName) == 0)
{
return L"Sam account name (3E0ABFD0-126A-11D0-A060-00AA006C33ED)";
}
return pszObjectType;
}
PWCHAR
DsrAceApplyToString(
IN PWCHAR pszApply)
{
if (pszApply == NULL)
{
return L"This object";
}
else if (lstrcmpi(pszApply, pszGuidUserClass) == 0)
{
return L"User objects (BF967ABA-0DE6-11D0-A285-00aa003049E2)";
}
return pszApply;
}
PWCHAR
DsrAceMaskToString(
IN DWORD dwType,
IN DWORD dwMask,
IN PWCHAR pszBuf)
{
WCHAR pszTemp[64];
*pszBuf = L'\0';
switch (dwType)
{
case ADS_ACETYPE_ACCESS_ALLOWED:
wcscpy(pszBuf, L"Allow: ");
break;
case ADS_ACETYPE_ACCESS_DENIED:
wcscpy(pszBuf, L"Deny: ");
break;
case ADS_ACETYPE_SYSTEM_AUDIT:
wcscpy(pszBuf, L"Audit: ");
break;
case ADS_ACETYPE_ACCESS_ALLOWED_OBJECT:
wcscpy(pszBuf, L"Allow obj: ");
break;
case ADS_ACETYPE_ACCESS_DENIED_OBJECT:
wcscpy(pszBuf, L"Deny obj: ");
break;
case ADS_ACETYPE_SYSTEM_AUDIT_OBJECT:
wcscpy(pszBuf, L"Audit obj: ");
break;
}
wsprintfW(pszTemp, L"(%x): ", dwMask);
wcscat(pszBuf, pszTemp);
if (dwMask == DSR_ADS_RIGHT_GENERIC_READ)
{
wcscat(pszBuf, L"Generic read");
}
else if (dwMask == 0xffffffff)
{
wcscat(pszBuf, L"Full control");
}
else
{
if (dwMask & ADS_RIGHT_READ_CONTROL)
wcscat(pszBuf, L"R ctrl, ");
if (dwMask & ADS_RIGHT_WRITE_DAC)
wcscat(pszBuf, L"R/W dac, ");
if (dwMask & ADS_RIGHT_WRITE_OWNER)
wcscat(pszBuf, L"W own, ");
if (dwMask & ADS_RIGHT_SYNCHRONIZE)
wcscat(pszBuf, L"Sync, ");
if (dwMask & ADS_RIGHT_ACCESS_SYSTEM_SECURITY)
wcscat(pszBuf, L"Sys, ");
if (dwMask & ADS_RIGHT_GENERIC_READ)
wcscat(pszBuf, L"R (gen), ");
if (dwMask & ADS_RIGHT_GENERIC_WRITE)
wcscat(pszBuf, L"W (gen), ");
if (dwMask & ADS_RIGHT_GENERIC_EXECUTE)
wcscat(pszBuf, L"Ex, ");
if (dwMask & ADS_RIGHT_GENERIC_ALL)
wcscat(pszBuf, L"All, ");
if (dwMask & ADS_RIGHT_DS_CREATE_CHILD)
wcscat(pszBuf, L"Cr cld, ");
if (dwMask & ADS_RIGHT_DS_DELETE_CHILD)
wcscat(pszBuf, L"Del cld, ");
if (dwMask & ADS_RIGHT_ACTRL_DS_LIST)
wcscat(pszBuf, L"List, ");
if (dwMask & ADS_RIGHT_DS_SELF)
wcscat(pszBuf, L"Self, ");
if (dwMask & ADS_RIGHT_DS_READ_PROP)
wcscat(pszBuf, L"R prop, ");
if (dwMask & ADS_RIGHT_DS_WRITE_PROP)
wcscat(pszBuf, L"W prop, ");
if (dwMask & ADS_RIGHT_DS_DELETE_TREE)
wcscat(pszBuf, L"Del tree, ");
if (dwMask & ADS_RIGHT_DS_LIST_OBJECT)
wcscat(pszBuf, L"List obj, ");
if (dwMask & ADS_RIGHT_DS_CONTROL_ACCESS)
wcscat(pszBuf, L"Ctrl acc, ");
}
return pszBuf;
}
PWCHAR
DsrAceFlagsToString(
IN DWORD dwAceFlags,
IN PWCHAR pszBuf)
{
WCHAR pszTemp[64];
*pszBuf = L'\0';
switch (dwAceFlags)
{
case 0:
wcscpy(pszBuf, L"This object only");
break;
case ADS_ACEFLAG_INHERIT_ACE:
wcscpy(pszBuf, L"This object and children");
break;
case ADS_ACEFLAG_NO_PROPAGATE_INHERIT_ACE:
wcscpy(pszBuf, L"No-prop inherit");
break;
case ADS_ACEFLAG_INHERIT_ONLY_ACE:
wcscpy(pszBuf, L"Inherit-only");
break;
case ADS_ACEFLAG_INHERITED_ACE:
wcscpy(pszBuf, L"Inherited");
break;
case ADS_ACEFLAG_VALID_INHERIT_FLAGS:
wcscpy(pszBuf, L"Valid inherit flags");
break;
case ADS_ACEFLAG_SUCCESSFUL_ACCESS:
wcscpy(pszBuf, L"Successful access");
break;
case ADS_ACEFLAG_FAILED_ACCESS:
wcscpy(pszBuf, L"Failed access");
break;
}
wsprintfW(pszTemp, L" (%x)", dwAceFlags);
wcscat(pszBuf, pszTemp);
return pszBuf;
}
//
// Traces out the contents of an ACE
//
VOID
DsrAceDescTrace(
IN IADs* pIads,
IN DSR_ACE_DESCRIPTOR* pA)
{
VARIANT var;
BSTR bstrProp = SysAllocString(pszDn);
HRESULT hr = S_OK;
WCHAR pszBuf[1024];
do
{
VariantInit(&var);
if (bstrProp == NULL)
{
hr = E_FAIL;
break;
}
hr = pIads->Get(bstrProp, &var);
DSR_BREAK_ON_FAILED_HR( hr );
DsrTraceEx(0, "%ls", V_BSTR(&var));
DsrTraceEx(0, "%ls",
DsrAceMaskToString(pA->dwAceType, pA->dwAccessMask, pszBuf));
DsrTraceEx(0, "To: %ls", pA->bstrTrustee);
DsrTraceEx(0, "Attribute: %ls",
DsrAceAttrToString(pA->bstrObjectType));
DsrTraceEx(0, "ApplyTo: %ls",
DsrAceApplyToString(pA->bstrInheritedObjectType));
DsrTraceEx(0, "Inheritance: %ls",
DsrAceFlagsToString(pA->dwAceFlags, pszBuf));
DsrTraceEx(0, "Flags: %x", pA->dwFlags);
DsrTraceEx(0, " ");
} while (FALSE);
// Cleanup
//
{
SysFreeString(bstrProp);
VariantClear(&var);
}
if (FAILED(hr))
{
DsrTraceEx(
0,
"{ %-8x %-2x %-2x %-2x %-40ls %ls %ls }",
pA->dwAccessMask,
pA->dwAceType,
pA->dwAceFlags,
pA->dwFlags,
pA->bstrTrustee,
pA->bstrObjectType,
pA->bstrInheritedObjectType);
}
}
//
// Adds the given ace to the given ds object
//
HRESULT
DsrAceAdd(
IN PWCHAR pszDC,
IN IADs* pIads,
IN DSR_ACE_DESCRIPTOR * pAceParams)
{
IADsSecurityDescriptor* pSD = NULL;
IADsAccessControlList* pAcl = NULL;
IDispatch* pAce = NULL;
IDispatch* pDispatch = NULL;
HRESULT hr = S_OK;
VARIANT var;
// Initialize
VariantInit(&var);
do
{
// Get the security descriptor
//
pIads->Get((PWCHAR)pszSecurityDesc, &var);
DSR_BREAK_ON_FAILED_HR( hr );
// Get the appropriate interface to the sd
//
V_DISPATCH(&var)->QueryInterface(
IID_IADsSecurityDescriptor,
(VOID**)&pSD);
DSR_BREAK_ON_FAILED_HR( hr );
// Get a reference to the discretionary acl
//
hr = pSD->get_DiscretionaryAcl(&pDispatch);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pDispatch->QueryInterface(
IID_IADsAccessControlList,
(VOID**)&pAcl);
DSR_BREAK_ON_FAILED_HR( hr );
// Don't add the ACE if it's already there.
//
hr = DsrAceFindInAcl(
pszDC,
pAcl,
pAceParams,
&pAce);
if (SUCCEEDED(hr) && pAce)
{
hr = S_OK;
break;
}
// Trace out the ACE
DsrAceDescTrace(pIads, pAceParams);
// Create the ACE
hr = DsrAceCreate(pAceParams, &pAce);
DSR_BREAK_ON_FAILED_HR( hr );
// Add the newly created ACE to the ACL
//
hr = pAcl->AddAce(pAce);
DSR_BREAK_ON_FAILED_HR( hr );
// Now commit the result in the ACL
//
hr = pSD->put_DiscretionaryAcl(pDispatch);
DSR_BREAK_ON_FAILED_HR( hr );
// Finally, commit the result in the ds object
//
hr = pIads->Put((PWCHAR)pszSecurityDesc, var);
DSR_BREAK_ON_FAILED_HR( hr );
} while (FALSE);
// Cleanup
{
DSR_RELEASE( pAce );
DSR_RELEASE( pAcl );
DSR_RELEASE( pDispatch );
DSR_RELEASE( pSD );
VariantClear(&var);
}
return DSR_ERROR(hr);
}
//
// Creates a new ACE object from the given parameters
//
HRESULT
DsrAceCreate(
IN DSR_ACE_DESCRIPTOR * pAceParams,
OUT IDispatch** ppAce)
{
IADsAccessControlEntry* pAce = NULL;
IDispatch* pRet = NULL;
HRESULT hr = S_OK;
do
{
// Create the new ACE
//
hr = CoCreateInstance(
CLSID_AccessControlEntry,
NULL,
CLSCTX_INPROC_SERVER,
IID_IADsAccessControlEntry,
(VOID**) &pAce);
DSR_BREAK_ON_FAILED_HR( hr );
// Initialize the values
//
hr = pAce->put_Trustee(pAceParams->bstrTrustee);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->put_AceFlags(pAceParams->dwAceFlags);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->put_Flags(pAceParams->dwFlags);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->put_AceType(pAceParams->dwAceType);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->put_AccessMask(pAceParams->dwAccessMask);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->put_ObjectType(pAceParams->bstrObjectType);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAce->put_InheritedObjectType(
pAceParams->bstrInheritedObjectType);
DSR_BREAK_ON_FAILED_HR( hr );
// Query the return value
//
hr = pAce->QueryInterface(IID_IDispatch, (VOID**)&pRet);
DSR_BREAK_ON_FAILED_HR( hr );
// Assign the return value
*ppAce = pRet;
} while (FALSE);
// Cleanup
{
if (FAILED (hr))
{
DSR_RELEASE(pRet);
}
DSR_RELEASE(pAce);
}
return hr;
}
//
// Finds the given ace in the given acl
//
HRESULT
DsrAceFind(
IN PWCHAR pszDC,
IN IADs* pObject,
IN DSR_ACE_DESCRIPTOR* pAceParams,
OUT VARIANT* pVarSD,
OUT IADsSecurityDescriptor** ppSD,
OUT IADsAccessControlList** ppAcl,
OUT IDispatch** ppAce)
{
IDispatch* pAcl = NULL;
HRESULT hr = S_OK;
do
{
// Get the security descriptor
//
pObject->Get((PWCHAR)pszSecurityDesc, pVarSD);
DSR_BREAK_ON_FAILED_HR( hr );
// Get the appropriate interface to the sd
//
V_DISPATCH(pVarSD)->QueryInterface(
IID_IADsSecurityDescriptor,
(VOID**)ppSD);
DSR_BREAK_ON_FAILED_HR( hr );
// Get a reference to the discretionary acl
//
hr = (*ppSD)->get_DiscretionaryAcl(&pAcl);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pAcl->QueryInterface(
IID_IADsAccessControlList,
(VOID**)ppAcl);
DSR_BREAK_ON_FAILED_HR( hr );
hr = DsrAceFindInAcl(
pszDC,
*ppAcl,
pAceParams,
ppAce);
DSR_BREAK_ON_FAILED_HR(hr);
} while (FALSE);
// Cleanup
{
DSR_RELEASE( pAcl );
if (*ppAce == NULL)
{
VariantClear(pVarSD);
DSR_RELEASE(*ppAcl);
DSR_RELEASE(*ppSD);
*ppAcl = NULL;
*ppSD = NULL;
}
}
return hr;
}
//
// Finds the given ACE in the given ACL
//
HRESULT
DsrAceFindInAcl(
IN PWCHAR pszDC,
IN IADsAccessControlList* pAcl,
IN DSR_ACE_DESCRIPTOR* pAceDesc,
OUT IDispatch** ppAce)
{
DSR_ACE_DESCRIPTOR CurAceParams, *pCurAceDesc = &CurAceParams;
IADsAccessControlEntry* pCurAce = NULL;
HRESULT hr = S_OK;
IUnknown* pUnknown = NULL;
IEnumVARIANT* pEnumVar = NULL;
IDispatch* pRet = NULL;
DWORD dwRetrieved;
VARIANT var;
do
{
// Get an enumerator of the aces
//
hr = pAcl->get__NewEnum(&pUnknown);
DSR_BREAK_ON_FAILED_HR( hr );
// Get the right interface to enumerate the aces
//
hr = pUnknown->QueryInterface(IID_IEnumVARIANT, (VOID**)&pEnumVar);
DSR_BREAK_ON_FAILED_HR( hr );
// Enumerate
//
pEnumVar->Reset();
VariantInit(&var);
ZeroMemory(pCurAceDesc, sizeof(DSR_ACE_DESCRIPTOR));
while ((pEnumVar->Next(1, &var, &dwRetrieved) == S_OK) &&
(dwRetrieved == 1)
)
{
// Get the reference to the ace
//
hr = V_DISPATCH(&var)->QueryInterface(
IID_IADsAccessControlEntry,
(VOID**)&pCurAce);
if (SUCCEEDED (hr))
{
// Read the ACE parameters
//
hr = DsrAceDescFromIadsAce(pszDC, pCurAce, pCurAceDesc);
if (SUCCEEDED (hr))
{
// Assign the ace if we have a match
//
if (DsrAceDescCompare(pCurAceDesc, pAceDesc) == 0)
{
pRet = V_DISPATCH(&var);
}
DsrAceDescClear(pCurAceDesc);
}
pCurAce->Release();
}
if (pRet == NULL)
{
VariantClear(&var);
}
else
{
break;
}
}
// Assign the return value
//
*ppAce = pRet;
} while (FALSE);
// Cleanup
{
DSR_RELEASE( pEnumVar );
DSR_RELEASE( pUnknown );
}
return hr;
}
//
// Removes the given ace from the given ds object
//
HRESULT
DsrAceRemove(
IN PWCHAR pszDC,
IN IADs* pIads,
IN DSR_ACE_DESCRIPTOR * pAceParams)
{
IADsSecurityDescriptor* pSD = NULL;
IADsAccessControlList* pAcl = NULL;
IADsAccessControlEntry* pIadsAce = NULL;
IDispatch* pAce = NULL;
DSR_ACE_DESCRIPTOR CurAceParams;
HRESULT hr = S_OK;
VARIANT varSD;
do
{
VariantInit(&varSD);
hr = DsrAceFind(pszDC, pIads, pAceParams, &varSD, &pSD, &pAcl, &pAce);
DSR_BREAK_ON_FAILED_HR( hr );
if (pAce)
{
// Make sure the ace is the same as we think
//
hr = pAce->QueryInterface(
IID_IADsAccessControlEntry,
(VOID**)&pIadsAce);
if (SUCCEEDED(hr))
{
DsrTraceEx(0, "ACE to be removed!");
DsrAceDescFromIadsAce(pszDC, pIadsAce, &CurAceParams);
DsrAceDescTrace(pIads, &CurAceParams);
DsrAceDescClear(&CurAceParams);
}
else
{
DsrTraceEx(0, "Unable to trace ACE that will be removed!\n");
}
// Remove the ace found if any.
//
// Trace out the ACE
hr = pAcl->RemoveAce(pAce);
DSR_BREAK_ON_FAILED_HR( hr );
// Now commit the result in the ACL
//
hr = pSD->put_DiscretionaryAcl(pAcl);
DSR_BREAK_ON_FAILED_HR( hr );
// Finally, commit the result in the ds object
//
hr = pIads->Put((PWCHAR)pszSecurityDesc, varSD);
DSR_BREAK_ON_FAILED_HR( hr );
}
else
{
DsrTraceEx(0, "DsrAceRemove: unable to match ACE for removal:");
DsrAceDescTrace(pIads, pAceParams);
}
} while (FALSE);
// Cleanup
{
DSR_RELEASE( pAce );
DSR_RELEASE( pIadsAce );
DSR_RELEASE( pAcl );
DSR_RELEASE( pSD );
VariantClear(&varSD);
}
return DSR_ERROR(hr);
}
//
// Cleans up after DsrAccessInfoInit
//
DWORD
DsrAccessInfoCleanup(
IN DSR_DOMAIN_ACCESS_INFO* pInfo)
{
if (pInfo)
{
// Cleanup the name of the DC
//
if (pInfo->pszDC)
{
DsrFree(pInfo->pszDC);
}
// Cleanup the ace applications
//
DsrAceAppCleanup(pInfo->pAcesUser, pInfo->dwAceCountUser);
DsrAceAppCleanup(pInfo->pAcesNt4, pInfo->dwAceCountNt4);
DsrAceAppCleanup(pInfo->pAcesW2k, pInfo->dwAceCountW2k);
// Release the hold on domain objects
//
DSR_RELEASE(pInfo->pUserClass);
DSR_RELEASE(pInfo->pRootDse);
DSR_RELEASE(pInfo->pDomain);
DsrFree(pInfo);
}
return NO_ERROR;
}
//
// Generates aces from the default user SD
//
HRESULT
DsrAccessInfoGenerateUserAces(
IN OUT DSR_DOMAIN_ACCESS_INFO* pInfo)
{
DSR_ACE_DESCRIPTOR* pAceSrc = NULL, *pAceList = NULL;
DSR_ACE_APPLICATION* pAceApp = NULL;
DWORD i, dwAceCount = 0;
HRESULT hr = S_OK;
VARIANT var;
VariantInit(&var);
do
{
// Read in the default user SD
//
hr = pInfo->pUserClass->Get((PWCHAR)pszDefSecurityDesc, &var);
DSR_BREAK_ON_FAILED_HR(hr);
// Generate a list of ACE descriptors based on the
// default user SD.
//
hr = DsrAceDescListFromString(
pInfo->pszDC,
V_BSTR(&var),
&pAceList,
&dwAceCount);
DSR_BREAK_ON_FAILED_HR(hr);
// Initialize a new array of ace applications big enough
// to hold the hard coded ones plus the ones we just read
// from the default SD of the user class.
//
pInfo->pAcesUser = (DSR_ACE_APPLICATION*)
DsrAlloc((sizeof(DSR_ACE_APPLICATION) * dwAceCount), TRUE);
if (pInfo->pAcesUser == NULL)
{
hr = E_OUTOFMEMORY;
break;
}
// Add the ACEs we read from the default user SD
//
pAceApp = pInfo->pAcesUser;
for (i = 0, pAceSrc = pAceList;
i < dwAceCount;
i++, pAceSrc++, pAceApp++)
{
pAceApp->pObject = pInfo->pDomain;
pAceApp->pObject->AddRef();
CopyMemory(
&(pAceApp->Ace),
pAceSrc,
sizeof(DSR_ACE_DESCRIPTOR));
pInfo->dwAceCountUser++;
// As we append the aces, we need to modify them
// so that they apply only to user objects in the
// domain.
pAceApp->Ace.bstrInheritedObjectType =
SysAllocString(pszGuidUserClass);
pAceApp->Ace.dwAceFlags = DSR_ADS_ACE_INHERITED;
pAceApp->Ace.dwFlags |= ADS_FLAG_INHERITED_OBJECT_TYPE_PRESENT;
if (pAceApp->Ace.dwAceType == ADS_ACETYPE_ACCESS_ALLOWED)
{
pAceApp->Ace.dwAceType =
ADS_ACETYPE_ACCESS_ALLOWED_OBJECT;
}
else if (pAceApp->Ace.dwAceType == ADS_ACETYPE_ACCESS_DENIED)
{
pAceApp->Ace.dwAceType =
ADS_ACETYPE_ACCESS_DENIED_OBJECT;
}
}
} while (FALSE);
// Cleanup
{
DSR_FREE(pAceList);
VariantClear(&var);
}
return hr;
}
//
// Generates the information needed to enable nt4 ras
// servers in a domain
//
HRESULT
DsrAccessInfoInit(
IN PWCHAR pszDomain,
OUT DSR_DOMAIN_ACCESS_INFO** ppInfo)
{
DSR_DOMAIN_ACCESS_INFO* pInfo = NULL;
IADsContainer* pDomContainer = NULL, *pSchemaContainer = NULL;
IADs* pDomain = NULL;
IDispatch* pDispatch = NULL;
PDOMAIN_CONTROLLER_INFO pDomainInfo = NULL;
HRESULT hr = S_OK;
do
{
// Allocate and zero the return value
//
pInfo = (DSR_DOMAIN_ACCESS_INFO*)
DsrAlloc(sizeof(DSR_DOMAIN_ACCESS_INFO), TRUE);
if (pInfo == NULL)
{
DSR_BREAK_ON_FAILED_HR(hr = E_OUTOFMEMORY);
}
// Get the name of a DC to query when needed
//
hr = DsGetDcNameW(
NULL,
pszDomain,
NULL,
NULL,
DS_DIRECTORY_SERVICE_REQUIRED,
&pDomainInfo);
if (hr != NO_ERROR)
{
hr = HRESULT_FROM_WIN32(hr);
break;
}
// Copy the string
//
pInfo->pszDC = (PWCHAR)
DsrAlloc(
(wcslen(pDomainInfo->DomainControllerName) + 1) *
sizeof(WCHAR),
FALSE);
if (pInfo->pszDC == NULL)
{
hr = E_OUTOFMEMORY;
break;
}
wcscpy(pInfo->pszDC, pDomainInfo->DomainControllerName);
// Get the well known domain containers
//
hr = DsrDomainGetContainers(
pszDomain,
&(pInfo->pRootDse),
&pDomContainer,
&pSchemaContainer);
DSR_BREAK_ON_FAILED_HR( hr );
// Get the interface to the domain object
//
hr = pDomContainer->QueryInterface(
IID_IADs,
(VOID**)&pDomain);
DSR_BREAK_ON_FAILED_HR( hr );
pInfo->pDomain = pDomain;
pInfo->pDomain->AddRef();
// Get the reference to the user class in the
// schema
hr = pSchemaContainer->GetObject(
(PWCHAR)pszUserClass,
(PWCHAR)pszUserCN,
&pDispatch);
DSR_BREAK_ON_FAILED_HR( hr );
hr = pDispatch->QueryInterface(
IID_IADs,
(VOID**)&(pInfo->pUserClass));
DSR_BREAK_ON_FAILED_HR( hr );
// Generate the ACEs from the default user SD
//
hr = DsrAccessInfoGenerateUserAces(pInfo);
DSR_BREAK_ON_FAILED_HR( hr );
// Create ace applications for all of the nt4
// aces
hr = DsrAceAppFromAppDesc(
g_pAcesNt4,
sizeof(g_pAcesNt4) / sizeof(*g_pAcesNt4),
pDomContainer,
pDomain,
&(pInfo->pAcesNt4),
&(pInfo->dwAceCountNt4));
DSR_BREAK_ON_FAILED_HR( hr );
// Create ace applications for all of the w2k
// aces
hr = DsrAceAppFromAppDesc(
g_pAcesW2k,
sizeof(g_pAcesW2k) / sizeof(*g_pAcesW2k),
pDomContainer,
pDomain,
&(pInfo->pAcesW2k),
&(pInfo->dwAceCountW2k));
DSR_BREAK_ON_FAILED_HR( hr );
// Assign the return value
*ppInfo = pInfo;
} while (FALSE);
// Cleanup
//
{
DSR_RELEASE(pDomain);
DSR_RELEASE(pDomContainer);
DSR_RELEASE(pSchemaContainer);
DSR_RELEASE(pDispatch);
if (FAILED (hr))
{
DsrAccessInfoCleanup(pInfo);
}
if (pDomainInfo)
{
NetApiBufferFree(pDomainInfo);
}
}
return hr;
}
//
// Discovers the access mode of the domain currently.
//
// Assumes COM is initialized
//
HRESULT
DsrDomainQueryAccessEx(
IN PWCHAR pszDomain,
OUT LPDWORD lpdwAccessFlags,
OUT DSR_DOMAIN_ACCESS_INFO** ppInfo)
{
DSR_DOMAIN_ACCESS_INFO* pInfo = NULL;
HRESULT hr = S_OK;
BOOL bOk = FALSE;
if (lpdwAccessFlags == NULL)
{
return ERROR_INVALID_PARAMETER;
}
do
{
// Initialize
//
*lpdwAccessFlags = 0;
// Read in the info that tells us what ACE's
// need to be set.
//
hr = DsrAccessInfoInit(
pszDomain,
&pInfo);
DSR_BREAK_ON_FAILED_HR( hr );
// Check for nt4 level access
//
bOk = FALSE;
hr = DsrAceAppQueryPresence(
pInfo->pszDC,
pInfo->pAcesNt4,
pInfo->dwAceCountNt4,
&bOk);
DSR_BREAK_ON_FAILED_HR(hr);
// If we don't have nt4 access, we have no access
//
if (bOk == FALSE)
{
*lpdwAccessFlags = 0;
break;
}
*lpdwAccessFlags |= MPRFLAG_DOMAIN_NT4_SERVERS;
// Check for w2k level access
//
bOk = FALSE;
hr = DsrAceAppQueryPresence(
pInfo->pszDC,
pInfo->pAcesW2k,
pInfo->dwAceCountW2k,
&bOk);
DSR_BREAK_ON_FAILED_HR(hr);
// If we don't have w2k access, no need to proceed
//
if (bOk == FALSE)
{
break;
}
*lpdwAccessFlags |= MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS;
} while (FALSE);
// Cleanup
{
if (FAILED(hr))
{
if (pInfo)
{
DsrAccessInfoCleanup(pInfo);
}
}
else
{
*ppInfo = pInfo;
}
}
return hr;
}
//
// Returns the access level of the given domain
//
DWORD
DsrDomainQueryAccess(
IN PWCHAR pszDomain,
OUT LPDWORD lpdwAccessFlags)
{
DSR_DOMAIN_ACCESS_INFO* pInfo = NULL;
HRESULT hr = S_OK;
do
{
// Initialize
hr = DsrComIntialize();
DSR_BREAK_ON_FAILED_HR( hr );
// Query the access
hr = DsrDomainQueryAccessEx(
pszDomain,
lpdwAccessFlags,
&pInfo);
DSR_BREAK_ON_FAILED_HR(hr);
} while (FALSE);
// Cleanup
{
if (pInfo)
{
DsrAccessInfoCleanup(pInfo);
}
DsrComUninitialize();
}
return DSR_ERROR(hr);
}
//
// Sets the ACES in the given domain to enable nt4 servers
//
DWORD
DsrDomainSetAccess(
IN PWCHAR pszDomain,
IN DWORD dwAccessFlags)
{
DSR_DOMAIN_ACCESS_INFO* pInfo = NULL;
HRESULT hr = S_OK;
BOOL bClean = TRUE;
DWORD dwCurAccess = 0;
do
{
// Initialize
hr = DsrComIntialize();
DSR_BREAK_ON_FAILED_HR( hr );
DsrTraceEx(
0,
"DsrDomainSetAccess: Req: %x",
dwAccessFlags);
// W2k mode always implies nt4 mode as well
//
if (dwAccessFlags & MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS)
{
dwAccessFlags |= MPRFLAG_DOMAIN_NT4_SERVERS;
}
// Discover the current access on the domain and
// initialize the info we need
//
hr = DsrDomainQueryAccessEx(
pszDomain,
&dwCurAccess,
&pInfo);
DSR_BREAK_ON_FAILED_HR(hr);
DsrTraceEx(
0,
"DsrDomainSetAccess: Cur: %x",
dwCurAccess);
// Remove all appropriate aces if the requested access
// is none.
if (dwAccessFlags == 0)
{
// Remove the nt4 mode aces if needed
//
if (dwCurAccess & MPRFLAG_DOMAIN_NT4_SERVERS)
{
hr = DsrAceAppRemove(
pInfo->pszDC,
pInfo->pAcesUser,
pInfo->dwAceCountUser);
DSR_BREAK_ON_FAILED_HR(hr);
hr = DsrAceAppRemove(
pInfo->pszDC,
pInfo->pAcesNt4,
pInfo->dwAceCountNt4);
DSR_BREAK_ON_FAILED_HR(hr);
}
// Remove the w2k mode aces if needed
//
if (dwCurAccess & MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS)
{
hr = DsrAceAppRemove(
pInfo->pszDC,
pInfo->pAcesW2k,
pInfo->dwAceCountW2k);
DSR_BREAK_ON_FAILED_HR(hr);
}
}
// Set nt4 mode if needed
//
if (dwAccessFlags & MPRFLAG_DOMAIN_NT4_SERVERS)
{
// Remove w2k level access if needed
//
if ((!(dwAccessFlags & MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS)) &&
(dwCurAccess & MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS))
{
hr = DsrAceAppRemove(
pInfo->pszDC,
pInfo->pAcesW2k,
pInfo->dwAceCountW2k);
DSR_BREAK_ON_FAILED_HR(hr);
}
// Add nt4 level access if needed
//
if (! (dwCurAccess & MPRFLAG_DOMAIN_NT4_SERVERS))
{
hr = DsrAceAppAdd(
pInfo->pszDC,
pInfo->pAcesUser,
pInfo->dwAceCountUser);
DSR_BREAK_ON_FAILED_HR(hr);
hr = DsrAceAppAdd(
pInfo->pszDC,
pInfo->pAcesNt4,
pInfo->dwAceCountNt4);
DSR_BREAK_ON_FAILED_HR(hr);
}
}
// Set w2k mode if needed
//
if (dwAccessFlags & MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS)
{
if (!(dwCurAccess & MPRFLAG_DOMAIN_W2K_IN_NT4_DOMAINS))
{
hr = DsrAceAppAdd(
pInfo->pszDC,
pInfo->pAcesW2k,
pInfo->dwAceCountW2k);
DSR_BREAK_ON_FAILED_HR(hr);
}
}
} while (FALSE);
// Cleanup
{
if (pInfo)
{
DsrAccessInfoCleanup(pInfo);
}
DsrComUninitialize();
}
return DSR_ERROR(hr);
}