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.
3079 lines
81 KiB
3079 lines
81 KiB
/*++
|
|
|
|
Copyright (c) 1987-1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
srvsess.c
|
|
|
|
Abstract:
|
|
|
|
Routines for managing the ServerSession structure.
|
|
|
|
Author:
|
|
|
|
Ported from Lan Man 2.0
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
Contains NT-specific code.
|
|
Requires ANSI C extensions: slash-slash comments, long external names.
|
|
|
|
Revision History:
|
|
|
|
12-Jul-1991 (cliffv)
|
|
Ported to NT. Converted to NT style.
|
|
|
|
--*/
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
|
|
#include "logonsrv.h" // Include files common to entire service
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Include files specific to this .c file
|
|
//
|
|
|
|
#include <lmaudit.h>
|
|
#include <lmshare.h>
|
|
#include <nbtioctl.h>
|
|
#include <kerberos.h> // KERB_UPDATE_ADDRESSES_REQUEST
|
|
|
|
#define MAX_WOC_INTERROGATE 8 // 2 hours
|
|
#define KILL_SESSION_TIME (4*4*24) // 4 Days
|
|
|
|
|
|
//
|
|
// Registry key where SocketAddressList is saved across reboots
|
|
//
|
|
#define NETLOGON_KEYWORD_SOCKETADDRESSLIST TEXT("SocketAddressList")
|
|
|
|
|
|
|
|
|
|
DWORD
|
|
NlGetHashVal(
|
|
IN LPSTR UpcaseOemComputerName,
|
|
IN DWORD HashTableSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a HashTable index for the specified ComputerName.
|
|
|
|
Notice that all sessions for a particular ComputerName hash to the same
|
|
value. The ComputerName make a suitable hash key all by itself.
|
|
Also, at times we visit all the session entries for a particular
|
|
ComputerName. By using only the ComputerName as the hash key, I
|
|
can limit my search to the single hash chain.
|
|
|
|
Arguments:
|
|
|
|
UpcaseOemComputerName - The upper case OEM name of the computer on
|
|
the client side of the secure channel setup.
|
|
|
|
HashTableSize - Number of entries in the hash table (must be a power of 2)
|
|
|
|
Return Value:
|
|
|
|
Returns an index into the HashTable.
|
|
|
|
--*/
|
|
{
|
|
UCHAR c;
|
|
DWORD value = 0;
|
|
|
|
while (c = *UpcaseOemComputerName++) {
|
|
value += (DWORD) c;
|
|
}
|
|
|
|
return (value & (HashTableSize-1));
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NlGetTdoNameHashVal(
|
|
IN PUNICODE_STRING TdoName,
|
|
OUT PUNICODE_STRING CanonicalTdoName,
|
|
OUT PULONG HashIndex
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a HashTable index for the specified TdoName
|
|
|
|
Arguments:
|
|
|
|
TdoName - The name of the TDO this secure channel is for
|
|
|
|
CanonicalTdoName - Returns the canonical TDO name corresponding to TdoName
|
|
The caller must free this buffer using RtlFreeUnicodeString
|
|
|
|
HashIndex - Returns the index into the DomServerSessionTdoNameHashTable
|
|
|
|
Return Value:
|
|
|
|
Status of the operation
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG Index;
|
|
WCHAR c;
|
|
DWORD value = 0;
|
|
|
|
|
|
//
|
|
// Convert the TdoName to lower case to ensure all versions hash to the same value
|
|
//
|
|
|
|
Status = RtlDowncaseUnicodeString(
|
|
CanonicalTdoName,
|
|
TdoName,
|
|
TRUE );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return Status;
|
|
}
|
|
|
|
|
|
//
|
|
// Canonicalize the TdoName
|
|
//
|
|
//
|
|
// Ditch the trailing . from DNS names
|
|
//
|
|
|
|
if ( CanonicalTdoName->Length > sizeof(WCHAR) &&
|
|
CanonicalTdoName->Buffer[(CanonicalTdoName->Length-sizeof(WCHAR))/sizeof(WCHAR)] == L'.' ) {
|
|
|
|
CanonicalTdoName->Length -= sizeof(WCHAR);
|
|
CanonicalTdoName->MaximumLength -= sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Compute the hash
|
|
//
|
|
for ( Index=0; Index < (CanonicalTdoName->Length/sizeof(WCHAR)); Index++ ) {
|
|
value += (DWORD) CanonicalTdoName->Buffer[Index];
|
|
}
|
|
|
|
*HashIndex = (value & (SERVER_SESSION_TDO_NAME_HASH_TABLE_SIZE-1));
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
NlCheckServerSession(
|
|
IN ULONG ServerRid,
|
|
IN PUNICODE_STRING AccountName,
|
|
IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a server session to represent this BDC account.
|
|
|
|
Arguments:
|
|
|
|
ServerRid - Rid of server to add to list.
|
|
|
|
AccountName - Specifies the account name of the account.
|
|
|
|
SecureChannelType - Specifies the secure channel type of the account.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
WCHAR LocalServerName[CNLEN+1];
|
|
LONG LocalServerNameSize;
|
|
PSERVER_SESSION ServerSession;
|
|
|
|
|
|
//
|
|
// Build a zero terminated server name.
|
|
//
|
|
// Strip the trailing postfix.
|
|
//
|
|
// Ignore servers with malformed names. They aren't really DCs so don't
|
|
// cloud the issue by failing to start netlogon.
|
|
//
|
|
|
|
LOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
|
|
|
|
LocalServerNameSize = AccountName->Length -
|
|
SSI_ACCOUNT_NAME_POSTFIX_LENGTH * sizeof(WCHAR);
|
|
|
|
if ( LocalServerNameSize < 0 ||
|
|
LocalServerNameSize + sizeof(WCHAR) > sizeof(LocalServerName) ) {
|
|
|
|
NlPrint((NL_SERVER_SESS,
|
|
"NlCheckServerSession: %wZ: Skipping add of invalid server name\n",
|
|
AccountName ));
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( AccountName->Buffer[LocalServerNameSize / sizeof(WCHAR)] != SSI_ACCOUNT_NAME_POSTFIX_CHAR ) {
|
|
|
|
NlPrint((NL_SERVER_SESS,
|
|
"NlCheckServerSession: %wZ: Skipping add of server name without $\n",
|
|
AccountName ));
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( LocalServerName, AccountName->Buffer, LocalServerNameSize );
|
|
LocalServerName[ LocalServerNameSize / sizeof(WCHAR) ] = L'\0';
|
|
|
|
|
|
|
|
//
|
|
// Don't add ourselves to the list.
|
|
//
|
|
|
|
if ( NlNameCompare( LocalServerName,
|
|
NlGlobalUnicodeComputerName,
|
|
NAMETYPE_COMPUTER ) == 0 ) {
|
|
|
|
NlPrint((NL_SERVER_SESS,
|
|
"NlCheckServerSession: " FORMAT_LPWSTR
|
|
": Skipping add of ourself\n",
|
|
LocalServerName ));
|
|
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Check that any existing secure channel has the secure channel type.
|
|
//
|
|
|
|
ServerSession = NlFindNamedServerSession( NlGlobalDomainInfo, LocalServerName);
|
|
if (ServerSession != NULL) {
|
|
|
|
//
|
|
// If the type is wrong,
|
|
// ditch the server session.
|
|
//
|
|
|
|
if ( ServerSession->SsSecureChannelType != NullSecureChannel &&
|
|
ServerSession->SsSecureChannelType != SecureChannelType ) {
|
|
NlPrint((NL_SERVER_SESS,
|
|
"NlCheckServerSession: %ws: Server session of type %ld already exists (deleting it)\n",
|
|
LocalServerName,
|
|
ServerSession->SsSecureChannelType ));
|
|
NlFreeNamedServerSession( NlGlobalDomainInfo, LocalServerName, TRUE );
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// On a PDC,
|
|
// pre-create the server session structure so the PDC can keep track of
|
|
// its BDCs.
|
|
//
|
|
|
|
if ( SecureChannelType == ServerSecureChannel &&
|
|
NlGlobalDomainInfo->DomRole == RolePrimary ) {
|
|
|
|
// Always force a pulse to a newly created server.
|
|
Status = NlInsertServerSession(
|
|
NlGlobalDomainInfo,
|
|
LocalServerName,
|
|
NULL, // not an interdomain trust account
|
|
NullSecureChannel,
|
|
SS_FORCE_PULSE | SS_BDC,
|
|
ServerRid,
|
|
0, // negotiated flags
|
|
NULL, // transport
|
|
NULL, // session key
|
|
NULL ); // authentication seed
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint((NL_CRITICAL,
|
|
"NlCheckServerSession: " FORMAT_LPWSTR
|
|
": Couldn't create server session entry (0x%lx)\n",
|
|
LocalServerName,
|
|
Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
NlPrint((NL_SERVER_SESS,
|
|
"NlCheckServerSession: " FORMAT_LPWSTR ": Added NT BDC account\n",
|
|
LocalServerName ));
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
UNLOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Number of machine accounts read from SAM on each call
|
|
//
|
|
#define MACHINES_PER_PASS 250
|
|
|
|
|
|
NTSTATUS
|
|
NlBuildNtBdcList(
|
|
PDOMAIN_INFO DomainInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the list of all Nt Bdc DC's in this domain from SAM.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
NTSTATUS SamStatus;
|
|
|
|
SAMPR_DISPLAY_INFO_BUFFER DisplayInformation;
|
|
PDOMAIN_DISPLAY_MACHINE MachineInformation = NULL;
|
|
ULONG SamIndex;
|
|
BOOL UseDisplayServer = TRUE;
|
|
|
|
|
|
|
|
//
|
|
// Loop building a list of BDC names from SAM.
|
|
//
|
|
// On each iteration of the loop,
|
|
// get the next several machine accounts from SAM.
|
|
// determine which of those names are DC names.
|
|
// Merge the DC names into the list we're currently building of all DCs.
|
|
//
|
|
|
|
SamIndex = 0;
|
|
DisplayInformation.MachineInformation.Buffer = NULL;
|
|
do {
|
|
//
|
|
// Arguments to SamrQueryDisplayInformation
|
|
//
|
|
ULONG TotalBytesAvailable;
|
|
ULONG BytesReturned;
|
|
ULONG EntriesRead;
|
|
|
|
DWORD i;
|
|
|
|
//
|
|
// Sam is so slow that we want to avoid having the service controller time us out
|
|
if ( !GiveInstallHints( FALSE ) ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
//
|
|
// Get the list of machine accounts from SAM
|
|
//
|
|
|
|
NlPrint((NL_SESSION_MORE,
|
|
"SamrQueryDisplayInformation with index: %ld\n",
|
|
SamIndex ));
|
|
|
|
if ( UseDisplayServer ) {
|
|
SamStatus = SamrQueryDisplayInformation(
|
|
DomainInfo->DomSamAccountDomainHandle,
|
|
DomainDisplayServer,
|
|
SamIndex,
|
|
MACHINES_PER_PASS,
|
|
0xFFFFFFFF,
|
|
&TotalBytesAvailable,
|
|
&BytesReturned,
|
|
&DisplayInformation );
|
|
|
|
// If this PDC is running a registry based SAM (as in the case of
|
|
// upgrade from NT 4.0), avoid DomainDisplayServer.
|
|
|
|
if ( SamStatus == STATUS_INVALID_INFO_CLASS ) {
|
|
UseDisplayServer = FALSE;
|
|
}
|
|
}
|
|
|
|
if ( !UseDisplayServer ) {
|
|
SamStatus = SamrQueryDisplayInformation(
|
|
DomainInfo->DomSamAccountDomainHandle,
|
|
DomainDisplayMachine,
|
|
SamIndex,
|
|
MACHINES_PER_PASS,
|
|
0xFFFFFFFF,
|
|
&TotalBytesAvailable,
|
|
&BytesReturned,
|
|
&DisplayInformation );
|
|
}
|
|
|
|
if ( !NT_SUCCESS(SamStatus) ) {
|
|
Status = SamStatus;
|
|
NlPrint((NL_CRITICAL,
|
|
"SamrQueryDisplayInformation failed: 0x%08lx\n",
|
|
Status));
|
|
goto Cleanup;
|
|
}
|
|
|
|
MachineInformation = (PDOMAIN_DISPLAY_MACHINE)
|
|
DisplayInformation.MachineInformation.Buffer;
|
|
EntriesRead = DisplayInformation.MachineInformation.EntriesRead;
|
|
|
|
|
|
NlPrint((NL_SESSION_MORE,
|
|
"SamrQueryDisplayInformation Completed: 0x%08lx %ld\n",
|
|
SamStatus,
|
|
EntriesRead ));
|
|
|
|
//
|
|
// Set up for the next call to Sam.
|
|
//
|
|
|
|
if ( SamStatus == STATUS_MORE_ENTRIES ) {
|
|
SamIndex = MachineInformation[EntriesRead-1].Index;
|
|
}
|
|
|
|
|
|
//
|
|
// Loop though the list of machine accounts finding the Server accounts.
|
|
//
|
|
|
|
for ( i=0; i<EntriesRead; i++ ) {
|
|
|
|
|
|
NlPrint((NL_SESSION_MORE,
|
|
"%ld %ld %wZ 0x%lx 0x%lx\n",
|
|
i,
|
|
MachineInformation[i].Index,
|
|
&MachineInformation[i].Machine,
|
|
MachineInformation[i].AccountControl,
|
|
MachineInformation[i].Rid ));
|
|
|
|
//
|
|
// Ensure the machine account is a server account.
|
|
//
|
|
|
|
if ( MachineInformation[i].AccountControl &
|
|
USER_SERVER_TRUST_ACCOUNT ) {
|
|
|
|
|
|
//
|
|
// Insert the server session.
|
|
//
|
|
|
|
Status = NlCheckServerSession(
|
|
MachineInformation[i].Rid,
|
|
&MachineInformation[i].Machine,
|
|
ServerSecureChannel );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Free the buffer returned from SAM.
|
|
//
|
|
SamIFree_SAMPR_DISPLAY_INFO_BUFFER( &DisplayInformation,
|
|
DomainDisplayMachine );
|
|
DisplayInformation.MachineInformation.Buffer = NULL;
|
|
|
|
} while ( SamStatus == STATUS_MORE_ENTRIES );
|
|
|
|
//
|
|
// Success
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
|
|
|
|
//
|
|
// Free locally used resources.
|
|
//
|
|
Cleanup:
|
|
|
|
SamIFree_SAMPR_DISPLAY_INFO_BUFFER( &DisplayInformation,
|
|
DomainDisplayMachine );
|
|
|
|
return Status;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
I_NetLogonGetIpAddresses(
|
|
OUT PULONG IpAddressCount,
|
|
OUT LPBYTE *IpAddresses
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns all of the IP Addresses assigned to this machine.
|
|
|
|
Arguments:
|
|
|
|
|
|
IpAddressCount - Returns the number of IP addresses assigned to this machine.
|
|
|
|
IpAddresses - Returns a buffer containing an array of SOCKET_ADDRESS
|
|
structures.
|
|
This buffer should be freed using I_NetLogonFree().
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR - Success
|
|
|
|
ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the operation.
|
|
|
|
ERROR_NETLOGON_NOT_STARTED - Netlogon is not started
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
ULONG BufferSize;
|
|
|
|
|
|
//
|
|
// If caller is calling when the netlogon service isn't running,
|
|
// tell it so.
|
|
//
|
|
|
|
if ( !NlStartNetlogonCall() ) {
|
|
return ERROR_NETLOGON_NOT_STARTED;
|
|
}
|
|
|
|
//
|
|
// Get the IP addresses.
|
|
//
|
|
|
|
*IpAddresses = NULL;
|
|
*IpAddressCount = 0;
|
|
|
|
*IpAddressCount = NlTransportGetIpAddresses(
|
|
0, // No special header,
|
|
FALSE, // Return pointers
|
|
(PSOCKET_ADDRESS *)IpAddresses,
|
|
&BufferSize );
|
|
|
|
if ( *IpAddressCount == 0 ) {
|
|
if ( *IpAddresses != NULL ) {
|
|
NetpMemoryFree( *IpAddresses );
|
|
}
|
|
*IpAddresses = NULL;
|
|
}
|
|
|
|
NetStatus = NO_ERROR;
|
|
|
|
//
|
|
// Indicate that the calling thread has left netlogon.dll
|
|
//
|
|
|
|
NlEndNetlogonCall();
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
ULONG
|
|
NlTransportGetIpAddresses(
|
|
IN ULONG HeaderSize,
|
|
IN BOOLEAN ReturnOffsets,
|
|
OUT PSOCKET_ADDRESS *RetIpAddresses,
|
|
OUT PULONG RetIpAddressSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Return all of the IP Addresses assigned to this machine.
|
|
|
|
Arguments:
|
|
|
|
HeaderSize - Size (in bytes) of a header to leave at the front of the returned
|
|
buffer.
|
|
|
|
ReturnOffsets - If TRUE, indicates that all returned pointers should
|
|
be offsets.
|
|
|
|
RetIpAddresses - Returns a buffer containing the IP Addresses
|
|
This buffer should be freed using NetpMemoryFree().
|
|
|
|
RetIpAddressSize - Size (in bytes) of RetIpAddresses
|
|
|
|
Return Value:
|
|
|
|
Returns the number of IP Addresses returned.
|
|
|
|
--*/
|
|
{
|
|
ULONG IpAddressCount;
|
|
ULONG IpAddressSize;
|
|
ULONG i;
|
|
|
|
PLIST_ENTRY ListEntry;
|
|
PNL_TRANSPORT TransportEntry = NULL;
|
|
PSOCKET_ADDRESS SocketAddresses;
|
|
LPBYTE OrigBuffer;
|
|
LPBYTE Where;
|
|
|
|
//
|
|
// Allocate a buffer that will be large enough.
|
|
//
|
|
|
|
*RetIpAddresses = NULL;
|
|
*RetIpAddressSize = 0;
|
|
|
|
EnterCriticalSection( &NlGlobalTransportCritSect );
|
|
if ( HeaderSize + NlGlobalWinsockPnpAddressSize == 0 ) {
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
return 0;
|
|
}
|
|
|
|
OrigBuffer = NetpMemoryAllocate( HeaderSize + NlGlobalWinsockPnpAddressSize );
|
|
|
|
if ( OrigBuffer == NULL ) {
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
return 0;
|
|
}
|
|
|
|
if ( NlGlobalWinsockPnpAddressSize == 0 ) {
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
*RetIpAddresses = (PSOCKET_ADDRESS)OrigBuffer;
|
|
*RetIpAddressSize = HeaderSize;
|
|
return 0;
|
|
}
|
|
|
|
SocketAddresses = (PSOCKET_ADDRESS)(OrigBuffer + HeaderSize);
|
|
*RetIpAddressSize = HeaderSize + NlGlobalWinsockPnpAddressSize;
|
|
Where = (LPBYTE)&SocketAddresses[NlGlobalWinsockPnpAddresses->iAddressCount];
|
|
|
|
|
|
//
|
|
// Loop through the list of addresses learned via winsock.
|
|
//
|
|
|
|
IpAddressCount = NlGlobalWinsockPnpAddresses->iAddressCount;
|
|
for ( i=0; i<IpAddressCount; i++ ) {
|
|
|
|
SocketAddresses[i].iSockaddrLength = NlGlobalWinsockPnpAddresses->Address[i].iSockaddrLength;
|
|
if ( ReturnOffsets ) {
|
|
SocketAddresses[i].lpSockaddr = (PSOCKADDR)(Where-OrigBuffer);
|
|
} else {
|
|
SocketAddresses[i].lpSockaddr = (PSOCKADDR)Where;
|
|
}
|
|
|
|
RtlCopyMemory( Where,
|
|
NlGlobalWinsockPnpAddresses->Address[i].lpSockaddr,
|
|
NlGlobalWinsockPnpAddresses->Address[i].iSockaddrLength );
|
|
|
|
Where += NlGlobalWinsockPnpAddresses->Address[i].iSockaddrLength;
|
|
|
|
}
|
|
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
|
|
*RetIpAddresses = (PSOCKET_ADDRESS)OrigBuffer;
|
|
return IpAddressCount;
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
NlTransportGetIpAddress(
|
|
IN LPWSTR TransportName,
|
|
OUT PULONG IpAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the IP Address associated with the specified transport.
|
|
|
|
Arguments:
|
|
|
|
TransportName - Name of the transport to query.
|
|
|
|
IpAddress - IP address of the transport.
|
|
Zero if the transport currently has no address or
|
|
if the transport is not IP.
|
|
|
|
Return Value:
|
|
|
|
TRUE: transport is an IP transport
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
BOOLEAN RetVal = FALSE;
|
|
|
|
IO_STATUS_BLOCK IoStatusBlock;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
UNICODE_STRING TransportNameString;
|
|
HANDLE TransportHandle = NULL;
|
|
ULONG IpAddresses[NBT_MAXIMUM_BINDINGS+1];
|
|
ULONG BytesReturned;
|
|
|
|
//
|
|
// Open the transport device directly.
|
|
//
|
|
|
|
*IpAddress = 0;
|
|
|
|
RtlInitUnicodeString( &TransportNameString, TransportName );
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&TransportNameString,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL );
|
|
|
|
Status = NtOpenFile(
|
|
&TransportHandle,
|
|
SYNCHRONIZE,
|
|
&ObjectAttributes,
|
|
&IoStatusBlock,
|
|
0,
|
|
0 );
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = IoStatusBlock.Status;
|
|
|
|
}
|
|
|
|
if (! NT_SUCCESS(Status)) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlTransportGetIpAddress: %ws Cannot NtOpenFile %lx\n",
|
|
TransportName,
|
|
Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Query the IP Address
|
|
//
|
|
|
|
if (!DeviceIoControl( TransportHandle,
|
|
IOCTL_NETBT_GET_IP_ADDRS,
|
|
NULL,
|
|
0,
|
|
IpAddresses,
|
|
sizeof(IpAddresses),
|
|
&BytesReturned,
|
|
NULL)) {
|
|
|
|
Status = NetpApiStatusToNtStatus(GetLastError());
|
|
if ( Status != STATUS_NOT_IMPLEMENTED ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlTransportGetIpAddress: %ws Cannot DeviceIoControl %lx\n",
|
|
TransportName,
|
|
Status ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Return IP Address
|
|
// (Netbt returns the address in host order.)
|
|
//
|
|
|
|
*IpAddress = htonl(*IpAddresses);
|
|
RetVal = TRUE;
|
|
|
|
|
|
Cleanup:
|
|
|
|
if ( TransportHandle != NULL ) {
|
|
(VOID) NtClose( TransportHandle );
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
VOID
|
|
NlNotifyKerberosOfIpAddresses(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Call the Kerberos package to let it know the IP addresses of the machine.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PKERB_UPDATE_ADDRESSES_REQUEST UpdateRequest = NULL;
|
|
ULONG UpdateRequestSize;
|
|
|
|
ULONG SocketAddressCount;
|
|
|
|
UNICODE_STRING KerberosPackageName;
|
|
|
|
NTSTATUS SubStatus;
|
|
PVOID OutputBuffer = NULL;
|
|
ULONG OutputBufferSize = 0;
|
|
|
|
//
|
|
// Initialization.
|
|
//
|
|
RtlInitUnicodeString(
|
|
&KerberosPackageName,
|
|
MICROSOFT_KERBEROS_NAME_W
|
|
);
|
|
|
|
//
|
|
// Grab a copy of the list so we don't call Kerberos with anything
|
|
// locked.
|
|
//
|
|
//
|
|
//
|
|
|
|
SocketAddressCount = NlTransportGetIpAddresses(
|
|
offsetof(KERB_UPDATE_ADDRESSES_REQUEST,Addresses),
|
|
FALSE,
|
|
(PSOCKET_ADDRESS *)&UpdateRequest,
|
|
&UpdateRequestSize );
|
|
|
|
if ( UpdateRequest == NULL ) {
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Fill in the header.
|
|
//
|
|
|
|
UpdateRequest->MessageType = KerbUpdateAddressesMessage;
|
|
UpdateRequest->AddressCount = SocketAddressCount;
|
|
|
|
|
|
//
|
|
// Pass them to Kerberos.
|
|
//
|
|
|
|
(VOID) LsaICallPackage(
|
|
&KerberosPackageName,
|
|
UpdateRequest,
|
|
UpdateRequestSize,
|
|
&OutputBuffer,
|
|
&OutputBufferSize,
|
|
&SubStatus );
|
|
|
|
NetpMemoryFree( UpdateRequest );
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NlReadRegSocketAddressList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read the Socket address list from the registry and save them in the globals
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
|
|
LPSOCKET_ADDRESS_LIST SocketAddressList = NULL;
|
|
HKEY ParmHandle = NULL;
|
|
ULONG SocketAddressSize = 0;
|
|
|
|
int i;
|
|
DWORD LocalEntryCount;
|
|
DWORD RegType;
|
|
|
|
//
|
|
// Open the key for Netlogon\Private
|
|
//
|
|
|
|
ParmHandle = NlOpenNetlogonKey( NL_PRIVATE_KEY );
|
|
|
|
if (ParmHandle == NULL) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"Cannot NlOpenNetlogonKey to get socket address list.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Read the entry from the registry
|
|
//
|
|
|
|
SocketAddressSize = 0;
|
|
NetStatus = RegQueryValueExW( ParmHandle,
|
|
NETLOGON_KEYWORD_SOCKETADDRESSLIST,
|
|
0, // Reserved
|
|
&RegType,
|
|
NULL,
|
|
&SocketAddressSize );
|
|
|
|
if ( NetStatus == NO_ERROR || NetStatus == ERROR_MORE_DATA) {
|
|
SocketAddressList = LocalAlloc( 0, SocketAddressSize );
|
|
|
|
if ( SocketAddressList == NULL ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetStatus = RegQueryValueExW( ParmHandle,
|
|
NETLOGON_KEYWORD_SOCKETADDRESSLIST,
|
|
0, // Reserved
|
|
&RegType,
|
|
(LPBYTE)SocketAddressList,
|
|
&SocketAddressSize );
|
|
|
|
}
|
|
|
|
if ( NetStatus != NO_ERROR ) {
|
|
if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"Cannot RegQueryValueExW to get socket address list. %ld\n",
|
|
NetStatus ));
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Validate the data.
|
|
//
|
|
|
|
if ( RegType != REG_BINARY ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"SocketAddressList isn't REG_BINARY %ld.\n",
|
|
RegType ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( SocketAddressSize < offsetof(SOCKET_ADDRESS_LIST, Address) ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"SocketAddressList is too small %ld.\n",
|
|
SocketAddressSize ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( SocketAddressList->iAddressCount * sizeof(SOCKET_ADDRESS) >
|
|
SocketAddressSize - offsetof(SOCKET_ADDRESS_LIST, Address) ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"SocketAddressList size wrong %ld %ld.\n",
|
|
SocketAddressList->iAddressCount * sizeof(SOCKET_ADDRESS),
|
|
SocketAddressSize - offsetof(SOCKET_ADDRESS_LIST, Address) ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Convert all offsets to pointers
|
|
//
|
|
|
|
for ( i=0; i<SocketAddressList->iAddressCount; i++ ) {
|
|
PSOCKET_ADDRESS SocketAddress;
|
|
|
|
//
|
|
// Ensure the offset and lengths are valid
|
|
//
|
|
|
|
SocketAddress = &SocketAddressList->Address[i];
|
|
|
|
if ( ((DWORD_PTR)SocketAddress->lpSockaddr) >= SocketAddressSize ||
|
|
(DWORD)SocketAddress->iSockaddrLength >= SocketAddressSize ||
|
|
((DWORD_PTR)SocketAddress->lpSockaddr)+SocketAddress->iSockaddrLength > SocketAddressSize ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"SocketAddressEntry bad %ld %p %ld.\n",
|
|
i,
|
|
((DWORD_PTR)SocketAddress->lpSockaddr),
|
|
SocketAddress->iSockaddrLength ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
SocketAddress->lpSockaddr = (LPSOCKADDR)
|
|
(((LPBYTE)SocketAddressList) + ((DWORD_PTR)SocketAddress->lpSockaddr) );
|
|
|
|
//
|
|
// If the address isn't valid,
|
|
// blow it away.
|
|
//
|
|
SocketAddress = &SocketAddressList->Address[i];
|
|
|
|
if ( SocketAddress->iSockaddrLength == 0 ||
|
|
SocketAddress->lpSockaddr == NULL ||
|
|
SocketAddress->lpSockaddr->sa_family != AF_INET ||
|
|
((PSOCKADDR_IN)(SocketAddress->lpSockaddr))->sin_addr.s_addr == 0 ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"SocketAddressEntry bogus.\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Swap the new list into the global.
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalTransportCritSect );
|
|
NlAssert( NlGlobalWinsockPnpAddresses == NULL );
|
|
SocketAddressSize -= offsetof(SOCKET_ADDRESS_LIST, Address);
|
|
if ( SocketAddressSize > 0 ) {
|
|
NlGlobalWinsockPnpAddresses = SocketAddressList;
|
|
SocketAddressList = NULL;
|
|
}
|
|
NlGlobalWinsockPnpAddressSize = SocketAddressSize;
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
|
|
Cleanup:
|
|
if ( SocketAddressList != NULL ) {
|
|
LocalFree( SocketAddressList );
|
|
}
|
|
|
|
if ( ParmHandle != NULL ) {
|
|
RegCloseKey( ParmHandle );
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
BOOLEAN
|
|
NlHandleWsaPnp(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle a WSA PNP event that IP addresses have changed
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
TRUE if the address list has changed
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
BOOLEAN RetVal = FALSE;
|
|
DWORD BytesReturned;
|
|
LPSOCKET_ADDRESS_LIST SocketAddressList = NULL;
|
|
HKEY ParmHandle = NULL;
|
|
LPSOCKET_ADDRESS_LIST RegBuffer = NULL;
|
|
ULONG SocketAddressSize = 0;
|
|
int i;
|
|
int j;
|
|
int MaxAddressCount;
|
|
|
|
//
|
|
// Ask for notification of address changes.
|
|
//
|
|
|
|
if ( NlGlobalWinsockPnpSocket == INVALID_SOCKET ) {
|
|
return FALSE;
|
|
}
|
|
|
|
NetStatus = WSAIoctl( NlGlobalWinsockPnpSocket,
|
|
SIO_ADDRESS_LIST_CHANGE,
|
|
NULL, // No input buffer
|
|
0, // No input buffer
|
|
NULL, // No output buffer
|
|
0, // No output buffer
|
|
&BytesReturned,
|
|
NULL, // No overlapped,
|
|
NULL ); // Not async
|
|
|
|
if ( NetStatus != 0 ) {
|
|
NetStatus = WSAGetLastError();
|
|
if ( NetStatus != WSAEWOULDBLOCK) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlHandleWsaPnp: Cannot WSAIoctl SIO_ADDRESS_LIST_CHANGE %ld\n",
|
|
NetStatus ));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get the list of IP addresses for this machine.
|
|
//
|
|
|
|
BytesReturned = 150; // Initial guess
|
|
for (;;) {
|
|
|
|
//
|
|
// Allocate a buffer that should be big enough.
|
|
//
|
|
|
|
if ( SocketAddressList != NULL ) {
|
|
LocalFree( SocketAddressList );
|
|
}
|
|
|
|
SocketAddressList = LocalAlloc( 0, BytesReturned );
|
|
|
|
if ( SocketAddressList == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlHandleWsaPnp: Cannot allocate buffer for WSAIoctl SIO_ADDRESS_LIST_QUERY %ld\n",
|
|
NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Get the list of IP addresses
|
|
//
|
|
|
|
NetStatus = WSAIoctl( NlGlobalWinsockPnpSocket,
|
|
SIO_ADDRESS_LIST_QUERY,
|
|
NULL, // No input buffer
|
|
0, // No input buffer
|
|
(PVOID) SocketAddressList,
|
|
BytesReturned,
|
|
&BytesReturned,
|
|
NULL, // No overlapped,
|
|
NULL ); // Not async
|
|
|
|
if ( NetStatus != 0 ) {
|
|
NetStatus = WSAGetLastError();
|
|
//
|
|
// If the buffer isn't big enough, try again.
|
|
//
|
|
if ( NetStatus == WSAEFAULT ) {
|
|
continue;
|
|
}
|
|
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlHandleWsaPnp: Cannot WSAIoctl SIO_ADDRESS_LIST_QUERY %ld %ld\n",
|
|
NetStatus,
|
|
BytesReturned ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
|
|
//
|
|
// Weed out any zero IP addresses and other invalid addresses
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalTransportCritSect );
|
|
j=0;
|
|
NlPrint(( NL_SERVER_SESS, "Winsock Addrs:" ));
|
|
for ( i=0; i<SocketAddressList->iAddressCount; i++ ) {
|
|
PSOCKET_ADDRESS SocketAddress;
|
|
|
|
//
|
|
// Copy this address to the front of the list.
|
|
//
|
|
SocketAddressList->Address[j] = SocketAddressList->Address[i];
|
|
|
|
//
|
|
// If the address isn't valid,
|
|
// skip it.
|
|
//
|
|
SocketAddress = &SocketAddressList->Address[j];
|
|
|
|
if ( SocketAddress->iSockaddrLength == 0 ||
|
|
SocketAddress->lpSockaddr == NULL ||
|
|
SocketAddress->lpSockaddr->sa_family != AF_INET ||
|
|
((PSOCKADDR_IN)(SocketAddress->lpSockaddr))->sin_addr.s_addr == 0 ) {
|
|
|
|
|
|
//
|
|
// Otherwise keep it.
|
|
//
|
|
} else {
|
|
|
|
#if NETLOGONDBG
|
|
ULONG IpAddress;
|
|
CHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1];
|
|
IpAddress = ((PSOCKADDR_IN)(SocketAddress->lpSockaddr))->sin_addr.s_addr;
|
|
NetpIpAddressToStr( IpAddress, IpAddressString );
|
|
NlPrint(( NL_SERVER_SESS, " %s", IpAddressString ));
|
|
#endif // NETLOGONDBG
|
|
|
|
SocketAddressSize += sizeof(SOCKET_ADDRESS) + SocketAddress->iSockaddrLength;
|
|
j++;
|
|
}
|
|
|
|
}
|
|
SocketAddressList->iAddressCount = j;
|
|
NlPrint(( NL_SERVER_SESS, " (%ld) ", j ));
|
|
|
|
//
|
|
// See if the list has changed
|
|
//
|
|
|
|
if ( NlGlobalWinsockPnpAddresses == NULL ) {
|
|
if ( SocketAddressSize > 0) {
|
|
NlPrint(( NL_SERVER_SESS, "List used to be empty." ));
|
|
RetVal = TRUE;
|
|
}
|
|
|
|
} else if ( SocketAddressSize == 0 ) {
|
|
NlPrint(( NL_SERVER_SESS, "List is now empty." ));
|
|
RetVal = TRUE;
|
|
|
|
} else if ( NlGlobalWinsockPnpAddresses->iAddressCount !=
|
|
SocketAddressList->iAddressCount ) {
|
|
NlPrint(( NL_SERVER_SESS, "List size changed %ld %ld.",
|
|
NlGlobalWinsockPnpAddresses->iAddressCount,
|
|
SocketAddressList->iAddressCount ));
|
|
RetVal = TRUE;
|
|
|
|
} else {
|
|
for ( i=0; i<SocketAddressList->iAddressCount; i++ ) {
|
|
if ( SocketAddressList->Address[i].iSockaddrLength !=
|
|
NlGlobalWinsockPnpAddresses->Address[i].iSockaddrLength ) {
|
|
NlPrint(( NL_SERVER_SESS, "Sockaddrlen changed." ));
|
|
RetVal = TRUE;
|
|
break;
|
|
}
|
|
if ( !RtlEqualMemory(
|
|
SocketAddressList->Address[i].lpSockaddr,
|
|
NlGlobalWinsockPnpAddresses->Address[i].lpSockaddr,
|
|
SocketAddressList->Address[i].iSockaddrLength ) ) {
|
|
NlPrint(( NL_SERVER_SESS, "Address changed." ));
|
|
RetVal = TRUE;
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
NlPrint(( NL_SERVER_SESS, "\n" ));
|
|
|
|
|
|
//
|
|
// Swap the new list into the global.
|
|
//
|
|
if ( NlGlobalWinsockPnpAddresses != NULL ) {
|
|
LocalFree( NlGlobalWinsockPnpAddresses );
|
|
NlGlobalWinsockPnpAddresses = NULL;
|
|
}
|
|
if ( SocketAddressSize > 0 ) {
|
|
NlGlobalWinsockPnpAddresses = SocketAddressList;
|
|
SocketAddressList = NULL;
|
|
}
|
|
NlGlobalWinsockPnpAddressSize = SocketAddressSize;
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
|
|
//
|
|
// Notify Kerberos of the list of addresses.
|
|
//
|
|
|
|
if ( RetVal ) {
|
|
NlNotifyKerberosOfIpAddresses();
|
|
}
|
|
|
|
//
|
|
// If the list changed,
|
|
// save it in the registry
|
|
//
|
|
|
|
if ( RetVal ) {
|
|
ULONG RegBufferSize;
|
|
ULONG RegEntryCount;
|
|
|
|
//
|
|
// Grab a copy of the address list with relative offsets and a header.
|
|
//
|
|
|
|
RegEntryCount = NlTransportGetIpAddresses(
|
|
offsetof(SOCKET_ADDRESS_LIST, Address),
|
|
TRUE, // Return offsets and not pointers
|
|
(PSOCKET_ADDRESS *)&RegBuffer,
|
|
&RegBufferSize );
|
|
|
|
//
|
|
// If we have no IP addresses, NlTransportGetIpAddresses has allocated
|
|
// the header only and returned the size of the header as the size of the
|
|
// allocated buffer. In this case, set the size of the buffer to 0 in
|
|
// order to clean up the registry value.
|
|
//
|
|
|
|
if ( RegBufferSize == offsetof(SOCKET_ADDRESS_LIST, Address) ) {
|
|
RegBufferSize = 0;
|
|
}
|
|
|
|
if ( RegBuffer != NULL ) {
|
|
|
|
//
|
|
// Fill in the header.
|
|
//
|
|
|
|
RegBuffer->iAddressCount = RegEntryCount;
|
|
|
|
//
|
|
// Open the key for Netlogon\Private
|
|
//
|
|
|
|
ParmHandle = NlOpenNetlogonKey( NL_PRIVATE_KEY );
|
|
|
|
if (ParmHandle == NULL) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"Cannot NlOpenNetlogonKey to save IP address list.\n" ));
|
|
} else {
|
|
|
|
NetStatus = RegSetValueExW( ParmHandle,
|
|
NETLOGON_KEYWORD_SOCKETADDRESSLIST,
|
|
0, // Reserved
|
|
REG_BINARY,
|
|
(LPBYTE)RegBuffer,
|
|
RegBufferSize );
|
|
|
|
if ( NetStatus != ERROR_SUCCESS ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"Cannot write '%ws' key to registry %ld.\n",
|
|
NETLOGON_KEYWORD_SOCKETADDRESSLIST,
|
|
NetStatus ));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
if ( SocketAddressList != NULL ) {
|
|
LocalFree( SocketAddressList );
|
|
}
|
|
|
|
if ( ParmHandle != NULL ) {
|
|
RegCloseKey( ParmHandle );
|
|
}
|
|
|
|
if ( RegBuffer != NULL ) {
|
|
NetpMemoryFree( RegBuffer );
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
NlTransportOpen(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the list of transports
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status of the operation
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
PLMDR_TRANSPORT_LIST TransportList;
|
|
PLMDR_TRANSPORT_LIST TransportEntry;
|
|
|
|
//
|
|
// Enumerate the transports supported by the server.
|
|
//
|
|
|
|
NetStatus = NlBrowserGetTransportList( &TransportList );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
NlPrint(( NL_CRITICAL, "Cannot NlBrowserGetTransportList %ld\n", NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Loop through the list of transports building a local list.
|
|
//
|
|
|
|
TransportEntry = TransportList;
|
|
|
|
while (TransportEntry != NULL) {
|
|
BOOLEAN IpTransportChanged;
|
|
(VOID) NlTransportAddTransportName(
|
|
TransportEntry->TransportName,
|
|
&IpTransportChanged );
|
|
|
|
if (TransportEntry->NextEntryOffset == 0) {
|
|
TransportEntry = NULL;
|
|
} else {
|
|
TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+
|
|
TransportEntry->NextEntryOffset);
|
|
}
|
|
|
|
}
|
|
|
|
MIDL_user_free(TransportList);
|
|
|
|
//
|
|
// Open a socket to get winsock PNP notifications on.
|
|
//
|
|
|
|
NlGlobalWinsockPnpSocket = WSASocket( AF_INET,
|
|
SOCK_DGRAM,
|
|
0, // PF_INET,
|
|
NULL,
|
|
0,
|
|
0 );
|
|
|
|
if ( NlGlobalWinsockPnpSocket == INVALID_SOCKET ) {
|
|
NetStatus = WSAGetLastError();
|
|
|
|
//
|
|
// If the address family isn't supported,
|
|
// we're done here.
|
|
//
|
|
if ( NetStatus == WSAEAFNOSUPPORT ) {
|
|
NetStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
NlPrint(( NL_CRITICAL, "Can't WSASocket %ld\n", NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open an event to wait on.
|
|
//
|
|
|
|
NlGlobalWinsockPnpEvent = CreateEvent(
|
|
NULL, // No security ettibutes
|
|
FALSE, // Auto reset
|
|
FALSE, // Initially not signaled
|
|
NULL); // No Name
|
|
|
|
if ( NlGlobalWinsockPnpEvent == NULL ) {
|
|
NetStatus = GetLastError();
|
|
NlPrint((NL_CRITICAL, "Cannot create Winsock PNP event %ld\n", NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Associate the event with new addresses becoming available on the socket.
|
|
//
|
|
|
|
NetStatus = WSAEventSelect( NlGlobalWinsockPnpSocket, NlGlobalWinsockPnpEvent, FD_ADDRESS_LIST_CHANGE );
|
|
|
|
if ( NetStatus != 0 ) {
|
|
NetStatus = WSAGetLastError();
|
|
NlPrint(( NL_CRITICAL, "Can't WSAEventSelect %ld\n", NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Grab the addresses from the registry (So we can properly detect if the list changed)
|
|
//
|
|
|
|
NlReadRegSocketAddressList();
|
|
|
|
//
|
|
// Get the initial list of IP addresses
|
|
//
|
|
|
|
if ( NlHandleWsaPnp() ) {
|
|
|
|
NlPrint(( NL_CRITICAL, "Address list changed since last boot. (Forget DynamicSiteName.)\n" ));
|
|
|
|
//
|
|
// Indicate that we no longer know what site we're in.
|
|
//
|
|
NlSetDynamicSiteName( NULL );
|
|
}
|
|
|
|
Cleanup:
|
|
return NetStatus;
|
|
}
|
|
|
|
BOOL
|
|
NlTransportAddTransportName(
|
|
IN LPWSTR TransportName,
|
|
OUT PBOOLEAN IpTransportChanged
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds a transport name to the list of transports.
|
|
|
|
Arguments:
|
|
|
|
TransportName - Name of the transport to add
|
|
|
|
IpTransportChanged - Returns TRUE if an IP transport is added or
|
|
the IP address of the transport changes.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
|
|
FALSE - memory allocation failure.
|
|
|
|
--*/
|
|
{
|
|
DWORD TransportNameLength;
|
|
PLIST_ENTRY ListEntry;
|
|
PNL_TRANSPORT TransportEntry = NULL;
|
|
ULONG OldIpAddress;
|
|
BOOLEAN WasIpTransport;
|
|
|
|
//
|
|
// Initialization.
|
|
//
|
|
|
|
*IpTransportChanged = FALSE;
|
|
|
|
//
|
|
// If the entry already exists, use it.
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalTransportCritSect );
|
|
for ( ListEntry = NlGlobalTransportList.Flink ;
|
|
ListEntry != &NlGlobalTransportList ;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
|
|
TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
|
|
|
|
if ( _wcsicmp( TransportName, TransportEntry->TransportName ) == 0 ) {
|
|
break;
|
|
}
|
|
|
|
TransportEntry = NULL;
|
|
}
|
|
|
|
//
|
|
// If there isn't already a transport entry,
|
|
// allocate and initialize one.
|
|
//
|
|
|
|
if ( TransportEntry == NULL ) {
|
|
|
|
//
|
|
// Allocate a buffer for the new entry.
|
|
//
|
|
|
|
TransportNameLength = wcslen( TransportName );
|
|
TransportEntry = LocalAlloc( 0,
|
|
sizeof(NL_TRANSPORT) +
|
|
TransportNameLength * sizeof(WCHAR) );
|
|
|
|
if ( TransportEntry == NULL ) {
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
NlPrint(( NL_CRITICAL, "NlTransportAddTransportName: no memory\n" ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Build the new entry and link it onto the tail of the list.
|
|
//
|
|
|
|
wcscpy( TransportEntry->TransportName, TransportName );
|
|
TransportEntry->IpAddress = 0;
|
|
TransportEntry->IsIpTransport = FALSE;
|
|
TransportEntry->DeviceHandle = INVALID_HANDLE_VALUE;
|
|
|
|
//
|
|
// Flag NwLnkIpx since it is poorly behaved.
|
|
//
|
|
// 1) The redir doesn't support it.
|
|
// 2) A datagram sent to it doesn't support the 0x1C name.
|
|
//
|
|
|
|
if ( _wcsicmp( TransportName, L"\\Device\\NwlnkIpx" ) == 0 ) {
|
|
TransportEntry->DirectHostIpx = TRUE;
|
|
} else {
|
|
TransportEntry->DirectHostIpx = FALSE;
|
|
}
|
|
|
|
|
|
InsertTailList( &NlGlobalTransportList, &TransportEntry->Next );
|
|
}
|
|
|
|
//
|
|
// Under all circumstances, update the IP address.
|
|
//
|
|
|
|
TransportEntry->TransportEnabled = TRUE;
|
|
|
|
OldIpAddress = TransportEntry->IpAddress;
|
|
WasIpTransport = TransportEntry->IsIpTransport;
|
|
|
|
TransportEntry->IsIpTransport = NlTransportGetIpAddress(
|
|
TransportName,
|
|
&TransportEntry->IpAddress );
|
|
|
|
if ( TransportEntry->IsIpTransport ) {
|
|
|
|
//
|
|
// If this is a new IP transport,
|
|
// count it.
|
|
//
|
|
|
|
if ( !WasIpTransport ) {
|
|
NlGlobalIpTransportCount ++;
|
|
*IpTransportChanged = TRUE;
|
|
}
|
|
|
|
//
|
|
// If the transport was just added,
|
|
// Indicate so.
|
|
//
|
|
|
|
if ( OldIpAddress == 0 ) {
|
|
#if NETLOGONDBG
|
|
CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
|
|
NetpIpAddressToStr( TransportEntry->IpAddress, IpAddress );
|
|
NlPrint(( NL_SERVER_SESS, "%ws: Transport Added (%s)\n", TransportName, IpAddress ));
|
|
#endif // NETLOGONDBG
|
|
*IpTransportChanged = TRUE;
|
|
|
|
//
|
|
// If the IP address hasn't changed,
|
|
// this is simply a superfluous PNP notification.
|
|
//
|
|
} else if ( OldIpAddress == TransportEntry->IpAddress ) {
|
|
#if NETLOGONDBG
|
|
CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
|
|
NetpIpAddressToStr( TransportEntry->IpAddress, IpAddress );
|
|
NlPrint(( NL_SERVER_SESS, "%ws: Transport Address is still (%s)\n", TransportName, IpAddress ));
|
|
#endif // NETLOGONDBG
|
|
|
|
//
|
|
// If the IP Address changed,
|
|
// let the caller know.
|
|
//
|
|
} else {
|
|
#if NETLOGONDBG
|
|
CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
|
|
CHAR OldIpAddressString[NL_IP_ADDRESS_LENGTH+1];
|
|
NetpIpAddressToStr( OldIpAddress, OldIpAddressString );
|
|
NetpIpAddressToStr( TransportEntry->IpAddress, IpAddress );
|
|
NlPrint(( NL_SERVER_SESS,
|
|
"%ws: Transport Ip Address changed from (%s) to (%s)\n",
|
|
TransportName,
|
|
OldIpAddressString,
|
|
IpAddress ));
|
|
#endif // NETLOGONDBG
|
|
*IpTransportChanged = TRUE;
|
|
}
|
|
|
|
//
|
|
// For non-IP transports,
|
|
// there's not much to do.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// If the transport used to be an IP transport,
|
|
// that doesn't seem possible (but) ...
|
|
//
|
|
|
|
if ( WasIpTransport ) {
|
|
NlGlobalIpTransportCount --;
|
|
*IpTransportChanged = TRUE;
|
|
}
|
|
|
|
NlPrint(( NL_SERVER_SESS, "%ws: Transport Added\n", TransportName ));
|
|
}
|
|
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
|
|
return TRUE;;
|
|
}
|
|
|
|
BOOLEAN
|
|
NlTransportDisableTransportName(
|
|
IN LPWSTR TransportName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Disables a transport name on the list of transports.
|
|
|
|
The TransportName is never removed thus preventing us from having to
|
|
maintain reference counts.
|
|
|
|
Arguments:
|
|
|
|
TransportName - Name of the transport to disable.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if an IP transport is disabled.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
//
|
|
// Find this transport in the list of transports.
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalTransportCritSect );
|
|
for ( ListEntry = NlGlobalTransportList.Flink ;
|
|
ListEntry != &NlGlobalTransportList ;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
PNL_TRANSPORT TransportEntry;
|
|
|
|
TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
|
|
|
|
if ( TransportEntry->TransportEnabled &&
|
|
_wcsicmp( TransportName, TransportEntry->TransportName ) == 0 ) {
|
|
ULONG OldIpAddress;
|
|
|
|
TransportEntry->TransportEnabled = FALSE;
|
|
OldIpAddress = TransportEntry->IpAddress;
|
|
TransportEntry->IpAddress = 0;
|
|
if ( TransportEntry->DeviceHandle != INVALID_HANDLE_VALUE ) {
|
|
NtClose( TransportEntry->DeviceHandle );
|
|
TransportEntry->DeviceHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
|
|
NlPrint(( NL_SERVER_SESS, "%ws: Transport Removed\n", TransportName ));
|
|
return (OldIpAddress != 0);
|
|
}
|
|
}
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
PNL_TRANSPORT
|
|
NlTransportLookupTransportName(
|
|
IN LPWSTR TransportName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a transport name equal to the one passed in. However, the
|
|
returned transport name is static and need not be freed.
|
|
|
|
Arguments:
|
|
|
|
TransportName - Name of the transport to look up
|
|
|
|
Return Value:
|
|
|
|
NULL - on any error
|
|
|
|
Otherwise, returns a pointer to the transport structure.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
//
|
|
// If we're not initialized yet,
|
|
// just return
|
|
//
|
|
|
|
if ( TransportName == NULL ) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Find this transport in the list of transports.
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalTransportCritSect );
|
|
for ( ListEntry = NlGlobalTransportList.Flink ;
|
|
ListEntry != &NlGlobalTransportList ;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
PNL_TRANSPORT TransportEntry;
|
|
|
|
TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
|
|
|
|
if ( TransportEntry->TransportEnabled &&
|
|
_wcsicmp( TransportName, TransportEntry->TransportName ) == 0 ) {
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
return TransportEntry;
|
|
}
|
|
}
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
|
|
return NULL;
|
|
}
|
|
|
|
PNL_TRANSPORT
|
|
NlTransportLookup(
|
|
IN LPWSTR ClientName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determine what transport the specified client is using to access this
|
|
server.
|
|
|
|
Arguments:
|
|
|
|
ClientName - Name of the client connected to this server.
|
|
|
|
Return Value:
|
|
|
|
NULL - The client isn't currently connected
|
|
|
|
Otherwise, returns a pointer to the transport structure
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
PSESSION_INFO_502 SessionInfo502;
|
|
DWORD EntriesRead;
|
|
DWORD TotalEntries;
|
|
DWORD i;
|
|
DWORD BestTime;
|
|
DWORD BestEntry;
|
|
PNL_TRANSPORT Transport;
|
|
|
|
WCHAR UncClientName[UNCLEN+1];
|
|
|
|
//
|
|
// Validate the client name
|
|
//
|
|
|
|
if ( wcslen(ClientName) > CNLEN ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlTransportLookup: Client name %ws too long\n",
|
|
ClientName ));
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Enumerate all the sessions from the particular client.
|
|
//
|
|
|
|
UncClientName[0] = '\\';
|
|
UncClientName[1] = '\\';
|
|
wcscpy( &UncClientName[2], ClientName );
|
|
|
|
NetStatus = NetSessionEnum(
|
|
NULL, // local
|
|
UncClientName, // Client to query
|
|
NULL, // user name
|
|
502,
|
|
(LPBYTE *)&SessionInfo502,
|
|
1024, // PrefMaxLength
|
|
&EntriesRead,
|
|
&TotalEntries,
|
|
NULL ); // No resume handle
|
|
|
|
if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlTransportLookup: " FORMAT_LPWSTR ": Cannot NetSessionEnum %ld\n",
|
|
UncClientName,
|
|
NetStatus ));
|
|
return NULL;
|
|
}
|
|
|
|
if ( EntriesRead == 0 ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlTransportLookup: " FORMAT_LPWSTR ": No session exists.\n",
|
|
UncClientName ));
|
|
(VOID) NetApiBufferFree( SessionInfo502 );
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Loop through the list of transports finding the best one.
|
|
//
|
|
|
|
BestTime = 0xFFFFFFFF;
|
|
|
|
for ( i=0; i<EntriesRead; i++ ) {
|
|
#ifdef notdef
|
|
//
|
|
// We're only looking for null sessions
|
|
//
|
|
if ( SessionInfo502[i].sesi502_username != NULL ) {
|
|
continue;
|
|
}
|
|
|
|
NlPrint(( NL_SERVER_SESS, "NlTransportLookup: "
|
|
FORMAT_LPWSTR " as " FORMAT_LPWSTR " on " FORMAT_LPWSTR "\n",
|
|
UncClientName,
|
|
SessionInfo502[i].sesi502_username,
|
|
SessionInfo502[i].sesi502_transport ));
|
|
#endif // notdef
|
|
|
|
//
|
|
// Find the latest session
|
|
//
|
|
|
|
if ( BestTime > SessionInfo502[i].sesi502_idle_time ) {
|
|
|
|
// NlPrint(( NL_SERVER_SESS, "NlTransportLookup: Best Entry\n" ));
|
|
BestEntry = i;
|
|
BestTime = SessionInfo502[i].sesi502_idle_time;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If an entry was found,
|
|
// Find this transport in the list of transports.
|
|
//
|
|
|
|
if ( BestTime != 0xFFFFFFFF ) {
|
|
Transport = NlTransportLookupTransportName(
|
|
SessionInfo502[BestEntry].sesi502_transport );
|
|
if ( Transport == NULL ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlTransportLookup: " FORMAT_LPWSTR ": Transport not found\n",
|
|
SessionInfo502[BestEntry].sesi502_transport ));
|
|
} else {
|
|
NlPrint(( NL_SERVER_SESS,
|
|
"NlTransportLookup: " FORMAT_LPWSTR ": Use Transport " FORMAT_LPWSTR "\n",
|
|
UncClientName,
|
|
Transport->TransportName ));
|
|
}
|
|
} else {
|
|
Transport = NULL;
|
|
}
|
|
|
|
(VOID) NetApiBufferFree( SessionInfo502 );
|
|
return Transport;
|
|
}
|
|
|
|
|
|
VOID
|
|
NlTransportClose(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free the list of transports
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status of the operation
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
PNL_TRANSPORT TransportEntry;
|
|
|
|
//
|
|
// Close the winsock PNP socket and event
|
|
//
|
|
EnterCriticalSection( &NlGlobalTransportCritSect );
|
|
if ( NlGlobalWinsockPnpSocket != INVALID_SOCKET ) {
|
|
closesocket( NlGlobalWinsockPnpSocket );
|
|
NlGlobalWinsockPnpSocket = INVALID_SOCKET;
|
|
}
|
|
|
|
if ( NlGlobalWinsockPnpEvent != NULL ) {
|
|
(VOID) CloseHandle( NlGlobalWinsockPnpEvent );
|
|
NlGlobalWinsockPnpEvent = NULL;
|
|
}
|
|
|
|
if ( NlGlobalWinsockPnpAddresses != NULL ) {
|
|
LocalFree( NlGlobalWinsockPnpAddresses );
|
|
NlGlobalWinsockPnpAddresses = NULL;
|
|
}
|
|
NlGlobalWinsockPnpAddressSize = 0;
|
|
|
|
//
|
|
// Delete all of the TransportNames.
|
|
//
|
|
while ( !IsListEmpty( &NlGlobalTransportList )) {
|
|
ListEntry = RemoveHeadList( &NlGlobalTransportList );
|
|
TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
|
|
if ( TransportEntry->DeviceHandle != INVALID_HANDLE_VALUE ) {
|
|
NtClose( TransportEntry->DeviceHandle );
|
|
TransportEntry->DeviceHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
LocalFree( TransportEntry );
|
|
}
|
|
NlGlobalIpTransportCount = 0;
|
|
LeaveCriticalSection( &NlGlobalTransportCritSect );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PSERVER_SESSION
|
|
NlFindNamedServerSession(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
IN LPWSTR ComputerName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find the specified entry in the Server Session Table.
|
|
|
|
Enter with the ServerSessionTable Sem locked
|
|
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Hosted domain with session to this computer.
|
|
|
|
ComputerName - The name of the computer on the client side of the
|
|
secure channel.
|
|
|
|
Return Value:
|
|
|
|
Returns a pointer to pointer to the found entry. If there is no such
|
|
entry, return a pointer to NULL.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ListEntry;
|
|
DWORD Index;
|
|
CHAR UpcaseOemComputerName[CNLEN+1];
|
|
ULONG OemComputerNameSize;
|
|
|
|
//
|
|
// Ensure the ServerSession Table is initialized.
|
|
//
|
|
|
|
if (DomainInfo->DomServerSessionHashTable == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Convert the computername to uppercase OEM for easier comparison.
|
|
//
|
|
|
|
Status = RtlUpcaseUnicodeToOemN(
|
|
UpcaseOemComputerName,
|
|
sizeof(UpcaseOemComputerName)-1,
|
|
&OemComputerNameSize,
|
|
ComputerName,
|
|
wcslen(ComputerName)*sizeof(WCHAR) );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
return NULL;
|
|
}
|
|
|
|
UpcaseOemComputerName[OemComputerNameSize] = '\0';
|
|
|
|
|
|
|
|
//
|
|
// Loop through this hash chain trying the find the right entry.
|
|
//
|
|
|
|
Index = NlGetHashVal( UpcaseOemComputerName, SERVER_SESSION_HASH_TABLE_SIZE );
|
|
|
|
for ( ListEntry = DomainInfo->DomServerSessionHashTable[Index].Flink ;
|
|
ListEntry != &DomainInfo->DomServerSessionHashTable[Index] ;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
PSERVER_SESSION ServerSession;
|
|
|
|
ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsHashList );
|
|
|
|
//
|
|
// Compare the worstation name
|
|
//
|
|
|
|
if ( lstrcmpA( UpcaseOemComputerName,
|
|
ServerSession->SsComputerName ) != 0 ) {
|
|
continue;
|
|
}
|
|
|
|
return ServerSession;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
NlSetServerSessionAttributesByTdoName(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
IN PUNICODE_STRING TdoName,
|
|
IN ULONG TrustAttributes
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function sets the specified TrustAttributes on all the server sessions
|
|
from the domain specified by TdoName.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Hosted domain with session to this computer.
|
|
|
|
TdoName - The Dns name of the TDO for the secure channel to find.
|
|
|
|
TrustAttributes - TrustAttributes to set
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY ListEntry;
|
|
DWORD Index;
|
|
UNICODE_STRING CanonicalTdoName;
|
|
PSERVER_SESSION ServerSession;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
RtlInitUnicodeString( &CanonicalTdoName, NULL );
|
|
LOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
|
|
//
|
|
// Ensure the ServerSession Table is initialized.
|
|
//
|
|
|
|
if (DomainInfo->DomServerSessionTdoNameHashTable == NULL) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Compute the canonical form of the name and the index into the hash table
|
|
//
|
|
|
|
Status = NlGetTdoNameHashVal( TdoName,
|
|
&CanonicalTdoName,
|
|
&Index );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Loop through this hash chain trying the find the right entries
|
|
//
|
|
|
|
for ( ListEntry = DomainInfo->DomServerSessionTdoNameHashTable[Index].Flink ;
|
|
ListEntry != &DomainInfo->DomServerSessionTdoNameHashTable[Index] ;
|
|
ListEntry = ListEntry->Flink) {
|
|
|
|
ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsTdoNameHashList );
|
|
|
|
//
|
|
// Compare the TDO name
|
|
// A case insensitive compare is done since the names are already canonicalized to compute the hash index
|
|
//
|
|
|
|
if ( RtlEqualUnicodeString( &CanonicalTdoName,
|
|
&ServerSession->SsTdoName,
|
|
FALSE ) ) {
|
|
|
|
if ( TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE ) {
|
|
if ( (ServerSession->SsFlags & SS_FOREST_TRANSITIVE) == 0 ) {
|
|
NlPrintDom((NL_SESSION_SETUP, DomainInfo,
|
|
"%wZ: server session from %s is now cross forest.\n",
|
|
&CanonicalTdoName,
|
|
ServerSession->SsComputerName ));
|
|
}
|
|
ServerSession->SsFlags |= SS_FOREST_TRANSITIVE;
|
|
} else {
|
|
if ( ServerSession->SsFlags & SS_FOREST_TRANSITIVE ) {
|
|
NlPrintDom((NL_SESSION_SETUP, DomainInfo,
|
|
"%wZ: server session from %s is now NOT cross forest.\n",
|
|
&CanonicalTdoName,
|
|
ServerSession->SsComputerName ));
|
|
}
|
|
ServerSession->SsFlags &= ~SS_FOREST_TRANSITIVE;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
RtlFreeUnicodeString( &CanonicalTdoName );
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
NlInsertServerSession(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
IN LPWSTR ComputerName,
|
|
IN LPWSTR TdoName OPTIONAL,
|
|
IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
|
|
IN DWORD Flags,
|
|
IN ULONG AccountRid,
|
|
IN ULONG NegotiatedFlags,
|
|
IN PNL_TRANSPORT Transport OPTIONAL,
|
|
IN PNETLOGON_SESSION_KEY SessionKey OPTIONAL,
|
|
IN PNETLOGON_CREDENTIAL AuthenticationSeed OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Inserts the described entry into the ServerSession Table.
|
|
|
|
The server session entry is created for two reasons: 1) it represents
|
|
the server side of a secure channel, and 2) on a PDC, it represents the
|
|
BDC account for a BDC in the domain. In the first role, it exists for
|
|
the duration of the secure channel (and this routine is called when the
|
|
client gets authenticated). In the second role, it exists as
|
|
long as the machine account exists (and this routine is called during
|
|
netlogon startup for each BDC account).
|
|
|
|
If an entry matching this ComputerName already exists
|
|
in the ServerSession Table, that entry will be overwritten.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Hosted domain for this server session
|
|
|
|
ComputerName - The name of the computer on the client side of the
|
|
secure channel.
|
|
|
|
TdoName - The name of the interdomain trust account. This parameter is
|
|
ignored if the SecureChannelType indicates this is not an uplevel interdomain trust.
|
|
|
|
SecureChannelType - The type of the secure channel.
|
|
|
|
Flags - Specifies the initial SsFlags to associate with the entry.
|
|
If the SS_BDC bit is set, the structure is considered to represent
|
|
a BDC account in the SAM database.
|
|
|
|
AccountRid - Specifies the RID of the client account.
|
|
|
|
NegotiatedFlags - Specifies the flags negotiated between the client
|
|
and this server.
|
|
|
|
Transport -- If this is a BDC secure channel, specifies the transport
|
|
to use to communicate with the BDC.
|
|
|
|
SessionKey -- Specifies th esession key to be used in the secure
|
|
communication with the client.
|
|
|
|
AuthenticationSeed - Specifies the Client Credential established as
|
|
a result of the client authentication.
|
|
|
|
Return Value:
|
|
|
|
NT STATUS code.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PSERVER_SESSION ServerSession = NULL;
|
|
UNICODE_STRING CanonicalTdoName;
|
|
ULONG TdoNameIndex;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
LOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
RtlInitUnicodeString( &CanonicalTdoName, NULL );
|
|
|
|
|
|
//
|
|
// Canonicalize the TdoName
|
|
//
|
|
|
|
if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
|
|
UNICODE_STRING TdoNameString;
|
|
|
|
//
|
|
// Compute the canonical form of the name and the index into the hash table
|
|
//
|
|
|
|
RtlInitUnicodeString( &TdoNameString, TdoName );
|
|
|
|
Status = NlGetTdoNameHashVal( &TdoNameString,
|
|
&CanonicalTdoName,
|
|
&TdoNameIndex );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we already have a server session for this client,
|
|
// check that the passed info is consistent with the
|
|
// existent one
|
|
//
|
|
|
|
ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName);
|
|
|
|
if ( ServerSession != NULL ) {
|
|
BOOLEAN DeleteExistingSession = FALSE;
|
|
|
|
//
|
|
// Beware of server with two concurrent calls outstanding
|
|
// (must have rebooted.)
|
|
// Namely, if the entry is currently locked, return an
|
|
// appropriate error.
|
|
//
|
|
if ( ServerSession->SsFlags & SS_LOCKED ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlInsertServerSession: server session locked for %ws\n",
|
|
ComputerName ));
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If there is a mismatch in the type of the account,
|
|
// simply note that fact and override the existing
|
|
// one. The mismatch can happen if
|
|
//
|
|
// * The client has been authenticated as a BDC but the
|
|
// current session is for a non-BDC. This may occur
|
|
// if we haven't yet processed a SAM notification about
|
|
// the new BDC account or SAM hasn't notified us yet.
|
|
// * The client has been authenticated as a non-BDC
|
|
// but the current session is for a BDC. This can
|
|
// occur if a machine had a BDC account and then later
|
|
// has been demoted or converted to a DC in another domain.
|
|
// * This is a insertion of a new BDC account (that is not yet
|
|
// authenticated) but the current session is for a non-BDC.
|
|
// This can happen if the client was an authenticated member
|
|
// server/workstation and has now been promoted to a BDC.
|
|
//
|
|
if ( SecureChannelType == ServerSecureChannel ) {
|
|
//
|
|
// Client comes in as a BDC but the current session
|
|
// is not for a BDC.
|
|
//
|
|
if ( (ServerSession->SsFlags & SS_BDC) == 0 ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlInsertServerSession: BDC connecting on non-BDC channel %ws\n",
|
|
ComputerName ));
|
|
}
|
|
} else if ( ServerSession->SsFlags & SS_BDC ) {
|
|
//
|
|
// Client comes in as a non-BDC but the current session
|
|
// is for a BDC.
|
|
//
|
|
if ( SecureChannelType != NullSecureChannel ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlInsertServerSession: non-BDC %ld connecting on BDC channel %ws\n",
|
|
SecureChannelType,
|
|
ComputerName ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this is an interdomain secure channel,
|
|
// ensure the existing structure has the correct TDO name.
|
|
//
|
|
|
|
|
|
if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
|
|
|
|
if ( ServerSession->SsSecureChannelType != TrustedDnsDomainSecureChannel ) {
|
|
DeleteExistingSession = TRUE;
|
|
} else {
|
|
|
|
if ( !RtlEqualUnicodeString( &CanonicalTdoName,
|
|
&ServerSession->SsTdoName,
|
|
FALSE ) ) {
|
|
DeleteExistingSession = TRUE;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the existing session is inadequate,
|
|
// delete it and create a new one.
|
|
//
|
|
|
|
if ( DeleteExistingSession ) {
|
|
if ( !NlFreeServerSession( ServerSession )) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlInsertServerSession: server session cannot be freed %ws\n",
|
|
ComputerName ));
|
|
|
|
Status = STATUS_ACCESS_DENIED;
|
|
goto Cleanup;
|
|
}
|
|
ServerSession = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If there is no current Server Session table entry,
|
|
// allocate one.
|
|
//
|
|
|
|
if ( ServerSession == NULL ) {
|
|
DWORD Index;
|
|
ULONG ComputerNameSize;
|
|
ULONG Size;
|
|
|
|
|
|
//
|
|
// Allocate the ServerSession Entry
|
|
//
|
|
|
|
Size = sizeof(SERVER_SESSION);
|
|
if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
|
|
Size += CanonicalTdoName.Length+sizeof(WCHAR);
|
|
}
|
|
|
|
ServerSession = NetpMemoryAllocate( Size );
|
|
|
|
if (ServerSession == NULL) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory( ServerSession, Size );
|
|
|
|
|
|
//
|
|
// Fill in the fields of the ServerSession entry.
|
|
//
|
|
|
|
ServerSession->SsSecureChannelType = NullSecureChannel;
|
|
ServerSession->SsSync = NULL;
|
|
InitializeListHead( &ServerSession->SsBdcList );
|
|
InitializeListHead( &ServerSession->SsPendingBdcList );
|
|
ServerSession->SsDomainInfo = DomainInfo;
|
|
|
|
//
|
|
// Convert the computername to uppercase OEM for easier comparison.
|
|
//
|
|
|
|
Status = RtlUpcaseUnicodeToOemN(
|
|
ServerSession->SsComputerName,
|
|
sizeof(ServerSession->SsComputerName)-1,
|
|
&ComputerNameSize,
|
|
ComputerName,
|
|
wcslen(ComputerName)*sizeof(WCHAR) );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NetpMemoryFree( ServerSession );
|
|
goto Cleanup;
|
|
}
|
|
|
|
ServerSession->SsComputerName[ComputerNameSize] = '\0';
|
|
|
|
//
|
|
// Allocate a hash table if there isn't one yet.
|
|
//
|
|
|
|
if ( DomainInfo->DomServerSessionHashTable == NULL ) {
|
|
DWORD i;
|
|
|
|
DomainInfo->DomServerSessionHashTable = (PLIST_ENTRY)
|
|
NetpMemoryAllocate( sizeof(LIST_ENTRY) *SERVER_SESSION_HASH_TABLE_SIZE);
|
|
|
|
if ( DomainInfo->DomServerSessionHashTable == NULL ) {
|
|
NetpMemoryFree( ServerSession );
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
for ( i=0; i< SERVER_SESSION_HASH_TABLE_SIZE; i++ ) {
|
|
InitializeListHead( &DomainInfo->DomServerSessionHashTable[i] );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Do interdomain trust specific initialization
|
|
//
|
|
|
|
if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
|
|
LPBYTE Where;
|
|
|
|
//
|
|
// Copy the TDO name into the buffer.
|
|
// Copy it in canonical form for faster comparison later.
|
|
//
|
|
|
|
Where = (LPBYTE)(ServerSession+1);
|
|
|
|
ServerSession->SsTdoName.Buffer = (LPWSTR)Where;
|
|
ServerSession->SsTdoName.MaximumLength = CanonicalTdoName.Length + sizeof(WCHAR);
|
|
ServerSession->SsTdoName.Length = CanonicalTdoName.Length;
|
|
|
|
RtlCopyMemory( Where, CanonicalTdoName.Buffer, CanonicalTdoName.Length + sizeof(WCHAR) );
|
|
ServerSession->SsTdoName.Buffer[ CanonicalTdoName.Length/sizeof(WCHAR) ] = '\0';
|
|
|
|
|
|
//
|
|
// Allocate a TdoName hash table if there isn't one yet.
|
|
//
|
|
|
|
if ( DomainInfo->DomServerSessionTdoNameHashTable == NULL ) {
|
|
DWORD i;
|
|
|
|
DomainInfo->DomServerSessionTdoNameHashTable = (PLIST_ENTRY)
|
|
NetpMemoryAllocate( sizeof(LIST_ENTRY) *SERVER_SESSION_TDO_NAME_HASH_TABLE_SIZE);
|
|
|
|
if ( DomainInfo->DomServerSessionTdoNameHashTable == NULL ) {
|
|
NetpMemoryFree( ServerSession );
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
for ( i=0; i< SERVER_SESSION_TDO_NAME_HASH_TABLE_SIZE; i++ ) {
|
|
InitializeListHead( &DomainInfo->DomServerSessionTdoNameHashTable[i] );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Insert the entry in the TDO name hash table.
|
|
//
|
|
|
|
InsertHeadList( &DomainInfo->DomServerSessionTdoNameHashTable[TdoNameIndex],
|
|
&ServerSession->SsTdoNameHashList );
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Link the allocated entry into the head of hash table.
|
|
//
|
|
// The theory is we lookup new entries more frequently than older
|
|
// entries.
|
|
//
|
|
|
|
Index = NlGetHashVal( ServerSession->SsComputerName, SERVER_SESSION_HASH_TABLE_SIZE );
|
|
|
|
InsertHeadList( &DomainInfo->DomServerSessionHashTable[Index],
|
|
&ServerSession->SsHashList );
|
|
|
|
//
|
|
// Link this entry onto the tail of the Sequential ServerSessionTable.
|
|
//
|
|
|
|
InsertTailList( &DomainInfo->DomServerSessionTable, &ServerSession->SsSeqList );
|
|
}
|
|
|
|
//
|
|
// Initialize BDC specific fields.
|
|
//
|
|
|
|
if ( Flags & SS_BDC ) {
|
|
|
|
//
|
|
// If we don't yet have this entry on the BDC list,
|
|
// add it.
|
|
//
|
|
if ( (ServerSession->SsFlags & SS_BDC) == 0 ) {
|
|
|
|
//
|
|
// Insert this entry at the front of the list of BDCs
|
|
//
|
|
InsertHeadList( &NlGlobalBdcServerSessionList,
|
|
&ServerSession->SsBdcList );
|
|
NlGlobalBdcServerSessionCount ++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize other fields
|
|
//
|
|
|
|
ServerSession->SsFlags |= Flags;
|
|
|
|
// NlAssert( ServerSession->SsAccountRid == 0 ||
|
|
// ServerSession->SsAccountRid == AccountRid );
|
|
// if ( AccountRid != 0 ) {
|
|
ServerSession->SsAccountRid = AccountRid;
|
|
// }
|
|
|
|
//
|
|
// If we're doing a new session setup,
|
|
// set the field that we learned from the session setup.
|
|
//
|
|
|
|
if ( AuthenticationSeed != NULL ) {
|
|
|
|
ServerSession->SsCheck = 0;
|
|
|
|
ServerSession->SsSecureChannelType = SecureChannelType;
|
|
ServerSession->SsNegotiatedFlags = NegotiatedFlags;
|
|
ServerSession->SsTransport = Transport;
|
|
|
|
ServerSession->SsFlags = ((USHORT) Flags) |
|
|
(ServerSession->SsFlags & SS_PERMANENT_FLAGS);
|
|
|
|
ServerSession->SsAuthenticationSeed = *AuthenticationSeed;
|
|
}
|
|
|
|
if ( SessionKey != NULL ) {
|
|
NlAssert( sizeof(*SessionKey) <= sizeof(ServerSession->SsSessionKey) );
|
|
RtlCopyMemory( &ServerSession->SsSessionKey,
|
|
SessionKey,
|
|
sizeof( *SessionKey ) );
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
|
|
RtlFreeUnicodeString( &CanonicalTdoName );
|
|
return Status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
NlFreeServerSession(
|
|
IN PSERVER_SESSION ServerSession
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free the specified Server Session table entry.
|
|
|
|
This routine is called with the Server Session table locked.
|
|
|
|
Arguments:
|
|
|
|
ServerSession - Specifies a pointer to the server session entry
|
|
to delete.
|
|
|
|
Return Value:
|
|
|
|
TRUE - the structure was deleted now
|
|
FALSE - the structure will be deleted later
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// If someone has an outstanding pointer to this entry,
|
|
// delay the deletion for now.
|
|
//
|
|
|
|
if ( ServerSession->SsFlags & SS_LOCKED ) {
|
|
ServerSession->SsFlags |= SS_DELETE_ON_UNLOCK;
|
|
NlPrintDom((NL_SERVER_SESS, ServerSession->SsDomainInfo,
|
|
"NlFreeServerSession: %s: Tried to free locked server session\n",
|
|
ServerSession->SsComputerName ));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If this entry represents a BDC account,
|
|
// don't delete the entry until the account is deleted.
|
|
//
|
|
|
|
if ( (ServerSession->SsFlags & SS_BDC) != 0 &&
|
|
(ServerSession->SsFlags & SS_BDC_FORCE_DELETE) == 0 ) {
|
|
NlPrint((NL_SERVER_SESS,
|
|
"NlFreeServerSession: %s: Didn't delete server session with BDC account.\n",
|
|
ServerSession->SsComputerName ));
|
|
return FALSE;
|
|
}
|
|
|
|
NlPrintDom((NL_SERVER_SESS, ServerSession->SsDomainInfo,
|
|
"NlFreeServerSession: %s: Freed server session\n",
|
|
ServerSession->SsComputerName ));
|
|
|
|
//
|
|
// Delink the entry from the computername hash list.
|
|
//
|
|
|
|
RemoveEntryList( &ServerSession->SsHashList );
|
|
|
|
//
|
|
// Delink the entry from the TdoName hash list.
|
|
//
|
|
|
|
if ( ServerSession->SsSecureChannelType == TrustedDnsDomainSecureChannel ) {
|
|
RemoveEntryList( &ServerSession->SsTdoNameHashList );
|
|
}
|
|
|
|
//
|
|
// Delink the entry from the sequential list.
|
|
//
|
|
|
|
RemoveEntryList( &ServerSession->SsSeqList );
|
|
|
|
|
|
//
|
|
// Handle special cleanup for the BDC_SERVER_SESSION
|
|
//
|
|
|
|
if ( ServerSession->SsFlags & SS_BDC ) {
|
|
|
|
//
|
|
// Remove the entry from the list of BDCs
|
|
//
|
|
|
|
RemoveEntryList( &ServerSession->SsBdcList );
|
|
NlGlobalBdcServerSessionCount --;
|
|
|
|
//
|
|
// Remove the entry from the list of pending BDCs
|
|
//
|
|
|
|
if ( ServerSession->SsFlags & SS_PENDING_BDC ) {
|
|
NlRemovePendingBdc( ServerSession );
|
|
}
|
|
|
|
|
|
//
|
|
// Clean up an sync context for this entry.
|
|
//
|
|
|
|
if ( ServerSession->SsSync != NULL ) {
|
|
CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
|
|
NetpMemoryFree( ServerSession->SsSync );
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Delete the entry
|
|
//
|
|
|
|
NetpMemoryFree( ServerSession );
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
NlUnlockServerSession(
|
|
IN PSERVER_SESSION ServerSession
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Unlock the specified Server Session table entry.
|
|
|
|
Arguments:
|
|
|
|
ServerSession - Specifies a pointer to the server session entry to unlock.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
|
|
LOCK_SERVER_SESSION_TABLE( ServerSession->SsDomainInfo );
|
|
|
|
//
|
|
// Unlock the entry.
|
|
//
|
|
|
|
NlAssert( ServerSession->SsFlags & SS_LOCKED );
|
|
ServerSession->SsFlags &= ~SS_LOCKED;
|
|
|
|
//
|
|
// If someone wanted to delete the entry while we had it locked,
|
|
// finish the deletion.
|
|
//
|
|
|
|
if ( ServerSession->SsFlags & SS_DELETE_ON_UNLOCK ) {
|
|
NlFreeServerSession( ServerSession );
|
|
//
|
|
// Indicate activity from the BDC
|
|
//
|
|
|
|
} else if (ServerSession->SsFlags & SS_PENDING_BDC) {
|
|
NlQuerySystemTime( &ServerSession->SsLastPulseTime );
|
|
}
|
|
|
|
UNLOCK_SERVER_SESSION_TABLE( ServerSession->SsDomainInfo );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
NlFreeNamedServerSession(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
IN LPWSTR ComputerName,
|
|
IN BOOLEAN AccountBeingDeleted
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the specified entry in the ServerSession Table.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Hosted domain this session is for
|
|
|
|
ComputerName - The name of the computer on the client side of the
|
|
secure channel.
|
|
|
|
AccountBeingDeleted - True to indicate that the account for this server
|
|
session is being deleted.
|
|
|
|
Return Value:
|
|
|
|
An NT status code.
|
|
|
|
--*/
|
|
{
|
|
PSERVER_SESSION ServerSession;
|
|
|
|
LOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
|
|
//
|
|
// Find the entry to delete.
|
|
//
|
|
|
|
ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
|
|
|
|
if ( ServerSession == NULL ) {
|
|
UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If the BDC account is being deleted,
|
|
// indicate that it's OK to delete this session structure.
|
|
//
|
|
|
|
if ( AccountBeingDeleted &&
|
|
(ServerSession->SsFlags & SS_BDC) != 0 ) {
|
|
ServerSession->SsFlags |= SS_BDC_FORCE_DELETE;
|
|
}
|
|
|
|
//
|
|
// Actually delete the entry.
|
|
//
|
|
|
|
NlFreeServerSession( ServerSession );
|
|
|
|
UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NlFreeServerSessionForAccount(
|
|
IN PUNICODE_STRING AccountName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the specified entry in the ServerSession Table.
|
|
|
|
Arguments:
|
|
|
|
AccountName - The name of the Account describing trust relationship being
|
|
deleted.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
WCHAR ComputerName[CNLEN+2]; // Extra for $ and \0
|
|
|
|
//
|
|
// Convert account name to a computer name by stripping the trailing
|
|
// postfix.
|
|
//
|
|
|
|
if ( AccountName->Length + sizeof(WCHAR) > sizeof(ComputerName) ||
|
|
AccountName->Length < SSI_ACCOUNT_NAME_POSTFIX_LENGTH * sizeof(WCHAR)){
|
|
return;
|
|
}
|
|
|
|
RtlCopyMemory( ComputerName, AccountName->Buffer, AccountName->Length );
|
|
ComputerName[ AccountName->Length / sizeof(WCHAR) -
|
|
SSI_ACCOUNT_NAME_POSTFIX_LENGTH ] = L'\0';
|
|
|
|
//
|
|
// Free the named server session (if any)
|
|
//
|
|
|
|
NlFreeNamedServerSession( NlGlobalDomainInfo, ComputerName, TRUE );
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NlServerSessionScavenger(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Scavenge the ServerSession Table.
|
|
|
|
For now, just clean up the SyncContext if a client doesn't use it
|
|
for a while.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Hosted domain to scavenge
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
//
|
|
// Find the next table entry that needs to be scavenged
|
|
//
|
|
|
|
LOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
|
|
for ( ListEntry = DomainInfo->DomServerSessionTable.Flink ;
|
|
ListEntry != &DomainInfo->DomServerSessionTable ;
|
|
) {
|
|
|
|
PSERVER_SESSION ServerSession;
|
|
|
|
ServerSession =
|
|
CONTAINING_RECORD(ListEntry, SERVER_SESSION, SsSeqList);
|
|
|
|
|
|
//
|
|
// Grab a pointer to the next entry before deleting this one
|
|
//
|
|
|
|
ListEntry = ListEntry->Flink;
|
|
|
|
//
|
|
// Increment the number of times this entry has been checked.
|
|
//
|
|
|
|
ServerSession->SsCheck ++;
|
|
|
|
|
|
//
|
|
// If this entry in the Server Session table has been around for many
|
|
// days without the client calling,
|
|
// free it.
|
|
//
|
|
// We wait several days before deleting an old entry. If an entry is
|
|
// deleted, the client has to rediscover us which may cause a lot of
|
|
// net traffic. After several days, that additional traffic isn't
|
|
// significant.
|
|
//
|
|
|
|
if (ServerSession->SsCheck > KILL_SESSION_TIME ) {
|
|
|
|
NlPrintDom((NL_SERVER_SESS, DomainInfo,
|
|
"NlServerSessionScavenger: %s: Free Server Session.\n",
|
|
ServerSession->SsComputerName ));
|
|
|
|
NlFreeServerSession( ServerSession );
|
|
|
|
|
|
//
|
|
// If this entry in the Server Session table has timed out,
|
|
// Clean up the SAM resources.
|
|
//
|
|
|
|
} else if (ServerSession->SsCheck > MAX_WOC_INTERROGATE) {
|
|
|
|
//
|
|
// Clean up the SYNC context for this session freeing up
|
|
// the SAM resources.
|
|
//
|
|
// We shouldn't timeout if the ServerSession Entry is locked,
|
|
// but we'll be careful anyway.
|
|
//
|
|
|
|
if ( (ServerSession->SsFlags & SS_LOCKED) == 0 &&
|
|
ServerSession->SsFlags & SS_BDC ) {
|
|
|
|
if ( ServerSession->SsSync != NULL ) {
|
|
|
|
NlPrintDom((NL_SERVER_SESS, DomainInfo,
|
|
"NlServerSessionScavenger: %s: Cleanup Sync context.\n",
|
|
ServerSession->SsComputerName ));
|
|
|
|
CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
|
|
NetpMemoryFree( ServerSession->SsSync );
|
|
ServerSession->SsSync = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
} // end for
|
|
|
|
UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
|
|
}
|