You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
814 lines
20 KiB
814 lines
20 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
domain.cxx
|
|
|
|
Abstract:
|
|
|
|
Routines implementing a cache of domains and the DC for that domain.
|
|
|
|
Author:
|
|
|
|
Cliff Van Dyke (cliffv) 29-June-2001
|
|
|
|
--*/
|
|
|
|
#include "pch.hxx"
|
|
#include <dsgetdc.h>
|
|
#include <lmcons.h>
|
|
#include <lmapibuf.h>
|
|
#include <windns.h>
|
|
|
|
//
|
|
// Structure definitions
|
|
//
|
|
// A cached domain
|
|
//
|
|
|
|
typedef struct _AZP_DOMAIN {
|
|
|
|
//
|
|
// Link to the next domain for this AzAuthorizationStore
|
|
// Access is serialized by the AzAuthorizationStore->DomainCritSect
|
|
//
|
|
|
|
LIST_ENTRY Next;
|
|
|
|
//
|
|
// Reference count in this structure
|
|
// Access is serialized by Interlocked increment/decrement
|
|
//
|
|
|
|
LONG ReferenceCount;
|
|
|
|
//
|
|
// Name of the domain represented by this structure
|
|
// Access is serialized by the AZP_DOMAIN->DomainCritSect
|
|
// In the case of InitializeFromName we do not have the DNS domain name.
|
|
// The boolean is used to distinguish the two cases.
|
|
//
|
|
|
|
AZP_STRING DomainName;
|
|
BOOLEAN IsDnsDomainName;
|
|
|
|
//
|
|
// A DCs in that domain
|
|
// Access is serialized by the AZP_DOMAIN->DomainCritSect
|
|
//
|
|
|
|
PAZP_DC Dc;
|
|
|
|
//
|
|
// A boolean indicating that the domain is down and the time when it went down.
|
|
// Access is serialized by the AZP_DOMAIN->DomainCritSect
|
|
//
|
|
|
|
BOOLEAN DomainIsDown;
|
|
LARGE_INTEGER DomainDownStartTime;
|
|
|
|
SAFE_CRITICAL_SECTION DomainCritSect;
|
|
|
|
} AZP_DOMAIN, *PAZP_DOMAIN;
|
|
|
|
PVOID
|
|
AzpReferenceDomain(
|
|
IN PAZP_AZSTORE AzAuthorizationStore,
|
|
IN LPWSTR DomainName,
|
|
IN BOOLEAN IsDnsDomainName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the domain structure for a domain. If none exists, it is allocated.
|
|
|
|
Arguments:
|
|
|
|
AzAuthorizationStore - The AzAuthorizationStore object for the authz policy database.
|
|
|
|
DomainName - Name of the domain being referenced.
|
|
|
|
IsDnsDomainName - Whether this is really a DNS Domain name or NetBios Name.
|
|
When we have a token avalaible we have the DNS Domain name. If not, in
|
|
the case in which we have initialized the context from Name we only
|
|
have a NetBios name.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to the referenced domain object. The pointer must be dereference by
|
|
calling AzpDereferenceDomain.
|
|
|
|
NULL: memory could not be allocated for the domain.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PAZP_DOMAIN Domain = NULL;
|
|
PLIST_ENTRY ListEntry;
|
|
AZP_STRING DomainNameString;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
ASSERT( AzAuthorizationStore->GenericObject.ObjectType == OBJECT_TYPE_AZAUTHSTORE );
|
|
ASSERT( AzAuthorizationStore->DomainCritSectInitialized );
|
|
|
|
AzpInitString( &DomainNameString, DomainName );
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->DomainCritSect );
|
|
|
|
//
|
|
// Walk the list finding an existing domain.
|
|
//
|
|
|
|
for ( ListEntry = AzAuthorizationStore->Domains.Flink ;
|
|
ListEntry != &AzAuthorizationStore->Domains ;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
Domain = CONTAINING_RECORD( ListEntry,
|
|
AZP_DOMAIN,
|
|
Next );
|
|
|
|
if ( AzpEqualStrings( &DomainNameString, &Domain->DomainName )) {
|
|
|
|
//
|
|
// Reference the domain
|
|
//
|
|
InterlockedIncrement( &Domain->ReferenceCount );
|
|
AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): Domain ref\n", Domain, Domain->DomainName.String, Domain->ReferenceCount ));
|
|
|
|
//
|
|
// Move it to the front of the list
|
|
//
|
|
|
|
RemoveEntryList( &Domain->Next );
|
|
InsertHeadList( &AzAuthorizationStore->Domains, &Domain->Next );
|
|
|
|
break;
|
|
}
|
|
|
|
Domain = NULL;
|
|
}
|
|
|
|
//
|
|
// If we didn't find one,
|
|
// allocate one.
|
|
//
|
|
|
|
if ( Domain == NULL ) {
|
|
|
|
Domain = (PAZP_DOMAIN) AzpAllocateHeap( sizeof(AZP_DOMAIN) + DomainNameString.StringSize, "DMDOM" );
|
|
|
|
if ( Domain != NULL ) {
|
|
|
|
//
|
|
// Initialize the domain
|
|
//
|
|
|
|
RtlZeroMemory( Domain, sizeof(*Domain) );
|
|
|
|
//
|
|
// Initialize the client context critical section
|
|
//
|
|
|
|
Status = SafeInitializeCriticalSection( &Domain->DomainCritSect, SAFE_DOMAIN );
|
|
|
|
if ( !NT_SUCCESS( Status )) {
|
|
AzpFreeHeap( Domain );
|
|
Domain = NULL;
|
|
|
|
} else {
|
|
|
|
RtlCopyMemory( (Domain+1),
|
|
DomainNameString.String,
|
|
DomainNameString.StringSize );
|
|
|
|
Domain->DomainName = DomainNameString;
|
|
Domain->DomainName.String = (LPWSTR)(Domain+1);
|
|
Domain->IsDnsDomainName = IsDnsDomainName;
|
|
|
|
|
|
// One to return to the caller.
|
|
// One for being in the linked list
|
|
Domain->ReferenceCount = 2;
|
|
|
|
InsertHeadList( &AzAuthorizationStore->Domains, &Domain->Next );
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->DomainCritSect );
|
|
return (PVOID) Domain;
|
|
}
|
|
|
|
|
|
VOID
|
|
AzpDereferenceDomain(
|
|
IN PVOID DomainHandle
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrements the reference count on the domain object.
|
|
If the reference count reaches zero, the object is deleted.
|
|
|
|
Arguments:
|
|
|
|
DomainHandle - Handle to the domain to dereference
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PAZP_DOMAIN Domain = (PAZP_DOMAIN) DomainHandle;
|
|
ULONG RefCount;
|
|
|
|
//
|
|
// Decrement the reference count
|
|
//
|
|
|
|
RefCount = InterlockedDecrement( &Domain->ReferenceCount );
|
|
AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): Domain deref\n", Domain, Domain->DomainName.String, Domain->ReferenceCount ));
|
|
|
|
//
|
|
// If the object is no longer referenced,
|
|
// delete it.
|
|
//
|
|
|
|
if ( RefCount == 0 ) {
|
|
|
|
if ( Domain->Dc != NULL ) {
|
|
AzpDereferenceDc( Domain->Dc );
|
|
}
|
|
|
|
SafeDeleteCriticalSection( &Domain->DomainCritSect );
|
|
AzpFreeHeap( Domain );
|
|
|
|
}
|
|
}
|
|
|
|
VOID
|
|
AzpUnlinkDomains(
|
|
IN PAZP_AZSTORE AzAuthorizationStore
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine unlinks all of the domains from the AzAuthorizationStore.
|
|
|
|
Arguments:
|
|
|
|
AzAuthorizationStore - The AzAuthorizationStore object for the authz policy database.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
ASSERT( AzAuthorizationStore->GenericObject.ObjectType == OBJECT_TYPE_AZAUTHSTORE );
|
|
ASSERT( AzAuthorizationStore->DomainCritSectInitialized );
|
|
SafeEnterCriticalSection( &AzAuthorizationStore->DomainCritSect );
|
|
|
|
//
|
|
// Free the list of domains
|
|
//
|
|
|
|
while ( !IsListEmpty( &AzAuthorizationStore->Domains ) ) {
|
|
PLIST_ENTRY ListEntry;
|
|
PAZP_DOMAIN Domain;
|
|
|
|
//
|
|
// Remove the entry from the list
|
|
//
|
|
|
|
ListEntry = RemoveHeadList( &AzAuthorizationStore->Domains );
|
|
|
|
Domain = CONTAINING_RECORD( ListEntry,
|
|
AZP_DOMAIN,
|
|
Next );
|
|
|
|
ASSERT( Domain->ReferenceCount == 1 );
|
|
AzpDereferenceDomain( Domain );
|
|
|
|
}
|
|
|
|
SafeLeaveCriticalSection( &AzAuthorizationStore->DomainCritSect );
|
|
}
|
|
|
|
DWORD
|
|
AzpLdapErrorToWin32Error(
|
|
IN ULONG LdapStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Map Ldap Error to Win 32 error.
|
|
|
|
Be a little bit more specific than LdapMapErrorToWin32
|
|
|
|
Arguments:
|
|
|
|
LdapStatus - LdapStatus code to map.
|
|
|
|
Return Value:
|
|
|
|
Corresponding win 32 status code
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Return a consistent status code for DC down
|
|
//
|
|
|
|
switch ( LdapStatus ) {
|
|
case LDAP_SERVER_DOWN :
|
|
case LDAP_UNAVAILABLE :
|
|
case LDAP_BUSY:
|
|
return ERROR_NO_SUCH_DOMAIN;
|
|
|
|
default:
|
|
return LdapMapErrorToWin32( LdapStatus );
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
AzpAllocateDc(
|
|
IN PAZP_STRING DcName,
|
|
OUT PAZP_DC *RetDc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a PAZP_DC structure and binds to the DC
|
|
|
|
Arguments:
|
|
|
|
DcName - Name of the Dc to allocate the structure for
|
|
|
|
RetDc - Returns a pointer to a structure representing a DC.
|
|
The caller should dereference this structure by calling AzpDereferenceDc
|
|
|
|
Return Value:
|
|
|
|
|
|
Status of the operation:
|
|
|
|
NO_ERROR: a DcName has been returned. The caller should try the named DC. If the DC is
|
|
expected to be down, the caller should call this routine again.
|
|
|
|
ERROR_NO_SUCH_DOMAIN: No DC could be found.
|
|
|
|
Others: resource errors. etc.
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
PAZP_DC Dc = NULL;
|
|
|
|
ULONG LdapStatus;
|
|
LDAP *LdapHandle = NULL;
|
|
|
|
//
|
|
// Initialize the LDAP connection
|
|
//
|
|
|
|
LdapHandle = ldap_init( DcName->String, LDAP_PORT );
|
|
|
|
if ( LdapHandle == NULL ) {
|
|
LdapStatus = LdapGetLastError();
|
|
AzPrint(( AZD_ACCESS,
|
|
"AzpAllocateDc: ldap_init failed on %ws: %ld: %s\n",
|
|
DcName->String,
|
|
LdapStatus,
|
|
ldap_err2stringA( LdapStatus )));
|
|
|
|
WinStatus = AzpLdapErrorToWin32Error( LdapStatus );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set our default options
|
|
//
|
|
|
|
WinStatus = AzpADSetDefaultLdapOptions( LdapHandle, DcName->String );
|
|
|
|
if (WinStatus != NO_ERROR)
|
|
{
|
|
AzPrint(( AZD_AD,
|
|
"AzpAllocateDc: AzpADSetDefaultLdapOptions failed on %ws: %ld\n",
|
|
DcName->String,
|
|
WinStatus
|
|
));
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Bind to the DC
|
|
//
|
|
|
|
LdapStatus = ldap_bind_s( LdapHandle,
|
|
NULL, // No DN of account to authenticate as
|
|
NULL, // Default credentials
|
|
LDAP_AUTH_NEGOTIATE );
|
|
|
|
if ( LdapStatus != LDAP_SUCCESS ) {
|
|
|
|
AzPrint(( AZD_ACCESS,
|
|
"AzpAllocateDc: ldap_bind failed on %ws: %ld: %s\n",
|
|
DcName->String,
|
|
LdapStatus,
|
|
ldap_err2stringA( LdapStatus )));
|
|
|
|
WinStatus = AzpLdapErrorToWin32Error(LdapStatus);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate the structure
|
|
//
|
|
|
|
Dc = (PAZP_DC) AzpAllocateHeap( sizeof(AZP_DC) + DcName->StringSize, "DMDC" );
|
|
|
|
if ( Dc == NULL ) {
|
|
WinStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Fill it in
|
|
//
|
|
|
|
RtlCopyMemory( (Dc+1),
|
|
DcName->String,
|
|
DcName->StringSize );
|
|
|
|
Dc->DcName = *DcName;
|
|
Dc->DcName.String = (LPWSTR)(Dc+1);
|
|
|
|
Dc->LdapHandle = LdapHandle;
|
|
LdapHandle = NULL;
|
|
|
|
|
|
// One to return to the caller.
|
|
Dc->ReferenceCount = 1;
|
|
AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): DC Allocaote\n", Dc, Dc->DcName.String, Dc->ReferenceCount ));
|
|
|
|
//
|
|
// Return it to the caller
|
|
//
|
|
|
|
*RetDc = Dc;
|
|
Dc = NULL;
|
|
WinStatus = NO_ERROR;
|
|
|
|
Cleanup:
|
|
if ( LdapHandle != NULL ) {
|
|
LdapStatus = ldap_unbind( LdapHandle );
|
|
|
|
ASSERT( LdapStatus == LDAP_SUCCESS );
|
|
}
|
|
return WinStatus;
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
AzpGetDc(
|
|
IN PAZP_AZSTORE AzAuthorizationStore,
|
|
IN PVOID DomainHandle,
|
|
IN OUT PULONG Context,
|
|
OUT PAZP_DC *RetDc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the DC for the domain represented by DomainHandle.
|
|
|
|
Arguments:
|
|
|
|
AzAuthorizationStore - The AzAuthorizationStore object for the authz policy database.
|
|
|
|
DomainHandle - A handle to the domain to find a DC for
|
|
|
|
Context - Specifies the context indicating how hard this routine has tried to find
|
|
a DC. The caller should call the routine in a loop. On the first call, the caller
|
|
should pass in a pointer DWORD set to zero. On subsequent calls, the caller should
|
|
pass in the value returned on the previous pass.
|
|
|
|
RetDc - Returns a pointer to a structure representing a DC.
|
|
The caller should dereference this structure by calling AzpDereferenceDc
|
|
|
|
Return Value:
|
|
|
|
Status of the operation:
|
|
|
|
NO_ERROR: a DcName has been returned. The caller should try the named DC. If the DC is
|
|
expected to be down, the caller should call this routine again.
|
|
|
|
ERROR_NO_SUCH_DOMAIN: No DC could be found.
|
|
|
|
Others: resource errors. etc.
|
|
|
|
--*/
|
|
{
|
|
DWORD WinStatus;
|
|
|
|
PAZP_DOMAIN Domain = (PAZP_DOMAIN) DomainHandle;
|
|
ULONG DsGetDcFlags;
|
|
|
|
PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
|
|
AZP_STRING CapturedString;
|
|
|
|
PAZP_DC Dc = NULL;
|
|
PAZP_DC TempDc;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
ASSERT( AzAuthorizationStore->GenericObject.ObjectType == OBJECT_TYPE_AZAUTHSTORE );
|
|
ASSERT( AzAuthorizationStore->DomainCritSectInitialized );
|
|
|
|
AzpInitString( &CapturedString, NULL );
|
|
SafeEnterCriticalSection( &Domain->DomainCritSect );
|
|
*RetDc = NULL;
|
|
|
|
|
|
//
|
|
// If the domain is down,
|
|
// check to see if it is time to try again.
|
|
//
|
|
|
|
if ( Domain->DomainIsDown ) {
|
|
|
|
//
|
|
// If we haven't waited long enough,
|
|
// fail immediately.
|
|
//
|
|
|
|
if ( !AzpTimeHasElapsed( &Domain->DomainDownStartTime,
|
|
AzAuthorizationStore->DomainTimeout ) ) {
|
|
|
|
WinStatus = ERROR_NO_SUCH_DOMAIN;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// If we have waited long enough,
|
|
// try again to find a DC.
|
|
//
|
|
|
|
if ( Domain->Dc != NULL ) {
|
|
AzpDereferenceDc( Domain->Dc ); // No use trying this stale DC
|
|
Domain->Dc = NULL;
|
|
}
|
|
Domain->DomainIsDown = FALSE;
|
|
}
|
|
|
|
//
|
|
// Loop through the various states internally so our caller doesn't need to
|
|
//
|
|
|
|
for ( ; *Context <= 2 ; (*Context)++ ) {
|
|
|
|
|
|
//
|
|
// If this is the first call,
|
|
// return the cached DC name.
|
|
//
|
|
|
|
if ( *Context == 0 ) {
|
|
|
|
//
|
|
// If there is no cached value,
|
|
// continue the loop to look harder.
|
|
//
|
|
|
|
if ( Domain->Dc == NULL ) {
|
|
continue;
|
|
}
|
|
|
|
|
|
//
|
|
// On the second call, use DsGetDcName.
|
|
// On the third call, use DsGetDcName withforce.
|
|
//
|
|
} else {
|
|
|
|
if (Domain->IsDnsDomainName) {
|
|
DsGetDcFlags = DS_IS_DNS_NAME | DS_IP_REQUIRED;
|
|
} else {;
|
|
//
|
|
// Make sure that this is NT5 domain or higher.
|
|
//
|
|
DsGetDcFlags = DS_IS_FLAT_NAME | DS_IP_REQUIRED |
|
|
DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME;
|
|
}
|
|
if ( *Context == 2 ) {
|
|
DsGetDcFlags |= DS_FORCE_REDISCOVERY;
|
|
}
|
|
|
|
//
|
|
// Free the values from the previous iteration
|
|
//
|
|
if ( DomainControllerInfo != NULL ) {
|
|
NetApiBufferFree( DomainControllerInfo );
|
|
DomainControllerInfo = NULL;
|
|
}
|
|
|
|
if ( Dc != NULL ) {
|
|
AzpDereferenceDc( Dc );
|
|
Dc = NULL;
|
|
}
|
|
|
|
AzpFreeString( &CapturedString );
|
|
|
|
//
|
|
// Find a DC in the user's account domain
|
|
//
|
|
|
|
WinStatus = DsGetDcName(
|
|
NULL,
|
|
Domain->DomainName.String,
|
|
NULL, // No guid
|
|
NULL, // No site,
|
|
DsGetDcFlags,
|
|
&DomainControllerInfo );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
|
|
//
|
|
// If the DC is down,
|
|
// DsGetDcName never lies,
|
|
// but map obnoxious status codes to a more likeable one
|
|
//
|
|
|
|
if ( WinStatus == WSAEHOSTUNREACH ||
|
|
WinStatus == ERROR_NO_SUCH_DOMAIN ) {
|
|
|
|
break;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Capture the DC name into a string structure.
|
|
//
|
|
|
|
AzpFreeString( &CapturedString );
|
|
|
|
WinStatus = AzpCaptureString( &CapturedString,
|
|
DomainControllerInfo->DomainControllerName + 2,
|
|
DNS_MAX_NAME_LENGTH,
|
|
FALSE ); // Null not OK
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the DC name is the same as the previous name,
|
|
// continue looping to try harder to find a DC.
|
|
//
|
|
|
|
if ( Domain->Dc != NULL &&
|
|
AzpEqualStrings( &CapturedString, &Domain->Dc->DcName) ) {
|
|
|
|
WinStatus = ERROR_NO_SUCH_DOMAIN;
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Allocate a structure representing the DC
|
|
//
|
|
|
|
WinStatus = AzpAllocateDc( &CapturedString,
|
|
&Dc );
|
|
|
|
if ( WinStatus != NO_ERROR ) {
|
|
if ( WinStatus == ERROR_NO_SUCH_DOMAIN ) {
|
|
continue;
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Swap the old/new Dcs
|
|
//
|
|
|
|
TempDc = Domain->Dc;
|
|
Domain->Dc = Dc;
|
|
Dc = TempDc;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Return the found DC to the caller.
|
|
//
|
|
|
|
ASSERT( Domain->Dc != NULL );
|
|
ASSERT( Domain->Dc->DcName.StringSize != 0 );
|
|
|
|
InterlockedIncrement( &Domain->Dc->ReferenceCount );
|
|
AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): DC ref\n", Domain->Dc, Domain->Dc->DcName.String, Domain->Dc->ReferenceCount ));
|
|
|
|
*RetDc = Domain->Dc;
|
|
WinStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
//
|
|
// No DC can be found via any mechanism
|
|
// Set the negative cache to indicate so.
|
|
//
|
|
|
|
Domain->DomainIsDown = TRUE;
|
|
GetSystemTimeAsFileTime( (PFILETIME)&Domain->DomainDownStartTime );
|
|
WinStatus = ERROR_NO_SUCH_DOMAIN;
|
|
|
|
|
|
Cleanup:
|
|
if ( DomainControllerInfo != NULL ) {
|
|
NetApiBufferFree( DomainControllerInfo );
|
|
}
|
|
|
|
AzpFreeString( &CapturedString );
|
|
|
|
if ( Dc != NULL ) {
|
|
AzpDereferenceDc( Dc );
|
|
Dc = NULL;
|
|
}
|
|
|
|
SafeLeaveCriticalSection( &Domain->DomainCritSect );
|
|
return WinStatus;
|
|
}
|
|
|
|
VOID
|
|
AzpDereferenceDc(
|
|
IN PAZP_DC Dc
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrements the reference count on the DC object.
|
|
If the reference count reaches zero, the object is deleted.
|
|
|
|
Arguments:
|
|
|
|
Dc - Pointer to the DC to dereference
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
ULONG RefCount;
|
|
|
|
//
|
|
// Decrement the reference count
|
|
//
|
|
|
|
RefCount = InterlockedDecrement( &Dc->ReferenceCount );
|
|
AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): DC deref\n", Dc, Dc->DcName.String, Dc->ReferenceCount ));
|
|
|
|
//
|
|
// If the object is no longer referenced,
|
|
// delete it.
|
|
//
|
|
|
|
if ( RefCount == 0 ) {
|
|
if ( Dc->LdapHandle != NULL ) {
|
|
ULONG LdapStatus;
|
|
LdapStatus = ldap_unbind( Dc->LdapHandle );
|
|
|
|
ASSERT( LdapStatus == LDAP_SUCCESS );
|
|
}
|
|
AzpFreeHeap( Dc );
|
|
}
|
|
}
|