Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2251 lines
63 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
netlogon.c
Abstract:
Netlogon routines to access the DS.
Rightfully these routines belong in netlogon. However, the current
interface to the DS is complex enough that the support routines
are substantial. Those routines are already duplicated in SAM and LSA.
Rather than introduce a new set, this module exports exactly what is
needed by Netlogon.
Author:
Cliff Van Dyke (CliffV) May 7, 1997
Environment:
User Mode
Revision History:
--*/
#include <lsapch2.h>
#include <dbp.h>
// #include <ntdsa.h>
#include <windns.h>
BOOLEAN
DsIsBeingBackSynced();
NTSTATUS
LsapDsReadSubnetObj(
IN PDSNAME SubnetObjName,
OUT PBOOL SubnetValid,
OUT PLSAP_SUBNET_INFO_ENTRY SubnetInfoEntry
)
/*++
Routine Description:
This function will read the specified subnet object and fill in the entry.
Arguments:
SubnetObjName - DsName of the subnet object
SubnetValid - On success, returns TRUE if the subnet object has been
read successfully and has been determined to be valid. Otherwise,
returns FALSE. A subnet object may be invalid if it was created
as a result of a subnet name collision in the DS or if the
associated site name object was created as a result of a site
name collision in the DS.
SubnetInfoEntry - On success, if the subnet has been determined
to be valid as indicated by the SubnetValid parameter, returns
the Subnet Information.
Returns:
STATUS_SUCCESS - Success
STATUS_INVALID_PARAMETER - A bad InformationClass level was encountered
STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG i;
PDSNAME DsName;
UNICODE_STRING SubnetName = {0};
UNICODE_STRING SiteName = {0};
LPWSTR SiteNameString = NULL;
BOOL LocalSubnetValid = TRUE; // used only on success
//
// Build the list of attribute IDs we need based on the information
// class
//
ATTR SubnetAttrVals[] = {
{ATT_SITE_OBJECT, {0, NULL} },
};
ATTRBLOCK ReadBlock, ReturnedBlock = { 0 };
WCHAR RdnBuffer[MAX_RDN_SIZE + 1];
ULONG RdnLen;
ATTRTYP RdnType;
LsapEnterFunc( "LsapDsReadSubnetObj" );
//
// The subnet name is the RDN of the subnet object itself.
//
// Return it to the caller.
//
Status = LsapDsMapDsReturnToStatus( GetRDNInfoExternal(
SubnetObjName,
RdnBuffer,
&RdnLen,
&RdnType ) );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// If the subnet object DN is mangled as a result
// of a subnet name collision in the DS, ignore this
// subnet object
//
if ( IsMangledRDNExternal(RdnBuffer, RdnLen, NULL) ) {
LocalSubnetValid = FALSE;
Status = STATUS_SUCCESS;
goto Cleanup;
}
LSAPDS_ALLOC_AND_COPY_STRING_TO_UNICODE_ON_SUCCESS(
Status,
&SubnetName,
RdnBuffer,
RdnLen*sizeof(WCHAR) );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// Read the required attributes from the subnet object
//
ReadBlock.attrCount = sizeof(SubnetAttrVals) / sizeof(ATTR);
ReadBlock.pAttr = SubnetAttrVals;
Status = LsapDsReadByDsName( SubnetObjName,
0,
&ReadBlock,
&ReturnedBlock );
//
// Allow for the case where the SiteObject attribute doesn't exist.
//
if ( Status == STATUS_NOT_FOUND ) {
LocalSubnetValid = TRUE;
Status = STATUS_SUCCESS;
goto Cleanup;
}
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// Now, marshal the attribute. There should be
// only one site object associated with a subnet.
//
if ( ReturnedBlock.attrCount > 0 ) {
NET_API_STATUS NetStatus;
//
// Validate the data
//
if ( ReturnedBlock.pAttr[0].attrTyp != ATT_SITE_OBJECT ||
ReturnedBlock.pAttr[0].AttrVal.valCount == 0 ) {
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
//
// Get the first value (should be only one)
//
DsName = LSAP_DS_GET_DS_ATTRIBUTE_AS_DSNAME( &ReturnedBlock.pAttr[0] );
//
// Get the site name RDN from the site DN
//
Status = LsapDsMapDsReturnToStatus( GetRDNInfoExternal(
DsName,
RdnBuffer,
&RdnLen,
&RdnType ) );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// If the site name is mangled as the result of a name
// colision in the DS, ignore this site attribute.
//
if ( IsMangledRDNExternal(RdnBuffer, RdnLen, NULL) ) {
LocalSubnetValid = FALSE;
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Verify that the site name can be
// used as a label in a DNS name
//
SiteNameString = LsapAllocateLsaHeap( (RdnLen + 1) * sizeof(WCHAR) );
if ( SiteNameString == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlCopyMemory( SiteNameString, RdnBuffer, RdnLen*sizeof(WCHAR) );
SiteNameString[RdnLen] = UNICODE_NULL;
NetStatus = DnsValidateName_W( SiteNameString, DnsNameDomainLabel );
//
// If the name can't be used as a DNS label,
// the subnet is invalid
//
if ( NetStatus != NO_ERROR && NetStatus != DNS_ERROR_NON_RFC_NAME ) {
LocalSubnetValid = FALSE;
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// OK, the site name is valid
//
LocalSubnetValid = TRUE;
//
// Get the site name
//
LSAPDS_ALLOC_AND_COPY_STRING_TO_UNICODE_ON_SUCCESS(
Status,
&SiteName,
RdnBuffer,
RdnLen*sizeof(WCHAR) );
}
Cleanup:
//
// On success, return the data
//
if ( NT_SUCCESS(Status) ) {
*SubnetValid = LocalSubnetValid;
}
if ( NT_SUCCESS(Status) && LocalSubnetValid ) {
SubnetInfoEntry->SubnetName = SubnetName;
SubnetInfoEntry->SiteName = SiteName;
} else {
if ( SubnetName.Buffer != NULL ) {
LsapFreeLsaHeap( SubnetName.Buffer );
}
if ( SiteName.Buffer != NULL ) {
LsapFreeLsaHeap( SiteName.Buffer );
}
}
if ( SiteNameString != NULL ) {
LsapFreeLsaHeap( SiteNameString );
}
LsapExitFunc( "LsapDsReadSubnetObj", Status );
return( Status );
}
NTSTATUS
LsapDsReadSiteObj(
IN PDSNAME SiteObjName,
OUT PBOOL SiteValid,
OUT PLSAP_SITE_INFO_ENTRY SiteInfoEntry
)
/*++
Routine Description:
This function will read the specified site object and fill in the entry.
It will check that the site name is not mangled as a result of site
name collision in the DS. It will also check that the site name can be
used as a DNS label in a DNS name.
Arguments:
SiteObjName - DsName of the site object
SiteValid - On success, returns TRUE if the site object has been
read successfully and has been determined to be valid. Otherwise,
returns FALSE. A site object may be invalid if it was created
as a result of a site name collision in the DS.
SitesInfoEntry - On success, if the site has been determined
to be valid as indicated by the SiteValid parameter, returns
the Site Information.
Returns:
STATUS_SUCCESS - Success
STATUS_INVALID_PARAMETER - A bad InformationClass level was encountered
STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed
--*/
{
NTSTATUS Status;
WCHAR RdnBuffer[MAX_RDN_SIZE + 1];
ULONG RdnLen;
ATTRTYP RdnType;
LsapEnterFunc( "LsapDsReadSiteObj" );
//
// The site name is the RDN of the site object itself.
//
// Return it to the caller.
//
Status = LsapDsMapDsReturnToStatus( GetRDNInfoExternal(
SiteObjName,
RdnBuffer,
&RdnLen,
&RdnType ) );
if ( NT_SUCCESS( Status ) ) {
//
// Return this site only if it's not mangled
// as the result of a name colision in the DS
//
if ( IsMangledRDNExternal(RdnBuffer, RdnLen, NULL) ) {
*SiteValid = FALSE;
} else {
LPWSTR SiteNameString = NULL;
//
// Verify that the site name can be
// used as a label in a DNS name
//
SiteNameString = LsapAllocateLsaHeap( (RdnLen + 1) * sizeof(WCHAR) );
if ( SiteNameString == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
} else {
NET_API_STATUS NetStatus;
RtlCopyMemory( SiteNameString, RdnBuffer, RdnLen*sizeof(WCHAR) );
SiteNameString[RdnLen] = UNICODE_NULL;
NetStatus = DnsValidateName_W( SiteNameString, DnsNameDomainLabel );
LsapFreeLsaHeap( SiteNameString );
//
// Return the site name only if it can be used as a DNS label
//
if ( NetStatus == NO_ERROR || NetStatus == DNS_ERROR_NON_RFC_NAME ) {
LSAPDS_ALLOC_AND_COPY_STRING_TO_UNICODE_ON_SUCCESS(
Status,
&SiteInfoEntry->SiteName,
RdnBuffer,
RdnLen*sizeof(WCHAR) );
//
// On success, indicate that the site is valid
//
if ( NT_SUCCESS(Status) ) {
*SiteValid = TRUE;
}
} else {
*SiteValid = FALSE;
}
}
}
}
LsapExitFunc( "LsapDsReadSiteObj", Status );
return( Status );
}
NTSTATUS
LsaIGetSiteName(
OUT PLSAP_SITENAME_INFO *SiteNameInformation
)
/*++
Routine Description:
This routine returns the GUID of this DSA and the SiteName of the
site this DSA is in.
Arguments:
SiteNameInformation - Returns a pointer to the site name information.
Buffer should be freed using LsaIFree_LSAP_SITENAME_INFO;
Returns:
STATUS_SUCCESS - Success
STATUS_INVALID_DOMAIN_STATE - The Ds is not installed or running at the time of the call
STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed
--*/
{
NTSTATUS Status;
BINDARG BindArg;
BINDRES *BindRes;
PLSAP_SITENAME_INFO SiteNameInfo = NULL;
PDSNAME SiteDsName = NULL;
BOOLEAN CloseTransaction = FALSE;
ULONG DsaOptions = 0;
//
// The list of attributes we need from the DSA object
//
ATTR DsaAttrVals[] = {
{ATT_OPTIONS, {0, NULL} },
};
ATTRBLOCK ReadBlock, ReturnedBlock;
WCHAR RdnBuffer[MAX_RDN_SIZE + 1];
ULONG RdnLen;
ATTRTYP RdnType;
ULONG i;
LsarpReturnCheckSetup();
LsapEnterFunc( "LsaIGetSiteName" );
//
// Make sure the DS is installed
//
if ( !LsaDsStateInfo.UseDs ) {
LsapExitFunc( "LsaIGetSiteName", STATUS_INVALID_DOMAIN_STATE );
return STATUS_INVALID_DOMAIN_STATE;
}
//
// See if we already have a transaction going
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
return Status;
}
//
// Get the DSA object's DSNAME.
//
RtlZeroMemory( &BindArg, sizeof(BindArg) );
Status = LsapDsMapDsReturnToStatus( DirBind( &BindArg,
&BindRes ));
LsapDsContinueTransaction();
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
//
// Read the required attributes from the DSA object
//
ReadBlock.attrCount = sizeof(DsaAttrVals) / sizeof(ATTR);
ReadBlock.pAttr = DsaAttrVals;
Status = LsapDsReadByDsName( BindRes->pCredents,
LSAPDS_READ_NO_LOCK,
&ReadBlock,
&ReturnedBlock );
if ( Status == STATUS_UNSUCCESSFUL ) {
Status = STATUS_NOT_FOUND;
}
//
// If the options attribute exists,
// get its value.
//
if ( Status != STATUS_NOT_FOUND ) {
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
//
// Get the attributes from the DSA object
//
for ( i = 0;
i < ReturnedBlock.attrCount && NT_SUCCESS( Status );
i++) {
//
// Handle the DSA Options attributes.
//
switch ( ReturnedBlock.pAttr[i].attrTyp ) {
case ATT_OPTIONS:
// Attribute is single valued, but ...
if ( ReturnedBlock.pAttr[i].AttrVal.valCount >= 1 ) {
DsaOptions = LSAP_DS_GET_DS_ATTRIBUTE_AS_ULONG( &ReturnedBlock.pAttr[ i ] );
}
break;
default:
Status = STATUS_INVALID_PARAMETER;
break;
}
}
}
//
// Compute the name of the site this DSA is in.
// (Simply trim three names off the DSA's DSNAME )
//
SiteDsName = LsapAllocateLsaHeap( BindRes->pCredents->structLen );
if ( SiteDsName == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
if ( TrimDSNameBy( BindRes->pCredents, 3, SiteDsName ) != 0 ) {
Status = STATUS_INTERNAL_ERROR;
goto Cleanup;
}
//
// The site name is the RDN of the site object.
//
Status = LsapDsMapDsReturnToStatus( GetRDNInfoExternal(
SiteDsName,
RdnBuffer,
&RdnLen,
&RdnType ) );
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
//
// Allocate a buffer to return to the caller.
//
SiteNameInfo = LsapAllocateLsaHeap( sizeof(LSAP_SITENAME_INFO) );
if ( SiteNameInfo == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Fill it in.
//
LSAPDS_ALLOC_AND_COPY_STRING_TO_UNICODE_ON_SUCCESS(
Status,
&SiteNameInfo->SiteName,
RdnBuffer,
RdnLen*sizeof(WCHAR) );
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
SiteNameInfo->DsaGuid = BindRes->pCredents->Guid;
SiteNameInfo->DsaOptions = DsaOptions;
Status = STATUS_SUCCESS;
//
// Free locally used resources
//
Cleanup:
//
// Destruction of the thread state will delete the memory alloced by the SearchNonUnique call
//
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
CloseTransaction );
if ( SiteDsName != NULL ) {
LsapFreeLsaHeap( SiteDsName );
}
if ( !NT_SUCCESS( Status ) ) {
LsaIFree_LSAP_SITENAME_INFO( SiteNameInfo );
} else {
*SiteNameInformation = SiteNameInfo;
}
LsarpReturnPrologue();
LsapExitFunc( "LsaIGetSiteName", Status );
return( Status );
}
NTSTATUS
LsaIQuerySiteInfo(
OUT PLSAP_SITE_INFO *SiteInformation
)
/*++
Routine Description:
This routine enumerates all of the sites objects and returns their names.
Returned site names are validated to be non-mangled. (A name can become
mangled as a result of a name collision in the DS where an object with a
mangled name is created in addition to the object with the intended name).
The sites are also verified to be valid for use as DNS labels in a DNS name.
This is done to ensure that netlogon will succeed to register DNS records
containing the site names returned.
Arguments:
SiteInformation - Returns a pointer to the site information.
Buffer should be freed using LsaIFree_LSAP_SITE_INFO;
Returns:
STATUS_SUCCESS - Success
STATUS_INVALID_DOMAIN_STATE - The Ds is not installed or running at the time of the call
STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed
--*/
{
NTSTATUS Status;
ULONG DsNameLen;
ULONG DsNameSize;
PDSNAME DsSiteContainer = NULL;
PDSNAME *DsNames = NULL;
ULONG Items;
ULONG i;
ATTRBLOCK *ReadAttrs;
BOOLEAN CloseTransaction = FALSE;
PLSAP_SITE_INFO SiteInfo = NULL;
BOOLEAN TsActive = FALSE;
ULONG Size;
ULONG ClassId;
//
// Attributes we want to look for
//
ATTRVAL SiteAttVals[] = {
{ sizeof(ULONG), (PUCHAR)&ClassId},
};
ATTR SiteAttrs[] = {
{ ATT_OBJECT_CLASS, {1, &SiteAttVals[0] } },
};
LsarpReturnCheckSetup();
ClassId = CLASS_SITE;
//
// Make sure the DS is installed
//
if ( !LsaDsStateInfo.UseDs ) {
return STATUS_INVALID_DOMAIN_STATE;
}
LsapEnterFunc( "LsaIQuerySiteInfo" );
//
// Build the name of the Site container.
//
// DSNameSizeFromLen doesn't want the trailing NULL that we'll give it by using
// the sizeof operators. It evens out, though, since we don't bother adding in the
// comma seperator that should be there as well.
//
DsNameLen = wcslen( LsaDsStateInfo.DsConfigurationContainer->StringName ) +
wcslen( LSAP_DS_SITES_CONTAINER ) + 1;
DsNameSize = DSNameSizeFromLen( DsNameLen );
DsSiteContainer = LsapAllocateLsaHeap( DsNameSize );
if ( DsSiteContainer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
} else {
DsSiteContainer->structLen = DsNameSize;
DsSiteContainer->NameLen = DsNameLen;
swprintf( DsSiteContainer->StringName,
L"%ws,%ws",
LSAP_DS_SITES_CONTAINER,
LsaDsStateInfo.DsConfigurationContainer->StringName );
}
//
// See if we already have a transaction going
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
TsActive = TRUE;;
//
// Search for the site objects
//
// Site objects must be directly in the sites container.
//
Status = LsapDsSearchNonUnique( LSAPDS_SEARCH_LEVEL | LSAPDS_OP_NO_TRANS,
DsSiteContainer,
SiteAttrs,
sizeof(SiteAttrs)/sizeof(SiteAttrs[0]),
&DsNames,
&Items );
if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Items = 0;
Status = STATUS_SUCCESS;
DsNames = NULL;
}
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
//
// Allocate a list of attribute blocks big enough to hold them all
//
Size = sizeof( LSAP_SITE_INFO ) +
Items * sizeof( LSAP_SITE_INFO_ENTRY );
SiteInfo = LsapAllocateLsaHeap( Size );
if ( SiteInfo == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlZeroMemory( SiteInfo, Size );
SiteInfo->SiteCount = 0;
//
// Read each of the enumerated site objects
//
for ( i = 0; i < Items; i++ ) {
BOOL SiteValid = FALSE;
Status = LsapDsReadSiteObj( DsNames[ i ] ,
&SiteValid,
&SiteInfo->Sites[SiteInfo->SiteCount] );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// If site is valid, count this entry
//
if ( SiteValid ) {
SiteInfo->SiteCount ++;
}
}
Status = STATUS_SUCCESS;
//
// Free locally used resources
//
Cleanup:
//
// Destruction of the thread state will delete the memory alloced by the SearchNonUnique call
//
if ( TsActive ) {
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
CloseTransaction );
}
if ( DsSiteContainer != NULL ) {
LsapFreeLsaHeap( DsSiteContainer );
}
if ( DsNames != NULL ) {
LsapFreeLsaHeap( DsNames );
}
if ( !NT_SUCCESS( Status ) ) {
LsaIFree_LSAP_SITE_INFO( SiteInfo );
} else {
*SiteInformation = SiteInfo;
}
LsarpReturnPrologue();
LsapExitFunc( "LsaIQuerySiteInfo", Status );
return( Status );
}
VOID
LsaIFree_LSAP_SITE_INFO(
IN PLSAP_SITE_INFO SiteInfo
)
/*++
Routine Description:
This routine free the LSAP_SITE_INFO strcture returned from
LsaIQuerySiteInfo.
Arguments:
SiteInformation - Specifies a pointer to the site information.
Returns:
None.
--*/
{
ULONG i;
if ( SiteInfo != NULL ) {
for ( i=0; i<SiteInfo->SiteCount; i++) {
if ( SiteInfo->Sites[i].SiteName.Buffer != NULL ) {
LsapFreeLsaHeap( SiteInfo->Sites[i].SiteName.Buffer );
}
}
LsapFreeLsaHeap( SiteInfo );
}
}
NTSTATUS
LsaIQuerySubnetInfo(
OUT PLSAP_SUBNET_INFO *SubnetInformation
)
/*++
Routine Description:
This routine enumerates all of the subnet objects returns their names
and the names of the sites they are in. Returned subnet and site names
are validated to be non-mangled. (A name can become mangled as a result
of a name collision in the DS where an object with a mangled name is
created in addition to the object with the intended name). The sites
are verified to be valid for use as DNS labels in a DNS name. This
is done to ensure that netlogon will succeed to register DNS records
containing the site names returned.
Arguments:
SubnetInformation - Returns a pointer to the subnet information.
Buffer should be freed using LsaIFree_LSAP_SUBNET_INFO;
Returns:
STATUS_SUCCESS - Success
STATUS_INVALID_DOMAIN_STATE - The Ds is not installed or running at the time of the call
STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed
--*/
{
NTSTATUS Status;
ULONG DsNameLen;
ULONG DsNameSize;
PDSNAME DsSubnetContainer = NULL;
PDSNAME DsSiteContainer = NULL;
PDSNAME *DsNames = NULL;
ULONG Items;
ULONG i;
ATTRBLOCK *ReadAttrs;
BOOLEAN CloseTransaction = FALSE;
BOOLEAN TsActive = FALSE;
PLSAP_SUBNET_INFO SubnetInfo = NULL;
ULONG Size;
ULONG ClassId;
//
// Attributes we want to look for
//
ATTRVAL SubnetAttVals[] = {
{ sizeof(ULONG), (PUCHAR)&ClassId},
};
ATTR SubnetAttrs[] = {
{ ATT_OBJECT_CLASS, {1, &SubnetAttVals[0] } },
};
LsarpReturnCheckSetup();
ClassId = CLASS_SUBNET;
//
// Make sure the DS is installed
//
if ( !LsaDsStateInfo.UseDs ) {
return STATUS_INVALID_DOMAIN_STATE;
}
LsapEnterFunc( "LsaIQuerySubnetInfo" );
//
// Build the name of the Subnet container.
//
// DSNameSizeFromLen doesn't want the trailing NULL that we'll give it by using
// the sizeof operators. It evens out, though, since we don't bother adding in the
// comma seperator that should be there as well.
//
DsNameLen = wcslen( LsaDsStateInfo.DsConfigurationContainer->StringName ) +
wcslen( LSAP_DS_SUBNET_CONTAINER ) + 1;
DsNameSize = DSNameSizeFromLen( DsNameLen );
DsSubnetContainer = LsapAllocateLsaHeap( DsNameSize );
if ( DsSubnetContainer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
} else {
DsSubnetContainer->structLen = DsNameSize;
DsSubnetContainer->NameLen = DsNameLen;
swprintf( DsSubnetContainer->StringName,
L"%ws,%ws",
LSAP_DS_SUBNET_CONTAINER,
LsaDsStateInfo.DsConfigurationContainer->StringName );
}
//
// See if we already have a transaction going
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
TsActive = TRUE;
//
// Search for the subnet objects
//
// Subnet objects must be directly in the subnet container.
//
Status = LsapDsSearchNonUnique( LSAPDS_SEARCH_LEVEL | LSAPDS_OP_NO_TRANS,
DsSubnetContainer,
SubnetAttrs,
sizeof(SubnetAttrs)/sizeof(SubnetAttrs[0]),
&DsNames,
&Items
);
if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Items = 0;
Status = STATUS_SUCCESS;
DsNames = NULL;
}
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
//
// Allocate a list of attribute blocks big enough to hold them all
//
Size = sizeof( LSAP_SUBNET_INFO ) +
Items * sizeof( LSAP_SUBNET_INFO_ENTRY );
SubnetInfo = LsapAllocateLsaHeap( Size );
if ( SubnetInfo == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlZeroMemory( SubnetInfo, Size );
SubnetInfo->SubnetCount = 0;
//
// Read each of the enumerated subnet objects
//
for ( i = 0; i < Items; i++ ) {
BOOL SubnetValid = FALSE;
Status = LsapDsReadSubnetObj( DsNames[ i ] ,
&SubnetValid,
&SubnetInfo->Subnets[SubnetInfo->SubnetCount] );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// If subnet/site valid, count this entry
//
if ( SubnetValid ) {
SubnetInfo->SubnetCount ++;
}
}
if ( DsNames != NULL ) {
LsapFreeLsaHeap( DsNames );
DsNames = NULL;
}
//
// Determine the number of site objects.
//
// The caller wants to be able to special case the single site case in
// for enterprises that's aren't interested in subnet objects.
//
{
//
// Build the name of the Site container.
//
// DSNameSizeFromLen doesn't want the trailing NULL that we'll give it by using
// the sizeof operators. It evens out, though, since we don't bother adding in the
// comma seperator that should be there as well.
//
DsNameLen = wcslen( LsaDsStateInfo.DsConfigurationContainer->StringName ) +
wcslen( LSAP_DS_SITES_CONTAINER ) + 1;
DsNameSize = DSNameSizeFromLen( DsNameLen );
DsSiteContainer = LsapAllocateLsaHeap( DsNameSize );
if ( DsSiteContainer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
} else {
DsSiteContainer->structLen = DsNameSize;
DsSiteContainer->NameLen = DsNameLen;
swprintf( DsSiteContainer->StringName,
L"%ws,%ws",
LSAP_DS_SITES_CONTAINER,
LsaDsStateInfo.DsConfigurationContainer->StringName );
}
//
// Search for the site objects
//
// Site objects must be directly in the sites container.
//
ClassId = CLASS_SITE;
Status = LsapDsSearchNonUnique( LSAPDS_SEARCH_LEVEL | LSAPDS_OP_NO_TRANS,
DsSiteContainer,
SubnetAttrs,
sizeof(SubnetAttrs)/sizeof(SubnetAttrs[0]),
&DsNames,
&Items );
if ( Status == STATUS_OBJECT_NAME_NOT_FOUND ) {
Items = 0;
Status = STATUS_SUCCESS;
DsNames = NULL;
}
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
//
// Simply tell the caller the number of valid sites
//
SubnetInfo->SiteCount = 0;
for ( i = 0; i < Items; i++ ) {
WCHAR RdnBuffer[MAX_RDN_SIZE + 1];
ULONG RdnLen;
ATTRTYP RdnType;
//
// Get the RDN of the site object
//
Status = LsapDsMapDsReturnToStatus( GetRDNInfoExternal(
DsNames[i],
RdnBuffer,
&RdnLen,
&RdnType ) );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// If the site object RDN is mangled as a result
// of a site name collision in the DS, ignore this
// site object
//
if ( IsMangledRDNExternal(RdnBuffer, RdnLen, NULL) ) {
continue;
//
// OK, the site name is not mangled. Verify that
// it can be used as a DNS label
//
} else {
NET_API_STATUS NetStatus;
LPWSTR SiteNameString = NULL;
SiteNameString = LsapAllocateLsaHeap( (RdnLen + 1) * sizeof(WCHAR) );
if ( SiteNameString == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
RtlCopyMemory( SiteNameString, RdnBuffer, RdnLen*sizeof(WCHAR) );
SiteNameString[RdnLen] = UNICODE_NULL;
NetStatus = DnsValidateName_W( SiteNameString, DnsNameDomainLabel );
LsapFreeLsaHeap( SiteNameString );
//
// If the name can't be used as a DNS label,
// ignore this site
//
if ( NetStatus != NO_ERROR && NetStatus != DNS_ERROR_NON_RFC_NAME ) {
continue;
}
}
//
// All checks succeeded. Count this site.
//
SubnetInfo->SiteCount ++;
}
}
Status = STATUS_SUCCESS;
//
// Free locally used resources
//
Cleanup:
//
// Destruction of the thread state will delete the memory alloced by the SearchNonUnique call
//
if ( TsActive ) {
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
CloseTransaction );
}
if ( DsSubnetContainer != NULL ) {
LsapFreeLsaHeap( DsSubnetContainer );
}
if ( DsSiteContainer != NULL ) {
LsapFreeLsaHeap( DsSiteContainer );
}
if ( DsNames != NULL ) {
LsapFreeLsaHeap( DsNames );
}
if ( !NT_SUCCESS( Status ) ) {
LsaIFree_LSAP_SUBNET_INFO( SubnetInfo );
} else {
*SubnetInformation = SubnetInfo;
}
LsarpReturnPrologue();
LsapExitFunc( "LsaIQuerySubnetInfo", Status );
return( Status );
}
VOID
LsaIFree_LSAP_SUBNET_INFO(
IN PLSAP_SUBNET_INFO SubnetInfo
)
/*++
Routine Description:
This routine free the LSAP_SUBNET_INFO strcture returned from
LsaIQuerySubnetInfo.
Arguments:
SubnetInformation - Specifies a pointer to the subnet information.
Returns:
None.
--*/
{
ULONG i;
if ( SubnetInfo != NULL ) {
for ( i=0; i<SubnetInfo->SubnetCount; i++) {
if ( SubnetInfo->Subnets[i].SubnetName.Buffer != NULL ) {
LsapFreeLsaHeap( SubnetInfo->Subnets[i].SubnetName.Buffer );
}
if ( SubnetInfo->Subnets[i].SiteName.Buffer != NULL ) {
LsapFreeLsaHeap( SubnetInfo->Subnets[i].SiteName.Buffer );
}
}
LsapFreeLsaHeap( SubnetInfo );
}
}
VOID
LsaIFree_LSAP_SITENAME_INFO(
IN PLSAP_SITENAME_INFO SiteNameInfo
)
/*++
Routine Description:
This routine frees the LSAP_SITENAME_INFO strcture returned from
LsaIGetSiteName.
Arguments:
SitenameInfo - Specifies a pointer to the sitename information.
Returns:
None.
--*/
{
ULONG i;
if ( SiteNameInfo != NULL ) {
if ( SiteNameInfo->SiteName.Buffer != NULL ) {
LsapFreeLsaHeap( SiteNameInfo->SiteName.Buffer );
}
LsapFreeLsaHeap( SiteNameInfo );
}
}
BOOLEAN
LsaIIsDsPaused(
VOID
)
/*++
Routine Description:
This routine determines DS wants us to avoid advertising it.
The only current reason is if the DS is backsyncing after a restore.
Arguments:
None
Returns:
TRUE: The DS is paused.
FALSE: The DS is not paused
--*/
{
//
// Simply return TRUE if the DS is backsyncing.
//
if ( SampUsingDsData() ) {
return DsIsBeingBackSynced();
}
return FALSE;
}
NTSTATUS
LsaISetClientDnsHostName(
IN PWSTR ClientName,
IN PWSTR ClientDnsHostName OPTIONAL,
IN POSVERSIONINFOEXW OsVersionInfo OPTIONAL,
IN PWSTR OsName OPTIONAL,
OUT PWSTR *OldDnsHostName OPTIONAL
)
/*++
Routine Description:
This routine will update the DnsHostName on the specified client object if it is
different from the one alread on the object
Arguments:
ClientName - Name of the client
DnsHostName - Dns host name that should be on the client
If not specified, the Dns Host name attribute will be removed from the object.
However, if OldDnsHostName is specified, this parameter will be completely
ignored.
OsVersionInfo - Version Info of the client
If not specified, the version attributes will be removed from the object.
OsName - Operation System name of the client
If not specified, the operating system name will be removed from the object.
OldDnsHostName - If specified, this parameter will returns a pointer to the
current DNS Host Name on the computer object.
A NULL pointer is returned if there is no current DNS Host Name.
This buffer should be freed using MIDL_user_free.
Returns:
STATUS_SUCCESS - Success
STATUS_OBJECT_NAME_NOT_FOUND - No such client was found
--*/
{
NTSTATUS Status;
NTSTATUS SavedStatus = STATUS_SUCCESS;
PDSNAME ServerPath;
PDSNAME *MachinePaths = NULL;
ULONG MachinePathCount;
ULONG MachinePathIndex;
ATTRBLOCK AttrBlock, Results, Results2, Results3;
PBYTE AllocatedBuffer = NULL;
PWSTR SamName;
ULONG SamNameSize;
PWSTR OsVersion;
ULONG OsVersionSize;
ATTRVAL ReplaceVals[ LsapDsMachineClientSetAttrsCount ];
ATTR ReplaceAttributes[ LsapDsMachineClientSetAttrsCount ];
ATTRBLOCK ReplaceAttrBlock;
ATTR LocalSamAccountAttr;
ATTRVAL RemoveVals[ LsapDsMachineClientSetAttrsCount ];
ATTR RemoveAttributes[ LsapDsMachineClientSetAttrsCount ];
ATTRBLOCK RemoveAttrBlock;
BOOLEAN CloseTransaction = FALSE;
BOOLEAN TsActive = FALSE;
PWSTR CurrentServerDnsHostName;
ULONG CurrentServerDnsHostNameLength;
PWSTR CurrentComputerDnsHostName = NULL;
ULONG CurrentComputerDnsHostNameLength = 0;
ULONG i;
struct _AttributesToUpdate {
PWSTR CurrentValue;
ULONG CurrentValueLength;
PWSTR NewValue;
} AttributesToUpdate[LsapDsMachineClientSetAttrsCount];
//
// The indices below must match the order of the element of LsapDsMachineClientSetAttrs
//
#define ATU_HOST_INDEX 0
#define ATU_OS_INDEX 1
#define ATU_OS_VERSION_INDEX 2
#define ATU_OS_SERVICE_PACK_INDEX 3
#define ATU_SERVICE_PRINCIPAL_NAME_INDEX 4
LsapEnterFunc( "LsaISetClientDnsHostName" );
//
// Initialization
//
if ( ARGUMENT_PRESENT( OldDnsHostName )) {
*OldDnsHostName = NULL;
}
RtlZeroMemory( &AttributesToUpdate, sizeof(AttributesToUpdate) );
//
// If we haven't initalized the Ds names, we might as well bail
//
if ( !LsaDsStateInfo.DsRoot ) {
return( STATUS_UNSUCCESSFUL );
}
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
goto SetDnsHostNameEnd;
}
TsActive = TRUE;
//
// Allocate a buffer for all of the temporary storage for this routine
//
SamNameSize = (wcslen( ClientName ) + 2) * sizeof(WCHAR);
OsVersionSize = (32+1+32+2+32+2) * sizeof(WCHAR);
AllocatedBuffer = LsapAllocateLsaHeap( SamNameSize +
OsVersionSize );
if ( AllocatedBuffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto SetDnsHostNameEnd;
}
SamName = (PWSTR)(AllocatedBuffer);
OsVersion = (PWSTR)(SamName + SamNameSize);
//
// Compute the new value of all of the attributes to set.
//
AttributesToUpdate[ATU_OS_INDEX].NewValue = OsName;
if ( OsVersionInfo != NULL ) {
AttributesToUpdate[ATU_OS_VERSION_INDEX].NewValue = OsVersion;
if ( OsVersionInfo->dwBuildNumber == 0 ) {
swprintf( AttributesToUpdate[ATU_OS_VERSION_INDEX].NewValue,
L"%ld.%ld",
OsVersionInfo->dwMajorVersion,
OsVersionInfo->dwMinorVersion );
} else {
swprintf( AttributesToUpdate[ATU_OS_VERSION_INDEX].NewValue,
L"%ld.%ld (%ld)",
OsVersionInfo->dwMajorVersion,
OsVersionInfo->dwMinorVersion,
OsVersionInfo->dwBuildNumber );
}
if ( OsVersionInfo->szCSDVersion[0] != L'\0' ) {
AttributesToUpdate[ATU_OS_SERVICE_PACK_INDEX].NewValue = OsVersionInfo->szCSDVersion;
}
}
//
// Only update the DnsHostName if the client isn't going to
//
if ( !ARGUMENT_PRESENT( OldDnsHostName )) {
AttributesToUpdate[ATU_HOST_INDEX].NewValue = ClientDnsHostName;
}
//
// Find the objects whose computer name is the one we were given...
//
swprintf( SamName, L"%ws$", ClientName );
RtlCopyMemory( &LocalSamAccountAttr, &LsapDsAttrs[LsapDsAttrSamAccountName], sizeof( ATTR ) );
LSAP_DS_SET_DS_ATTRIBUTE_STRING( &LocalSamAccountAttr, SamName );
Status = LsapDsSearchNonUnique( LSAPDS_OP_NO_TRANS,
LsaDsStateInfo.DsRoot,
&LocalSamAccountAttr,
1,
&MachinePaths,
&MachinePathCount );
if ( !NT_SUCCESS( Status ) ) {
goto SetDnsHostNameEnd;
}
//
// Process each of the objects by that name
//
for ( MachinePathIndex=0; MachinePathIndex<MachinePathCount; MachinePathIndex++ ) {
PDSNAME MachinePath;
MachinePath = MachinePaths[MachinePathIndex];
//
// Read the current "Client Set" attributes name from the machine object
//
AttrBlock.attrCount = LsapDsMachineClientSetAttrsCount;
AttrBlock.pAttr = LsapDsMachineClientSetAttrs;
Status = LsapDsReadByDsName( MachinePath,
0,
&AttrBlock,
&Results );
if ( Status == STATUS_NOT_FOUND ) {
Results.attrCount = 0;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS( Status ) ) {
if ( SavedStatus == STATUS_SUCCESS ) {
SavedStatus = Status;
}
continue;
}
//
// Loop through the each attribute returned from the DS
//
for ( i = 0; i < Results.attrCount; i++ ) {
ULONG j;
//
// Loop through the list of attributes we understand
//
for ( j=0; j<LsapDsMachineClientSetAttrsCount; j++ ) {
if ( Results.pAttr[i].attrTyp == LsapDsMachineClientSetAttrs[j].attrTyp ) {
// Attribute is single valued, but ...
if ( Results.pAttr[i].AttrVal.valCount >= 1 ) {
//
//
AttributesToUpdate[j].CurrentValue =
LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR(&Results.pAttr[ i ] );
// length in count of characters.
AttributesToUpdate[j].CurrentValueLength =
LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results.pAttr[ i ] ) / sizeof( WCHAR );
//
// If this is the DnsHostName attribute,
// and the caller doesn't want us to set it,
// simply remember the current value.
//
if ( Results.pAttr[i].attrTyp == ATT_DNS_HOST_NAME &&
ARGUMENT_PRESENT( OldDnsHostName )) {
if ( CurrentComputerDnsHostName == NULL &&
AttributesToUpdate[j].CurrentValueLength != 0 ) {
CurrentComputerDnsHostName = MIDL_user_allocate( AttributesToUpdate[j].CurrentValueLength * sizeof(WCHAR) + sizeof(WCHAR) );
if ( CurrentComputerDnsHostName == NULL ) {
if ( SavedStatus == STATUS_SUCCESS ) {
SavedStatus = STATUS_INSUFFICIENT_RESOURCES;
}
} else {
CurrentComputerDnsHostNameLength = AttributesToUpdate[j].CurrentValueLength;
RtlCopyMemory( CurrentComputerDnsHostName,
AttributesToUpdate[j].CurrentValue,
AttributesToUpdate[j].CurrentValueLength * sizeof(WCHAR) );
CurrentComputerDnsHostName[CurrentComputerDnsHostNameLength] = L'\0';
}
}
//
// Don't change the value on the computer object.
//
AttributesToUpdate[j].CurrentValue = NULL;
AttributesToUpdate[j].CurrentValueLength = 0;
}
//
// If this is the ServerPrincipalName attribute, we are
// prepared to remove it for NT3.5 and NT4 clients.
// However, don't touch this attribute if there is any
// doubt about the OS version that the client runs.
//
if ( Results.pAttr[i].attrTyp == ATT_SERVICE_PRINCIPAL_NAME &&
(OsVersionInfo == NULL ||
(OsVersionInfo->dwMajorVersion != 3 &&
OsVersionInfo->dwMajorVersion != 4)) ){
AttributesToUpdate[j].CurrentValue = NULL;
AttributesToUpdate[j].CurrentValueLength = 0;
}
}
break;
}
}
//
// If the DS returned an attribute we didn't query,
//
if ( j >= LsapDsMachineClientSetAttrsCount ) {
if ( SavedStatus == STATUS_SUCCESS ) {
SavedStatus = STATUS_INVALID_PARAMETER;
}
}
}
//
// Loop through each attribute of interest deciding to
// remove it or replace it.
//
RemoveAttrBlock.attrCount = 0;
RemoveAttrBlock.pAttr = RemoveAttributes;
ReplaceAttrBlock.attrCount = 0;
ReplaceAttrBlock.pAttr = ReplaceAttributes;
for ( i=0; i<LsapDsMachineClientSetAttrsCount; i++ ) {
//
// Write out the new name if it is different that the old name.
//
// Difference is defined as:
// A current name is present and is different from the one we're being asked to write
// There is no current name and there is a new name
// There is a current name and there is no new name (delete the current name)
//
if (( AttributesToUpdate[i].NewValue && AttributesToUpdate[i].CurrentValue &&
(AttributesToUpdate[i].CurrentValueLength != wcslen( AttributesToUpdate[i].NewValue ) ||
_wcsnicmp( AttributesToUpdate[i].NewValue,
AttributesToUpdate[i].CurrentValue,
AttributesToUpdate[i].CurrentValueLength))) ||
( AttributesToUpdate[i].CurrentValue == NULL && AttributesToUpdate[i].NewValue != NULL) ||
( AttributesToUpdate[i].CurrentValue != NULL && AttributesToUpdate[i].NewValue == NULL ) ) {
ULONG attrIndex;
//
// If the new attribute is NULL,
// remove the attribute from the DS
//
if ( AttributesToUpdate[i].NewValue == NULL ) {
RemoveAttributes[ RemoveAttrBlock.attrCount ].attrTyp =
LsapDsMachineClientSetAttrs[i].attrTyp;
RemoveAttributes[ RemoveAttrBlock.attrCount ].AttrVal.valCount = 1;
RemoveAttributes[ RemoveAttrBlock.attrCount ].AttrVal.pAVal =
&RemoveVals[ RemoveAttrBlock.attrCount ];
RtlZeroMemory( &RemoveVals[ RemoveAttrBlock.attrCount ],
sizeof( RemoveVals[ RemoveAttrBlock.attrCount ] ));
RemoveAttrBlock.attrCount ++;
//
// If the new attribute is not NULL,
// replace the attribute in the DS
//
} else {
ReplaceAttributes[ ReplaceAttrBlock.attrCount ].attrTyp =
LsapDsMachineClientSetAttrs[i].attrTyp;
ReplaceAttributes[ ReplaceAttrBlock.attrCount ].AttrVal.valCount = 1;
ReplaceAttributes[ ReplaceAttrBlock.attrCount ].AttrVal.pAVal =
&ReplaceVals[ ReplaceAttrBlock.attrCount ];
RtlZeroMemory( &ReplaceVals[ ReplaceAttrBlock.attrCount ],
sizeof( ReplaceVals[ ReplaceAttrBlock.attrCount ] ));
LSAP_DS_SET_DS_ATTRIBUTE_STRING(
&ReplaceAttributes[ ReplaceAttrBlock.attrCount ],
AttributesToUpdate[i].NewValue );
ReplaceAttrBlock.attrCount ++;
}
}
}
//
// If there are any attributes to replace,
// do it now.
//
if ( ReplaceAttrBlock.attrCount != 0 ) {
Status = LsapDsWriteByDsName( MachinePath,
LSAPDS_REPLACE_ATTRIBUTE,
&ReplaceAttrBlock );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"Replace of attributes to %ws failed with 0x%lx\n",
SamName,
Status ));
}
}
//
// If there are any attributes to remove,
// do it now.
//
if ( RemoveAttrBlock.attrCount != 0 ) {
Status = LsapDsWriteByDsName( MachinePath,
LSAPDS_REMOVE_ATTRIBUTE,
&RemoveAttrBlock );
if ( !NT_SUCCESS( Status ) ) {
LsapDsDebugOut(( DEB_ERROR,
"Remove of attributes to %ws failed with 0x%lx\n",
SamName,
Status ));
}
}
//
// ASSERT: We're done with the machine object
//
// Get the name of the Server this computer is linked to, if any.
//
AttrBlock.attrCount = LsapDsServerReferenceCountBl;
AttrBlock.pAttr = LsapDsServerReferenceBl;
Status = LsapDsReadByDsName( MachinePath,
0,
&AttrBlock,
&Results3 );
if ( !NT_SUCCESS( Status ) ) {
if ( Status != STATUS_NOT_FOUND ) {
if ( SavedStatus == STATUS_SUCCESS ) {
SavedStatus = Status;
}
} else {
Status = STATUS_SUCCESS;
}
continue;
}
if ( Results3.attrCount == 0 ) {
continue;
}
ServerPath = LSAP_DS_GET_DS_ATTRIBUTE_AS_DSNAME( &Results3.pAttr[ 0 ] );
CurrentServerDnsHostName = NULL;
CurrentServerDnsHostNameLength = 0;
//
// Read the current host name from the server object
// No point in doing the read if we're doing a delete
//
if ( CurrentComputerDnsHostName != NULL ) {
//
// Read the current host name from the server object
//
AttrBlock.attrCount = LsapDsMachineDnsHostCount;
AttrBlock.pAttr = LsapDsMachineDnsHost;
Status = LsapDsReadByDsName(ServerPath,
0,
&AttrBlock,
&Results2 );
if ( Status == STATUS_NOT_FOUND ) {
Results2.attrCount = 0;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS( Status ) ) {
if ( SavedStatus == STATUS_SUCCESS ) {
SavedStatus = Status;
}
continue;
}
if( Results2.attrCount == 1) {
CurrentServerDnsHostName = LSAP_DS_GET_DS_ATTRIBUTE_AS_PWSTR(&Results2.pAttr[ 0 ] );
// length in count of characters.
CurrentServerDnsHostNameLength =
LSAP_DS_GET_DS_ATTRIBUTE_LENGTH( &Results2.pAttr[ 0 ] ) / sizeof( WCHAR );
}
}
//
// Write out the new name if it is different that the old name.
// Difference is defined as:
// A current name is present and is different from the one we're being asked to write
// There is no current name and there is a new name
// There is a current name and there is no new name (delete the current name)
//
if ( (CurrentComputerDnsHostName &&
CurrentServerDnsHostName &&
(CurrentServerDnsHostNameLength != CurrentComputerDnsHostNameLength ||
_wcsnicmp( CurrentComputerDnsHostName, CurrentServerDnsHostName, CurrentServerDnsHostNameLength))) ||
( CurrentServerDnsHostName == NULL && CurrentComputerDnsHostName != NULL ) ||
( CurrentComputerDnsHostName == NULL ) ) {
ATTRVAL WriteVals[ 1 ];
ATTR WriteAttributes[ 1 ];
RtlZeroMemory( &WriteVals, sizeof( ATTRVAL ) );
WriteAttributes[ 0 ].attrTyp = LsapDsAttributeIds[ LsapDsAttrMachineDns ];
WriteAttributes[ 0 ].AttrVal.valCount = 1;
WriteAttributes[ 0 ].AttrVal.pAVal = &WriteVals[ 0 ];
if ( CurrentComputerDnsHostName ) {
LSAP_DS_SET_DS_ATTRIBUTE_STRING( &WriteAttributes[ 0 ], CurrentComputerDnsHostName );
}
AttrBlock.attrCount = 1;
AttrBlock.pAttr = WriteAttributes;
Status = LsapDsWriteByDsName(ServerPath,
CurrentComputerDnsHostName ?
LSAPDS_REPLACE_ATTRIBUTE :
LSAPDS_REMOVE_ATTRIBUTE,
&AttrBlock );
if ( !NT_SUCCESS( Status ) ) {
if ( CurrentComputerDnsHostName ) {
LsapDsDebugOut(( DEB_ERROR,
"Write of Dns domain name %ws on server object failed with 0x%lx\n",
CurrentComputerDnsHostName,
Status ));
} else {
LsapDsDebugOut(( DEB_ERROR,
"Removal of Dns domain name from server object failed with 0x%lx\n",
Status ));
}
}
}
}
SetDnsHostNameEnd:
Status = Status == STATUS_SUCCESS ? SavedStatus : Status;
if ( NT_SUCCESS(Status) ) {
if ( ARGUMENT_PRESENT( OldDnsHostName )) {
*OldDnsHostName = CurrentComputerDnsHostName;
CurrentComputerDnsHostName = NULL;
}
}
if ( CurrentComputerDnsHostName != NULL ) {
MIDL_user_free( CurrentComputerDnsHostName );
}
if ( MachinePaths != NULL ) {
LsapFreeLsaHeap( MachinePaths );
}
if ( AllocatedBuffer != NULL ) {
LsapFreeLsaHeap( AllocatedBuffer );
}
if ( TsActive ) {
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
CloseTransaction );
}
LsapExitFunc( "LsaISetClientDnsHostName", Status );
return Status;
}
NTSTATUS
LsaIQueryUpnSuffixes(
OUT PLSAP_UPN_SUFFIXES *UpnSuffixes
)
/*++
Routine Description:
This routine enumerates all of the configured UPN and SPN suffixes
Arguments:
UpnSuffixes - Returns a pointer to the UPN Suffixes
Buffer should be freed using LsaIFree_LSAP_UPN_SUFFIXES
Returns:
STATUS_SUCCESS - Success
STATUS_INVALID_DOMAIN_STATE - The Ds is not installed or running at the time of the call
STATUS_INSUFFICIENT_RESOURCES - A memory allocation failed
--*/
{
NTSTATUS Status;
BOOLEAN CloseTransaction = FALSE;
ULONG i;
ULONG j;
PDSNAME DsName;
BOOLEAN TsActive = FALSE;
PLSAP_UPN_SUFFIXES Names = NULL;
ULONG NameCount;
ULONG NameIndex;
//
// Build the list of attribute IDs we need based on the information
// class
//
ATTR UpnSuffixesAttrVals[] = {
{ATT_UPN_SUFFIXES, {0, NULL} },
{ATT_MS_DS_SPN_SUFFIXES, {0, NULL} },
};
ATTRBLOCK ReadBlock, ReturnedBlock = { 0 };
// WCHAR RdnBuffer[MAX_RDN_SIZE + 1];
// ULONG RdnLen;
// ATTRTYP RdnType;
LsarpReturnCheckSetup();
LsapEnterFunc( "LsaIQueryUpnSuffixes" );
//
// Make sure the DS is installed
//
if ( !LsaDsStateInfo.UseDs ) {
Status = STATUS_INVALID_DOMAIN_STATE;
goto Cleanup;
}
//
// See if we already have a transaction going
//
Status = LsapDsInitAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
&CloseTransaction );
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
TsActive = TRUE;;
//
// Read the required attributes from the parititions container object
//
ReadBlock.attrCount = sizeof(UpnSuffixesAttrVals) / sizeof(ATTR);
ReadBlock.pAttr = UpnSuffixesAttrVals;
Status = LsapDsReadByDsName( LsaDsStateInfo.DsPartitionsContainer,
0,
&ReadBlock,
&ReturnedBlock );
//
// Allow for the case where the Partitions container doesn't exist.
//
if ( Status == STATUS_NOT_FOUND ) {
ReturnedBlock.attrCount = 0;
Status = STATUS_SUCCESS;
}
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// Determine the number of suffixes to return
//
NameCount = 0;
for ( i = 0;
i < ReturnedBlock.attrCount;
i++) {
switch ( ReturnedBlock.pAttr[i].attrTyp ) {
case ATT_UPN_SUFFIXES:
case ATT_MS_DS_SPN_SUFFIXES:
NameCount += ReturnedBlock.pAttr[i].AttrVal.valCount;
break;
default:
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
}
//
// Allocate a block to return to the caller
//
Names = LsapAllocateLsaHeap( sizeof(LSAP_UPN_SUFFIXES) +
NameCount * sizeof(UNICODE_STRING) );
if ( Names == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
//
// Return the suffixes.
//
NameIndex = 0;
for ( i = 0;
i < ReturnedBlock.attrCount;
i++) {
switch ( ReturnedBlock.pAttr[i].attrTyp ) {
case ATT_UPN_SUFFIXES:
case ATT_MS_DS_SPN_SUFFIXES:
for ( j = 0; j < ReturnedBlock.pAttr[i].AttrVal.valCount; j++ ) {
Status = STATUS_SUCCESS;
LSAPDS_ALLOC_AND_COPY_STRING_TO_UNICODE_ON_SUCCESS(
Status,
&Names->Suffixes[NameIndex],
ReturnedBlock.pAttr[i].AttrVal.pAVal[ j ].pVal,
ReturnedBlock.pAttr[i].AttrVal.pAVal[ j ].valLen );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
NameIndex++;
}
break;
default:
Status = STATUS_INVALID_PARAMETER;
goto Cleanup;
}
}
ASSERT( NameCount == NameIndex );
Names->SuffixCount = NameIndex;
Status = STATUS_SUCCESS;
//
// Free locally used resources
//
Cleanup:
//
// Destruction of the thread state will delete the memory alloced by the SearchNonUnique call
//
if ( TsActive ) {
LsapDsDeleteAllocAsNeededEx( LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION,
NullObject,
CloseTransaction );
}
if ( !NT_SUCCESS( Status ) ) {
LsaIFree_LSAP_UPN_SUFFIXES( Names );
} else {
*UpnSuffixes = Names;
}
LsarpReturnPrologue();
LsapExitFunc( "LsaIQueryUpnSuffixes", Status );
return( Status );
}
VOID
LsaIFree_LSAP_UPN_SUFFIXES(
IN PLSAP_UPN_SUFFIXES UpnSuffixes
)
/*++
Routine Description:
This routine free the LSAP_SUBNET_INFO strcture returned from
LsaIQuerySubnetInfo.
Arguments:
SubnetInformation - Specifies a pointer to the subnet information.
Returns:
None.
--*/
{
ULONG i;
if ( UpnSuffixes != NULL ) {
for ( i=0; i<UpnSuffixes->SuffixCount; i++) {
if ( UpnSuffixes->Suffixes[i].Buffer != NULL ) {
LsapFreeLsaHeap( UpnSuffixes->Suffixes[i].Buffer );
}
}
LsapFreeLsaHeap( UpnSuffixes );
}
}
VOID
NTAPI
LsaINotifyNetlogonParametersChangeW(
IN LSAP_NETLOGON_PARAMETER Parameter,
IN DWORD dwType,
IN PWSTR lpData,
IN DWORD cbData
)
/*++
Routine Description:
A way for Netlogon to notify LSA of changes to the values under its
'Parameters' key that Lsa cares about
Parameters:
Parameter the value that has changed
dwType type of value
lpData pointer to the data
cbData number of bytes in the lpData buffer
Returns:
Nothing
--*/
{
ASSERT( Parameter == LsaEmulateNT4 );
ASSERT( dwType == REG_DWORD );
ASSERT( lpData );
ASSERT( cbData );
if ( Parameter == LsaEmulateNT4 ) {
LsapDbState.EmulateNT4 = ( *( DWORD * )lpData != 0 );
}
return;
}