/*++ 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 #include // #include #include 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; iSiteCount; 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; iSubnetCount; 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= 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; iSuffixes[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; iSuffixCount; 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; }