/* 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:///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); }