/*++ 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 #include #include #include #include #include #include #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; iAzpeObjectType(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 ;CN=... // extract the ; 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"", // 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 ;; // We need to extract the 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 (iAzpePersistenceGuid(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"", // 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"", // 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"", // 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"", // 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; }