Leaked source code of windows server 2003
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.
 
 
 
 
 
 

9630 lines
245 KiB

/*++
Copyright (c) 2001 Microsoft Corporation
Module Name:
adstore.cxx
Abstract:
This file implements a AD policy store provider
Author:
Chaitanya Upadhyay (chaitu) Aug-2001
--*/
#include "pch.hxx"
#define AZD_COMPONENT AZD_AD
#include <ntldap.h>
#include <ntdsapi.h>
#include <wininet.h>
#include <string.h>
#include <stdlib.h>
#include <ntsam.h>
#include <iads.h>
#include "stdafx.h"
#include "resource.h"
#include "azdisp.h"
#include "adstore.hxx"
//
// Procedures implemented by the LDAP provider and exported to the core
//
DWORD
WINAPI
AzpADPersistOpen(
IN LPCWSTR PolicyUrl,
IN AZPE_OBJECT_HANDLE pAzAuthorizationStore,
IN ULONG lFlags,
IN BOOL bCreatePolicy,
OUT PAZPE_PERSIST_CONTEXT PersistContext,
OUT LPWSTR *pwszTargetMachine
);
DWORD
WINAPI
AzpADPersistUpdateCache(
IN AZPE_PERSIST_CONTEXT PersistContext,
IN ULONG lPersistFlags,
OUT ULONG * pulUpdated
);
DWORD
WINAPI
AzpADPersistUpdateChildrenCache(
IN AZPE_PERSIST_CONTEXT PersistContext,
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
IN ULONG lPersistFlags
);
VOID
WINAPI
AzpADPersistClose(
IN AZPE_PERSIST_CONTEXT PersistContext
);
DWORD
WINAPI
AzpADPersistSubmit(
IN AZPE_PERSIST_CONTEXT PersistContext,
IN AZPE_OBJECT_HANDLE pObject,
IN ULONG lFlags,
IN BOOLEAN bDelete
);
DWORD
WINAPI
AzpADPersistRefresh(
IN AZPE_PERSIST_CONTEXT PersistContext,
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
IN ULONG lPersistFlags
);
DWORD
AdCheckSecurityPrivilege(
IN PAZP_AD_CONTEXT pContext
);
DWORD
AzpADBuildNameSearchFilter(
IN AZPE_OBJECT_HANDLE pObject,
IN ULONG lPersistFlags,
OUT PWSTR * ppSearchFilter
);
//
// Define a provider info telling the core our interface
//
AZPE_PROVIDER_INFO LdapProviderInfo = {
AZPE_PROVIDER_INFO_VERSION_2,
AZ_AD_PROVIDER_NAME,
AzpADPersistOpen,
AzpADPersistUpdateCache,
AzpADPersistClose,
AzpADPersistSubmit,
AzpADPersistRefresh,
AzpADPersistUpdateChildrenCache
};
PAZPE_AZROLES_INFO AdAzrolesInfo;
//
// AD store APIs exposed to the persistence manager layer
//
DWORD
AdProviderInitialize(
IN PAZPE_AZROLES_INFO AzrolesInfo,
OUT PAZPE_PROVIDER_INFO *ProviderInfo
)
/*++
Routine Description
Routine to initialize the XML provider
Arguments
AzrolesInfo - Specifies the interface to routines in azroles.dll
ProviderInfo - Returns a pointer to the provider info for the provider
Return Value
NO_ERROR - Provider was properly initialized
ERROR_UNKNOWN_REVISION - AzrolesInfo is a version the provider doesn't support
--*/
{
//
// Ensure the azroles info is a version we understand
//
if ( AzrolesInfo->AzrolesInfoVersion < AZPE_AZROLES_INFO_VERSION_2 ) {
return ERROR_UNKNOWN_REVISION;
}
AdAzrolesInfo = AzrolesInfo;
*ProviderInfo = &LdapProviderInfo;
return NO_ERROR;
}
BOOL
AzpPressOn (
IN DWORD dwStatusCode,
IN ULONG ulPersistFlags,
IN OUT LPDWORD SavedStatusCode
)
/*++
Routine Description:
This routine test if we should presson in case of errors.
Arguments:
dwStatusCode - The status code
ulPersistFlags - Our persist flag. At current implementation, presson
should only happen if we are updating cache.
SavedStatusCode - Specifies the status code that will eventually be returned to
the AzRoles persist engine. On input, specifies the status from prior operations.
On output, is updated to include dwStatusCode.
Return Value:
True if the condition indicates that we should presson. False otherwise.
Note:
1. Presson should only happen when we are out of resource and the persistence
operation is a cache update.
2. We will need to add more "out of resource" error codes as we discover more.
3. Caller, in general, should return the error code even though we presson.
--*/
{
//
// If the previous status code is success,
// update it to reflect the new error.
//
if ( *SavedStatusCode == NO_ERROR ) {
*SavedStatusCode = dwStatusCode;
}
//
// Tell the caller whether to continue or not
//
if ( dwStatusCode == NO_ERROR ||
(ulPersistFlags & AZPE_FLAGS_PERSIST_UPDATE_CACHE) ||
(ulPersistFlags & AZPE_FLAGS_PERSIST_UPDATE_CHILDREN_CACHE) &&
dwStatusCode != ERROR_NOT_ENOUGH_MEMORY &&
dwStatusCode != ERROR_OUTOFMEMORY &&
dwStatusCode != NTE_NO_MEMORY // crypto service provider
)
{
return TRUE;
}
else
{
return FALSE;
}
}
DWORD
AzpADSetObjectOptions(
IN PAZP_AD_CONTEXT pContext,
IN AZPE_OBJECT_HANDLE pObject,
IN ULONG lPersistFlags,
IN BOOL IsWritable,
IN BOOL CanCreateChildren
)
/*++
Routine Description:
This routine creates a new object of type object type (if not
AzAuthorizationStore) and populates it with common data information such as
description, GUID (for non authorization store objects).
Arguments:
pContext - Context describing the store
pObject - Handle to the to set options on
lPersistFlags - lPersistFlags from the persist engine describing the
operation
IsWritable - TRUE if the current caller can write the object
CanCreateChildren - TRUE is the current caller can create children under the object
Return Values:
NO_ERROR - The object was created and populated successfully
--*/
{
DWORD WinStatus = 0;
ULONG ObjectOptions = 0;
ULONG ObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
if ( IsWritable ) {
ObjectOptions |= AZPE_OPTIONS_WRITABLE;
}
if ( IsContainerObject( ObjectType )) {
ObjectOptions |= AZPE_OPTIONS_SUPPORTS_DACL;
ObjectOptions |= AZPE_OPTIONS_SUPPORTS_SACL;
}
if ( IsDelegatorObject( ObjectType )) {
ObjectOptions |= AZPE_OPTIONS_SUPPORTS_DELEGATION;
}
if ( ObjectType == OBJECT_TYPE_AZAUTHSTORE && pContext->HasSecurityPrivilege ) {
ObjectOptions |= AZPE_OPTIONS_HAS_SECURITY_PRIVILEGE;
}
if ( ObjectType == OBJECT_TYPE_AZAUTHSTORE ) {
ObjectOptions |= AZPE_OPTIONS_SUPPORTS_LAZY_LOAD;
}
if ( IsContainerObject( ObjectType ) && CanCreateChildren ) {
ObjectOptions |= AZPE_OPTIONS_CREATE_CHILDREN;
}
//
// Set the various object options
//
WinStatus = AdAzrolesInfo->AzpeSetObjectOptions(
pObject,
lPersistFlags,
ObjectOptions );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADSetObjectOptions: AzpeSetObjectOptions failed"
": %ld\n",
WinStatus
));
}
return WinStatus;
}
DWORD
AzpADPersistOpenEx(
IN LPCWSTR PolicyUrl,
IN PAZP_AD_CONTEXT OldAdContext,
IN AZPE_OBJECT_HANDLE pAzAuthorizationStore,
IN ULONG lPersistFlags,
IN BOOL bCreatePolicy,
OUT PAZPE_PERSIST_CONTEXT PersistContext OPTIONAL,
OUT LPWSTR *pwszTargetMachine OPTIONAL
)
/*++
Routine Description:
This routine is shared betwen AzpADPersistOpen and AzpADPersistUpdateCache.
This routine reads the authorization policy from the database, or
initializes the pAzAuthorizationStore object for policy to be written to the
object.
Check for existing LDAP handles in the cache.
On entry, pAzAuthorizationStore->PersistCritSect must be locked.
Arguments:
PolicyUrl - Specifies the location of the policy store
OldADContext - On AzUpdateCache, specifies a context from a previous call
pAzAuthorizationStore - Specifies the policy database that is to be read.
lPersistFlags - lPersistFlags from the persist engine describing the
operation
AZPE_FLAGS_PERSIST_OPEN - Call is the original AzInitialize
AZPE_FLAGS_PERSIST_UPDATE_CACHE - Call is an AzUpdateCache
bCreatePolicy - TRUE if the policy database is to be created.
FALSE if the policy database already exists
PersistContext - On Success, returns a context that should be passed on all
subsequent calls.
The caller should call XMLPersistClose to free this context.
This parameter is only returned for AzInitialize calls
pwszTargetMachine - pointer to the target machine name where account resolution
should occur. This should be the machine where the ldap data
is stored.
Return Value:
NO_ERROR - The operation was successful
ERROR_NOT_ENOUGH_MEMORY - not enough memory
ERROR_CURRENT_DOMAIN_NOT_ALLOWED - The current domain will not work for
this version of azroles. This may be because of two
reasons:
1) The domain is not .Net native
2) The schema is incompatible
Other status codes
--*/
{
DWORD WinStatus = 0;
PAZP_AD_CONTEXT pADContext = NULL;
BOOL bCloseADContext = FALSE;
ULONG LdapStatus = 0;
ULONG i = 0;
ULONG PolicyUrlSize;
LDAP_URL_COMPONENTS LdapUrlComponents;
BOOL bFreeLdapUrlComps = FALSE;
//
// Validation
//
ASSERT( pAzAuthorizationStore != NULL );
//
// On an Open, allocate a new AD context
//
if ( lPersistFlags & AZPE_FLAGS_PERSIST_OPEN ) {
//
// Initialization of context describing this provider
//
PolicyUrlSize = (ULONG)((wcslen(PolicyUrl) + 1) * sizeof(WCHAR));
pADContext = (PAZP_AD_CONTEXT) AzpAllocateHeap(
sizeof( AZP_AD_CONTEXT ) + PolicyUrlSize,
"AdPlCtxt" );
if ( pADContext == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
RtlZeroMemory( pADContext, sizeof( AZP_AD_CONTEXT ) );
pADContext->referenceCount = 1;
bCloseADContext = TRUE;
//
// Cache the PolicyUrl
//
pADContext->PolicyUrl = (LPWSTR)(pADContext+1);
RtlCopyMemory( pADContext->PolicyUrl,
PolicyUrl,
PolicyUrlSize );
//
// Cache the authorization store handle
//
pADContext->AzStoreHandle = pAzAuthorizationStore;
//
// Set the server controls
//
for ( i = 0; i < AZ_AD_MAX_SERVER_CONTROLS; i++ ) {
pADContext->pLdapControls[i] =
(PLDAPControl) AzpAllocateHeap( sizeof( LDAPControl ), "AdLdapCt" );
if ( pADContext->pLdapControls[i] == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
pADContext->pLdapControls[i]->ldctl_oid = AZ_AD_SERVER_CONTROLS[i];
pADContext->pLdapControls[i]->ldctl_iscritical = TRUE;
pADContext->pLdapControls[i]->ldctl_value.bv_len = 0;
pADContext->pLdapControls[i]->ldctl_value.bv_val = NULL;
}
pADContext->pLdapControls[i] = NULL;
//
// On an UpdateCache, use the existing AD context
//
} else {
//
// Simply increment the reference count to it
//
if (NULL != OldAdContext)
{
pADContext = OldAdContext;
pADContext->referenceCount++;
bCloseADContext = TRUE;
}
else
{
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
}
//
// Check context for existing LDAP handle to DC.
//
// If none exists (or CreatePolicy is TRUE), create a new handle
// and bind to it.
// Cache the new handle. Set the reference count to it.
// If it does exist already in cache, simply increment
// the reference count.
// The LDAP handle is cached in the PersistContext.
//
if ( pADContext->ld == NULL ) {
//
// Crack the policy URL
//
RtlZeroMemory( &LdapUrlComponents, sizeof( LDAP_URL_COMPONENTS ) );
if ( !AzpLdapCrackUrl( &pADContext->PolicyUrl,
&LdapUrlComponents ) ) {
WinStatus = GetLastError();
AzPrint(( AZD_AD,
"AzpADPersistOpenEx: AzpLdapCrackUrl failed on %ws: %ld\n",
PolicyUrl,
WinStatus
));
goto Cleanup;
}
bFreeLdapUrlComps = TRUE;
//
// Now initialize the LDAP handle
//
pADContext->ld = ldap_init(
LdapUrlComponents.pszHost,
LdapUrlComponents.Port
);
if ( pADContext->ld == NULL ) {
LdapStatus = LdapGetLastError();
AzPrint(( AZD_AD,
"AzpADPersistOpenEx: ldap_init failed on %ws: %ld: %s\n",
LdapUrlComponents.pszHost,
LdapStatus,
ldap_err2stringA( LdapStatus )
));
WinStatus = LdapMapErrorToWin32( LdapStatus );
goto Cleanup;
}
//
// Set our default options
//
WinStatus = AzpADSetDefaultLdapOptions(pADContext->ld,
NULL // no domain name available
);
if (WinStatus != NO_ERROR)
{
AzPrint(( AZD_AD,
"AzpADPersistOpenEx: AzpADSetDefaultLdapOptions failed on %ws: %ld\n",
LdapUrlComponents.pszHost,
WinStatus
));
goto Cleanup;
}
//
// Bind to the DC
//
LdapStatus = ldap_bind_s( pADContext->ld,
NULL, // No DN of account to authenticate as
NULL, // Default credentials
LDAP_AUTH_NEGOTIATE
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpADPersistOpenEx: ldap_bind failed on %ws: %ld\n",
LdapUrlComponents.pszHost,
WinStatus
));
goto Cleanup;
}
//
// We need to make sure that the DC is in a .Net native domain at least
// It is also needed that the proper schema has been installed in the
// DC. If either of these is not true, the error needs to be reported
// back to the user.
//
WinStatus = AzpADCheckCompatibility(
pADContext->ld
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADPersistOpenEx: AzpCheckDomainVersion failed"
" on %ws: %ld",
LdapUrlComponents.pszDN,
WinStatus
));
goto Cleanup;
}
//
// Check for Policy existence
// Should not exist if bCreatePolicy is TRUE and should exist
// if bCreatePolicy is FALSE
//
WinStatus = AzpCheckPolicyExistence(
pADContext->ld,
LdapUrlComponents.pszDN,
bCreatePolicy
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Now cache the DN
//
if ( pADContext->pContextInfo == NULL ) {
pADContext->pContextInfo = (PWCHAR)
AzpAllocateHeap( (wcslen( LdapUrlComponents.pszDN ) + 1) *
sizeof( WCHAR ), "AdCtxIn" );
if ( pADContext->pContextInfo == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( pADContext->pContextInfo, LdapUrlComponents.pszDN );
}
}
//
// pwszTargetMachine is an optional parameter to this function
// if target machine name is requested, get the information from ldap (pADContext->ld)
//
if ( pwszTargetMachine ) {
//
// the host name that ldap_init connected to should be available from ldap_get_option
// pwszServerName points to the internal structure of ldap ID. No memory is allocated
//
PWCHAR pwszServerName=NULL;
LdapStatus = ldap_get_optionW(pADContext->ld,
LDAP_OPT_HOST_NAME,
&pwszServerName
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpADPersistOpenEx: ldap_get_option failed : %ld\n",
WinStatus
));
goto Cleanup;
} else {
*pwszTargetMachine = (PWSTR)
AdAzrolesInfo->AzpeAllocateMemory( (wcslen(pwszServerName) + 1) * sizeof( WCHAR ) );
if ( *pwszTargetMachine ) {
wcscpy( *pwszTargetMachine, pwszServerName );
} else {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
}
}
//
// On an open,
// determine whether the current user has SE_SECURITY_PRIVILEGE on the target machine
//
if ( lPersistFlags & AZPE_FLAGS_PERSIST_OPEN ) {
WinStatus = AdCheckSecurityPrivilege( pADContext );
if ( WinStatus == NO_ERROR ) {
pADContext->HasSecurityPrivilege = TRUE;
} else if ( WinStatus == ERROR_DS_INSUFF_ACCESS_RIGHTS ) {
pADContext->HasSecurityPrivilege = FALSE;
} else {
goto Cleanup;
}
}
//
// Start reading the AD policy store if bCreatePolicy is FALSE or
// if we want to update the cache
//
if ( (!bCreatePolicy && (lPersistFlags & AZPE_FLAGS_PERSIST_OPEN) != 0) ||
(lPersistFlags & AZPE_FLAGS_PERSIST_UPDATE_CACHE) != 0 ) {
WinStatus = AzpReadADStore( pADContext,
pAzAuthorizationStore,
lPersistFlags
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADPersistOpenEx: AzpReadADStore failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// If we didn't read the adstore,
// we still need to tell the core about the authorization store object
//
} else {
LONG MajorVersion = 1;
LONG MinorVersion = 0;
//
// Tell the core that this is a version 1.0 store
//
WinStatus = AdAzrolesInfo->AzpeSetProperty(
pAzAuthorizationStore,
lPersistFlags,
AZ_PROP_AZSTORE_MAJOR_VERSION,
&MajorVersion );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
WinStatus = AdAzrolesInfo->AzpeSetProperty(
pAzAuthorizationStore,
lPersistFlags,
AZ_PROP_AZSTORE_MINOR_VERSION,
&MinorVersion );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Tell the core the object options
//
WinStatus = AzpADSetObjectOptions(
pADContext,
pAzAuthorizationStore,
lPersistFlags,
TRUE, // A created ADstore is assumed to be writable
TRUE
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
}
//
// On an Initialize,
// return the context to the caller.
//
if (AZPE_FLAGS_PERSIST_OPEN & lPersistFlags) {
// return the persist context to the caller
*PersistContext = (AZPE_PERSIST_CONTEXT) pADContext;
bCloseADContext = FALSE;
}
WinStatus = NO_ERROR;
Cleanup:
//
// On error,
// close the LDAP handle
//
if ( WinStatus != NO_ERROR &&
pADContext != NULL &&
pADContext->ld != NULL ) {
ldap_unbind_s ( pADContext->ld );
pADContext->ld = NULL;
}
if ( bCloseADContext ) {
AzpADPersistClose( (AZPE_PERSIST_CONTEXT)pADContext );
}
if ( bFreeLdapUrlComps ) {
AzpLdapFreeUrlComponents( &LdapUrlComponents );
}
if ( WinStatus != NO_ERROR &&
pwszTargetMachine && *pwszTargetMachine ) {
AdAzrolesInfo->AzpeFreeMemory( *pwszTargetMachine );
*pwszTargetMachine = NULL;
}
AdAzrolesInfo->AzpeObjectFinished( pAzAuthorizationStore, WinStatus );
return WinStatus;
}
DWORD
AzpADPersistOpen(
IN LPCWSTR PolicyUrl,
IN AZPE_OBJECT_HANDLE pAzAuthorizationStore,
IN ULONG lPersistFlags,
IN BOOL fCreatePolicy,
OUT PAZPE_PERSIST_CONTEXT PersistContext,
OUT LPWSTR *pwszTargetMachine
)
/*++
Routine Description:
This routine submits reads the authz policy database from storage.
This routine also reads the policy database into cache.
On Success, the caller should call AzpADPersistClose to free any resources
consumed by the open.
On entry, AzAuthorizationStore->PersistCritSect must be locked.
Arguments:
PolicyUrl - Specifies the location of the policy store
pAzAuthorizationStore - Specifies the policy database that is to be read.
lPersistFlags - lPersistFlags from the persist engine describing the operation
AZPE_FLAGS_PERSIST_OPEN - Call is the original AzInitialize
fCreatePolicy - TRUE if the policy database is to be created.
FALSE if the policy database already exists
PersistContext - On Success, returns a context that should be passed on all
subsequent calls.
The caller should call AzpADPersistClose to free this context.
pwszTargetMachine - pointer to the target machine name where account resolution
should occur. This should be the machine where the ldap data
is stored.
Return Value:
NO_ERROR - The operation was successful
ERROR_NOT_ENOUGH_MEMORY - not enough memory
ERROR_CURRENT_DOMAIN_NOT_ALLOWED - The current domain will not work for
this version of azroles. This may be because of two
reasons:
1) The domain is not .Net native
2) The schema is incompatible
Other status codes
--*/
{
//
// Call the worker routine
//
AZASSERT(AZPE_FLAGS_PERSIST_OPEN & lPersistFlags);
return AzpADPersistOpenEx(
PolicyUrl,
NULL, // No previous context
pAzAuthorizationStore,
lPersistFlags,
fCreatePolicy,
PersistContext,
pwszTargetMachine );
}
DWORD
AzpADPersistUpdateCache(
IN AZPE_PERSIST_CONTEXT PersistContext,
IN ULONG lPersistFlags,
OUT ULONG * pulUpdatedFlag
)
/*++
Routine Description:
This routine updates the cache to reflect the current contents of the
policy database.
On entry, AzAuthorizationStore->PersistCritSect must be locked.
Arguments:
PersistContext - Specifies the policy database that is to be updated
lPersistFlags - lPersistFlags from the persist engine describing the operation
AZPE_FLAGS_PERSIST_UPDATE_CACHE - Call is an AzUpdateCache
pulUpdatedFlag - Passing back information whether the function call has truly
caused an update. Due to possible efficient updateCache
implementation by providers, it may decide to do nothing.
Currently, we only one bit (AZPE_FLAG_CACHE_UPDATE_STORE_LEVEL) to
indicate that the update is actually carried out.
Return Value:
NO_ERROR - The operation was successful
ERROR_NOT_ENOUGH_MEMORY - not enough memory
Other status codes
--*/
{
PAZP_AD_CONTEXT pADContext = (PAZP_AD_CONTEXT)PersistContext;
//
// Call the worker routine
//
AZASSERT(AZPE_FLAGS_PERSIST_UPDATE_CACHE & lPersistFlags);
AZASSERT( pADContext != NULL );
AZASSERT( pulUpdatedFlag != NULL );
//
// Assume no real update
//
*pulUpdatedFlag = 0;
BOOL bNeedUpdate;
//
// This call will also update our context's uSNChanged attribute
// because we know that this needs to be updated.
//
DWORD dwStatus = AzpADStoreHasUpdate(TRUE, pADContext, &bNeedUpdate);
if (NO_ERROR != dwStatus)
{
goto error;
}
else if (bNeedUpdate)
{
//
// Call the worker routine
//
dwStatus = AzpADPersistOpenEx(
pADContext->PolicyUrl,
pADContext,
pADContext->AzStoreHandle,
lPersistFlags,
FALSE, // Don't create the store
NULL,
NULL ); // Don't return a new persist context
if (NO_ERROR == dwStatus)
{
*pulUpdatedFlag = AZPE_FLAG_CACHE_UPDATE_STORE_LEVEL;
}
}
error:
return dwStatus;
}
DWORD
AzpADPersistUpdateChildrenCache(
IN AZPE_PERSIST_CONTEXT PersistContext,
IN AZPE_OBJECT_HANDLE AzpeObjectHandle,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine updates the children (upto one level) into the cache.
Arguments:
PersistContext - Specifies the policy database's context
AzpeObjectHandle - Handle to the objects whose children need to be updated
lPersistFlags - lPersistFlags from the persist engine describing the operation
AZPE_FLAGS_PERSIST_UPDATE_CHILDREN_CACHE - Call is an AzUpdateChildrenCache
Return Value:
NO_ERROR - The operation was successful
ERROR_NOT_ENOUGH_MEMORY - not enough memory
Other status code
--*/
{
DWORD WinStatus = 0;
DWORD WinStatusActual = 0;
LDAPSearch* pSearchHandle = NULL;
PWCHAR pDN = NULL;
AZ_AD_CHILD_FILTERS ChildFilters;
PAZP_AD_CONTEXT pADContext = (PAZP_AD_CONTEXT) PersistContext;
ULONG ObjectType = AdAzrolesInfo->AzpeObjectType( AzpeObjectHandle );
ULONG i = 0;
//
// Build the DN for the parent object
//
WinStatus = AzpADBuildDN(
pADContext,
AzpeObjectHandle,
&pDN,
NULL, // no parent DN
FALSE, // parent DN of AzAuthorizationStore object not needed
NULL // no child object container needs to be built
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADPersistUpdateChildrenCache: AzpADBuildDN failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// If the current object is an AzApplication object, then read the Scope object
// children if there are any
//
if ( ObjectType == OBJECT_TYPE_APPLICATION ) {
pSearchHandle = ldap_search_init_page(
pADContext->ld,
pDN,
LDAP_SCOPE_ONELEVEL,
ApplicationChildFilters[0].Filter,
AllObjectReadAttrs[ApplicationChildFilters[0].lObjectType],
FALSE,
pADContext->pLdapControls,
NULL,
0,
0,
NULL
);
if ( pSearchHandle == NULL ) {
WinStatus = LdapMapErrorToWin32( LdapGetLastError() );
if (!AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpADPersistUpdateChildrenCache: failed to init paged search handle: %ld\n",
WinStatus
));
goto Cleanup;
}
} else {
WinStatus = AzpADReadPagedResult(
pADContext,
pSearchHandle,
AzpeObjectHandle,
ApplicationChildFilters[0].lObjectType,
lPersistFlags
);
if ( !AzpPressOn( WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpADPersistUpdateChildrenCache: failed to read paged results: %ld\n",
WinStatus
));
goto Cleanup;
}
ldap_search_abandon_page( pADContext->ld, pSearchHandle );
pSearchHandle = NULL;
} // if ( pSearchHandle )
i = 1;
ChildFilters = ApplicationChildFilters[i];
} else { // if ( OBJECT_TYPE_APPlCATION )
ChildFilters = ScopeChildFilters[i];
}
while ( ChildFilters.Filter != NULL ) {
WinStatus = AzpReadADObjectContainer(
pADContext,
pDN,
ChildFilters.pContainerPrefix,
ChildFilters.lPrefixLength,
ChildFilters.Filter,
ChildFilters.lObjectType,
AzpeObjectHandle,
lPersistFlags
);
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpADReadHasChildrenObject: Reading of Child container object"
"failed: %ld\n",
WinStatus
));
goto Cleanup;
}
i++;
if ( ObjectType == OBJECT_TYPE_APPLICATION ) {
ChildFilters = ApplicationChildFilters[i];
} else {
ChildFilters = ScopeChildFilters[i];
}
} // while ( ChildFilters != NULL )
Cleanup:
//
// Need to release these resources
//
if ( pSearchHandle != NULL ) {
ldap_search_abandon_page( pADContext->ld, pSearchHandle );
}
if ( pDN != NULL ) {
AzpFreeHeap( pDN );
}
return WinStatusActual;
}
VOID
AzpADPersistClose(
IN AZPE_PERSIST_CONTEXT PersistContext
)
/*++
Routine Description:
This routine closes LDAP handle to the AD policy store.
The reference count to the LDAP handle is simply decremented.
If the reference count is 0, unbind from the AD policy store and
release the memory allocated for the handle.
On entry, pAzAuthorizationStore->PersistCritSect must be locked.
Arguments:
pAzAuthorizationStore - Handle to the AzAuthorizationManager object caching the
LDAP handle
Return Value:
None
--*/
{
PAZP_AD_CONTEXT pADContext = NULL;
ULONG i = 0;
//
// Validation
//
pADContext = (PAZP_AD_CONTEXT)PersistContext;
ASSERT( pADContext != NULL );
//
// Decrement the reference count
//
pADContext->referenceCount--;
//
// If it is zero, then unbind from the AD policy store, and
// release allocated memory to context
//
if ( pADContext->referenceCount == 0 ) {
if ( pADContext->ld != NULL ) {
ldap_unbind_s ( pADContext->ld );
}
for ( i = 0; i < AZ_AD_MAX_SERVER_CONTROLS; i++ ) {
if ( pADContext->pLdapControls[i] != NULL ) {
AzpFreeHeap( pADContext->pLdapControls[i] );
}
}
if ( pADContext->pContextInfo != NULL ) {
AzpFreeHeap( pADContext->pContextInfo );
}
AzpFreeHeap( pADContext );
}
}
DWORD
AzpADPersistSubmit(
IN AZPE_PERSIST_CONTEXT PersistContext,
IN AZPE_OBJECT_HANDLE pObject,
IN ULONG lPersistFlags,
IN BOOLEAN bDelete
)
/*++
Routine Description:
This routine submits changes made to the policy to the policy store.
If the object is being created, GenericObject->PersistenceGuid will be
zero. Once the object has been added to the DS, try to read the
objectGUID attribute of the newly created object in DS to get the GUID
to set the PersistenceGuid of the object in cache. If the read fails,
ignore the problem. It is assumed that the GUID will be populated during
a refresh at a future point of time.
On entry, PersistCritSect must be locked
Arguments:
PersistContext - Specifies the policy database that is to be manipulated
pObject - Specifies the object in the database that is to be updated
in the underlying store.
lPersistFlags - lPersistFlags from the persist engine describing the
operation.
AZPE_FLAGS_PERSIST_SUBMIT - Call is the AzPersistSubmit
bDelete - TRUE if the object and all of its children are to be deleted.
FALSE if the object is to be updated.
Return Values:
NO_ERROR - The operation was successful
Other status codes
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
PWCHAR pDN = NULL;
PAZP_AD_CONTEXT pContext = (PAZP_AD_CONTEXT)PersistContext;
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
BOOL bDeleteStore = bDelete && (OBJECT_TYPE_AZAUTHSTORE == lObjectType);
//
// Validation
//
ASSERT( pObject != NULL );
ASSERT( pContext != NULL );
BOOL bChangeSubmitted = FALSE;
BOOL bNeedReadBackUSN;
BOOL bUpdateStoreObject = AzpADNeedUpdateStoreUSN(pContext, pObject, &bNeedReadBackUSN);
//
// For a new (or renamed) object, if it is an application or scope,
// then we need to make sure that the same name object hasn't been
// created since this cache is updated. Other types of object
// are fine because the name is part of the DN and we will get
// flagged by AD upon submitting that the object already exists.
//
if ( (AdAzrolesInfo->AzpeDirtyBits(pObject) & (AZ_DIRTY_CREATE | AZ_DIRTY_NAME) ) &&
( lObjectType == OBJECT_TYPE_APPLICATION || lObjectType == OBJECT_TYPE_SCOPE)
)
{
BOOL bHasUpdate = FALSE;
WinStatus = AzpADStoreHasUpdate(FALSE, pContext, &bHasUpdate);
if ( WinStatus != NO_ERROR )
{
AzPrint(( AZD_AD,
"AzpADPersistSubmit: AzpADStoreHasUpdate failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// somebody has updated the store outside of us. We need to
// make sure the same object doesn't exist yet.
//
if (bHasUpdate)
{
LDAPMessage * pResult = NULL;
//
// Get the store handle
//
AZPE_OBJECT_HANDLE hStore = AdAzrolesInfo->AzpeGetAuthorizationStore(pObject);
//
// Build the store DN for search
//
PWSTR pStoreDN = NULL;
PWSTR pSearchFilter = NULL;
WinStatus = AzpADBuildDN(pContext,
hStore,
&pStoreDN,
NULL,
TRUE,
NULL
);
if ( WinStatus != NO_ERROR )
{
AzPrint(( AZD_AD,
"AzpADPersistSubmit: AzpADBuildDN failed:"
"%ld\n",
WinStatus
));
goto Cleanup;
}
//
// Build the search filter
//
WinStatus = AzpADBuildNameSearchFilter(pObject, lPersistFlags, &pSearchFilter);
if ( WinStatus != NO_ERROR )
{
AzPrint(( AZD_AD,
"AzpADPersistSubmit: AzpADBuildNameSearchFilter failed:"
"%ld\n",
WinStatus
));
goto Cleanup;
}
else
{
//
// See if there is any object of this class and this name
//
LdapStatus = ldap_search_s(
pContext->ld,
pStoreDN,
LDAP_SCOPE_SUBTREE,
pSearchFilter,
NULL,
0,
&pResult
);
if (LdapStatus != LDAP_SUCCESS)
{
WinStatus = LdapMapErrorToWin32( LdapStatus );
}
else
{
LDAPMessage * pEntry = ldap_first_entry( pContext->ld, pResult );
//
// It is bad if we find any entry
//
if ( pEntry != NULL )
{
WinStatus = ERROR_ALREADY_EXISTS;
}
}
}
if (pResult != NULL)
{
ldap_msgfree( pResult );
pResult = NULL;
}
if (pStoreDN != NULL)
{
AzpFreeHeap(pStoreDN);
pStoreDN = NULL;
}
if (pSearchFilter != NULL)
{
AdAzrolesInfo->AzpeFreeMemory(pSearchFilter);
pSearchFilter = NULL;
}
if ( WinStatus != NO_ERROR )
{
AzPrint(( AZD_AD, "Submitting a new object with name that has been submitted by other instances of azstore object",
WinStatus ));
goto Cleanup;
}
}
}
//
// Build the DN for the object
//
WinStatus = AzpADBuildDN(
pContext,
pObject,
&pDN,
NULL, // do not have parent DN
FALSE, // parent DN of authorization store object not needed
NULL // not a call to create child object container DN
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADPersistSubmit: AzpADBuildDN failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// If the object needs to be deleted from the AD policy store,
// then simply use the server side control to tree delete
// so that non-leaf objects may be deleted successfully,
// and call the LDAP API to delete the object
//
if ( bDelete ) {
LdapStatus = ldap_delete_ext_s(
pContext->ld,
pDN,
pContext->pLdapControls,
NULL
);
if ( LdapStatus != LDAP_NO_SUCH_OBJECT &&
LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpADPersistSubmit: Failed to delete object"
" %ws: %ld\n",
pDN,
WinStatus
));
goto Cleanup;
}
else if (LdapStatus == LDAP_SUCCESS)
{
bChangeSubmitted = TRUE;
}
} else {
//
// Submit changes to AD
//
//
// Check if any attributes are dirty at all
// If none are dirty, then simply return to the calling
// function with NO_ERROR
//
if ( AdAzrolesInfo->AzpeDirtyBits(pObject) == 0x0 ) {
WinStatus = NO_ERROR;
goto Cleanup;
}
//
// Call update routine with different argument values
// for different objects
//
WinStatus = AzpUpdateADObject(
pContext,
pContext->ld,
pObject,
pDN,
ObjectAttributes[lObjectType].pObjectClass,
ObjectAttributes[lObjectType].pObjectAttrs,
lPersistFlags
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADPersistSubmit: Update "
"failed for object %ws: %ld\n",
pDN,
WinStatus
));
goto Cleanup;
}
bChangeSubmitted = TRUE;
} // if (bDelete) ... else
WinStatus = NO_ERROR;
//
// There is really nothing to update if the store has been deleted
//
if (!bDeleteStore)
{
if (bChangeSubmitted && bUpdateStoreObject)
{
WinStatus = AzpADUpdateStoreObjectForUSN(bNeedReadBackUSN, pObject, pContext);
}
else if (bChangeSubmitted && bNeedReadBackUSN)
{
//
// change has submitted, but we don't need to update the store object
// because there is an outside change, or we have just updated the store object
// by ourselves, but we have determiend that we need to update our USN.
//
BOOL bIgnored;
WinStatus = AzpADStoreHasUpdate(TRUE, pContext, &bIgnored);
}
}
Cleanup:
//
// Free resources
//
if ( pDN != NULL ) {
AzpFreeHeap( pDN );
}
return WinStatus;
}
DWORD
AzpADPersistRefresh(
IN AZPE_PERSIST_CONTEXT PersistContext,
IN AZPE_OBJECT_HANDLE pObject,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine updates the attributes of the object from the policy database.
On entry, PersistCritSect must be locked.
Arguments:
PersistContext - Specifies the policy database that is to be manipulated
pObject - Specifies the object in the database whose cache entry is to be
updated
The pObject->PersistenceGuid field should be non-zero on
lPersistFlags - lPersistFlags from the persist engine describing the
operation
AZPE_FLAGS_PERSIST_REFRESH - Call is an AzPersistRefresh
Return Value:
NO_ERROR - The operation was successful
ERROR_NOT_ENOUGH_MEMORY - not enough memory
Other status codes
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
LDAPMessage *pResult = NULL;
LDAPMessage *pEntry = NULL;
PWCHAR pDN = NULL;
AZPE_OBJECT_HANDLE pParentObject = NULL;
PAZP_AD_CONTEXT pContext = (PAZP_AD_CONTEXT)PersistContext;
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
//
// Validation
//
ASSERT( pObject != NULL );
ASSERT( pContext != NULL );
//
// If the object has never been submitted,
// there is no need to refresh.
//
if ( AdAzrolesInfo->AzpeDirtyBits( pObject ) & AZ_DIRTY_CREATE ) {
goto Cleanup;
}
//
// Build the DN for the object
//
WinStatus = AzpADBuildDN(
pContext,
pObject,
&pDN,
NULL,
FALSE,
NULL
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADPersistRefresh: AzpADBuildDN failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// Now read the object from the AD store and pass the returned
// result to AzpReadADStoreObject to read in the attributes
//
LdapStatus = ldap_search_ext_s(
pContext->ld,
pDN,
LDAP_SCOPE_BASE,
AZ_AD_ALL_CLASSES,
AllObjectReadAttrs[lObjectType],
0,
pContext->pLdapControls,
NULL,
NULL,
0,
&pResult
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpADPersistRefresh: Search on object failed"
":%ld\n",
WinStatus
));
goto Cleanup;
}
pEntry = ldap_first_entry( pContext->ld, pResult );
if ( pEntry != NULL ) {
//
// get parent object of this object
// for AzAuthorizationStore, it will return NULL which is ok
// no need to free the handle
//
pParentObject = AdAzrolesInfo->AzpeParentOfChild(pObject);
WinStatus = AzpReadADStoreObject(
pContext,
pContext->ld,
pEntry,
&pObject,
lObjectType,
pParentObject,
ObjectAttributes[lObjectType].pObjectAttrs,
lPersistFlags
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADPersistRefresh: AzpReadADStoreObject failed"
"for %s: %ld\n",
pDN,
WinStatus
));
goto Cleanup;
}
}
WinStatus = NO_ERROR;
Cleanup:
AdAzrolesInfo->AzpeObjectFinished( pObject, WinStatus );
if ( pDN != NULL ) {
AzpFreeHeap( pDN );
}
if ( pResult != NULL ) {
ldap_msgfree( pResult );
}
return WinStatus;
}
DWORD
AzpADPersistChildCreate(
IN PAZP_AD_CONTEXT pContext,
IN LDAPMessage *pAttrEntry,
OUT PBOOL pbChildCreate
)
/*++
Routine Description:
This routine determines if children can be created under a particular object. Currently,
this is only called for the AzScope objects. If the AzScope is delegated, then the AzScope
object is not writable, but children can be created under it.
Arguments:
pContext - Specifies the policy database that is to be manipulated
//pObject - object that is queried for writable attribute
pAttrEntry - Structure containing read in attribute values
pbChildCreate - pointer to return the boolean in
Return Value:
NO_ERROR - The operation was successful
--*/
{
DWORD WinStatus = NO_ERROR;
PWCHAR *ppValueList = NULL;
PWCHAR pSearchString = NULL;
ULONG lValueCount = 0;
ULONG i = 0;
//
// Validation
//
ASSERT( pContext != NULL );
//
// Set pbChildCreate to a default value
//
*pbChildCreate = FALSE;
ppValueList = ldap_get_values( pContext->ld,
pAttrEntry,
AZ_AD_OBJECT_CHILD_CREATE
);
if ( ppValueList != NULL ) {
lValueCount = ldap_count_values( ppValueList );
for ( i=0; i<lValueCount; i++) {
pSearchString = ppValueList[i];
if ( _wcsicmp( pSearchString, AZ_AD_OBJECT_CONTAINER ) ) {
*pbChildCreate = TRUE;
break;
}
}
} else {
WinStatus = LdapMapErrorToWin32( LdapGetLastError() );
if ( WinStatus == ERROR_INVALID_PARAMETER ) {
WinStatus = NO_ERROR;
}
}
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
return WinStatus;
}
DWORD
AzpADPersistWritable(
IN PAZP_AD_CONTEXT pContext,
IN AZPE_OBJECT_HANDLE pObject,
IN LDAPMessage *pAttrEntry,
OUT PBOOL pbWritable,
OUT PBOOL pbCanCreateChildren
)
/*++
Routine Description:
This routine determines if the persist object is writable. If the object
is a parent object, then the allowedAttributesEffective field is read. If
not, then the object's parent object's CanCreateChildren value is returned.
If the child can be created, then it is Writable.
Arguments:
pContext - Specifies the policy database that is to be manipulated
pObject - object that is queried for writable attribute
pAttrEntry - Structure containing read in attribute values
pbWritable - pointer to return the boolean in
pbCanCreateChildren - pointer to return boolen specifying if children can be created
Return Value:
NO_ERROR - The operation was successful
--*/
{
DWORD WinStatus = 0;
PWCHAR *ppValueList = NULL;
ULONG lValueCount = 0;
ULONG i = 0;
PWCHAR *ppResultString = NULL;
PWCHAR pSearchAttribute = NULL;
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
//
// Validation
//
ASSERT( pObject != NULL );
ASSERT( pContext != NULL );
//
// Set pbWritable to a default value
//
*pbWritable = FALSE;
if ( IsContainerObject(lObjectType) ) {
ppValueList = ldap_get_values( pContext->ld,
pAttrEntry,
AZ_AD_OBJECT_WRITEABLE
);
if ( ppValueList != NULL ) {
lValueCount = ldap_count_values( ppValueList );
//
// sort the retrieved attributes
//
qsort( (PVOID)ppValueList,
(size_t)lValueCount,
sizeof( PWCHAR ),
AzpCompareSortStrings
);
//
// Now check if the common object attributes are present in the
// sorted list or not
//
*pbWritable = TRUE;
for ( i = 0; i < AZ_AD_MAX_ATTRS; i++ ) {
if ( i < AZ_AD_COMMON_ATTRS ) {
//
// for AzApplication and AzScope attributes, check the
// msDS-AzApplicationName/msDS-AzScopeName attribute
// For others check the name attribute
//
if ( (lObjectType == OBJECT_TYPE_APPLICATION ||
lObjectType == OBJECT_TYPE_SCOPE) &&
(CommonAttrs[i].AttrType == AZ_PROP_NAME) ) {
pSearchAttribute =
AZ_AD_OBJECT_NAMES[lObjectType];
//
// Group objects don't support the application data attribute
//
} else if ( lObjectType == OBJECT_TYPE_GROUP &&
CommonAttrs[i].AttrType == AZ_PROP_APPLICATION_DATA ) {
continue;
//
// All others use the attribute name in the table
//
} else {
pSearchAttribute = CommonAttrs[i].Attr;
}
} else if ( ObjectAttributes[lObjectType].
pObjectAttrs[i-AZ_AD_COMMON_ATTRS].AttrType ==
AZ_AD_END_LIST ) {
break;
} else {
pSearchAttribute =
ObjectAttributes[lObjectType].
pObjectAttrs[i-AZ_AD_COMMON_ATTRS].Attr;
}
//
// Perform a binary search on the sorted strings
//
ppResultString = (PWCHAR *) bsearch(
(PWCHAR) &pSearchAttribute,
(PWCHAR) ppValueList,
lValueCount,
sizeof( PWCHAR ),
AzpCompareSortStrings
);
if ( ppResultString == NULL ) {
AzPrint(( AZD_AD,
"AzpADPersistWritable: %ws attribute isn't writable\n",
pSearchAttribute ));
*pbWritable = FALSE;
break;
}
} // for (i = 0; i < AZ_AD_MAX_ATTRS; i++ )
}
//
// Now check if children can be created under this object.
//
WinStatus = AzpADPersistChildCreate(
pContext,
pAttrEntry,
pbCanCreateChildren
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADPersistWritable: Error calling AzpADPersistChildCreate: %ld\n",
WinStatus
));
goto Cleanup;
}
} else { // if ( IsContainerObject(lObjectType) )
*pbWritable = AdAzrolesInfo->AzpeCanCreateChildren(pObject);
}
WinStatus = NO_ERROR;
Cleanup:
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
return WinStatus;
}
DWORD
AdCheckSecurityPrivilege(
IN PAZP_AD_CONTEXT pContext
)
/*++
Routine Description:
Determine whether the caller has SE_SECURITY_PRIVILEGE on the machine containing
the AD store.
Arguments:
pContext - context for the store
Return Value:
NO_ERROR - The caller has privilege
ERROR_DS_INSUFF_ACCESS_RIGHTS - The security descriptor could not be read
Other errors
--*/
{
DWORD WinStatus;
PSECURITY_DESCRIPTOR pSD = NULL;
//
// Validation
//
ASSERT( pContext != NULL );
//
// Get the NTSecurityDescriptor for the object
//
WinStatus = AzpADReadNTSecurityDescriptor(
pContext,
pContext->AzStoreHandle,
NULL,
TRUE, // need to read authorization store's AD parent
&pSD,
TRUE, // read SACL
FALSE
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AdCheckSecurityPrivilege: AzpADReadNTSecurityDescriptor"
" failed: %ld\n",
WinStatus
));
goto Cleanup;
}
WinStatus = NO_ERROR;
Cleanup:
//
// free local resources
//
if ( pSD != NULL ) {
AzpFreeHeap( pSD );
}
return WinStatus;
}
//
// Routines used by AzpADPersistOpen to open
// a policy store into the core cache
//
DWORD
AzpReadADStore(
IN PAZP_AD_CONTEXT pContext,
IN AZPE_OBJECT_HANDLE pAzAuthorizationStore,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine is called if there is a new policy not being created and
there does not exist a policy in cache already. This routine will read
the policy from the AD store into the cache.
Arguments:
pContext - context for the store
pAzAuthorizationStore - Structure to start reading policy from. This structure
also contains a handle to the AD store for LDAP communication to
occur.
lPersistFlags - lPersistFlags from the persist engine describing the
operation
Return Value:
NO_ERROR - The policy was successfully read into the cache
Other status code - There was problem reading the policy into the cache
--*/
{
DWORD WinStatus = 0;
DWORD WinStatusActual = 0;
ULONG LdapStatus = 0;
LDAP *pLdapHandle = NULL;
LDAPMessage *pResult = NULL;
LDAPMessage *pObjectEntry = NULL;
AZPE_OBJECT_HANDLE pObject = NULL;
PWCHAR *ppValueList = NULL;
//
// Validation
//
ASSERT( pAzAuthorizationStore != NULL );
//
// Start by reading in the AzAuthorizationStore object properties
// into the cache, and then traverse the tree reading in
// the various objects
//
pLdapHandle = pContext->ld;
//
// Search for the base DN to start reading the policy from
// the base level. This will return us the attributes for
// the AzAuthorizationStore object only. From there, we can search
// the base DN for application and application groups individually
//
LdapStatus = ldap_search_ext_s(
pLdapHandle,
pContext->pContextInfo,
LDAP_SCOPE_BASE,
AZ_AD_AZSTORE_FILTER,
AuthorizationStoreReadAttrs,
0,
pContext->pLdapControls,
NULL,
NULL,
0,
&pResult // buffer to store read attributes
);
if ( LdapStatus == LDAP_NO_SUCH_OBJECT ) {
WinStatusActual = ERROR_FILE_NOT_FOUND;
goto Cleanup;
} else if ( LdapStatus != LDAP_SUCCESS ) {
WinStatusActual = LdapMapErrorToWin32( LdapStatus );
goto Cleanup;
}
pObjectEntry = ldap_first_entry( pLdapHandle, pResult );
//
// We have a handle to the first entry in the DS policy (AzAuthorizationStore
// object)
//
//
// Read AD store if there are entries
//
if ( pObjectEntry != NULL ) {
//
// if we are reading an existing poicy, let's check the versions
// to determine if we can continue reading
//
WinStatus = AzpCheckVersions (pLdapHandle, pResult);
if ( NO_ERROR != WinStatus ) {
AzPrint(( AZD_AD,
"AzpADPersistOpenEx: AzpCheckVersions failed with error: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// populate the AzAuthorizationStore object in the cache
//
pObject = pAzAuthorizationStore;
WinStatus = AzpReadADStoreObject( pContext,
pLdapHandle,
pObjectEntry,
&pObject,
OBJECT_TYPE_AZAUTHSTORE,
NULL,
AzStoreAttrs,
lPersistFlags
);
//
// If there were not enough resources and we are opening the policy
// for the first time, then return error.
// Else, remember the error and press on
//
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpReadADStoreObject failed: %ld\n",
WinStatus
));
goto Cleanup;
}
WinStatus = AzpADReadAzStoreChildren(
pContext,
pObject,
lPersistFlags
);
//
// If insufficient resources and AZPE_FLAGS_PERSIST_OPEN
// then return error. Else press on.
//
if (!AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual)) {
AzPrint(( AZD_AD,
"AzpReadADStore: AzpADReadAzStoreChildren failed: %ld\n",
WinStatus
));
goto Cleanup;
}
} else { // if ( pObjectEntry != NULL )
//
// If the entries returned for the authorization store search
// are null, then the user has not been able to read the
// authorization store object. We need to return with an error
// ERROR_DS_INSUFF_ACCESS_RIGHTS
//
WinStatusActual = ERROR_DS_INSUFF_ACCESS_RIGHTS;
}
Cleanup:
pContext = NULL;
pObject = NULL;
//
// Need to release these resources
//
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
if ( pResult != NULL ) {
ldap_msgfree( pResult );
}
return WinStatusActual;
}
DWORD
AzpADReadAzStoreChildren(
IN PAZP_AD_CONTEXT pContext,
IN AZPE_OBJECT_HANDLE pParentObject,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine reads in childern of the authorization store object
Argumetns:
pContext - Context for the LDAP AD provider
pChildFilter - Child filter to search the base DN on
ObjectType - Identifies the object type that we are trying to read
pParentObject - Pointer to the parent object, AzAuthorizationStore
lPersistFlags - lPersistFlags from the persist engine describing the
operation
Return Values:
NO_ERROR - The object was read from the DS and into the cache successfully
Other status codes (ERROR_NOT_ENOUGH_MEMORY, Ldap status codes)
--*/
{
DWORD WinStatus = 0;
DWORD WinStatusActual = 0;
PLDAPSearch pSearchHandle = NULL;
//
// Validation
//
ASSERT( pContext != NULL );
//
// Read in the AzApplicationGroup objects under the AzAuthorizationStore
// object from the Applicaton Group container that exists in AD store.
//
WinStatus = AzpReadADObjectContainer(
pContext,
pContext->pContextInfo,
GROUP_OBJECT_CONTAINER_NAME_PREFIX,
GROUP_OBJECT_CONTAINER_NAME_PREFIX_LENGTH,
AZ_AD_APP_GROUP_FILTER,
OBJECT_TYPE_GROUP,
pParentObject,
lPersistFlags
);
if (!AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual)) {
AzPrint((
AZD_AD,
"AzpADReadAzStoreChildren: Failed to read Application Groups: %ld\n",
WinStatus
));
}
//
// Search the AzAuthorizationStore object for all Application objects
//
pSearchHandle = ldap_search_init_page(
pContext->ld,
pContext->pContextInfo,
LDAP_SCOPE_ONELEVEL,
AZ_AD_APPLICATION_FILTER,
AllObjectReadAttrs[OBJECT_TYPE_APPLICATION],
FALSE,
pContext->pLdapControls,
NULL,
0,
0,
NULL
);
if ( pSearchHandle == NULL ) {
WinStatus = LdapMapErrorToWin32( LdapGetLastError() );
if (!AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual)) {
AzPrint(( AZD_AD,
"AzpADReadAzStoreChildren: Failed to create paged result handle: %ld\n",
WinStatus
));
goto Cleanup;
}
} else {
WinStatus = AzpADReadPagedResult(
pContext,
pSearchHandle,
pParentObject,
OBJECT_TYPE_APPLICATION,
lPersistFlags
);
//
// If insufficient resources and AZPE_FLAGS_PERSIST_OPEN
// then return error. Else press on.
//
if (!AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual)) {
AzPrint(( AZD_AD,
"AzpADReadAzStoreChildren: AzpADReadPagedResult failed: %ld\n",
WinStatus
));
goto Cleanup;
}
ldap_search_abandon_page( pContext->ld, pSearchHandle );
pSearchHandle = NULL;
} // if ( pSearchHandle )
Cleanup:
if ( pSearchHandle != NULL ) {
ldap_search_abandon_page( pContext->ld, pSearchHandle );
}
return WinStatusActual;
}
DWORD
AzpADReadPagedResult(
IN PAZP_AD_CONTEXT pADContext,
IN LDAPSearch *pSearchHandle,
IN AZPE_OBJECT_HANDLE ParentObjectHandle,
IN ULONG ChildObjectType,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine reads in the page results for the passed in LDAP search handle
Arguments:
pADContext - Context for the AD store provider
pSearchHandle - Handle to the page search results
ParentObjectHandle - Handle to the object on which the search was initially performed
ChildObjectType - Type of object for child
lPersistFlags - Flags from the persistence provider
Return Value:
NO_ERROR - The operation succeeded
ERROR_NOT_ENOUGH_MEMORY - not enough memory
Other status code
--*/
{
DWORD WinStatus = 0;
DWORD WinStatusActual = 0;
ULONG LdapStatus = 0;
LDAPMessage *pObjectResult = NULL;
LDAPMessage *pObjectEntry = NULL;
ULONG NumberOfEntries = 0;
AZPE_OBJECT_HANDLE pObject = NULL;
//
// Validation
//
AZASSERT( pADContext != NULL || pSearchHandle != NULL );
//
// Loop reading all the paged results from the DS
//
while ( (LdapStatus = ldap_get_next_page_s( pADContext->ld,
pSearchHandle,
NULL,
AZ_AD_PAGE_SEARCH_COUNT,
NULL,
&pObjectResult
)) == LDAP_SUCCESS ) {
NumberOfEntries = ldap_count_entries( pADContext->ld, pObjectResult );
if ( NumberOfEntries > 0 ) {
//
// get a handle to the first object
//
pObjectEntry = ldap_first_entry( pADContext->ld, pObjectResult );
while ( pObjectEntry != NULL ) {
WinStatus = AzpReadADStoreObject(
pADContext,
pADContext->ld,
pObjectEntry,
&pObject,
ChildObjectType,
ParentObjectHandle,
ObjectAttributes[ChildObjectType].pObjectAttrs,
lPersistFlags
);
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpADReadPagedResult: Reading of child object"
"failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// if we are updating the cache, and the object's children have
// been lazy loaded, then update the children as well
//
if ( (lPersistFlags & AZPE_FLAGS_PERSIST_UPDATE_CACHE)!=0 &&
AdAzrolesInfo->AzpeUpdateChildren(pObject)) {
WinStatus = AzpADPersistUpdateChildrenCache(
(AZPE_PERSIST_CONTEXT) pADContext,
pObject,
lPersistFlags
);
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpADReadPagedResult: Updating children failed: %ld\n",
WinStatus
));
goto Cleanup;
}
}
if ( pObject != NULL ) {
AdAzrolesInfo->AzpeObjectFinished( pObject, WinStatus );
pObject = NULL;
}
pObjectEntry = ldap_next_entry( pADContext->ld, pObjectEntry );
} //while ( pObjectEntry != NULL )
} // if ( NumberOfEntries > 0 )
if ( pObjectResult != NULL ) {
ldap_msgfree( pObjectResult );
pObjectResult = NULL;
}
} // while ( ldap_get_next_page_s() )
if ( LdapStatus != LDAP_SUCCESS && LdapStatus != LDAP_NO_RESULTS_RETURNED ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
if (!AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual)) {
AzPrint(( AZD_AD,
"AzpADReadPagedResult: Failed to read paged LDAP result: %ld\n",
WinStatus
));
goto Cleanup;
}
}
Cleanup:
if ( pObjectResult != NULL ) {
ldap_msgfree( pObjectResult );
}
if ( pObject != NULL ) {
AdAzrolesInfo->AzpeObjectFinished( pObject, WinStatus );
pObject = NULL;
}
return WinStatusActual;
}
DWORD
AzpReadADObjectContainer(
IN PAZP_AD_CONTEXT pContext,
IN PWCHAR pParentDN,
IN PWCHAR pContainerPrefix,
IN ULONG lPrefixLength,
IN PWCHAR pChildFilter,
IN ULONG lObjectType,
IN AZPE_OBJECT_HANDLE pParentObject,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine reads containers for objects such as AzOperation,
AzTask, AzRole, and AzApplicationGroup
Arguments:
pContext - Context for the store
pParentDN - DN for the parent
pContainerPrefix - Prefix for the container that needs to be read
lPrefixLength - Length of the container prefix
pChildFilter - Filter for the child objects that need to be read
lObjectType - Type of object to be read
pParentObject - Handle to the parent object
lPersistFlags - Flags from the persist engine describing the operation
Return Values:
NO_ERROR - The object(s) were read successfully
ERROR_NOR_ENOUGH_MEMORY
ERROR_DS_NO_SUCH_OBJECT - The container does not exist and the AD store is in
a corrupted state
Other status codes from LDAP routines
--*/
{
UNREFERENCED_PARAMETER(lPrefixLength);
DWORD WinStatus = 0;
DWORD WinStatusActual = 0;
LPWSTR pContainerDN = NULL;
LPCWSTR pParentStart = NULL;
LPCWSTR pParentEnd = NULL;
LDAPSearch *pSearchHandle = NULL;
//
// Build the DN for the Container object using the passed in prefix
//
pParentStart = pParentDN + BUILD_CN_PREFIX_LENGTH;
pParentEnd = AzpGetAuthorizationStoreParent( pParentStart );
if (NULL == pParentEnd)
{
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
pParentEnd--;
pContainerDN = (PWCHAR) AzpAllocateHeap( (BUILD_CN_PREFIX_LENGTH + wcslen(pContainerPrefix) +
(pParentEnd-pParentStart) + BUILD_CN_SUFFIX_LENGTH +
wcslen(pParentDN) + 1) * sizeof( WCHAR ),
"CONTDN" );
if ( pContainerDN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( pContainerDN, BUILD_CN_PREFIX );
wcscat( pContainerDN, pContainerPrefix );
wcsncat( pContainerDN, pParentStart, (pParentEnd-pParentStart));
wcscat( pContainerDN, BUILD_CN_SUFFIX );
wcscat( pContainerDN, pParentDN );
//
// Now search the Container for AzMan objects specified in filter
//
pSearchHandle = ldap_search_init_page(
pContext->ld,
pContainerDN,
LDAP_SCOPE_ONELEVEL,
pChildFilter,
AllObjectReadAttrs[lObjectType],
FALSE,
pContext->pLdapControls,
NULL,
0,
0,
NULL
);
if ( pSearchHandle == NULL ) {
WinStatus = LdapMapErrorToWin32( LdapGetLastError() );
goto Cleanup;
} else {
WinStatus = AzpADReadPagedResult(
pContext,
pSearchHandle,
pParentObject,
lObjectType,
lPersistFlags
);
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpReadADObjectContainer: Reading of child object"
"failed: %ld\n",
WinStatus
));
goto Cleanup;
}
ldap_search_abandon_page( pContext->ld, pSearchHandle );
pSearchHandle = NULL;
} // if ( pSearchHandle )
WinStatusActual = WinStatus;
Cleanup:
if ( pSearchHandle != NULL ) {
ldap_search_abandon_page( pContext->ld, pSearchHandle );
}
if ( pContainerDN != NULL ) {
AzpFreeHeap( pContainerDN );
}
return WinStatusActual;
}
DWORD
AzpReadADStoreForCommonData(
IN PAZP_AD_CONTEXT pContext,
IN LDAP* pLdapHandle,
IN LDAPMessage* pEntry,
IN ULONG ObjectType,
IN AZPE_OBJECT_HANDLE pParentObject,
OUT AZPE_OBJECT_HANDLE *ppObject,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine creates a new object of type object type (if not
AzAuthorizationStore) and populates it with common data information such as
description, GUID (for non authorization store objects).
Arguments:
pContext - context for the store
pLdapHandle - Handle to the DS policy store
pEntry - LDAPMessage structure containing all the property information
ObjectType - Type of object to be created
pParentObject - Handle to the parent object in the cache
ppObject - Handle to the object to be created and populated
lPersistFlags - lPersistFlags from the persist engine describing the
operation
Return Values:
NO_ERROR - The object was created and populated successfully
ERROR_OBJECT_ALREADY_EXISTS - An object with the same name already
exists
--*/
{
DWORD WinStatus = 0;
LPWSTR ObjectName = NULL;
LDAP_BERVAL **ppBerVal = NULL;
PWCHAR *ppValueList = NULL;
ULONG i = 1; // to read desciption and application data
//
// Validation
//
ASSERT( pEntry != NULL );
//
// Initialization
//
//
// No need to create object if it is an AzAuthorizationStore type
// This object has already been created by the core
//
if ( ObjectType != OBJECT_TYPE_AZAUTHSTORE ) {
//
// Validate parent object
//
ASSERT( pParentObject != NULL );
//
// Obtain the name of the object from
// DS to pass to the object creation in cache routine
//
WinStatus = AzpInitializeObjectName( pLdapHandle,
&ObjectName,
pEntry,
ObjectType );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpReadADStoreForCommonData: AzpInitializeObjectName failed"
": %ld\n",
WinStatus
));
goto Cleanup;
}
//
// Get the GUID for this object
//
ppBerVal = ldap_get_values_len( pLdapHandle,
pEntry,
AZ_AD_OBJECT_GUID
);
if ( ppBerVal == NULL ) { // error in recovering GUID
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Create object in the cache
//
WinStatus = AdAzrolesInfo->AzpeCreateObject(
pParentObject,
ObjectType,
ObjectName,
(GUID*)(ppBerVal[0]->bv_val),
lPersistFlags,
ppObject
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpReadADStoreForCommonData: AzpeCreateObject failed"
": %ld\n",
WinStatus
));
goto Cleanup;
}
//
// make sure that the object was infact created in the cache
//
ASSERT ( *ppObject != NULL );
//
// for AzApplication and AzScope objects, set the pGuidCN
// attribute so that it may be used to identify them later on
// This is done if the objects are being created (no need for
// when just the object is being refreshed).
//
if ( (lPersistFlags & AZPE_FLAGS_PERSIST_OPEN) &&
(ObjectType == OBJECT_TYPE_APPLICATION ||
ObjectType == OBJECT_TYPE_SCOPE) ) {
//
// Get the CN
//
ppValueList = ldap_get_values(
pLdapHandle,
pEntry,
AZ_AD_OBJECT_CN
);
if ( ppValueList == NULL ) {
//
// Error in recovering CN
//
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
LPWSTR TempString;
WinStatus = AzpCreateGuidCN(
&TempString,
*ppValueList
);
AdAzrolesInfo->AzpeSetProviderData( *ppObject, (PVOID)TempString );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpReadADStoreForCommonData: AzpADCreateGuidCN failed"
" for %ws: %ld\n",
ObjectName,
WinStatus
));
goto Cleanup;
}
}
} // if ( ObjectType != OBJECT_TYPE_AZAUTHSTORE )
//
// Get the description and application data and populate those
// properties for the newly created object in cache. Applicatio
// data does not exist AzApplicationGroup objects, and hence no need
// to read that attribute if object is AzApplicationGroup
//
while ( CommonAttrs[i].AttrType != AZ_AD_END_LIST ) {
if ( (CommonAttrs[i].AttrType == AZ_PROP_APPLICATION_DATA &&
ObjectType != OBJECT_TYPE_GROUP) ||
(CommonAttrs[i].AttrType != AZ_PROP_APPLICATION_DATA) ) {
WinStatus = AzpReadAttributeAndSetProperty(
pContext,
pEntry,
pLdapHandle,
*ppObject,
CommonAttrs[i].AttrType,
CommonAttrs[i].Attr,
ENUM_AZ_BSTR,
lPersistFlags
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpReadADStoreForCommonData: Read description failed"
": %ld\n",
WinStatus
));
goto Cleanup;
}
}
i++;
}
WinStatus = NO_ERROR;
Cleanup:
//
// release resources
//
AdAzrolesInfo->AzpeFreeMemory( ObjectName );
if ( ppBerVal != NULL ) {
ldap_value_free_len( ppBerVal );
}
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
return WinStatus;
}
DWORD
AzpInitializeObjectName(
IN LDAP* pLdapH,
OUT LPWSTR *pObjectName,
IN LDAPMessage* pEntry,
IN ULONG ObjectType
)
/*++
Routine Description:
This routine gets the name of the object from AD so that the object
may be created in cache
Arguments:
pLdapH - Handle to the DS policy store
pObjectName - The retrieved object name
pEntry - LDAPMessage structure from which to retrieve the name
ObjectType - Type of object whose name is to be retrieved. For
AzApplication, AzScope, retrieve AZ_AD_APPLICATION_NAME,
AZ_AD_SCOPE_NAME.
For all other objects retrieve AZ_AD_OBJECT_CN.
Returns:
NO_ERROR - There was no error in retrieving the name
Other status codes
--*/
{
DWORD WinStatus = 0;
ULONG StringSize;
PWCHAR *ppValueList = NULL;
PWCHAR pTempObjectName = NULL;
//
// Depending on type of objects, retrieve name attribute
// accordingly
//
pTempObjectName = AZ_AD_OBJECT_NAMES[ObjectType];
//
// Get the list of values for the attribute entry "cn"
// For AzApplication, AzScope, we need to get
// the attributes msDS-AzApplicationName, msDS-AzScopeName
//
ppValueList = ldap_get_values(
pLdapH,
pEntry,
pTempObjectName
);
if ( ppValueList == NULL ) {
//
// error in recovering name
//
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Allocate a buffer to return the string in
StringSize = (ULONG)((wcslen( *ppValueList )+1) * sizeof(WCHAR));
*pObjectName = (LPWSTR) AdAzrolesInfo->AzpeAllocateMemory( StringSize );
if ( *pObjectName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
RtlCopyMemory( *pObjectName, *ppValueList, StringSize );
WinStatus = NO_ERROR;
Cleanup:
//
// Release the resource
//
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
return WinStatus;
}
DWORD
AzpReadADStoreObject(
IN PAZP_AD_CONTEXT pContext,
IN LDAP* pLdapHandle,
IN LDAPMessage* pEntry,
IN OUT AZPE_OBJECT_HANDLE *ppObject,
IN ULONG ObjectType,
IN AZPE_OBJECT_HANDLE pParentObject,
IN AZ_AD_ATTRS Attrs[],
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine reads & creates an object in cache (if needed) and then
reads in the values of the attributes for Az object
from the AD store into the local cache.
Arguments:
pLdapHandle - A handle to the AD store to read in the attributes into
the cache
pEntry - Buffer storing the read attributes from the calling function
ppObject - Handle to the object in the cache
ObjectType - Type of the object
pParentObject - Pointer to the parent object
Attrs - List of attributes that need to read for the object of type
objectType
lPersistFlags - lPersistFlags from the persist engine describing the
operation
Return Value:
NO_ERROR - The values were read into the cache successfully
Other status code returned from called functions
--*/
{
DWORD WinStatus = 0;
DWORD WinStatusActual = 0;
BOOL IsWritable;
BOOL CanCreateChildren = FALSE;
ULONG i; // to traverse the list of attributes to be read
PWCHAR *ppValueList = NULL;
//
// Create/read an object into cache and populate
// it with property types common to all objects
//
WinStatus = AzpReadADStoreForCommonData(
pContext,
pLdapHandle,
pEntry,
ObjectType,
pParentObject,
ppObject,
lPersistFlags
);
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ||
*ppObject == NULL ) {
AzPrint(( AZD_AD,
"AzpReadADStoreObject: Object creation and common"
" data read failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// will read the uSNChanged attributes
//
if (ObjectType == OBJECT_TYPE_AZAUTHSTORE && lPersistFlags & AZPE_FLAGS_PERSIST_OPEN)
{
pContext->ullUSNChanged = AzpADReadUSNChanged(pLdapHandle, pEntry, &(pContext->HasObjectVersion) );
}
//
// Object was created/read successfully.
// Now read in other properties for it.
//
for ( i = 0; Attrs[i].AttrType != AZ_AD_END_LIST; i++ ) {
WinStatus = AzpReadAttributeAndSetProperty( pContext,
pEntry,
pLdapHandle,
*ppObject,
Attrs[i].AttrType,
Attrs[i].Attr,
Attrs[i].DataType,
lPersistFlags
);
//
// If there were insufficient resources, then fail for
// AZPE_FLAGS_PERSIST_OPEN. Update should press on, toherwise
// no progress would ever be made in updating the cache.
//
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpReadAttributeAndSetProperty failed: %ld\n",
WinStatus
));
goto Cleanup;
}
}
//
// Get the DN of the object from the LDAPMessage structure. If
// the GUIDized CN name of an AzApplication or AzScope object is
// NULL, then we have reached here from a code path for an update
// cache. Extract the Guidized CN name from the DN and store it
// for AzApplication and AzScope objects
//
// Populate the policy admins/readers for the container
// objects
//
if ( IsContainerObject( ObjectType ) ) {
PWCHAR RealDn = NULL;
//
// Get the DN
//
ppValueList = ldap_get_values(
pLdapHandle,
pEntry,
AZ_AD_OBJECT_DN
);
if ( ppValueList == NULL ) {
//
// There should be a Distinguished name for every object
// If we failed to retrieve it, that means there was a lack of
// memory is storing/retrieving the value
//
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
goto Cleanup;
}
}
if ( ppValueList != NULL ) {
//
// the DN could be in the format of <GUID=...>;CN=...
// extract the <GUID part out before passing over to the next function
//
RealDn = wcschr(*ppValueList, L';');
if ( RealDn == NULL ) {
//
// if <GUID...>; format is not there, use the whole string as DN
//
RealDn = *ppValueList;
} else {
//
// move to the next char for CN=...
//
RealDn++;
}
WinStatus = AzpApplyPolicyAcls(
pContext,
*ppObject,
RealDn,
lPersistFlags,
FALSE ); // Don't just do PolicyAdmins
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpReadADStoreObject: Failed to apply"
" policy ACLs: %ld\n",
WinStatus
));
goto Cleanup;
}
}
//
// Compute the GUIDized CN name of the object we read.
//
if ( ObjectType == OBJECT_TYPE_APPLICATION ||
ObjectType == OBJECT_TYPE_SCOPE ) {
LPWSTR TempString;
//
// If we don't already know the GUIDized CN,
// build it from the DN we read.
//
TempString = (LPWSTR) AdAzrolesInfo->AzpeGetProviderData( *ppObject );
if ( TempString == NULL ) {
LPWSTR CnEnd;
ULONG CnLen;
//
// If the DN couldn't be read,
// that's fatal. We really nead the name.
//
if ( RealDn == NULL ) {
if ( WinStatusActual == NO_ERROR ) {
WinStatusActual = ERROR_NOT_ENOUGH_MEMORY;
}
goto Cleanup;
}
//
// Truncate the extraneous data from the DN
//
// RealDn is of the form CN=Guid,...
// Truncate the ...
//
CnEnd = wcschr( RealDn, L',' );
if ( CnEnd == NULL ) {
if ( WinStatusActual == NO_ERROR ) {
WinStatusActual = ERROR_INVALID_NAME;
}
goto Cleanup;
}
CnEnd ++;
CnLen = (ULONG)(CnEnd-RealDn);
//
// Allocate a buffer for the string
//
TempString = (LPWSTR) AzpAllocateHeap( (CnLen+1) * sizeof(WCHAR), "BLDCN4" );
if ( TempString == NULL ) {
if ( WinStatusActual == NO_ERROR ) {
WinStatusActual = ERROR_NOT_ENOUGH_MEMORY;
}
goto Cleanup;
}
//
// Save the CN for later use
//
wcsncpy( TempString, RealDn, CnLen );
TempString[CnLen] = '\0';
AdAzrolesInfo->AzpeSetProviderData( *ppObject, (PVOID)TempString );
}
}
} // if IsContainerObject
//
// A check is made if the object is writable or not. If the object is of a non-container type, then
// the parents IsWritable property is read. If that is true, then the thought is that if the parent
// object is writable, then all its children object are writable as well.
//
// However, for delegated scope objects, the object is not writable but children for it can
// be created. Figure this out.
//
WinStatus = AzpADPersistWritable(
pContext,
*ppObject,
pEntry,
&IsWritable,
&CanCreateChildren );
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpReadADStoreObject: AzpADPersistWritable failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// Tell the core about the object options
//
WinStatus = AzpADSetObjectOptions(
pContext,
*ppObject,
lPersistFlags,
IsWritable,
CanCreateChildren );
if ( !AzpPressOn(WinStatus, lPersistFlags, &WinStatusActual) ) {
AzPrint(( AZD_AD,
"AzpADSetObjectOptions failed: %ld\n",
WinStatus
));
goto Cleanup;
}
Cleanup:
//
// If error has occured, return to calling function with WinStatus
// for cleanup
//
return WinStatusActual;
}
DWORD
AzpReadAttributeAndSetProperty(
IN PAZP_AD_CONTEXT pContext,
IN LDAPMessage *pAttrEntry,
IN LDAP* pLdapH,
IN OUT AZPE_OBJECT_HANDLE pObject,
IN ULONG AttrType,
IN LPWSTR pAttr,
IN ULONG DataType,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine reads the values from a passed LDAPMessage structure, and
calls persistence layer API to update the cache. Link attributes need
to be handled in a different way. When a link attribute is encountered,
it is assumed that the linked object is already loaded in cache
(sequence of loading objects followed).
Set the object list accordingly and add the object referenced by
AZ_AD_OBJECT_NAME accordingly.
Arguments:
pContext - context for the store
pAttrEntry - Pointer to LDAPMessage to read values from
pLdapH - Handle to the AD store from where the policy was read
pObject - Pointer to the object in the cache under which
which needs to be modified
AttrType - Property ID of attribute of object of type ObjectType that
needs to to filled in
pAttr - Value for attribute of type AttrType that will be filled in
DataType - Data type of attribute value
lPersistFlags - lPersistFlags from the persist engine describing the
operation
Return Value:
NO_ERROR - The attribute for the particular object was successfully
populated
Other status codes returned from ldap_* and object set property calls
--*/
{
DWORD WinStatus = 0;
PWCHAR *ppValueList = NULL;
LONG LongTypeData = 0;
PVOID pVoidTypeData = NULL;
BOOL bBoolTypeData = FALSE;
//
// Check if this is a linked attribute, and then call the routine to set
// the linked attribute
//
switch ( AttrType ) {
case AZ_PROP_TASK_OPERATIONS:
case AZ_PROP_TASK_TASKS:
case AZ_PROP_ROLE_MEMBERS:
case AZ_PROP_ROLE_OPERATIONS:
case AZ_PROP_ROLE_TASKS:
case AZ_PROP_GROUP_MEMBERS:
case AZ_PROP_GROUP_NON_MEMBERS:
WinStatus = AzpReadLinkedAttribute(
pContext,
pLdapH,
pAttrEntry,
pObject,
AttrType,
pAttr,
lPersistFlags
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpReadAttributeAndSetProperty:"
"AzpADReadLinkedAttribute failed for attribute %ws "
"of %ws: %ld\n",
pAttr,
L"<Unknown>", // AzpeObjectName(pObject),
WinStatus
));
}
goto Cleanup;
default:
break;
}
//
// Get the list of values for the attribute entry
//
ppValueList = ldap_get_values(
pLdapH,
pAttrEntry,
pAttr
);
if ( ppValueList != NULL ) {
//
// Need to convert the PWCHAR attribute value to its true
// type to be stored in the cache.
//
switch (DataType) {
case ENUM_AZ_LONG:
LongTypeData = _wtol( *ppValueList );
pVoidTypeData = (PVOID) &LongTypeData;
break;
case ENUM_AZ_GROUP_TYPE:
LongTypeData = _wtol( *ppValueList );
if ( LongTypeData == AZ_AD_BASIC_GROUP ) {
LongTypeData = AZ_GROUPTYPE_BASIC;
} else {
LongTypeData = AZ_GROUPTYPE_LDAP_QUERY;
}
pVoidTypeData = (PVOID) &LongTypeData;
break;
case ENUM_AZ_BSTR:
pVoidTypeData = (PVOID) *ppValueList;
break;
case ENUM_AZ_BOOL:
if ( !_wcsicmp( *ppValueList, L"TRUE" ) ) {
bBoolTypeData = TRUE;
pVoidTypeData = (PVOID) &bBoolTypeData;
} else {
bBoolTypeData = FALSE;
pVoidTypeData = (PVOID) &bBoolTypeData;
}
break;
default:
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
WinStatus = AdAzrolesInfo->AzpeSetProperty(
pObject,
lPersistFlags,
AttrType,
pVoidTypeData
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
} // if ( ppValueList != NULL )
WinStatus = NO_ERROR;
Cleanup:
//
// Free up used resource
//
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
return WinStatus;
}
DWORD
AzpReadLinkedAttribute(
IN PAZP_AD_CONTEXT pContext,
IN LDAP* pLdapH,
IN LDAPMessage *pAttrEntry,
IN OUT AZPE_OBJECT_HANDLE pObject,
IN ULONG AttrType,
IN LPWSTR pAttr,
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine reads the linked attributes of objects and stores the
SID or GUID value in the linked attribute of the cache object linking
to them.
Arguments:
pContext - context for the store
pAttrEntry - Pointer to LDAPMessage to read values from
pLdapH - Handle to the AD store from where the policy was read
pObject - Handle to the object in the cache under which
which needs to be modified
AttrType - Property ID of attribute of object of type ObjectType that needs
to filled in
pAttr - Value for attribute of type AttrType that will be filled in
DataType - Data type of attribute value
lPersistFlags - lPersistFlags from the persist engine describing the
operation
Return Value:
NO_ERROR - The attribute for the particular object was successfully
populated
Other status codes
--*/
{
DWORD WinStatus = 0;
PWCHAR *ppValueList = NULL;
PWCHAR *ppV = NULL;
GUID guid;
PSID pSid = NULL;
ULONG lTempAttrType = 0;
//
// Validation
//
ASSERT( pObject != NULL );
ASSERT( pAttrEntry != NULL );
//
// Get the list of values for the attribute entry
//
DWORD dwCurrentStart = 0;
DWORD dwReadCount = 0;
LDAPMessage *pResult = NULL;
LDAPMessage *pObjectEntry = NULL;
LPWSTR pObjectDN = NULL;
//
// We may have to read ranged attribute
//
LPCWSTR pwsFmt = L"%s;range=%d-%d";
LPCTSTR pwsFmtRem = L"%s;range=%d-*";
//
// Here 21 is for the 2 maximum lengths DWORD decimal integer (10 each) and 1 for zero terminator
// Actually, this length is more than needed.
//
size_t Length = wcslen(pAttr) + wcslen(pwsFmt) + 21;
LPWSTR pwszRangedAttr = (LPWSTR) AzpAllocateHeap( sizeof(WCHAR) * Length, "RNGATTR" );
if (pwszRangedAttr == NULL)
{
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
while (WinStatus == NO_ERROR)
{
memset(pwszRangedAttr, 0, sizeof(WCHAR) * Length);
wnsprintf(pwszRangedAttr, (int)Length, pwsFmt, pAttr, dwCurrentStart, dwCurrentStart + MAX_RANGE_ATTR_READ_ATTEMPT - 1);
BOOL bTryRange = (dwReadCount == MAX_RANGE_ATTR_READ_ATTEMPT);
dwReadCount = 0;
if (!bTryRange)
{
ppValueList = ldap_get_values(
pLdapH,
pAttrEntry,
pAttr
);
}
else
{
PWCHAR pwszRangedAttrList[] = {pwszRangedAttr, NULL};
if ( pResult != NULL )
{
ldap_msgfree( pResult );
pResult = NULL;
}
if (pObjectDN == NULL)
{
//
// build only once.
//
WinStatus = AzpADBuildDN(
pContext,
pObject,
&pObjectDN,
NULL,
FALSE,
NULL
);
if (WinStatus != NO_ERROR)
{
goto Cleanup;
}
}
//
// we need to do ranged search using the current ranged attribute
//
BOOL bAlreadyRetried = FALSE;
TryAgain:
LONG LdapStatus = ldap_search_ext_s(
pLdapH,
pObjectDN,
LDAP_SCOPE_BASE,
AZ_AD_ALL_CLASSES,
pwszRangedAttrList,
0,
pContext->pLdapControls,
NULL,
NULL,
0,
&pResult
);
if ( LdapStatus != LDAP_SUCCESS )
{
if (LdapStatus != LDAP_OPERATIONS_ERROR &&
LdapStatus != LDAP_NO_RESULTS_RETURNED &&
bAlreadyRetried)
{
WinStatus = LdapMapErrorToWin32( LdapStatus );
goto Cleanup;
}
}
else
{
pObjectEntry = ldap_first_entry( pLdapH, pResult );
}
if (pObjectEntry != NULL)
{
ppValueList = ldap_get_values( pLdapH, pObjectEntry, pwszRangedAttr );
}
if ( (ppValueList == NULL || *ppValueList == NULL) && !bAlreadyRetried )
{
//
// Believe or not, we may fail to read the last batch when the batch
// has less than 1500 values. Only in this case can we read the values
// with open ended wildcard. Not well designed implementation
//
//
// Re-build the wildcard attribute
//
memset(pwszRangedAttr, 0, sizeof(WCHAR) * Length);
wnsprintf(pwszRangedAttr, (int)Length, pwsFmtRem, pAttr, dwCurrentStart);
//
// don't leak our last search result
//
if ( pResult != NULL )
{
ldap_msgfree( pResult );
pResult = NULL;
}
bAlreadyRetried = TRUE;
goto TryAgain; // i feel this is a good use of goto.
}
}
if ( ppValueList != NULL ) {
ppV = ppValueList;
if (*ppV == NULL)
{
//
// When we start reading the first batch, we don't know that we need
// to read the ranged attribute. But a non-null empty list indicates
// that we need to try the ranged reading. After this, we will already
// know that we need to do ranged reading.
//
ppValueList = ldap_get_values( pLdapH, pAttrEntry, pwszRangedAttr );
if ( ppValueList != NULL )
{
ppV = ppValueList;
}
}
while ( *ppV != NULL ) {
//
// value(s) exist for this attribute
// Need to save the current attribute type. This is needed
// because of maintenance of group app members/non-members and
// role app members in the core cache, but no such attribute exists
// in the DS. Both SID (non)members in DS are stored under the same
// attribute. Restore the AttrType for next value at the end of the
// while loop.
//
lTempAttrType = AttrType;
WinStatus = AzpADParseLinkedAttributeValue(
*ppV,
&pSid,
&guid,
&AttrType,
pContext
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpReadLinkedAttribute:"
"AzpADParseLinkedAttributeValue failed for %ws: %ld\n",
*ppV,
WinStatus
));
goto Cleanup;
}
//
// Now add the linked object to the link property
// for this object
//
if ( pSid != NULL ) {
WinStatus = AdAzrolesInfo->AzpeAddPropertyItemSid(
pObject,
lPersistFlags,
AttrType,
pSid );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpReadAttributeAndSetProperty: "
"AzpeAddPropertyItem failed"
": %ld\n",
WinStatus ));
goto Cleanup;
}
LocalFree( pSid );
pSid = NULL;
} else { // if ( pSid!=NULL )
//
// Add the linked object to the link property
// for this object
//
//
// In anticipation for NDNC, we will add all objects without
// SID using its GUIDs.
//
WinStatus = AdAzrolesInfo->AzpeAddPropertyItemGuid(
pObject,
lPersistFlags,
AttrType,
&guid
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADReadAttributeAndSetProperty:"
"AzpeAddPropertyItemGuid failed on"
": %ld\n",
WinStatus ));
goto Cleanup;
}
} // if ( bSidObject ) ... else
//
// increment to the next attribute value
//
dwReadCount++;
ppV++;
AttrType = lTempAttrType;
} // while ( *ppValueList != NULL )
} // if ( ppValueList != NULL )
if ( ppValueList != NULL )
{
ldap_value_free ( ppValueList );
ppValueList = NULL;
}
if (dwReadCount < MAX_RANGE_ATTR_READ_ATTEMPT)
{
break;
}
else
{
dwCurrentStart += MAX_RANGE_ATTR_READ_ATTEMPT;
}
}
WinStatus = NO_ERROR;
Cleanup:
//
// Free up used resource
//
if ( pSid != NULL ) {
LocalFree( pSid );
}
if ( ppValueList != NULL )
{
ldap_value_free ( ppValueList );
}
if ( pResult != NULL )
{
ldap_msgfree( pResult );
}
if (pwszRangedAttr != NULL)
{
AzpFreeHeap(pwszRangedAttr);
}
if (pObjectDN != NULL)
{
AzpFreeHeap(pObjectDN);
}
return WinStatus;
}
DWORD
AzpADParseLinkedAttributeValue(
IN PWCHAR pValue,
OUT PSID *ppSid,
OUT GUID *pGuid,
IN OUT PULONG pAttrType,
IN PAZP_AD_CONTEXT pContext
)
/*++
Routine Description:
This routine parses a linked attribute value to return the GUID or SID.
Arguments:
pValue - The linked attribute value to be parsed
ppSid - Pointer to returned SID if SID exists
pGuid - The returned GUID if SID does not exist
pAttrType - Pointer to type of attribute that is being parsed
pContext - Context for the LDAP provider
Return Values:
NO_ERROR - The value was parsed successfully
ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
LDAPMessage *pResult = NULL;
LDAPMessage *pEntry = NULL;
PWCHAR *ppValueList = NULL;
PWCHAR pSearchDN = NULL;
ULONG lGroupType = 0;
//
// Validation
//
ASSERT( pValue != NULL );
//
// Parse the guid and sid (if any)
//
WinStatus = AzpADGetGuidAndSID(pValue, pGuid, ppSid, &pSearchDN);
if (NO_ERROR != WinStatus)
{
goto Cleanup;
}
//
// If the attribute we are reading happens to be AZ_PROP_*_MEMBERS
// or AZ_PROP_GROUP_NON_MEMBERS, then we need to read the object
// with the specified DN to check their objectType. If they are groups,
// and they are ldap-query based, then their GUID needs to be read, else
// read their SID
//
if ( *pAttrType == AZ_PROP_GROUP_MEMBERS ||
*pAttrType == AZ_PROP_GROUP_NON_MEMBERS ||
*pAttrType == AZ_PROP_ROLE_MEMBERS ) {
//
// Perform a base level search on the DN we are parsing
// The DN is in the form <GUID=xxxxx>;<SID=yyyyy>;<DN>
// We need to extract the <DN> and perform the search on that
//
LdapStatus = ldap_search_s(
pContext->ld,
pSearchDN,
LDAP_SCOPE_BASE,
AZ_AD_ALL_CLASSES,
NULL,
0,
&pResult
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpADParseLinkedAttributeValue: Failed to run search for"
" group type for %ws: %ld\n",
pSearchDN,
WinStatus
));
goto Cleanup;
}
pEntry = ldap_first_entry( pContext->ld, pResult );
if ( pEntry != NULL ) {
ppValueList = ldap_get_values( pContext->ld,
pEntry,
AZ_AD_GROUP_TYPE
);
if ( ppValueList != NULL ) {
//
// We have the group type. Compare it with known
// group types to identify whether SID should be read
// or GUID
//
lGroupType = _wtol( *ppValueList );
if ( lGroupType == AZ_AD_BASIC_GROUP ||
lGroupType == AZ_AD_QUERY_GROUP ) {
//
// we have no need for the SID, free it
//
if (NULL != *ppSid)
{
LocalFree((HLOCAL)*ppSid);
*ppSid = NULL;
}
if ( *pAttrType == AZ_PROP_GROUP_MEMBERS ) {
*pAttrType = AZ_PROP_GROUP_APP_MEMBERS;
} else if ( *pAttrType == AZ_PROP_GROUP_NON_MEMBERS ) {
*pAttrType = AZ_PROP_GROUP_APP_NON_MEMBERS;
} else if ( *pAttrType == AZ_PROP_ROLE_MEMBERS ) {
*pAttrType = AZ_PROP_ROLE_APP_MEMBERS;
}
}
}
}
} // if AttrType is member/non-member/role member
WinStatus = NO_ERROR;
Cleanup:
//
// release local memory
//
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
if ( pResult != NULL ) {
ldap_msgfree( pResult );
}
return WinStatus;
}
DWORD
AzpADGetGuidAndSID (
IN LPCWSTR pwstrValue,
OUT GUID * pGuid,
OUT PSID * ppSid OPTIONAL,
OUT LPWSTR * ppwstrDN
)
/*
Description:
This routine parses a linked attribute value to return the
GUID and SID (if present and requested).
Arguments:
pwstrValue - The linked attribute value to be parsed
pGuid - Receives GUID
ppSid - Receives the SID if requested and present in pwstrValue
ppwstrDN - Point to the DN portion when this function successfully returns.
Return Values:
NO_ERROR - The value was parsed successfully
ERROR_INVALID_PARAMETER
ERROR_NOT_ENOUGH_MEMORY
Note:
1. The returned SID must be released by LocalFree.
2. On error, the passed back ppwstrDN can't be trusted.
*/
{
if ( NULL == pwstrValue || NULL == pGuid || NULL == ppwstrDN )
{
return ERROR_INVALID_PARAMETER;
}
//
// Some good clean initial values
//
if (NULL != ppSid)
{
*ppSid = NULL;
}
*pGuid = GUID_NULL;
*ppwstrDN = NULL;
//
// These tiny array will be used for converting double wchars to
// a BYTE for both binary GUID and SID
//
WCHAR hexByte[3];
hexByte[2] = L'\0';
DWORD dwStatus = NO_ERROR;
//
// We require that GUID is present
//
LPWSTR pwszCur = wcsstr( pwstrValue, GUID_LINK_PREFIX );
if (NULL == pwszCur)
{
return ERROR_INVALID_PARAMETER;
}
//
// skip over the prefix itself
//
pwszCur += GUID_LINK_PREFIX_LENGTH;
//
// Get to the matching suffix
//
LPWSTR pwszEnd = wcsstr( pwszCur, GUIDSID_LINK_SUFFIX );
if (NULL == pwszEnd)
{
return ERROR_INVALID_PARAMETER;
}
else
{
AZASSERT( pwszEnd - pwszCur == sizeof(GUID) * 2 );
PBYTE pbguid = (PBYTE)pGuid;
//
// Decode hex digit stream
//
for ( int i = 0; i < sizeof (GUID); i++ )
{
hexByte[0] = towlower( pwszCur[2 * i] );
hexByte[1] = towlower( pwszCur[2 * i + 1] );
if ( iswxdigit( hexByte[0] ) && iswxdigit( hexByte[1] ) )
{
*pbguid = (BYTE) wcstol( hexByte, NULL , 16 );
pbguid++;
}
else
{
//
// If the byte is not a hex digit, then some error has occured
// Return this to the caller
//
return ERROR_INVALID_PARAMETER;
}
}
}
//
// move pass the suffix and the delimiting ';'
//
pwszCur = pwszEnd + GUIDSID_LINK_SUFFIX_LENGTH + 1;
//
// If no SID is present, then this will be where DN portion starts
//
*ppwstrDN = pwszCur;
//
// Find the SID prefix
//
pwszCur = wcsstr( pwstrValue, SID_LINK_PREFIX );
if ( NULL != pwszCur )
{
pwszCur += SID_LINK_PREFIX_LENGTH;
pwszEnd = wcsstr( pwszCur, GUIDSID_LINK_SUFFIX );
if (NULL == pwszEnd)
{
//
// no matching suffix is invalid
//
dwStatus = ERROR_INVALID_PARAMETER;
}
else
{
//
// if SID requested, then we need to generate the binary SID
// using the byte string.
//
if (NULL != ppSid)
{
BYTE byteSid[SECURITY_MAX_SID_SIZE];
PBYTE pbDest = byteSid;
while ( pwszCur < (pwszEnd - 1) )
{
hexByte[0] = towlower( *pwszCur );
hexByte[1] = towlower( *(pwszCur + 1) );
if ( iswxdigit( hexByte[0] ) && iswxdigit( hexByte[1] ) )
{
*pbDest = (BYTE) wcstol( hexByte, NULL , 16 );
pbDest++;
}
else
{
//
// If the byte is not a hex digit, then some error has occured
// Return this to the caller
//
return ERROR_INVALID_PARAMETER;
}
pwszCur += 2;
//
// We will terminate if we have already passed the max we can take
//
if ( pbDest > byteSid + SECURITY_MAX_SID_SIZE )
{
break;
}
}
DWORD cbSid = GetLengthSid( (PSID)byteSid );
//
// Call LocalFree in calling function
//
*ppSid = (PSID) LocalAlloc( LPTR, cbSid );
if ( *ppSid == NULL )
{
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
CopyMemory( *ppSid, (PSID)byteSid, cbSid );
}
}
//
// move pass the suffix and the delimiting ';'.
// Just in case, GUID follows SID, we don't update the DN position blindly
//
if (*ppwstrDN < pwszEnd)
{
*ppwstrDN = pwszEnd + GUIDSID_LINK_SUFFIX_LENGTH + 1;
}
}
}
return dwStatus;
}
DWORD
AzpApplyPolicyAcls(
IN PAZP_AD_CONTEXT pContext,
IN OUT AZPE_OBJECT_HANDLE pObject,
IN PWCHAR pDN,
IN ULONG lPersistFlags,
IN BOOL OnlyAddPolicyAdmins
)
/*++
Routine Description:
This routine applies the store ACLs into the policy admins and readers
list for the passed in object. If the object is one that can give delegation
rights, then reads in the delegated user's list as well.
Arguments:
pContext - context for the store
pObject - Pointer to object on whom the store ACLs need to be applied
pDN - DN of the object to the ACLs off from.
lPersistFlags - Flags from the persist engine
OnlyAddPolicyAdmins - TRUE if only PolicyAdmins is to be updated in azroles
Return Values:
NO_ERROR - The ACLs were loaded successfully
Other status codes
--*/
{
DWORD WinStatus = 0;
BOOLEAN DoSacl;
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
PSECURITY_DESCRIPTOR pSD = NULL;
//
// Validation
//
ASSERT( pObject != NULL );
ASSERT( pDN != NULL );
//
// Only do the SACL if requested and the caller has privilege
//
DoSacl = !OnlyAddPolicyAdmins && pContext->HasSecurityPrivilege;
//
// Get the AZ_AD_NT_SECURITY_DESCRIPTOR for the object
//
WinStatus = AzpADReadNTSecurityDescriptor(
pContext,
pObject,
pDN,
FALSE, // no need to read authorization store's DS parent
&pSD,
DoSacl, // Optionally read SACL
TRUE // read DACL
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpApplyPolicyAcls: AzpADReadNTSecurityDescriptor failed"
" :%ld\n",
WinStatus
));
goto Cleanup;
}
//
// Set the policy readers, admins and delegated users
//
WinStatus = AdAzrolesInfo->AzpeSetSecurityDescriptorIntoCache(
pObject,
pSD,
lPersistFlags,
(lObjectType == OBJECT_TYPE_SCOPE)?&DelegatedScopeAdminsRights:&PolicyAdminsRights,
OnlyAddPolicyAdmins ? NULL : &PolicyReadersRights,
OnlyAddPolicyAdmins ? NULL : &DelegatedParentReadersInheritRights,
(DoSacl ? &AdSaclRights : NULL)
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADApplyPolicyAcls: AzpeSetSecurityDescriptorIntoCache failed:"
": %ld\n",
WinStatus
));
goto Cleanup;
}
WinStatus = NO_ERROR;
Cleanup:
//
// Free local resources
//
AdAzrolesInfo->AzpeFreeMemory( pSD );
return WinStatus;
}
//
// Routines used by AzpADPersistSubmit to submit
// objects in the cache to the AD policy store
//
DWORD
AzpUpdateADObject(
IN PAZP_AD_CONTEXT pContext,
IN LDAP* pLdapHandle,
IN OUT AZPE_OBJECT_HANDLE pObject,
IN PWCHAR pDN,
IN PWCHAR pObjectClass,
IN AZ_AD_ATTRS ObjectAttrs[],
IN ULONG lPersistFlags
)
/*++
Routine Description:
This routine updates the DS for a object according
to the dirty bits of the object.
Arguments:
pContext - context for the store
pLdapHandle - Handle to the DS policy store
pObject - Object that needs to be updated in the DS. If it is a newly
created object then the DS is read for the GUID value of this
object, and the GUID for this object in the cache is set.
pDN - The DN of the object
pObjectClass - Class of the object in DS
ObjectAttrs - A NULL terminated list of attributes to be updated. Pass
NULL if there are no specific attributes that need to be read
lPersistFlags - lPersistFlags from the persist engine describing the
operation
Return Values:
NO_ERROR - The object was successfully added/modified in the DS
Other status codes (LDAP)
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
LDAPMod **ppAttributeList = NULL;
ULONG i = 0;
LDAPMessage* pResult = NULL;
LDAPMessage* pEntry = NULL;
LDAP_BERVAL **ppBerVal = NULL;
LDAPControl **ppLdapServerControl = NULL;
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
ULONG lDirtyBits = AdAzrolesInfo->AzpeDirtyBits(pObject);
BOOL IsWritable;
BOOL CanCreateChildren = FALSE;
PWCHAR pObjectContainerDN = NULL;
ULONG lIndex = 0;
BOOL bCreateFlag = lDirtyBits & AZ_DIRTY_CREATE;
//
// Validation
//
ASSERT( pObject != NULL );
ASSERT( pDN != NULL );
//
// Initialize the attribute list. If nay linked attribute is being updated,
// then
//
WinStatus = AzpADAllocateAttrHeap(
AZ_AD_MAX_ATTRS,
&ppAttributeList
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Get server controls
//
ppLdapServerControl = pContext->pLdapControls;
//
// Check if this is a new object. If so, then set the
// objectClass of the object being added to DS
//
if ( bCreateFlag ) {
WinStatus = AzpGetAttrsForCreateObject(
pObjectClass,
ppAttributeList
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpUpdateADObject: AzpGetAttrsForCreateObject failed for"
" %ws: %ld\n",
pDN,
WinStatus
));
goto Cleanup;
}
lIndex++;
}
//
// Check if any of the common attributes need to be updated
//
if ( lDirtyBits & AZ_DIRTY_COMMON_ATTRS ) {
WinStatus = AzpGetADCommonAttrs(
pLdapHandle,
pObject,
CommonAttrs,
lPersistFlags,
ppAttributeList,
&lIndex,
bCreateFlag
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpUpdateADObject: AzpGetADCommonAttrs failed"
": %ld\n",
WinStatus
));
goto Cleanup;
}
}
//
// Loop through the attributes. Best is to know what the
// maximum number of attributes any object may have, and break when
// you encounter the end of list attribute for the object
//
for ( i = 0; i < (AZ_AD_MAX_NON_COMMON_ATTRS + 1); i++ ) {
if ( ObjectAttrs[i].AttrType == AZ_AD_END_LIST ) {
//
// No more attributes to be read
//
break;
}
//
// Update only if the particular object attribute is dirty
// If during create, the group type of an AzApplicationGroup
// has not been set dirty, we need to add that to the
// attribute structure. Also during create, for AzOperation objects,
// it might not be necessary that an operation Id be present. However,
// DS expects an operation ID to be present.
//
if ( AzpIsAttrDirty( pObject, ObjectAttrs[i] ) ||
( bCreateFlag &&
( (ObjectAttrs[i].AttrType == AZ_PROP_GROUP_TYPE) ||
(ObjectAttrs[i].AttrType == AZ_PROP_OPERATION_ID) ) ) ) {
//
// Get specific object properties
//
WinStatus = AzpGetSpecificProperty(
pObject,
ppAttributeList,
&lIndex,
ObjectAttrs[i],
lPersistFlags,
bCreateFlag
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpUpdateADObject: Get property failed "
" for %s: %ld\n",
pDN,
WinStatus ));
goto Cleanup;
}
if ( ppAttributeList[lIndex] != NULL ) {
ppAttributeList[lIndex]->mod_op = bCreateFlag ? LDAP_MOD_ADD : LDAP_MOD_REPLACE;
ppAttributeList[lIndex]->mod_type = ObjectAttrs[i].Attr;
lIndex++;
}
} // if ( lDirtyBits & ObjectAttrs[i].lDirtyBit )
} // for (i<AZ_AD_MAX_ATTRS)
//
// Create/modify the DS object with the properties read from cache
// Do so only if scalar properties have changed
//
if ( ppAttributeList[0] != NULL ) {
if ( bCreateFlag ) {
LdapStatus = ldap_add_s(
pLdapHandle,
pDN,
ppAttributeList
);
} else {
LdapStatus = ldap_modify_ext_s(
pLdapHandle,
pDN,
ppAttributeList,
ppLdapServerControl,
NULL
);
}
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpUpdateADObject: Failed to add/modify"
" %ws : %ld %ld\n",
pDN,
WinStatus,
LdapStatus ));
goto Cleanup;
}
PWCHAR Attrs[] = {
AZ_AD_OBJECT_GUID,
AZ_AD_OBJECT_WRITEABLE,
AZ_AD_OBJECT_CHILD_CREATE,
NULL
};
//
// Search the object for base scope level
//
LdapStatus = ldap_search_s(
pLdapHandle,
pDN,
LDAP_SCOPE_BASE,
AZ_AD_ALL_CLASSES,
Attrs,
0,
&pResult
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpUpdateADObject: Failed to search object"
" %s: %ld\n",
pDN,
WinStatus
));
goto Cleanup;
}
pEntry = ldap_first_entry( pLdapHandle, pResult );
if ( pEntry == NULL ) {
//
// should have a value
//
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Now read the GUID of the object in DS and set it for the cached object
// This is read only if the object has been added to the DS and the object is
// not AzAuthorizationStore
//
if ( lObjectType != OBJECT_TYPE_AZAUTHSTORE && bCreateFlag ) {
//
// Retrieve the GUID
//
ppBerVal = ldap_get_values_len(
pLdapHandle,
pEntry,
AZ_AD_OBJECT_GUID
);
//
// There should be no error in recovering the GUID
//
if ( ppBerVal == NULL ) {
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Assign the GUID to the object
//
*AdAzrolesInfo->AzpePersistenceGuid(pObject) = *( (GUID*)ppBerVal[0]->bv_val);
}
//
// Tell azroles whether this object is writable
//
// Also, figure out if children can be created under it
//
WinStatus = AzpADPersistWritable(
pContext,
pObject,
pEntry,
&IsWritable,
&CanCreateChildren );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpUpdateADObject: Read writable failed"
": %ld\n",
WinStatus
));
goto Cleanup;
}
//
// Tell the core about the object options
//
WinStatus = AzpADSetObjectOptions(
pContext,
pObject,
lPersistFlags,
IsWritable,
CanCreateChildren );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
}
if ( bCreateFlag &&
(lObjectType == OBJECT_TYPE_APPLICATION ||
lObjectType == OBJECT_TYPE_AZAUTHSTORE ||
lObjectType == OBJECT_TYPE_SCOPE) ) {
//
// If this newly created DS object is an AzApplication/AzAuthorizationStore/AzScope
// object, then we need to create a child container objects under it that will
// hold child objects such as AzOperation, AzTask, etc except child AzScope
// object, and all AzAuthorizationStore AzApplicationGroup children
//
for ( i = 0; i < OBJECT_TYPE_COUNT; i++ ) {
//
// Since we do not have Op, Task, Role objects as children of
// authorization store, create only container for groups if object is
// authorization store. Also, there are no containers for authorization store,
// application and scope objects. Scope objects do not have any containers
// for operation objects.
//
if ( (!IsContainerObject(i)) &&
( ( i == OBJECT_TYPE_GROUP && lObjectType == OBJECT_TYPE_AZAUTHSTORE ) ||
( i != OBJECT_TYPE_OPERATION && lObjectType == OBJECT_TYPE_SCOPE ) ||
( lObjectType == OBJECT_TYPE_APPLICATION ) ) ) {
WinStatus = AzpADBuildDN(
pContext,
pObject,
&pObjectContainerDN,
pDN,
FALSE, // no AzAuthStore parent DN needed
&AdChildObjectContainers[i]
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
WinStatus = AzpCreateADObject(
pLdapHandle,
pObjectContainerDN
);
if ( WinStatus != NULL ) {
goto Cleanup;
}
AzpFreeHeap( pObjectContainerDN );
pObjectContainerDN = NULL;
}
} // for loop
}
//
// If the object is of type AzAuthorizationStore or AzApplication or AzScope,
// then stamp it with an updated DACL and SACL on the object.
// If the object is of type AzScope, then stamp it with scope admin
// rights (This is so that scope admins are not able to change properties
// of the scope objects. They should be able to read the scope object, and
// create and delete children. No DACL writing is allowed)
//
if ( IsContainerObject( lObjectType ) ) {
WinStatus = AzpUpdateObjectAcls(
pContext,
pObject,
pDN,
lPersistFlags,
TRUE,
(lObjectType==OBJECT_TYPE_SCOPE)?
(PAZP_POLICY_USER_RIGHTS *)&ADDelegatedScopeAdminsRights:(PAZP_POLICY_USER_RIGHTS *)&ADPolicyAdminsRights,
(PAZP_POLICY_USER_RIGHTS *)&ADPolicyReadersRights,
(lObjectType ==
OBJECT_TYPE_SCOPE)?NULL:(PAZP_POLICY_USER_RIGHTS *)&ADDelegatedParentReadersRights
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpUpdateADObject: AzpADUpdateObjectAcls failed for"
" %ws: %ld\n",
pDN,
WinStatus
));
goto Cleanup;
}
}
WinStatus = NO_ERROR;
Cleanup:
//
// Free up resources
//
if ( pObjectContainerDN != NULL ) {
AzpFreeHeap( pObjectContainerDN );
}
if ( ppAttributeList != NULL ) {
AzpADFreeAttrHeap( &ppAttributeList, TRUE );
}
if ( ppBerVal != NULL ) {
ldap_value_free_len( ppBerVal );
}
if ( pResult != NULL ) {
ldap_msgfree( pResult );
}
return WinStatus;
}
DWORD
AzpCreateADObject(
IN LDAP *pLdapHandle,
IN PWCHAR pDN
)
/*++
Routine Description:
This routine creates child container object for operation, task,
group or role objects. These container objects are children of
either application or scope objects.
Arguments:
pLdapHandle - Handle to the DS store
pDN - DN of the container object being created
Return Values:
NO_ERROR - object was created successfully in DS
Other Ldap Status codes
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
ULONG i = 0;
LDAPMod **ppAttributeList = NULL;
//
// validation
//
ASSERT( pDN != NULL );
//
// Set the mandatory attribute needed before adding the object
// to AD store - the objectClass of the object
//
ppAttributeList = (LDAPMod **) AzpAllocateHeap(
sizeof( LDAPMod *) * 2, "ATRLST" );
if ( ppAttributeList == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
ppAttributeList[0] = NULL;
ppAttributeList[1] = NULL;
WinStatus = AzpADAllocateAttrHeapModVals(
&ppAttributeList[0], 2
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
ppAttributeList[0]->mod_type = AZ_AD_OBJECT_CLASS;
ppAttributeList[0]->mod_values[0] =
(PWCHAR) AzpAllocateHeap( ( wcslen( AZ_AD_OBJECT_CONTAINER ) + 1 ) *
sizeof( WCHAR ), "MODVALi" );
if ( ppAttributeList[0]->mod_values[0] == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( ppAttributeList[i]->mod_values[0], AZ_AD_OBJECT_CONTAINER );
ppAttributeList[0]->mod_op = LDAP_MOD_ADD;
//
// Now add the object to DS
//
LdapStatus = ldap_add_s(
pLdapHandle,
pDN,
ppAttributeList
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpCreateADObject: Failed to add object %ws: %ld %ld\n",
pDN,
WinStatus,
LdapStatus
));
goto Cleanup;
}
WinStatus = NO_ERROR;
Cleanup:
//
// Release allocated memory
//
if ( ppAttributeList != NULL ) {
AzpADFreeAttrHeap( &ppAttributeList, TRUE );
}
return WinStatus;
}
DWORD
AzpGetAttrsForCreateObject(
IN PWCHAR pObjectClass,
IN LDAPMod **ppAttributeList
)
/*++
Routine Description:
This routine populates the attribute structure for creation of an object in AD
Arguments:
pObjectClass - Class name for the object
ppAttributeList - Attribute list structure to populate
Return Values:
NO_ERROR - The object was created successfully in AD
Other status codes
--*/
{
DWORD WinStatus = 0;
//
// validation
//
ASSERT( pObjectClass != NULL );
WinStatus = AzpADAllocateAttrHeapModVals(
&ppAttributeList[0], 2
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
ppAttributeList[0]->mod_type = AZ_AD_OBJECT_CLASS;
ppAttributeList[0]->mod_values[0] = (PWCHAR)
AzpAllocateHeap( ( wcslen( pObjectClass ) + 1 ) *
sizeof( WCHAR ), "MODVALi" );
if ( ppAttributeList[0]->mod_values[0] == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( ppAttributeList[0]->mod_values[0], pObjectClass );
ppAttributeList[0]->mod_op = LDAP_MOD_ADD;
WinStatus = NO_ERROR;
Cleanup:
return WinStatus;
}
DWORD
AzpGetADCommonAttrs(
IN LDAP* pLdapHandle,
IN AZPE_OBJECT_HANDLE pObject,
IN AZ_AD_ATTRS ObjectAttrs[],
IN ULONG lPersistFlags,
OUT LDAPMod** ppAttributeList,
IN OUT PULONG plIndex,
IN BOOL bCreateFlag
)
/*++
Routine Description:
This routine updates the common attributes of all objects
Arguments:
pLdapHandle - Handle to the DS policy store
pObject - Object that needs to be updated in the DS
ObjectAttrs - A NULL terminated list of attributes to be updated. Pass
NULL if there are no specific attributes that need to be read
lPersistFlags - lPersistFlags from the persist engine describing the
operation
ppAttributeList - Attribute list to be updated with common attributes
plIndex - number of attributes added to the attribute list structure
bCreateFlag - Object is being created
Return Values:
NO_ERROR - The object was successfully added/modified in the DS
Other status codes (LDAP)
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
ULONG i = 0;
PVOID pVoidStringValue = NULL;
PWCHAR pNewRdn = NULL;
PWCHAR pGuidDN = NULL;
PWCHAR pGuidString = NULL;
BOOL bNameChanged = FALSE;
//
// Validation
//
ASSERT( pObject != NULL );
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
ULONG lDirtyBits = AdAzrolesInfo->AzpeDirtyBits(pObject);
//
// First check if the name has been changed. This will
// result in a call to ldap_rename_ext_s for all objects
// except AzApplication and AzScope whose names are
// stored in a different attribute. This is of course
// when the object is not being created. We need to get
// the GUID of the object and use that as the DN, since the name
// has already changed in cache, but not in the AD store.
//
if ( (lDirtyBits & AZ_DIRTY_NAME) &&
!(bCreateFlag) &&
( (lObjectType != OBJECT_TYPE_APPLICATION) &&
(lObjectType != OBJECT_TYPE_SCOPE) ) ) {
//
// Create the GUIDized DN
//
WinStatus = UuidToString( AdAzrolesInfo->AzpePersistenceGuid(pObject), &pGuidString );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
pGuidDN = (PWCHAR) AzpAllocateHeap( (GUID_LINK_PREFIX_LENGTH +
wcslen( pGuidString ) +
GUIDSID_LINK_SUFFIX_LENGTH + 1) *
sizeof( WCHAR ), "GUIDCN" );
if ( pGuidDN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( pGuidDN, GUID_LINK_PREFIX );
wcscat( pGuidDN, pGuidString );
wcscat( pGuidDN, GUIDSID_LINK_SUFFIX );
//
// Now get the new name for the object
//
WinStatus = AdAzrolesInfo->AzpeGetProperty(
pObject,
lPersistFlags,
AZ_PROP_NAME,
&pVoidStringValue
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpUpdateADCommonAttrs: AzpeGetProperty failed"
" for object name: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// Create the new RDN
//
pNewRdn = (PWCHAR) AzpAllocateHeap(
( wcslen((PWCHAR)pVoidStringValue) +
BUILD_CN_PREFIX_LENGTH + 1) *
sizeof( WCHAR ), "NEWRDN" );
if ( pNewRdn == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( pNewRdn, BUILD_CN_PREFIX );
wcscat( pNewRdn, (PWCHAR)pVoidStringValue );
//
// now call the routine to change the name of the object
//
LdapStatus = ldap_rename_ext_s(
pLdapHandle,
pGuidDN,
pNewRdn,
NULL, // same parent
TRUE,
NULL, // no server side control
NULL // no client side control
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpADUpdateCommonAttrs: Failed to rename %ws"
": %ld\n",
pGuidDN,
WinStatus
));
goto Cleanup;
}
bNameChanged = TRUE;
} // if ( lDirtyBits & AZ_DIRTY_NAME )
//
// Now get the other common attributes if they are dirty and add
// them to the DS object
//
for ( i = 0; i < AZ_AD_COMMON_ATTRS; i++ ) {
if ( ObjectAttrs[i].AttrType == AZ_AD_END_LIST ) {
//
// No more attributes need to be read
//
break;
}
//
// update the attribute
// However, if the attribute is the application data attribute and object
// is AzApplicationGroup, then don't update. AzApplicationGroup objects are
// the only objects that do not have the application data field
// Also, if the name of the object has already changed, then do not update
// it again
//
if (!((lObjectType == OBJECT_TYPE_GROUP) &&
(ObjectAttrs[i].AttrType == AZ_PROP_APPLICATION_DATA)) &&
(lDirtyBits & ObjectAttrs[i].lDirtyBit) ) {
if ( ObjectAttrs[i].AttrType == AZ_PROP_NAME &&
bNameChanged ) {
continue;
}
WinStatus = AzpGetSpecificProperty(
pObject,
ppAttributeList,
plIndex,
ObjectAttrs[i],
lPersistFlags,
bCreateFlag
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADUpdateCommonAttrs: Get property failed "
": %ld\n",
WinStatus ));
goto Cleanup;
}
if ( !bCreateFlag || ppAttributeList[*plIndex] != NULL ) {
ppAttributeList[*plIndex]->mod_op = bCreateFlag ? LDAP_MOD_ADD : LDAP_MOD_REPLACE;
//
// If the object is AzApplication or AzScope
// and the property being set is AZ_PROP_NAME,
// then set the msDS-AzapplicationName or
// msDS-AzScopeName
//
if ( ObjectAttrs[i].AttrType == AZ_PROP_NAME ) {
ppAttributeList[*plIndex]->mod_type = AZ_AD_OBJECT_NAMES[lObjectType];
} else {
ppAttributeList[*plIndex]->mod_type = ObjectAttrs[i].Attr;
}
(*plIndex)++;
}
} // if ( lDirtyBits & ObjectAttrs[i].lDirtyBit )
} //for ( i < AZ_AD_COMMON_ATTRS )
WinStatus = NO_ERROR;
Cleanup:
//
// Free up used resources
//
if ( pGuidDN != NULL ) {
AzpFreeHeap( pGuidDN );
}
if ( pGuidString != NULL ) {
RpcStringFree( &pGuidString );
}
if ( pNewRdn != NULL ) {
AzpFreeHeap( pNewRdn );
}
if ( pVoidStringValue != NULL ) {
AzpFreeHeap( pVoidStringValue );
}
return WinStatus;
}
DWORD
AzpGetSpecificProperty(
IN AZPE_OBJECT_HANDLE pObject,
OUT PLDAPMod *ppAttribute,
IN OUT ULONG *plIndex,
IN AZ_AD_ATTRS ObjectAttr,
IN ULONG lPersistFlags,
IN BOOL bCreateFlag
)
/*++
Routine Description:
This routine reads in specific attributes of objects to
an attribute list array element. This array will be used
by the calling function to update the AD policy store for this
object.
Arguments:
pObject - Handle to the object whose attributes will be read
ppAttribute - Attribute list
plIndex - Index into the attribute list
ObjectAttr - Attribute that needs to be read from cache
lPersistFlags - lPersistFlags from the persist engine describing the
operation
bCreateFlag - TRUE if the object is being created in DS
Return Values:
NO_ERROR - The values was read in to the array element successfully
Other status codes
--*/
{
DWORD WinStatus = 0;
PWCHAR pStringValue = NULL;
WCHAR pStringNumber[32];
PVOID pVoidTypeData = NULL;
LONG lData = 0;
//
// Validation
//
ASSERT( pObject != NULL );
//
// Check if the attribute is a linked attribute. These need
// to be handled in a different way. The problem arises
// when we try to read attributes like msDS-NonMembers for
// Group object. The core differentiates between non-members
// that are AzApplicationGrooups (stored in the Guid list)
// and other non-members like NTGroups/NTUsers. (stored in the
// Sid list). The AD store does not differentiate
// between the two and stores links to each of them in the
// same attribute. So, when we want to get property for
// non-members from the core cache, we need to get both
// the Sid non-members and Guid non-members and store them in
// the same attribute in AD
//
if ( ObjectAttr.DataType == ENUM_AZ_SID_ARRAY ||
ObjectAttr.DataType == ENUM_AZ_GUID_ARRAY ) {
//
// If the attribute is a linked attribute, then it is possible
// that there are more than one values for the attribute. Each
// value is a name of an object that this object links to.
// The linked object is assumed to already exist in DS.
// If it does not, DS will respond with a "constraint violation"
// error. Report this error back to the persistence manager layer.
//
WinStatus = AzpHandleSubmitLinkedAttribute(
pObject,
ppAttribute,
ObjectAttr,
plIndex
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpGetSpecificProperty: AzpADHandleSubmitLinkedAttribute"
" failed for %s: %ld\n",
L"<Unknown>", // AzpeObjectName(pObject),
WinStatus
));
goto Cleanup;
}
} else {
//
// Extract the property from the object
//
WinStatus = AdAzrolesInfo->AzpeGetProperty(
pObject,
lPersistFlags,
ObjectAttr.AttrType,
&pVoidTypeData );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADGetSpecificProperty: Get Property failed"
": %ld\n",
WinStatus ));
goto Cleanup;
}
switch ( ObjectAttr.DataType ) {
case ENUM_AZ_LONG:
lData = *((PLONG)pVoidTypeData);
wsprintf( pStringNumber, L"%d", lData );
pStringValue = pStringNumber;
break;
case ENUM_AZ_GROUP_TYPE:
lData = *((PLONG)pVoidTypeData);
if ( lData == AZ_GROUPTYPE_BASIC ) {
lData = AZ_AD_BASIC_GROUP;
} else {
lData = AZ_AD_QUERY_GROUP;
}
wsprintf( pStringNumber, L"%d", lData );
pStringValue = pStringNumber;
break;
case ENUM_AZ_BOOL:
//
// There will always be a value for all BOOLs
//
if ( *((BOOL *)pVoidTypeData) ) {
pStringValue = AZ_AD_TRUE;
} else {
pStringValue = AZ_AD_FALSE;
}
break;
case ENUM_AZ_BSTR:
//
// If no value exists, that means the attribute
// has been deleted. Pass the string to
// AD to delete the attribute
//
pStringValue = (PWCHAR) pVoidTypeData;
break;
default:
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
} // switch ... case
if ( *pStringValue != L'\0' || !bCreateFlag ) {
if ( ppAttribute[*plIndex] == NULL ) {
WinStatus = AzpADAllocateAttrHeapModVals(
&(ppAttribute[*plIndex]), 2
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
}
if ( *pStringValue != L'\0' ) {
ppAttribute[*plIndex]->mod_values[0] =
(PWCHAR) AzpAllocateHeap(
(wcslen( pStringValue )
+ 1 ) * sizeof( WCHAR ), "MODVALi" );
if ( ppAttribute[*plIndex]->mod_values[0] == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( ppAttribute[*plIndex]->mod_values[0], pStringValue );
}
}
} // if ( AttrType == SID_ARRAY || AttrType == GUID_ARRAY ) ... else
WinStatus = NO_ERROR;
Cleanup:
if ( pVoidTypeData != NULL ) {
AzpFreeHeap( pVoidTypeData );
}
return WinStatus;
}
DWORD
AzpHandleSubmitLinkedAttribute(
IN AZPE_OBJECT_HANDLE pObject,
IN OUT PLDAPMod *ppAttribute,
IN AZ_AD_ATTRS ObjectAttr,
IN OUT PULONG plIndex
)
/*++
Routine Description:
This routine handles the linked attribute of an object being
submitted to the AD policy store. It is assumed that the linked
object already exists in the DS.
Arguments:
pObject - Pointer to the linking object whose link attribute needs
to be handled
ppAttribute - Pointer to the link attribute structure for submission of
the linking object
ObjectAttr - Object attribute to be read
plIndex - index into the attribute list
Return Values:
NO_ERROR - The link attribute structure was successfully populated
Other status codes (including LDAP)
--*/
{
DWORD WinStatus = 0;
ULONG i = 0;
PAZP_DELTA_ENTRY *pSidList = NULL;
PAZP_DELTA_ENTRY *pGuidList = NULL;
PAZP_DELTA_ENTRY pDeltaEntry = NULL;
ULONG lSidArrayCount = 0;
ULONG lGuidArrayCount = 0;
ULONG lTotalCount = 0;
PWCHAR pSidString = NULL;
PWCHAR pGuidString = NULL;
ULONG AttrType;
BOOLEAN bSidValues = FALSE;
//
// We need to keep the list for linked attributes being added
// separate from linked attributes being deleted. This holds good
// for both Sid lists and GUID lists.
//
ULONG lAddIndex;
ULONG lDeleteIndex;
//
// Validation
//
ASSERT( pObject != NULL );
//
// Set the AttrType to type of attibute being read. This
// is used so that when we are reading Sid objects, we will
// also make sure to read if there are changed Guid objects
// (see previous comment on AD store having same attribute for
// Sid and Guid objects, and core cache being able to differentiate
// between them)
//
AttrType = ObjectAttr.AttrType;
//
// Check ObjectAttr if valuesd being read are Sid objects
//
if ( ObjectAttr.DataType == ENUM_AZ_SID_ARRAY ) {
//
// Also read Guid objects
//
switch ( AttrType ) {
case AZ_PROP_ROLE_MEMBERS:
AttrType = AZ_PROP_ROLE_APP_MEMBERS;
break;
case AZ_PROP_GROUP_MEMBERS:
AttrType = AZ_PROP_GROUP_APP_MEMBERS;
break;
case AZ_PROP_GROUP_NON_MEMBERS:
AttrType = AZ_PROP_GROUP_APP_NON_MEMBERS;
break;
default:
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
//
// Get the Sid Object list
//
WinStatus = AdAzrolesInfo->AzpeGetDeltaArray(
pObject,
ObjectAttr.AttrType,
&lSidArrayCount,
&pSidList );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpHandleSubmitLinkedAttribute: AzpeGetDeltaArray"
" failed for %ws: %ld\n",
L"<Unknown>", // AzpeObjectName(pObject),
WinStatus
));
goto Cleanup;
}
if ( lSidArrayCount > 0 ) {
bSidValues = TRUE;
}
}
//
// Read the Guid object list
//
WinStatus = AdAzrolesInfo->AzpeGetDeltaArray(
pObject,
AttrType,
&lGuidArrayCount,
&pGuidList );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADHandleSubmitLinkedAttribute: AzpeGetDeltaArray"
" failed for %ws: %ld\n",
L"<Unknown>", // AzpeObjectName(pObject),
WinStatus
));
goto Cleanup;
}
//
// Total count
//
lTotalCount = lSidArrayCount + lGuidArrayCount;
//
// reset i
//
i = 0;
ULONG index;
ULONG lAddCount = 0;
ULONG lDeleteCount = 0;
//
// Count the number of Sids/Guids being added or deleted
//
for ( i = 0; i < lSidArrayCount; i++ ) {
pDeltaEntry = pSidList[i];
if ( pDeltaEntry->DeltaFlags & AZP_DELTA_ADD ) {
lAddCount++;
} else {
lDeleteCount++;
}
}
for ( i = 0; i < lGuidArrayCount; i++ ) {
pDeltaEntry = pGuidList[i];
if ( pDeltaEntry->DeltaFlags & AZP_DELTA_ADD ) {
lAddCount++;
} else {
lDeleteCount++;
}
}
//
// If there are any Sid objects, loop through them
// first
//
// Set lAddIndex to *plIndex, and lDeleteIndex
// to the next index value if lAddCount is not zero
// If lAddCount is zero, then there are no SIDs/GUIDs to be
// added, and lDeleteIndex will be *plIndex
//
lAddIndex = *plIndex;
lDeleteIndex = (lAddCount==0)?lAddIndex:(lAddIndex+1);
i = 0;
if ( bSidValues ) {
for ( ; i < lSidArrayCount; i++ ) {
pDeltaEntry = pSidList[i];
AZASSERT( (pDeltaEntry->DeltaFlags & AZP_DELTA_SID) );
index = (pDeltaEntry->DeltaFlags & AZP_DELTA_ADD) ? lAddIndex:lDeleteIndex;
//
// Convert Sid to String Sid
//
if ( !ConvertSidToStringSid(
pDeltaEntry->Sid,
&pSidString
) ) {
WinStatus = GetLastError();
AzPrint(( AZD_AD,
"AzpHandleSubmitLinkedAttribute:"
" ConvertSidToStringSid failed: %ld\n",
WinStatus
));
goto Cleanup;
}
if ( ppAttribute[index] == NULL ) {
WinStatus = AzpADAllocateAttrHeapModVals(
&(ppAttribute[index]),
((pDeltaEntry->DeltaFlags & AZP_DELTA_ADD)?(lAddCount+1):(lDeleteCount+1))
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
ppAttribute[index]->mod_type = ObjectAttr.Attr;
//
// Check is we need to add the GUID link or we need to delete
// the GUID link
//
ppAttribute[index]->mod_op = (pDeltaEntry->DeltaFlags & AZP_DELTA_ADD) ? LDAP_MOD_ADD:LDAP_MOD_DELETE;
}
WinStatus = AzpADAllocateHeapLinkAttribute( pSidString,
&(ppAttribute[index]->mod_values),
TRUE // Is Sid
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
LocalFree( pSidString );
pSidString = NULL;
} // for ( i < lSidArrayCount )
} // if ( bSidValues )
//
// We have all the Sid values that we need
// Now get the guid values
//
for ( ; i < lTotalCount; i++ ) {
pDeltaEntry = pGuidList[i-lSidArrayCount];
index = (pDeltaEntry->DeltaFlags & AZP_DELTA_ADD) ? lAddIndex:lDeleteIndex;
WinStatus = UuidToString(
&pDeltaEntry->Guid,
&pGuidString
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpHandleSubmitLinkedAttribute:"
" UuidToString failed: %ld\n",
WinStatus
));
goto Cleanup;
}
if ( ppAttribute[index] == NULL ) {
WinStatus = AzpADAllocateAttrHeapModVals(
&(ppAttribute[index]),
((pDeltaEntry->DeltaFlags & AZP_DELTA_ADD)?(lAddCount+1):(lDeleteCount+1))
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
ppAttribute[index]->mod_type = ObjectAttr.Attr;
//
// Check is we need to add the GUID link or we need to delete
// the GUID link
//
ppAttribute[index]->mod_op = (pDeltaEntry->DeltaFlags & AZP_DELTA_ADD) ? LDAP_MOD_ADD:LDAP_MOD_DELETE;
}
//
// We have the Guid value. Now allocate memory
// to attribute value structure
//
WinStatus = AzpADAllocateHeapLinkAttribute( pGuidString,
&(ppAttribute[index]->mod_values),
FALSE // Is Sid
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
RpcStringFree( &pGuidString );
pGuidString = NULL;
}
*plIndex = (ppAttribute[lDeleteIndex] == NULL) ? lDeleteIndex : (lDeleteIndex+1);
WinStatus = NO_ERROR;
Cleanup:
//
// release memory
//
if ( pGuidString != NULL ) {
RpcStringFree( &pGuidString );
}
if ( pSidString != NULL ) {
LocalFree( pSidString );
}
return WinStatus;
}
DWORD
AzpADAllocateHeapLinkAttribute(
IN PWCHAR pString,
IN OUT PWCHAR **ppModVals,
IN BOOLEAN bIsSid
)
/*++
Routine Description:
This routine adds an input string to a multi-valued linked attribute value
Arguments:
pString - String to be added to the multi-values linked attribute
ppModVals - Multi-values linked attribute values
bIsSid - TRUE if the link is a SID link (else GUID link)
Return Values:
NO_ERROR - The input string was added successfully
ERROR_NOT_ENOUGH_MEMORY - There was a memory resource problem
--*/
{
DWORD WinStatus = 0;
ULONG size = 0;
ULONG index = 0;
//
// Validation
//
ASSERT( pString != NULL );
while ( (*ppModVals)[index] != NULL ) {
index++;
}
size = (bIsSid?SID_LINK_PREFIX_LENGTH:GUID_LINK_PREFIX_LENGTH) +
(ULONG) wcslen( pString ) +
GUIDSID_LINK_SUFFIX_LENGTH + 1;
(*ppModVals)[index] = (PWCHAR) AzpAllocateHeap( size * sizeof( WCHAR ), "lMODVAL" );
if ( (*ppModVals)[index] == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( (*ppModVals)[index], (bIsSid?SID_LINK_PREFIX:GUID_LINK_PREFIX) );
wcscat( (*ppModVals)[index], pString );
wcscat( (*ppModVals)[index], GUIDSID_LINK_SUFFIX );
WinStatus = NO_ERROR;
Cleanup:
return WinStatus;
}
DWORD
AzpUpdateObjectAcls(
IN PAZP_AD_CONTEXT pContext,
IN OUT AZPE_OBJECT_HANDLE pObject,
IN PWCHAR pDN,
IN ULONG lPersistFlags,
IN BOOL bIsOnObjectSelf,
IN PAZP_POLICY_USER_RIGHTS *ppPolicyAdminRights OPTIONAL,
IN PAZP_POLICY_USER_RIGHTS *ppPolicyReaderRights OPTIONAL,
IN PAZP_POLICY_USER_RIGHTS *ppDelegatedPolicyUsersRights OPTIONAL
)
/*++
Routine Description:
This routine submits any ACL changes to the persist
object passed
Arguments:
pContext - context for the store
pObject - Pointer to object whose ACL needs to be updated
pDN - DN of the passed in object
lPersistFlags - Flags from ther persist engine layer
bIsOnObjectSelf - When we recursively calls (from store object), we shouldn't go update
the DACL on store object for delegated users. Pass true if this update
is on the object itself, not container objects.
ppPolicyAdminRights - Rights for policy admins
ppPolicyReaderRights - Rights for policy readers
ppDelegatedPolicyUsersRights - Rights for delegated users
Return Values:
NO_ERROR - The ACLs were updated successfully
Other status codes
--*/
{
DWORD WinStatus;
ULONG DirtyBits = AdAzrolesInfo->AzpeDirtyBits( pObject );
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType( pObject );
PAZP_POLICY_USER_RIGHTS pStoreDelegatedUsersAttributeRights = NULL;
GUID * pStoreObjectVersionGuid = NULL;
PSECURITY_DESCRIPTOR pOldSd = NULL;
PSECURITY_DESCRIPTOR pNewSd = NULL;
BOOL UpdateDacl = FALSE;
BOOL UpdateSacl = FALSE;
BOOL DoingSubcontainer;
BOOL EmptyPolicyAdmins = FALSE;
SECURITY_INFORMATION si = 0;
ULONG i = 0;
PWCHAR pContainerDN = NULL;
//
// validation
//
ASSERT( pObject != NULL );
//
// Determine whether the DACL and/or SACL need to be updated
//
if ( DirtyBits & AZ_DIRTY_CREATE ) {
UpdateDacl = TRUE;
UpdateSacl = pContext->HasSecurityPrivilege;
} else {
if ( DirtyBits & (AZ_DIRTY_POLICY_READERS|AZ_DIRTY_POLICY_ADMINS|AZ_DIRTY_DELEGATED_POLICY_USERS) ) {
UpdateDacl = TRUE;
}
if ( DirtyBits & AZ_DIRTY_APPLY_STORE_SACL ) {
UpdateSacl = TRUE;
}
if ( !UpdateDacl && !UpdateSacl ) {
WinStatus = NO_ERROR;
goto Cleanup;
}
}
if ( bIsOnObjectSelf && UpdateDacl && lObjectType == OBJECT_TYPE_AZAUTHSTORE)
{
pStoreDelegatedUsersAttributeRights = &StoreDelegatedUsersAttributeRights;
pStoreObjectVersionGuid = &AZ_AD_OBJECT_VERSION_GUID;
}
//
// Delegator objects have ACLed container objects for each child object type.
// Determine if this call is for one of those containers
//
DoingSubcontainer = (ppPolicyAdminRights == NULL) && (ppPolicyReaderRights == NULL);
//
// Subcontainers always inherit their SACL from their parent
//
if ( DoingSubcontainer ) {
UpdateSacl = FALSE;
}
//
// If the object is a container AzRole object, then the child container objects
// in its bucket need to be stamped first
//
if ( IsContainerObject( lObjectType ) && !DoingSubcontainer ) {
for ( i = 0; i < OBJECT_TYPE_COUNT; i++ ) {
if ( (!IsContainerObject(i)) &&
( ( i == OBJECT_TYPE_GROUP && lObjectType == OBJECT_TYPE_AZAUTHSTORE ) ||
( i != OBJECT_TYPE_OPERATION && lObjectType == OBJECT_TYPE_SCOPE ) ||
( lObjectType == OBJECT_TYPE_APPLICATION ) ) ) {
WinStatus = AzpADBuildDN(
pContext,
pObject,
&pContainerDN,
pDN,
FALSE,
&AdChildObjectContainers[i]
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Now call this function recursively again to stamp
// the container object
//
WinStatus = AzpUpdateObjectAcls(
pContext,
pObject,
pContainerDN,
lPersistFlags,
FALSE, // recursively now, not on the object self
NULL,
NULL,
(PAZP_POLICY_USER_RIGHTS *)&ADDelegatedContainerReadersRights
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
AzpFreeHeap( pContainerDN );
pContainerDN = NULL;
}
} // for loop
}
//
// If we didn't just create the object,
// Get the existing file security descriptor so we can merge in the changes
//
if ( (DirtyBits & AZ_DIRTY_CREATE) == 0 ) {
WinStatus = AzpADReadNTSecurityDescriptor(
pContext,
pObject,
pDN,
FALSE, // no need to read authorization store's DS parent
&pOldSd,
UpdateSacl, // read SACL
UpdateDacl // read DACL
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADUpdateObjectAcls: AzpADReadNTSecurityDescriptor failed"
" :%ld\n",
WinStatus
));
goto Cleanup;
}
}
//
// Compute the new Security Descriptor
//
WinStatus = AdAzrolesInfo->AzpeGetSecurityDescriptorFromCache(
pObject,
lPersistFlags,
(UpdateDacl ? ppPolicyAdminRights : NULL),
(UpdateDacl ? ppPolicyReaderRights : NULL),
(UpdateDacl ? ppDelegatedPolicyUsersRights : NULL),
(UpdateDacl && ppDelegatedPolicyUsersRights != NULL)?(GUID *)&AZ_AD_CONTAINER_GUID:NULL,
pStoreDelegatedUsersAttributeRights,
pStoreObjectVersionGuid,
(UpdateSacl ? &AdSaclRights : NULL),
pOldSd,
&pNewSd);
if ( WinStatus == ERROR_EMPTY ) {
EmptyPolicyAdmins = TRUE;
WinStatus = NO_ERROR;
}
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADUpdateObjectAcls: AzpeGetSecurityDescriptorFromCache failed:"
" %ld\n",
WinStatus
));
goto Cleanup;
}
//
// Update the SACL on the object
//
if ( UpdateSacl ) {
si |= SACL_SECURITY_INFORMATION;
}
//
// Update the DACL on the object
//
if ( UpdateDacl ) {
si |= DACL_SECURITY_INFORMATION;
}
//
// Set the DACL/SACL onto the object
//
WinStatus = AzpADStampSD(
pContext,
pDN,
si,
pNewSd );
if ( WinStatus != NO_ERROR ) {
//
// Unable to set the new security descriptor
//
AzPrint(( AZD_AD,
"AzpADUpdateObjectAcls: AzpADStampSD failed with SACL/DACL: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// If AzpADStampSD changed PolicyAdmins,
// load the PolicyAdmins back into the the azroles core.
//
if ( EmptyPolicyAdmins && !DoingSubcontainer ) {
WinStatus = AzpApplyPolicyAcls(
pContext,
pObject,
pDN,
lPersistFlags,
TRUE ); // Only update PolicyAdmins
if ( WinStatus != NO_ERROR ) {
//
// Unable to get the new security descriptor
//
AzPrint(( AZD_AD,
"AzpADUpdateObjectAcls: AzpApplyPolicyAcls failed with DACL: %ld\n",
WinStatus
));
goto Cleanup;
}
}
WinStatus = NO_ERROR;
Cleanup:
//
// free locally used memory
//
if ( pContainerDN != NULL ) {
AzpFreeHeap( pContainerDN );
}
AdAzrolesInfo->AzpeFreeMemory( pNewSd );
if ( pOldSd != NULL ) {
AzpFreeHeap( pOldSd );
}
return WinStatus;
}
//
// Utility routines used by AD policy store APIs
//
DWORD
AzpADBuildDN(
IN PAZP_AD_CONTEXT pContext,
IN OUT AZPE_OBJECT_HANDLE pObject,
IN OUT PWCHAR *ppDN,
IN PWCHAR pParentDN,
IN BOOL bAzAuthorizationStoreParent,
IN PAZ_AD_CHILD_OBJECT_CONTAINERS pChildObjectContainer
)
/*++
Routine Description:
This routine builds the DN for an object.
Arguments:
pContext - context for the store
pObject - Pointer to the object whose DN is being built. For
AzApplication and AzScope objects, a temporary GUID
is assigned which will help identify them later on
ppDN - The resulting DN
pParentDN - DN of the parent object. NULL passed if parent object DN not known.
bAzAuthorizationStoreParent - TRUE if DN needs to be built for AzAuthStore parent in DS
pChildObjectContainer - Child Object container for which the DN needs to be built
Pass NULL if the object is not a child object container
Return Values:
NO_ERROR - The DN was build successfully
ERROR_NOT_ENOUGH_MEMORY - insufficient resources
--*/
{
DWORD WinStatus = 0;
PWCHAR pAzAuthorizationStoreDN = NULL;
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
//
// Validation
//
ASSERT( pObject != NULL );
//
// If the DN needs to be build for AdCheckSecurityPrivilege ever-
// present object
//
if ( bAzAuthorizationStoreParent ) {
WinStatus = AzpADBuildDNForAzStoreParent(
pContext,
ppDN
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADBuildDN: AzpADBuildDNForBuiltinObject failed:"
"%ld\n",
WinStatus
));
}
goto Cleanup;
}
//
// For AzApplication, AzScope object, we need a temporary GUID
// assigned to those objects which can be used during the DN creation.
// This is different from the GUID assigned to the object itself - that
// will be assigned when the object has been added to the DS and read
// for the GUID
//
if ( (lObjectType == OBJECT_TYPE_APPLICATION && (pChildObjectContainer == NULL)) ||
lObjectType == OBJECT_TYPE_SCOPE ) {
LPWSTR TempString = (LPWSTR) AdAzrolesInfo->AzpeGetProviderData( pObject );
if ( TempString == NULL ) {
WinStatus = AzpCreateGuidCN( &TempString, NULL );
AdAzrolesInfo->AzpeSetProviderData( pObject, (PVOID)TempString );
}
}
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// We need to create the DN of the object. The DN will
// consist of the object's objectName, its parent's objectName,
// upto and including the AzAuthorizationStore objectName. For
// AzApplication and AzScope objects, the RDN will have to be
// changed depending on the object's name size. For all child
// objects of AzApplication (except Scopes), the DN will include an
// extra parent which does not exist in the cache. This is to support
// delegation in AzRoles, and allow for same name role and task objects
// This parent will always be a child of the AzApplication object, and its
// DN is simply the GUID of the AzApplication object prefixed with
// *_OBJECT_CONTAINER_NAME_PREFIX.
// A similar logic is to be used for objects that are children of the AzScope
// object.
// For all child objects of AzAuthorizationStore that are not AzApplications,
// we need to create the DN with parent as the AZ_AD_OBJECT_CONTAINER
// under AzAuthorizationStore, and its DN is the RDN of the authorization store
// object prefixed with GROUP_OBJECT_CONTAINER_NAME_PREFIX.
//
// Get the DN for the AzAuthorizationStore object. The URL is in the format
// msldap://servername/CN=AzAuthorizationStoreObjectName,OU=SomeOrganizationalUnit,DC=X
// or msldap://CN=AzAuthorizationStoreObjectName,OU=SomeOrganizationalUnit,DC=X
//
pAzAuthorizationStoreDN = wcsrchr( pContext->PolicyUrl,
L'/' );
ASSERT( pAzAuthorizationStoreDN != NULL );
pAzAuthorizationStoreDN++;
//
// If we need to create the DN for the AZ_AD_OBJECT_CONTAINER
//
if ( pChildObjectContainer ) {
WinStatus = AzpADObjectContainerRDN(
pObject,
ppDN,
pParentDN,
pAzAuthorizationStoreDN,
TRUE,
pChildObjectContainer
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADBuildDN: AzpADObjectContainerRDN failed:"
"%ld\n",
WinStatus
));
}
goto Cleanup;
}
if ( lObjectType != OBJECT_TYPE_AZAUTHSTORE ) {
WinStatus = AzpADBuildChildObjectDN(
pObject,
ppDN,
pParentDN,
pAzAuthorizationStoreDN
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADBuildDN: AzpADBuildChildObjectDN failed for %s"
": %ld\n",
L"<Unknown>", // AzpeObjectName(pObject),
WinStatus
));
goto Cleanup;
}
} else {
//
// Object type is authorization store. Hence the DN of the object
// is the policy URL of the store
//
*ppDN = (PWCHAR) AzpAllocateHeap(
(wcslen( pAzAuthorizationStoreDN ) + 1) *
+ sizeof( WCHAR ), "URLDN" );
if ( *ppDN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( *ppDN, pAzAuthorizationStoreDN );
}
WinStatus = NO_ERROR;
Cleanup:
pAzAuthorizationStoreDN = NULL;
return WinStatus;
}
DWORD
AzpADBuildChildObjectDN(
IN AZPE_OBJECT_HANDLE pObject,
OUT PWCHAR *ppDN,
IN PWCHAR pParentDN,
IN PWCHAR pPolicyDN
)
/*++
Routine Description:
This routine is a worker routine for AzpADBuildDN. Once we have the DN,
concatenate it with the DN extracted from AzpLdapCrackUrl in
AzpADPersistOpen.
The DN needs to be in the format
CN=objName,CN=CN=parentObj1Name,...,CN=AzAuthorizationStoreName,DC=X
Arguments:
pObject - Pointer to object for whom the DN needs to be built
ppDN - The built DN string
pParentDN - DN of the parent object
pPolicyDN - DN of policy itself
Return Values:
NO_ERROR - The DN was build successfully
Other status codes
--*/
{
DWORD WinStatus = 0;
DWORD lObjectType = AdAzrolesInfo->AzpeObjectType( pObject );
PWCHAR pTempCN = NULL;
PWCHAR pTempParentCN = NULL;
PWCHAR pObjectContainerCN = NULL;
ULONG lObjectContainerCNLength = 0;
size_t lStringSize = 0;
AZPE_OBJECT_HANDLE pTempObject = NULL;
//
// Validation
//
ASSERT( pObject != NULL );
//
// Set pTempCN to point to CN
//
WinStatus = AzpGetCNForDN(
pObject,
&pTempCN);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADBuildDN: AzpADGetCNForDN failed: %ld\n",
WinStatus
));
goto Cleanup;
}
//
// If the object whose DN is being built is a child of AzApplication/AzScope and
// not a scope object, then it needs to go into a child container object
// under the AzApplication/AzScope object. The same holds true for AzApplicationGroup
// objects that are children of AzAuthorizationStore
//
if ( !IsContainerObject( lObjectType ) ) {
WinStatus = AzpADObjectContainerRDN(
AdAzrolesInfo->AzpeParentOfChild(pObject),
&pObjectContainerCN,
pParentDN,
pPolicyDN,
FALSE, // Object container is not being created
&AdChildObjectContainers[lObjectType]
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADBuildDN: AzpADObjectContainerRDN failed"
" for %ws: %ld\n",
((PGENERIC_OBJECT)pObject)->ObjectName->ObjectName.String,
WinStatus
));
goto Cleanup;
}
lObjectContainerCNLength = (ULONG)wcslen( pObjectContainerCN );
}
//
// Allocate memory to the returned DN value
//
lStringSize = wcslen( pTempCN ) + wcslen( pPolicyDN ) + lObjectContainerCNLength;
*ppDN = (PWCHAR) AzpAllocateHeap(
( lStringSize + 1 ) * sizeof( WCHAR ), "BLDDN"
);
if ( *ppDN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
//
// Copy the CN of the object into the returning DN value.
// After that, get the parent CN and keep adding the equivalent
// size of the length of the parent's CN to the returning DN
// value.
//
wcscpy( *ppDN, pTempCN );
if ( lObjectContainerCNLength != 0 ) {
wcscat( *ppDN, pObjectContainerCN );
}
AzpFreeHeap( pTempCN );
pTempCN = NULL;
pTempCN = (PWCHAR) AzpAllocateHeap(
( lStringSize + 1 ) *
sizeof( WCHAR ), "TMPCN"
);
if ( pTempCN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( pTempCN, *ppDN );
pTempObject = AdAzrolesInfo->AzpeParentOfChild(pObject);
while ( pTempObject != NULL &&
AdAzrolesInfo->AzpeObjectType(pTempObject) != OBJECT_TYPE_AZAUTHSTORE ) {
//
// Get the parent's CN
//
WinStatus = AzpGetCNForDN( pTempObject,
&pTempParentCN
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADBuildDN: AzpGetCNForDN failed: %ld\n",
WinStatus
));
goto Cleanup;
}
if ( pTempCN == NULL ) {
//
// Save the current returning DN value as the temporary CN
// value
//
pTempCN = (PWCHAR) AzpAllocateHeap(
( lStringSize + 1 ) *
sizeof( WCHAR ), "TMPCN"
);
if ( pTempCN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( pTempCN, *ppDN );
}
//
// Free the returning DN value and reallocate memory
// to accomodate the parent's CN
//
lStringSize += wcslen( pTempParentCN );
AzpFreeHeap( *ppDN );
*ppDN = NULL;
*ppDN = (PWCHAR) AzpAllocateHeap( ( lStringSize + 1 )
* sizeof( WCHAR ), "BLDDN"
);
if ( *ppDN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( *ppDN, pTempCN );
wcscat( *ppDN, pTempParentCN );
AzpFreeHeap( pTempParentCN );
pTempParentCN = NULL;
AzpFreeHeap( pTempCN );
pTempCN = NULL;
pTempObject = AdAzrolesInfo->AzpeParentOfChild(pTempObject);
}
//
// Concatenate the policy DN to the above created DN value
//
wcscat( *ppDN, pPolicyDN );
WinStatus = NO_ERROR;
Cleanup:
if ( pTempCN != NULL ) {
AzpFreeHeap( pTempCN );
}
if ( pTempParentCN != NULL ) {
AzpFreeHeap( pTempParentCN );
}
if ( pObjectContainerCN != NULL ) {
AzpFreeHeap( pObjectContainerCN );
}
pTempObject = NULL;
return WinStatus;
}
DWORD
AzpGetCNForDN(
IN AZPE_OBJECT_HANDLE pObject,
OUT PWCHAR *ppCN
)
/*++
Routine Description:
This routine generates a CN for the passed in object. The CN is the quoted or escaped object Name.
For example, CN=ObjectName
CN=Object\,Name
Arguments:
pObject - Pointer to object whose CN needs to be built
ppCN - Pointer to returned CN
Return Values:
NO_ERROR - CN was built successfully
ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD WinStatus = 0;
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
LPWSTR ObjectName = NULL;
LPWSTR QuotedObjectName = NULL;
DWORD cObjectNameLen = 0;
DWORD cQuotedObjectNameLen = 0;
//
// Validation
//
ASSERT( pObject != NULL );
//
// Handle case for application and scope objects differently
// They already have their CN stored in an attribute pGuidCN
//
if ( lObjectType == OBJECT_TYPE_APPLICATION ||
lObjectType == OBJECT_TYPE_SCOPE ) {
LPWSTR TempString;
TempString = (LPWSTR) AdAzrolesInfo->AzpeGetProviderData( pObject );
ASSERT( TempString != NULL );
*ppCN = (PWCHAR) AzpAllocateHeap(
( wcslen( TempString ) + 1 ) * sizeof( WCHAR ),
"BLDCN2" );
if ( *ppCN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( *ppCN, TempString );
} else {
//
// Get the object name
//
WinStatus = AdAzrolesInfo->AzpeGetProperty( pObject,
0, // No Flags
AZ_PROP_NAME,
(PVOID *)&ObjectName );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// Escape the returned object name so that special characters may be used
//
cObjectNameLen = (DWORD) wcslen( ObjectName );
cQuotedObjectNameLen = 2*cObjectNameLen+1; // Twice the number of chars to include escape char. '\'
QuotedObjectName = (PWCHAR) AzpAllocateHeap( cQuotedObjectNameLen*sizeof(WCHAR), "QTRDN" );
if ( QuotedObjectName == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
WinStatus = DsQuoteRdnValue( cObjectNameLen, ObjectName, &cQuotedObjectNameLen, QuotedObjectName );
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpGetCNForDN: Failed to quote RDN for object %ws: %ld\n",
ObjectName,
WinStatus
));
goto Cleanup;
}
//
// Build a CN containing the object name
//
*ppCN = (PWCHAR) AzpAllocateHeap(
(BUILD_CN_PREFIX_LENGTH +
cQuotedObjectNameLen +
BUILD_CN_SUFFIX_LENGTH + 1) *
sizeof( WCHAR ),
"BLDCN3" );
if ( *ppCN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( *ppCN, BUILD_CN_PREFIX );
wcscat( *ppCN, QuotedObjectName );
wcscat( *ppCN, BUILD_CN_SUFFIX );
}
WinStatus = NO_ERROR;
Cleanup:
AdAzrolesInfo->AzpeFreeMemory( ObjectName );
AdAzrolesInfo->AzpeFreeMemory( QuotedObjectName );
return WinStatus;
}
DWORD
AzpCreateGuidCN(
OUT PWCHAR *ppCN,
IN PWCHAR pGuidString OPTIONAL
)
/*++
Routine Description:
This routine creates a GUIDized CN. This routine is called specifically
for AzApplication, and AzScope objects since their names can be longer than
64 chars and RDN cannot.
Arguments:
ppCN - The created CN
pGuidString - An optional GUID string to be used for the CN
Return Values:
NO_ERROR - The CN was created successfully
ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD WinStatus = 0;
PWCHAR pGuidValue = NULL;
GUID guid;
if ( pGuidString == NULL ) {
//
// Get a GUID for the object.
//
WinStatus = UuidCreate( &guid );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
WinStatus = UuidToString( &guid,
&pGuidValue );
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
} else {
//
// use the passed GUID string
//
pGuidValue = pGuidString;
}
*ppCN = (PWCHAR) AzpAllocateHeap ( (wcslen( pGuidValue ) +
BUILD_CN_PREFIX_LENGTH +
BUILD_CN_SUFFIX_LENGTH + 1 ) *
sizeof( WCHAR ), "BLDCN" );
if ( *ppCN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( *ppCN, BUILD_CN_PREFIX );
wcscat( *ppCN, pGuidValue );
wcscat( *ppCN, BUILD_CN_SUFFIX );
WinStatus = NO_ERROR;
Cleanup:
if ( pGuidString == NULL && pGuidValue != NULL ) {
RpcStringFree( &pGuidValue );
}
return WinStatus;
}
DWORD
AzpADObjectContainerRDN(
IN AZPE_OBJECT_HANDLE pParentObject,
OUT PWCHAR *ppCN,
IN LPCWSTR pParentDN OPTIONAL,
IN LPCWSTR pPolicyDN,
IN BOOL bObjectContainerCreate,
IN PAZ_AD_CHILD_OBJECT_CONTAINERS pChildObjectContainer
)
/*++
Routine Description:
This routine creates a CN for an AZ_AD_OBJECT_CONTAINER object. It simply
takes the stringized GUID name for the passed in parent object if the parent
object is AzApplication/AzScope, and prefixes it with *_OBJECT_CONTAINER_NAME_PREFIX.
If the parent is AzAuthorizationStore, it takes the name of the authorization store
object and prefixes it with *_OBJECT_CONTAINER_NAME_PREFIX
Arguments:
pParentObject - Handle to the parent object
ppCN - The created CN
pParentDN - An optional parameter containing the DN of the parent object.
This is used when someone wants to create the the whole DN for
AZ_AD_OBJECT_CONTAINER during its creation
pPolicyDN - DN of the policy store
bObjectContainerCreate - TRUE if the DN is needed for the object container
creation
pChildObjectContainer - Prefix for child object container. NULL if child object
container RDN is not being constructed
Return Values:
NO_ERROR - The CN was created successfully
ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD WinStatus = 0;
PWCHAR pPrefixGuidString = NULL;
PWCHAR pGuidString = NULL;
LPCWSTR pAzStoreNameStart = NULL;
LPCWSTR pAzStoreNameEnd = NULL;
ULONG lParentDNLength = 0;
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType( pParentObject );
size_t lStringSize = 0;
//
// Validation
// The container object can only be a child of an AzApplication object
// or AzScope or AzAuthorizationStore object
//
ASSERT( pParentObject != NULL );
ASSERT( lObjectType == OBJECT_TYPE_APPLICATION ||
lObjectType == OBJECT_TYPE_AZAUTHSTORE ||
lObjectType == OBJECT_TYPE_SCOPE );
if ( pParentDN == NULL ) {
pParentDN = pPolicyDN;
}
lParentDNLength = (ULONG)wcslen( pParentDN );
if ( lObjectType == OBJECT_TYPE_APPLICATION ||
lObjectType == OBJECT_TYPE_SCOPE ) {
//
// Get the stringized GUID name from the parent
//
pGuidString = (PWCHAR) AdAzrolesInfo->AzpeGetProviderData( pParentObject );
ASSERT( pGuidString != NULL );
pPrefixGuidString = pGuidString + BUILD_CN_PREFIX_LENGTH;
lStringSize = BUILD_CN_PREFIX_LENGTH + pChildObjectContainer->lPrefixLength +
wcslen( pPrefixGuidString );
if ( bObjectContainerCreate ) {
lStringSize += lParentDNLength;
}
} else {
pAzStoreNameStart = pParentDN + BUILD_CN_PREFIX_LENGTH;
pAzStoreNameEnd = AzpGetAuthorizationStoreParent( pAzStoreNameStart );
if (NULL == pAzStoreNameEnd)
{
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
lStringSize = BUILD_CN_PREFIX_LENGTH + pChildObjectContainer->lPrefixLength +
(pAzStoreNameEnd-pAzStoreNameStart);
if ( bObjectContainerCreate ) {
lStringSize += lParentDNLength;
}
}
*ppCN = (PWCHAR) AzpAllocateHeap (
( lStringSize + 1 ) * sizeof( WCHAR ),
"CONTRDN" );
if ( *ppCN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( *ppCN, BUILD_CN_PREFIX );
wcscat( *ppCN, pChildObjectContainer->pObjectContainerPrefix );
if ( lObjectType == OBJECT_TYPE_APPLICATION ||
lObjectType == OBJECT_TYPE_SCOPE ) {
wcscat( *ppCN, pPrefixGuidString );
} else {
wcsncat( *ppCN, pAzStoreNameStart,
(pAzStoreNameEnd-pAzStoreNameStart) );
}
if ( bObjectContainerCreate ) {
wcscat( *ppCN, pParentDN );
}
WinStatus = NO_ERROR;
Cleanup:
pGuidString = NULL;
pPrefixGuidString = NULL;
return WinStatus;
}
DWORD
AzpADBuildDNForAzStoreParent(
IN PAZP_AD_CONTEXT pContext,
OUT PWCHAR *ppDN
)
/*++
Routine Description:
This routine builds a DN for the container object in DS
that contains (will contain) the authorization store object
Arguments:
pContext - context for the store
pObject - Pointer to object to get policy URl from
ppDN - The created DN
Return Values:
NO_ERROR - DN was created successfully
ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD WinStatus = 0;
LPCWSTR pParentDN = NULL;
//
// The policy URl is in the format
// msldap://servername:port/CN=AzAuthStoreObject,DC=X
// We need to extract the DC=X part. This is
// the object that will always exist in DS
// We need to take care of escaped characters
//
pParentDN = AzpGetAuthorizationStoreParent( pContext->PolicyUrl );
if (NULL == pParentDN)
{
WinStatus = ERROR_INVALID_PARAMETER;
goto Cleanup;
}
*ppDN = (PWCHAR) AzpAllocateHeap(
(wcslen( pParentDN ) + 1) *
+ sizeof( WCHAR ), "ADPARDN" );
if ( *ppDN == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( *ppDN, pParentDN );
WinStatus = NO_ERROR;
Cleanup:
pParentDN = NULL;
return WinStatus;
}
LPCWSTR
AzpGetAuthorizationStoreParent(
IN LPCWSTR PolicyDN
)
/*++
Routine description:
This routine returns the parent DN for the Authorization Store
Arguments:
PolicyDN - DN for the AzAuthorizationStore
Return Value:
The DN of the parent of the AuthorizationStore.
NULL will be returned if the passed in parameter doesn't contain the
comma delimiter.
--*/
{
LPCWSTR pReturnStr = NULL;
LPCWSTR pEscChar = NULL;
//
// validation
//
ASSERT( PolicyDN != NULL );
pReturnStr = pEscChar = wcschr( PolicyDN, L',' );
if (NULL != pEscChar)
{
DWORD EscCharCnt = 0;
while ( *(--pEscChar) == L'\\' ) {
EscCharCnt++;
}
pReturnStr++;
if ( (EscCharCnt%2) ) {
pReturnStr = AzpGetAuthorizationStoreParent( pReturnStr );
}
}
return pReturnStr;
}
BOOL
AzpLdapCrackUrl(
IN OUT PWCHAR *ppszUrl,
OUT PLDAP_URL_COMPONENTS pLdapUrlComponents
)
/*++
Routine Description:
Crack an LDAP URL into its relevant parts. The result must
be freed using AzpLdapFreeUrlComponents
Parameters:
ppszUrl - LDAP URL to be cracked
pLdapUrlComponents - Structure storing the URL components
Return Value:
TRUE - URL was cracked successfully
FALSE - URL could not be cracked
--*/
{
BOOL bResult = TRUE;
ULONG cbUrl = INTERNET_MAX_PATH_LENGTH;
PWCHAR pszHostInfo = NULL;
PWCHAR pszDN = NULL;
PWCHAR pszToken = NULL;
WCHAR psz[INTERNET_MAX_PATH_LENGTH+1];
//
// Validation
//
ASSERT( *ppszUrl != NULL );
if ( *ppszUrl == NULL ) {
return FALSE;
}
//
// Get rid of trailing '/'s from the URL
//
DWORD Len = (DWORD)wcslen(*ppszUrl);
while ( Len > 0 && (*ppszUrl)[Len - 1] == L'/' ) {
(*ppszUrl)[Len - 1] = L'\0';
Len--;
}
//
// Capture the URL and initialize the out parameter
//
__try
{
if ( !InternetCanonicalizeUrl(
*ppszUrl,
psz,
&cbUrl,
ICU_NO_ENCODE | ICU_DECODE
) ) {
bResult = FALSE;
goto Cleanup;
}
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
SetLastError( GetExceptionCode() );
bResult = FALSE;
goto Cleanup;
}
//
// Find the host
//
pszHostInfo = wcschr( psz, L'/' );
//
// if pszHostInfo is NULL, do not do the following
//
if ( pszHostInfo ) {
pszHostInfo += 2;
pszToken = pszHostInfo;
//
// If there is no host specified, we need to default to the local host
// assuming it is the DC
// The URL will then be in the form ldap://CN=
//
if ( !_wcsnicmp( pszToken, BUILD_CN_PREFIX,
BUILD_CN_PREFIX_LENGTH ) ) {
//
// no host name specified
//
pszHostInfo = NULL;
} else {
//
// find the next / to separate host name and DN
//
while ( ( *pszToken != L'\0' ) && ( *pszToken != L'/' ) )
pszToken++;
if ( *pszToken == L'/' ) {
*pszToken = L'\0';
pszToken += 1;
}
while ( *pszToken == L'/' ) {
pszToken++;
}
}
}
//
// Find the DN
//
if ( pszToken != NULL ) {
pszDN = L"";
if ( *pszToken != L'\0' && *pszToken != L'?') {
//
// remember the DN
//
pszDN = pszToken;
//
// look for ? and terminiate it if found
//
pszToken = wcschr(pszToken, L'?');
if ( pszToken ) {
*pszToken = L'\0';
}
}
} else {
SetLastError( ERROR_INVALID_PARAMETER );
bResult = FALSE;
goto Cleanup;
}
//
// Build up URL components
//
bResult = AzpLdapParseCrackedHost( pszHostInfo, pLdapUrlComponents );
if ( bResult ) {
bResult = AzpLdapParseCrackedDN( pszDN, pLdapUrlComponents );
}
if ( !bResult ) {
AzpLdapFreeUrlComponents( pLdapUrlComponents );
}
Cleanup:
return( bResult );
}
BOOL
AzpLdapParseCrackedHost(
IN PWCHAR pszHost OPTIONAL,
OUT PLDAP_URL_COMPONENTS pLdapUrlComponents
)
/*++
Routine Description:
This procedure parses the cracked host string from LdapCrackUrl
Arguments:
pszHost - Parsed host name
pLdapUrlComponents - Structure storing the URL components
Return Value:
TRUE - Cracked host name was parsed successfully
FALSE - There was an error in parsing the cracked host name
--*/
{
PWCHAR pszPort;
if ( pszHost == NULL ) {
pLdapUrlComponents->pszHost = NULL;
pLdapUrlComponents->Port = LDAP_PORT;
return( TRUE );
}
pszPort = wcschr( pszHost, L':' );
if ( pszPort != NULL ) {
*pszPort = L'\0';
pszPort++;
}
pLdapUrlComponents->pszHost = (PWCHAR) AzpAllocateHeap(
(wcslen( pszHost ) + 1)*
sizeof( WCHAR ), "ADCRKHST"
);
if ( pLdapUrlComponents->pszHost == NULL ) {
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return( FALSE );
}
wcscpy( pLdapUrlComponents->pszHost, pszHost );
pLdapUrlComponents->Port = 0;
//
// Get port number
//
if ( pszPort != NULL ) {
pLdapUrlComponents->Port = _wtol( pszPort );
}
if ( pLdapUrlComponents->Port == 0 ) {
pLdapUrlComponents->Port = LDAP_PORT;
}
return( TRUE );
}
BOOL
AzpLdapParseCrackedDN(
IN PWCHAR pszDN,
OUT PLDAP_URL_COMPONENTS pLdapUrlComponents
)
/*++
Routine Desciption:
Parse the cracked DN
Arguments:
pszDN - The parsed DN
pLdapUrlComponents - Structure storing the URL components
Return Value:
TRUE - Cracked DN was parsed successfully
FALSE - There was an error in parsing the cracked DN
--*/
{
BOOL bResult = FALSE;
DWORD Len = (DWORD)wcslen( pszDN );
pLdapUrlComponents->pszDN = (PWCHAR) AzpAllocateHeap(
(Len + 1) *
sizeof( WCHAR), "ADCRKDN"
);
if ( pLdapUrlComponents->pszDN == NULL ) {
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
return bResult;
}
wcscpy( pLdapUrlComponents->pszDN, pszDN );
//
// make sure the DN does not end with a /
//
while ( Len > 0 &&
pLdapUrlComponents->pszDN[Len-1] == L'/' ) {
pLdapUrlComponents->pszDN[Len-1] = L'\0';
Len--;
}
bResult = TRUE;
return bResult;
}
VOID
AzpLdapFreeUrlComponents(
IN OUT PLDAP_URL_COMPONENTS pLdapUrlComponents
)
/*++
Routine Description:
Frees allocated URL components returned from LdapCrackUrl
Arguments:
pLdapUrlComponents - Allocated structure to be freed
Return Value:
(VOID)
--*/
{
AzpFreeHeap( pLdapUrlComponents->pszHost );
pLdapUrlComponents->pszHost = NULL;
AzpFreeHeap( pLdapUrlComponents->pszDN );
pLdapUrlComponents->pszDN = NULL;
}
INT __cdecl
AzpCompareSortStrings(
IN const void *pArg1,
IN const void *pArg2
)
/*++
Routine Description:
This routine compares two strings for the qsort/bsearch API
Arguments:
pArg1 - First string for comparison
pArg2 - Second String for comparison
Return Values:
<0 - First string is smaller
0 - strings are same
>0 - First string is larger
--*/
{
return _wcsicmp( * (PWCHAR *) pArg1, * (PWCHAR *) pArg2);
}
DWORD
AzpCheckPolicyExistence(
LDAP* pLdapH,
PWCHAR pDN,
BOOL bCreatePolicy
)
/*++
Routine Description:
This routine runs a preliminary base scope search on the passed
DN to check if the policy exists for the given URL.
If bCreatePolicy is TRUE and the policy does exist, return
ERROR_ALREADY_EXISTS. If bCreatePolicy is FALSE, and the policy
does not exist, return ERROR_FILE_NOT_FOUND.
Arguments:
pLdapH - LDAP handle to DS
pDN - DN of the store policy to be checked for existence
bCreatePolicy - Bool indicating if policy needs to be created or not
Return Value:
NO_ERROR if the policy does not exist
ERROR_ALREADY_EXISTS if policy does exist and bCreatePolicy is TRUE
ERROR_FILE_NOT_FOUND if policy does not exist and bCreatePolicy is FALSE
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
LDAPMessage* pResult = NULL;
//
// Validation
//
ASSERT( pDN != NULL);
//
// Perform a base level search on the passed DN
//
LdapStatus = ldap_search_s(
pLdapH,
pDN,
LDAP_SCOPE_BASE,
AZ_AD_ALL_CLASSES,
NULL,
0,
&pResult
);
if ( LdapStatus == LDAP_NO_SUCH_OBJECT && !bCreatePolicy ) {
WinStatus = ERROR_FILE_NOT_FOUND;
goto Cleanup;
} else if ( LdapStatus == LDAP_SUCCESS && bCreatePolicy ) {
WinStatus = ERROR_ALREADY_EXISTS;
goto Cleanup;
}
WinStatus = NO_ERROR;
Cleanup:
if ( pResult != NULL ) {
ldap_msgfree( pResult );
}
return WinStatus;
}
DWORD
AzpADCheckCompatibility(
LDAP* pLdapH
)
/*++
Routine Description:
This routine checks the domain's behavior version on the domainDns object
of the DC to be at least AZ_AD_MIN_DOMAIN_BEHAVIOR_VERSION. It further calls
a sub-routine to check if the schema is upto-date with version number
AZ_AD_MIN_SCHEMA_OBJECT_VERSION.
Arguments:
pLdapH - LDAP handle to the DS
Return Value:
NO_ERROR - The domain's behavior version is upto par
ERROR_CURRENT_DOMAIN_NOT_ALLOWED - The current domain will not work for
this version of azroles. This may be because of two
reasons:
1) The domain is not .Net native
2) The schema is incompatible
Other Ldap Status codes
--*/
{
DWORD WinStatus = NO_ERROR;
ULONG LdapStatus = LDAP_SUCCESS;
LDAPMessage* pResult = NULL;
LDAPMessage* pEntry = NULL;
PWCHAR *ppValueList = NULL;
PWCHAR attrs[] = {
LDAP_OPATT_DEFAULT_NAMING_CONTEXT_W,
LDAP_OPATT_SCHEMA_NAMING_CONTEXT_W
};
ULONG i = 0;
LdapStatus = ldap_search_s(
pLdapH,
NULL,
LDAP_SCOPE_BASE,
AZ_AD_ALL_CLASSES,
NULL,
0,
&pResult
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
goto Cleanup;
}
pEntry = ldap_first_entry( pLdapH, pResult );
if ( pEntry != NULL ) {
//
// Check if we are talking to an ADAM
//
ppValueList = ldap_get_values ( pLdapH,
pEntry,
LDAP_OPATT_SUPPORTED_CAPABILITIES_W
);
if ( ppValueList == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
else
{
i = 0;
while (ppValueList[i] != NULL)
{
if (_wcsicmp(ppValueList[i++], AZ_AD_ADAM_OID) == 0)
{
//
// we are talking to ADAM, so don't need further checking
//
goto Cleanup;
}
}
ldap_value_free( ppValueList );
ppValueList = NULL;
}
for ( i = 0; i < sizeof(attrs)/sizeof(PWSTR); i++ ) {
ppValueList = ldap_get_values( pLdapH,
pEntry,
attrs[i]
);
if ( ppValueList == NULL ) {
//
// There should be the attribute we are looking for.
// If we failed to retrieve it, that means there was a lack of
// memory is storing/retrieving the value
//
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
WinStatus = AzpADCheckCompatibilityEx(
pLdapH,
*ppValueList,
i
);
if ( WinStatus != NO_ERROR ) {
goto Cleanup;
}
//
// free this memory for the next iteration
//
ldap_value_free( ppValueList );
ppValueList = NULL;
}
}
WinStatus = NO_ERROR;
Cleanup:
//
// release local memory
//
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
if ( pResult != NULL ) {
ldap_msgfree( pResult );
}
return WinStatus;
}
DWORD
AzpADCheckCompatibilityEx(
LDAP* pLdapH,
PWCHAR pDN,
ULONG index
)
/*++
Routine Description:
Worker routine for AzpADCheckCompatibility
Arguments:
pLdapH - LDAP handle to the DC
pDN - DN of the object being queried
index - index into the array identifying behavior to be checked
Return Values:
NO_ERROR - The version is OK
ERROR_CURRENT_DOMAIN_NOT_ALLOWED - The current domain will not work for
this version of azroles. This may be because of two
reasons:
1) The domain is not .Net native
2) The schema is incompatible
Other LDAP status codes
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
PWCHAR *ppValueList = 0;
LDAPMessage* pResult = NULL;
LDAPMessage* pEntry = NULL;
PWCHAR attrs[] = {
AZ_AD_DOMAIN_BEHAVIOR,
AZ_AD_SCHEMA_OBJECT_VERSION
};
LONG attrsVal[] = {
AZ_AD_MIN_DOMAIN_BEHAVIOR_VERSION,
AZ_AD_MIN_SCHEMA_OBJECT_VERSION
};
LdapStatus = ldap_search_s(
pLdapH,
pDN,
LDAP_SCOPE_BASE,
AZ_AD_ALL_CLASSES,
NULL,
0,
&pResult
);
if ( LdapStatus != NO_ERROR ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
goto Cleanup;
}
pEntry = ldap_first_entry( pLdapH, pResult );
if ( pEntry != NULL ) {
ppValueList = ldap_get_values(
pLdapH,
pEntry,
attrs[index]
);
if ( ppValueList != NULL ) {
if ( _wtol( *ppValueList ) >= attrsVal[index] ) {
WinStatus = NO_ERROR;
goto Cleanup;
} else {
WinStatus = ERROR_CURRENT_DOMAIN_NOT_ALLOWED;
goto Cleanup;
}
}
}
//
// If the values were not retrieved, then there has been
// a memory resource problem
//
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
Cleanup:
//
// local cleanup
//
if ( ppValueList != NULL ) {
ldap_value_free( ppValueList );
}
if ( pResult != NULL ) {
ldap_msgfree( pResult );
}
return WinStatus;
}
DWORD
AzpCheckVersions(
LDAP * pLdapH,
LDAPMessage * pResult
)
/*++
Routine Description:
This routine checks if the versions of the authorization store persisted
in the store will allow us to continue reading or not.
Arguments:
pLdapH - LDAP handle to DS
pResult - the search results that contains the major and minor versions
Return Value:
NO_ERROR - we can continue reading.
ERROR_REVISION_MISMATCH if we can't continue reading due to version mismatch
Other status codes
Note:
Our current implementation is that we will support reading for minor version
mismatch but not reading for major version mismatch.
--*/
{
//
// Validation
//
ASSERT( pLdapH != NULL && pResult != NULL);
DWORD WinStatus = ERROR_REVISION_MISMATCH;
PWCHAR * ppValueList = ldap_get_values (
pLdapH,
pResult,
L"msDS-AzMajorVersion"
);
if ( ppValueList != NULL )
{
LPWSTR pszStop;
//
// this casting will catch those negative integers
//
ULONG ulMajorVersion = (ULONG)(wcstol( ppValueList[0], &pszStop, 10));
//
// we want this to be perfect string for a number. Nothing should
// be left after the wcstol.
//
if (ulMajorVersion <= AzGlCurrAzRolesMajorVersion && pszStop[0] == L'\0')
{
WinStatus = NO_ERROR;
}
ldap_value_free(ppValueList);
}
return WinStatus;
}
DWORD
AzpADReadNTSecurityDescriptor(
IN PAZP_AD_CONTEXT pContext,
IN AZPE_OBJECT_HANDLE pObject,
IN PWCHAR pOptDN OPTIONAL,
IN BOOL bAzAuthorizationStoreParent,
OUT PSECURITY_DESCRIPTOR *ppSD,
IN BOOL bReadSacl,
IN BOOL bReadDacl
)
/*++
Routine Description:
This routine is a worker routine that reads the NT security
descriptor for a given object
Arguments:
pContext - context for the store
pObject - Handle to object whose security descriptor or its container
child's security descriptor needs to be read
pDN - Optional DN for the object
pEntry - An optional LDAPMessage structure to read the DN off from
bAzAuthorizationStoreParent - TRUE if the AzAuthorizationStore objects DS parent's NT security
descriptor needs to be read
ppSD - Pointer to returned security descriptor
The returned security descriptor should be freed using AzpFreeHeap.
bReadSacl - TRUE if SACL needs to be read
bReadDacl - TRUE if DACL needs to be read
Return Values:
NO_ERROR - The security descriptor was read successfully
ERROR_DS_INSUFF_ACCESS_RIGHTS - The security descriptor could not be read
Other status codes
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
PWCHAR pDN = NULL;
LDAPMessage *pResult = NULL;
LDAPMessage *pEntry = NULL;
LDAP_BERVAL **ppBerVal = NULL;
BYTE berValue[2*sizeof(ULONG)];
PWCHAR ppAttr[] = { AZ_AD_NT_SECURITY_DESCRIPTOR, NULL };
SECURITY_INFORMATION info = 0;
LDAPControl se_info_control = {
TEXT(LDAP_SERVER_SD_FLAGS_OID),
{
5, (PCHAR)berValue
},
TRUE
};
PLDAPControl server_controls[] = {
&se_info_control,
NULL
};
//
// Validation
//
ASSERT( pObject != NULL );
ASSERT( pContext != NULL );
ASSERT( pContext->PolicyUrl != NULL );
//
// Set the SECURITY_INFORMATION according to flags passed in
//
if ( bReadDacl ) {
info = DACL_SECURITY_INFORMATION;
}
if ( bReadSacl ) {
info |= SACL_SECURITY_INFORMATION;
}
//
// Get the LDAP store context for LDAP handle
//
//
// Build DN for object whose NT security descriptor needs to be got.
// Pass bAzAuthorizationStoreParent - need this to read the security descriptor
// of an already existing DS object
//
if ( pOptDN == NULL ) {
WinStatus = AzpADBuildDN(
pContext,
pObject,
&pDN,
NULL,
bAzAuthorizationStoreParent,
NULL
);
if ( WinStatus != NO_ERROR ) {
AzPrint(( AZD_AD,
"AzpADReadNTSecurityDescriptor: AzpADBuildDN failed: %ld\n",
WinStatus
));
goto Cleanup;
}
pOptDN = pDN;
}
berValue[0] = 0x30;
berValue[1] = 0x03;
berValue[2] = 0x02;
berValue[3] = 0x01;
berValue[4] = (BYTE)((ULONG)info & 0xF);
//
// Search the object with the above DN to get the
// NTSecurityDescriptor
//
LdapStatus = ldap_search_ext_s(
pContext->ld,
pOptDN,
LDAP_SCOPE_BASE,
AZ_AD_ALL_CLASSES,
ppAttr,
0,
(PLDAPControl *)server_controls,
NULL,
NULL,
0,
&pResult
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpADReadNTSecurityDescriptor: Failed to perform"
" search on %ws: %ld\n",
pDN,
WinStatus
));
goto Cleanup;
}
pEntry = ldap_first_entry( pContext->ld, pResult );
if ( pEntry != NULL ) {
ppBerVal = ldap_get_values_len( pContext->ld,
pEntry,
AZ_AD_NT_SECURITY_DESCRIPTOR );
if ( ppBerVal != NULL ) {
(*ppSD) = (PSECURITY_DESCRIPTOR) AzpAllocateHeap(
ppBerVal[0]->bv_len, "ADSD" );
if ( (*ppSD) == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
memcpy( *ppSD,
(SECURITY_DESCRIPTOR *) ppBerVal[0]->bv_val,
ppBerVal[0]->bv_len
);
} else {
WinStatus = ERROR_DS_INSUFF_ACCESS_RIGHTS;
goto Cleanup;
}
} else {
//
// AZ_AD_NT_SECURITY_DESCRIPTOR value should exist if user
// has sufficient rights to read it
//
WinStatus = ERROR_DS_INSUFF_ACCESS_RIGHTS;
goto Cleanup;
}
WinStatus = NO_ERROR;
Cleanup:
//
// free used resources
//
if ( pDN != NULL ) {
AzpFreeHeap( pDN );
}
if ( ppBerVal != NULL ) {
ldap_value_free_len( ppBerVal );
}
if ( pResult != NULL ) {
ldap_msgfree( pResult );
}
return WinStatus;
}
DWORD
AzpADStampSD(
IN PAZP_AD_CONTEXT pContext,
IN PWCHAR pDN,
IN SECURITY_INFORMATION SeInfo,
IN PSECURITY_DESCRIPTOR pSD
)
/*++
Routine Description:
This routine stamps an updated security descriptor onto the object represented
by the passed in DN.
Arguments:
pContext - context for the store
pDN - DN of the object being updated
dSize - Size of the security descriptor
SeInfo - Security information about the security descriptor
pSD - The updated security descriptor that needs to be stamped on
Return Values:
NO_ERROR - The new security descriptor was stamped onto the object in DS
successfully
Other status codes
--*/
{
DWORD WinStatus = 0;
ULONG LdapStatus = 0;
PLDAPMod ppAttrList[2];
PLDAP_BERVAL ppBerVals[2];
LDAPMod Attribute;
LDAP_BERVAL BerVal;
BYTE ControlBuffer[5];
LDAPControl SeInfoControl = {
LDAP_SERVER_SD_FLAGS_OID_W,
{
5, (PCHAR) ControlBuffer
},
TRUE
};
PLDAPControl ServerControls[2] = {
&SeInfoControl,
NULL
};
//
// Validation
//
ASSERT( pContext != NULL );
ASSERT( IsValidSecurityDescriptor( pSD ) );
//
// Get the LDAP store context for LDAP handle
//
ControlBuffer[0] = 0x30;
ControlBuffer[1] = 0x3;
ControlBuffer[2] = 0x02;
ControlBuffer[3] = 0x01;
ControlBuffer[4] = (BYTE)((ULONG)SeInfo & 0xF);
ppAttrList[0] = &Attribute;
ppAttrList[1] = NULL;
ppBerVals[0] = &BerVal;
ppBerVals[1] = NULL;
BerVal.bv_val = (PCHAR) pSD;
BerVal.bv_len = GetSecurityDescriptorLength( pSD );
Attribute.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
Attribute.mod_type = AZ_AD_NT_SECURITY_DESCRIPTOR;
Attribute.mod_values = (PWSTR *) ppBerVals;
//
// Perform the modification on the object
//
LdapStatus = ldap_modify_ext_s(
pContext->ld,
pDN,
ppAttrList,
(PLDAPControl *)ServerControls,
NULL
);
if ( LdapStatus != LDAP_SUCCESS ) {
WinStatus = LdapMapErrorToWin32( LdapStatus );
AzPrint(( AZD_AD,
"AzpADStampSD: Failed to update security descriptor"
" on %ws: %ld\n",
pDN,
WinStatus
));
goto Cleanup;
}
WinStatus = NO_ERROR;
Cleanup:
return WinStatus;
}
BOOL
AzpIsAttrDirty(
IN AZPE_OBJECT_HANDLE pObject,
IN AZ_AD_ATTRS ObjectAttr
)
/*++
Routine Description:
Determines if a particular attribute is dirty. Separate
function needed because of dual membership attributes
for AzApplicationGroup and AzRole objects. DS stores the
members/non-members in one attribute, but the core stores
them separated on whether the (non)members are SID (non)members
or AzApplicationGroup (non)members.
Arguments:
pObject - Pointer to object whose attribute needs to be checked
ObjectAttr - Object attribute needed to be checked
Return Values:
TRUE - If the particular attribute is dirty
FALSE - If the particular attribute is not dirty
--*/
{
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
ULONG lDirtyBits = AdAzrolesInfo->AzpeDirtyBits(pObject);
//
// Validation
//
ASSERT( pObject != NULL );
//
// Is this object of type AzApplicationGroup?
//
if ( lObjectType == OBJECT_TYPE_GROUP ) {
if ( ObjectAttr.AttrType == AZ_PROP_GROUP_MEMBERS ) {
return ( (lDirtyBits & AZ_DIRTY_GROUP_MEMBERS) ||
(lDirtyBits & AZ_DIRTY_GROUP_APP_MEMBERS) );
} else if ( ObjectAttr.AttrType == AZ_PROP_GROUP_NON_MEMBERS ) {
return ( (lDirtyBits & AZ_DIRTY_GROUP_NON_MEMBERS) ||
(lDirtyBits & AZ_DIRTY_GROUP_APP_NON_MEMBERS) );
}
} else if ( lObjectType == OBJECT_TYPE_ROLE ) {
if ( ObjectAttr.AttrType == AZ_PROP_ROLE_MEMBERS ) {
return ( (lDirtyBits & AZ_DIRTY_ROLE_MEMBERS) ||
(lDirtyBits & AZ_DIRTY_ROLE_APP_MEMBERS) );
}
}
return ( lDirtyBits & ObjectAttr.lDirtyBit );
}
DWORD
AzpADAllocateAttrHeap(
IN DWORD dwCount,
OUT PLDAPMod **ppAttribute
)
/*++
Routine Description:
This routine allocates memory to a attribute list structure list
Arguments:
ppAttrList - Pointer to attribute list that needs memory
Return Status:
NO_ERROR - Memory was allocated successfully
ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD WinStatus = 0;
ULONG i = 0;
*ppAttribute = ( LDAPMod **) AzpAllocateHeap( dwCount * sizeof(LDAPMod *), "ATRSTR" );
if ( *ppAttribute == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
for ( i = 0; i < dwCount; i++ ) {
(*ppAttribute)[i] = NULL;
}
Cleanup:
return WinStatus;
}
DWORD
AzpADAllocateAttrHeapModVals(
IN OUT LDAPMod **ppAttribute,
IN ULONG lCount
)
/*++
Routine Description:
This routine allocates memory to a attribute list structure element
Arguments:
ppAttribute - Pointer to attribute list that needs memory
lCount - Number of attribute values needed
Return Status:
NO_ERROR - Memory was allocated successfully
ERROR_NOT_ENOUGH_MEMORY
--*/
{
DWORD WinStatus = 0;
ULONG i = 0;
*ppAttribute = (LDAPMod *) AzpAllocateHeap( sizeof( LDAPMod ), "ATRLST" );
if ( *ppAttribute == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
(*ppAttribute)->mod_values =
(PWCHAR *) AzpAllocateHeap( lCount * sizeof( PWCHAR ), "ATRMOD" );
if ( (*ppAttribute)->mod_values == NULL ) {
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
for ( i = 0; i < lCount; i++) {
(*ppAttribute)->mod_values[i] = NULL;
}
Cleanup:
return WinStatus;
}
VOID
AzpADFreeAttrHeap(
OUT PLDAPMod **ppAttributeList,
IN BOOL bDeleteAttrList
)
/*++
Routine Description:
This routine frees the heap allocated to the LDAPMod structures
Arguments:
ppAttributeList - Attribute list to be deleted
bDeleteAttrList - TRUE if the entire list needs to be deleted
FALSE is only sub-components need to be deleted
Return Values:
None
--*/
{
ULONG lAttrValueCount = 0;
ULONG lAttrListCount = 0;
if ( (*ppAttributeList) != NULL ) {
//
// Iterate through the list freeing the heaps.
//
while ( (*ppAttributeList)[lAttrListCount] != NULL ) {
if ( (*ppAttributeList)[lAttrListCount]->mod_values != NULL ) {
while ( (*ppAttributeList)[lAttrListCount]->mod_values[lAttrValueCount]
!= NULL ) {
AzpFreeHeap( (*ppAttributeList)[lAttrListCount]->
mod_values[lAttrValueCount] );
lAttrValueCount++;
}
AzpFreeHeap( (*ppAttributeList)[lAttrListCount]->mod_values );
}
AzpFreeHeap( (*ppAttributeList)[lAttrListCount] );
(*ppAttributeList)[lAttrListCount] = NULL;
lAttrValueCount = 0;
lAttrListCount++;
}
if ( bDeleteAttrList ) {
AzpFreeHeap( (*ppAttributeList) );
(*ppAttributeList) = NULL;
}
}
}
DWORD
AzpADStoreHasUpdate (
IN BOOL bUpdateContext,
IN OUT PAZP_AD_CONTEXT pContext,
OUT BOOL * pbHasUpdate
)
/*++
Description:
Determine whether AD store has been modified since our store loaded.
For AD store, we rely on the uSNChanged attribute of the Authorization
store object to tell us if any change to the store has happened.
Arguments:
bUpdateContext - Whether or not this function call should update the Context's
uSNChanged attribute.
pContext - The persist context. If bUpdateContext is true, then
this function call will update the context's uSNChanged attribute
pbHasUpdate - Receives the test result.
Return Value:
NO_ERROR - If test is successful
Various error codes if error is encountered.
--*/
{
AZASSERT(pContext != NULL);
AZASSERT(pbHasUpdate != NULL);
DWORD dwStatus = NO_ERROR;
*pbHasUpdate = FALSE;
//
// search for the uSNChanged attribute of the authorization store object
//
ULONG LdapStatus = 0;
LDAP *pLdapHandle = NULL;
LDAPMessage *pResult = NULL;
LDAPMessage *pObjectEntry = NULL;
pLdapHandle = pContext->ld;
LPWSTR usnObjVer[] = {AD_USNCHANGED, AD_OBJECTVERSION, NULL};
LdapStatus = ldap_search_ext_s(
pLdapHandle,
pContext->pContextInfo,
LDAP_SCOPE_BASE,
AZ_AD_AZSTORE_FILTER,
usnObjVer,
0,
pContext->pLdapControls,
NULL,
NULL,
0,
&pResult // buffer to store read attributes
);
if ( LdapStatus == LDAP_NO_SUCH_OBJECT )
{
//
// most likely the objectVersion attribute is missing.
// Try one more time.
//
if ( pResult != NULL )
{
ldap_msgfree(pResult);
pResult = NULL;
}
LPWSTR usn[] = {AD_USNCHANGED, NULL};
LdapStatus = ldap_search_ext_s(
pLdapHandle,
pContext->pContextInfo,
LDAP_SCOPE_BASE,
AZ_AD_AZSTORE_FILTER,
usn,
0,
pContext->pLdapControls,
NULL,
NULL,
0,
&pResult // buffer to store read attributes
);
}
if ( LdapStatus == LDAP_NO_SUCH_OBJECT )
{
dwStatus = ERROR_FILE_NOT_FOUND;
goto Cleanup;
} else if ( LdapStatus != LDAP_SUCCESS ) {
dwStatus = LdapMapErrorToWin32( LdapStatus );
goto Cleanup;
}
pObjectEntry = ldap_first_entry( pLdapHandle, pResult );
ULONGLONG ullStoreUSN = 0;
if ( pObjectEntry != NULL )
{
ullStoreUSN = AzpADReadUSNChanged(pLdapHandle, pObjectEntry, &(pContext->HasObjectVersion) );
}
else
{
dwStatus = ERROR_DS_INSUFF_ACCESS_RIGHTS;
goto Cleanup;
}
*pbHasUpdate = (pContext->ullUSNChanged < ullStoreUSN);
if (bUpdateContext)
{
pContext->ullUSNChanged = ullStoreUSN;
}
Cleanup:
if ( pResult != NULL )
{
ldap_msgfree(pResult);
}
return dwStatus;
}
ULONGLONG
AzpADReadUSNChanged (
IN LDAP * pLdapHandle,
IN LDAPMessage * pEntry,
OUT BOOLEAN * pbHasObjectVersion
)
/*++
Description:
Read and return the uSNChanged attribute value.
Arguments:
pLdapHandle - The ldap handle.
pEntry - The search entry
pbHasObjectVersion - Whether the object has ObjectVersion attribute or not
Return Value:
The uSNChanged value. 0 if the reseach result doesn't have the attribute.
--*/
{
AZASSERT(pLdapHandle != NULL);
AZASSERT(pEntry != NULL);
AZASSERT(pbHasObjectVersion != NULL);
ULONGLONG ullUSN = 0;
PWCHAR *ppValueList = ldap_get_values(pLdapHandle, pEntry, AD_USNCHANGED );
if ( ppValueList != NULL )
{
ullUSN =_wtoi64( *ppValueList );
ldap_value_free( ppValueList );
}
ppValueList = ldap_get_values(pLdapHandle, pEntry, AD_OBJECTVERSION );
if ( ppValueList != NULL )
{
*pbHasObjectVersion = TRUE;
ldap_value_free( ppValueList );
}
else
{
*pbHasObjectVersion = FALSE;
}
return ullUSN;
}
BOOL
AzpADNeedUpdateStoreUSN (
IN PAZP_AD_CONTEXT pContext,
IN AZPE_OBJECT_HANDLE hObject,
OUT BOOL *pbReadBackUSN
)
/*++
Description:
Determine if the Authorization Store object needs to be updated
when the given object is to be updated. We will update the authorization
store object's ObjectVersion attribute when any changes to the store's
objects happen (except for batch updates) so that we can very quickly
determine if the store has been modified by testing its uSNChanged attribute.
Arguments:
pContext - The persist context
hObject - The handle to the object
pbReadBackUSN - If the uSNChanged attribute needs to be read back or not. In batch
update mode, we don't even want to read back. If other process has
updated the store, we also won't read back the uSNChanged so that
we still know that our cache is not in sync with the store.
Return Value:
True if and only if the store's uSNChanged attribute needs to be updated.
--*/
{
AZASSERT(pContext != NULL);
AZASSERT(hObject != NULL);
AZASSERT(pbReadBackUSN != NULL);
*pbReadBackUSN = FALSE;
//
// if we are doing batch mode, we don't update uSNChanged attributes no matter what
//
if (AdAzrolesInfo->AzpeAzStoreIsBatchUpdateMode(hObject))
{
return FALSE;
}
ULONG ulObjectType = AdAzrolesInfo->AzpeObjectType(hObject);
//
// When the object is the AuthorizationStore itself, then any
// change to it will cause the uSNChanged attribute to get updated
// So, no need to update by us.
//
BOOL bNeedUpdateUSN = (ulObjectType != OBJECT_TYPE_AZAUTHSTORE);
//
// Now, let's see if someone else has updated our store or not. If yes,
// then we don't need to update and we don't need to read back either.
//
BOOL bHasUpdate;
DWORD dwStatus = AzpADStoreHasUpdate(FALSE, pContext, &bHasUpdate);
if (ERROR_FILE_NOT_FOUND == dwStatus)
{
//
// The store object doesn't exist. The USN needs to be read back
//
*pbReadBackUSN = TRUE;
return FALSE;
}
else if (NO_ERROR != dwStatus || bHasUpdate)
{
//
// If we encounter problems, then we won't try to do more reading.
// If someone else has modified it, then we won't read back either
//
return FALSE;
}
else
{
//
// As long as we are the one to update the store,
// including we are updating the az store itself,
// we need to read back the USN.
//
*pbReadBackUSN = TRUE;
return bNeedUpdateUSN;
}
}
DWORD
AzpADUpdateStoreObjectForUSN (
IN BOOL bReadBackUSN,
IN AZPE_OBJECT_HANDLE hObject,
IN OUT PAZP_AD_CONTEXT pContext
)
{
AZASSERT(pContext != NULL);
LPWSTR pwszDN = NULL;
LDAPMod **ppAttributeList = NULL;
//
// We need the store object's DN
//
AZPE_OBJECT_HANDLE hAzStore = AdAzrolesInfo->AzpeGetAuthorizationStore(hObject);
DWORD WinStatus = AzpADBuildDN(pContext,
hAzStore,
&pwszDN,
NULL,
FALSE,
NULL
);
if ( NO_ERROR == WinStatus )
{
//
// we only have at most two attribute to modify
//
if (pContext->HasObjectVersion)
{
//
// if we already have the objectVersion attribute, then we need
// to delete it and write it back to bump up uSNChanged attribute
//
WinStatus = AzpADAllocateAttrHeap(3, &ppAttributeList);
}
else
{
WinStatus = AzpADAllocateAttrHeap(2, &ppAttributeList);
}
if (NO_ERROR != WinStatus)
{
goto Cleanup;
}
if (pContext->HasObjectVersion)
{
//
// delete the old one
//
WinStatus = AzpADAllocateAttrHeapModVals(&ppAttributeList[0], 2);
if ( ppAttributeList[0] == NULL )
{
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
ppAttributeList[0]->mod_type = L"objectVersion";
ppAttributeList[0]->mod_op = LDAP_MOD_DELETE;
//
// Add the value back
//
WinStatus = AzpADAllocateAttrHeapModVals(&ppAttributeList[1], 2);
if ( ppAttributeList[1] == NULL )
{
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
ppAttributeList[1]->mod_type = L"objectVersion";
ppAttributeList[1]->mod_op = LDAP_MOD_ADD;
ppAttributeList[1]->mod_values[0] =
(PWCHAR) AzpAllocateHeap(2 * sizeof( WCHAR ), "MODVALi" );
if ( ppAttributeList[1]->mod_values[0] == NULL )
{
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( ppAttributeList[1]->mod_values[0], L"1" );
}
else
{
WinStatus = AzpADAllocateAttrHeapModVals(&ppAttributeList[0], 2);
if ( ppAttributeList[0] == NULL )
{
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
ppAttributeList[0]->mod_type = L"objectVersion";
ppAttributeList[0]->mod_op = LDAP_MOD_ADD;
ppAttributeList[0]->mod_values[0] =
(PWCHAR) AzpAllocateHeap(2 * sizeof( WCHAR ), "MODVALi" );
if ( ppAttributeList[0]->mod_values[0] == NULL )
{
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
goto Cleanup;
}
wcscpy( ppAttributeList[0]->mod_values[0], L"1" );
}
ULONG ldapStatus = ldap_modify_ext_s(
pContext->ld,
pwszDN,
ppAttributeList,
pContext->pLdapControls,
NULL
);
if ( LDAP_SUCCESS != ldapStatus )
{
WinStatus = LdapMapErrorToWin32( ldapStatus );
goto Cleanup;
}
//
// we know for sure that our store has objectVersion attribute now
//
pContext->HasObjectVersion = 1;
}
if (NO_ERROR == WinStatus && bReadBackUSN)
{
//
// Now, we need to read the USN back if so instructed.
//
BOOL bIgnored;
WinStatus = AzpADStoreHasUpdate(TRUE, pContext, &bIgnored);
}
Cleanup:
if (ppAttributeList != NULL)
{
AzpADFreeAttrHeap(&ppAttributeList, TRUE);
}
if (pwszDN != NULL)
{
AzpFreeHeap(pwszDN);
}
return WinStatus;
}
DWORD
AzpADBuildNameSearchFilter(
IN AZPE_OBJECT_HANDLE pObject,
IN ULONG lPersistFlags,
OUT PWSTR * ppSearchFilter
)
/*++
Routine Description:
This routine build a name based search filter
Arguments:
pObject - Object whose type and name will be used to build the search filter.
lPersistFlags - lPersistFlags from the persist engine describing the operation
ppSerachFilter - receives the search filter.
Return Values:
NO_ERROR - If the filter is successfully build
Other status codes
Note:
Currently, this routine only support application and scope objects.
When we have need to support other objects, we can easily expand this routine.
Caller is responsible for freeing the by calling AdAzrolesInfo->AzpeFreeMemory
--*/
{
if (pObject == NULL || ppSearchFilter == NULL)
{
return ERROR_INVALID_PARAMETER;
}
*ppSearchFilter = NULL;
DWORD dwStatus = NO_ERROR;
//
// We only support building name search filter to application and
// scope objects at this point, which can be easily extended when needed.
//
ULONG lObjectType = AdAzrolesInfo->AzpeObjectType(pObject);
if (OBJECT_TYPE_APPLICATION != lObjectType && OBJECT_TYPE_SCOPE != lObjectType )
{
return ERROR_NOT_SUPPORTED;
}
else
{
LPCWSTR pwszFmt;
if ( OBJECT_TYPE_APPLICATION == lObjectType )
{
pwszFmt = L"(&(objectClass=msds-azapplication)(msds-azapplicationname=%s))";
}
else
{
pwszFmt = L"(&(objectClass=msds-azscope)(msds-azscopename=%s))";
}
LPWSTR pwszName = NULL;
dwStatus = AdAzrolesInfo->AzpeGetProperty(pObject, lPersistFlags, AZ_PROP_NAME, (PVOID*)&pwszName);
if (NO_ERROR == dwStatus)
{
AZASSERT(pwszName != NULL);
//
// Adding 1 is really not necessary because of the '%' in the pwszFmt. But
// to make it less suspicious to readers, let us do it anyway.
//
size_t Len = wcslen(pwszFmt) + wcslen( pwszName ) + 1;
*ppSearchFilter = (PWSTR)AdAzrolesInfo->AzpeAllocateMemory(Len * sizeof(WCHAR));
if (*ppSearchFilter == NULL)
{
dwStatus = ERROR_NOT_ENOUGH_MEMORY;
}
else
{
wnsprintfW(*ppSearchFilter, (int)Len, pwszFmt, pwszName);
}
}
if ( NULL != pwszName )
{
AdAzrolesInfo->AzpeFreeMemory(pwszName);
}
}
return dwStatus;
}