Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1728 lines
39 KiB

/*++
Copyright (c) 1987-1991 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.
//
#define INITSSI_ALLOCATE // Allocate all ssiinit.h global variables
#include <logonsrv.h> // Include files common to entire service
#undef INITSSI_ALLOCATE
//
// Include files specific to this .c file
//
#include <lmapibuf.h>
#include <lmaudit.h>
#include <lmerr.h>
#include <lmserver.h>
#include <lmshare.h>
#include <tstring.h> // TOUPPER
#define MAX_WOC_INTERROGATE 8 // 2 hours
#define KILL_SESSION_TIME (4*4*24) // 4 Days
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
NlAddBdcServerSession(
IN ULONG ServerRid,
IN PUNICODE_STRING AccountName OPTIONAL,
IN DWORD Flags
)
/*++
Routine Description:
Create a server session to represent this BDC account.
Arguments:
ServerRid - Rid of server to add to list.
AccountName - Optionally specifies the account name of the account.
Flags - Specifies the initial SsFlags to associate with the entry.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
PUNICODE_STRING ServerName;
WCHAR LocalServerName[CNLEN+1];
LONG LocalServerNameSize;
SAMPR_ULONG_ARRAY Use = {0, NULL};
SAMPR_RETURNED_USTRING_ARRAY Names = {0, NULL};
//
// If we were given an account name,
// just use it.
if ( AccountName != NULL ) {
ServerName = AccountName;
//
// Convert the specified ServerRid into a server name.
//
} else {
Status = SamrLookupIdsInDomain(
NlGlobalDBInfoArray[SAM_DB].DBHandle,
1,
&ServerRid,
&Names,
&Use );
if ( !NT_SUCCESS(Status) ) {
Names.Element = NULL;
Use.Element = NULL;
if ( Status = STATUS_NONE_MAPPED ) {
Status = STATUS_SUCCESS;
}
goto Cleanup;
}
NlAssert( Names.Count == 1 );
NlAssert( Names.Element != NULL );
NlAssert( Use.Count == 1 );
NlAssert( Use.Element != NULL );
if( Use.Element[0] != SidTypeUser ) {
Status = STATUS_SUCCESS;
goto Cleanup;
}
ServerName = (PUNICODE_STRING)&Names.Element[0];
}
//
// Build a zero terminated server name.
//
// Strip the trailing postfix.
//
// Ignore servers with malformed names. They aren't really BDCs so don't
// cloud the issue by failing to start netlogon.
//
LocalServerNameSize = ServerName->Length;
if ( (Flags & SS_LM_BDC) == 0 ) {
LocalServerNameSize -= SSI_ACCOUNT_NAME_POSTFIX_LENGTH * sizeof(WCHAR);
}
if ( LocalServerNameSize < 0 ||
LocalServerNameSize + sizeof(WCHAR) > sizeof(LocalServerName) ) {
NlPrint((NL_SERVER_SESS,
"NlAddBdcServerSession: %wZ: Skipping add of invalid server name\n",
ServerName ));
Status = STATUS_SUCCESS;
goto Cleanup;
}
RtlCopyMemory( LocalServerName, ServerName->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,
"NlAddBdcServerSession: " FORMAT_LPWSTR
": Skipping add of ourself\n",
LocalServerName ));
Status = STATUS_SUCCESS;
goto Cleanup;
}
// Always force a pulse to a newly created server.
Status = NlInsertServerSession(
LocalServerName,
Flags | SS_FORCE_PULSE,
ServerRid,
NULL,
NULL );
if ( !NT_SUCCESS(Status) ) {
NlPrint((NL_CRITICAL,
"NlAddBdcServerSession: " FORMAT_LPWSTR
": Couldn't create server session entry (0x%lx)\n",
LocalServerName,
Status ));
goto Cleanup;
}
NlPrint((NL_SERVER_SESS,
"NlAddBdcServerSession: " FORMAT_LPWSTR ": Added %s BDC account\n",
LocalServerName,
(Flags & SS_LM_BDC) ? "LANMAN" : "NT" ));
Status = STATUS_SUCCESS;
Cleanup:
if( Names.Element != NULL ) {
SamIFree_SAMPR_RETURNED_USTRING_ARRAY( &Names );
}
if( Use.Element != NULL ) {
SamIFree_SAMPR_ULONG_ARRAY( &Use );
}
return Status;
}
NTSTATUS
NlBuildLmBdcList(
VOID
)
/*++
Routine Description:
Get the list of all Lanman DC's in this domain from SAM.
Arguments:
None
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
SAMPR_ULONG_ARRAY RelativeIdArray = {0, NULL};
SAMPR_ULONG_ARRAY UseArray = {0, NULL};
RPC_UNICODE_STRING GroupNameString;
SAMPR_HANDLE GroupHandle = NULL;
ULONG ServersGroupRid;
PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
ULONG i;
//
// Determine the RID of the Servers group.
//
RtlInitUnicodeString( (PUNICODE_STRING)&GroupNameString,
SSI_SERVER_GROUP_W );
Status = SamrLookupNamesInDomain(
NlGlobalDBInfoArray[SAM_DB].DBHandle,
1,
&GroupNameString,
&RelativeIdArray,
&UseArray );
if ( !NT_SUCCESS(Status) ) {
RelativeIdArray.Element = NULL;
UseArray.Element = NULL;
// Its OK if the SERVERS group doesn't exist
if ( Status == STATUS_NONE_MAPPED ) {
Status = STATUS_SUCCESS;
}
goto Cleanup;
}
//
// We should get back exactly one entry of info back.
//
NlAssert( UseArray.Count == 1 );
NlAssert( UseArray.Element != NULL );
NlAssert( RelativeIdArray.Count == 1 );
NlAssert( RelativeIdArray.Element != NULL );
if ( UseArray.Element[0] != SidTypeGroup ) {
Status = STATUS_SUCCESS;
goto Cleanup;
}
ServersGroupRid = RelativeIdArray.Element[0];
//
// Open the SERVERS group
//
Status = SamrOpenGroup( NlGlobalDBInfoArray[SAM_DB].DBHandle,
0, // No desired access
ServersGroupRid,
&GroupHandle );
if ( !NT_SUCCESS(Status) ) {
GroupHandle = NULL;
goto Cleanup;
}
//
// Enumerate members in the SERVERS group.
//
Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
if (!NT_SUCCESS(Status)) {
MembersBuffer = NULL;
goto Cleanup;
}
//
// For each member of the SERVERS group,
// add an entry in the downlevel servers table.
//
for ( i=0; i < MembersBuffer->MemberCount; i++ ) {
Status = NlAddBdcServerSession( MembersBuffer->Members[i],
NULL,
SS_BDC | SS_LM_BDC );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
}
//
// Success
//
Status = STATUS_SUCCESS;
//
// Free locally used resources.
//
Cleanup:
SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
if ( MembersBuffer != NULL ) {
SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
}
if( GroupHandle != NULL ) {
(VOID) SamrCloseHandle( &GroupHandle );
}
return Status;
}
//
// Number of machine accounts read from SAM on each call
//
#define MACHINES_PER_PASS 250
NTSTATUS
NlBuildNtBdcList(
VOID
)
/*++
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;
//
// 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;
//
// Get the list of machine accounts from SAM
//
SamStatus = SamrQueryDisplayInformation(
NlGlobalDBInfoArray[SAM_DB].DBHandle,
DomainDisplayMachine,
SamIndex,
MACHINES_PER_PASS,
0xFFFFFFFF,
&TotalBytesAvailable,
&BytesReturned,
&DisplayInformation );
if ( !NT_SUCCESS(SamStatus) ) {
NlPrint((NL_CRITICAL,
"SamrQueryDisplayInformation failed: 0x%08lx\n",
Status));
Status = SamStatus;
goto Cleanup;
}
MachineInformation = (PDOMAIN_DISPLAY_MACHINE)
DisplayInformation.MachineInformation.Buffer;
EntriesRead = DisplayInformation.MachineInformation.EntriesRead;
//
// Set up for the next call to Sam.
//
if ( SamStatus == STATUS_MORE_ENTRIES ) {
SamIndex = MachineInformation[EntriesRead-1].Index + 1;
}
//
// Loop though the list of machine accounts finding the Server accounts.
//
for ( i=0; i<EntriesRead; i++ ) {
//
// Ensure the machine account is a server account.
//
if ( MachineInformation[i].AccountControl &
USER_SERVER_TRUST_ACCOUNT ) {
//
// Insert the server session.
//
Status = NlAddBdcServerSession(
MachineInformation[i].Rid,
&MachineInformation[i].Machine,
SS_BDC );
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;
}
VOID
NlTransportOpen(
VOID
)
/*++
Routine Description:
Initialize the list of transports
Arguments:
None
Return Value:
Status of the operation
--*/
{
NET_API_STATUS NetStatus;
PSERVER_TRANSPORT_INFO_0 TransportInfo0;
DWORD EntriesRead;
DWORD TotalEntries;
DWORD i;
DWORD BufferSize;
LPBYTE Where;
NlGlobalTransportCount = 0;
//
// Enumerate the transports supported by the server.
//
NetStatus = NetServerTransportEnum(
NULL, // local
0, // level 0
(LPBYTE *) &TransportInfo0,
0xFFFFFFFF, // PrefMaxLength
&EntriesRead,
&TotalEntries,
NULL ); // No resume handle
if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
NlPrint(( NL_CRITICAL, "Cannot NetServerTransportEnum %ld\n", NetStatus ));
return;
}
if ( EntriesRead == 0 ) {
NlPrint(( NL_CRITICAL, "NetServerTransportEnum returned 0 entries\n" ));
(VOID) NetApiBufferFree( TransportInfo0 );
return;
}
//
// Allocate a buffer to contain just the transport names.
//
BufferSize = 0;
for ( i=0; i<EntriesRead; i++ ) {
BufferSize += sizeof(LPWSTR) +
wcslen(TransportInfo0[i].svti0_transportname) * sizeof(WCHAR) +
sizeof(WCHAR);
}
NlGlobalTransportList = NetpMemoryAllocate( BufferSize );
if ( NlGlobalTransportList == NULL ) {
NlPrint(( NL_CRITICAL, "NlTransportOpen: no memory\n" ));
(VOID) NetApiBufferFree( TransportInfo0 );
return;
}
//
// Copy the transport names into the buffer.
//
Where = (LPBYTE)(&NlGlobalTransportList[EntriesRead]);
for ( i=0; i<EntriesRead; i++ ) {
DWORD Size;
NlGlobalTransportList[i] = (LPWSTR) Where;
Size = wcslen(TransportInfo0[i].svti0_transportname) * sizeof(WCHAR) +
sizeof(WCHAR);
RtlCopyMemory( Where,
TransportInfo0[i].svti0_transportname,
Size );
Where += Size;
NlPrint(( NL_SERVER_SESS, "Server Transport %ld: " FORMAT_LPWSTR "\n",
i,
TransportInfo0[i].svti0_transportname ));
}
NlGlobalTransportCount = EntriesRead;
(VOID) NetApiBufferFree( TransportInfo0 );
return;
}
LPWSTR
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 name
--*/
{
DWORD i;
//
// If we're not initialized yet,
// just return
//
if ( TransportName == NULL || NlGlobalTransportCount == 0 ) {
return NULL;
}
//
// Find this transport in the list of transports.
//
for ( i=0; i<NlGlobalTransportCount; i++ ) {
if ( _wcsicmp( TransportName, NlGlobalTransportList[i] ) == 0 ) {
return NlGlobalTransportList[i];
}
}
return NULL;
}
LPWSTR
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 name
--*/
{
NET_API_STATUS NetStatus;
PSESSION_INFO_502 SessionInfo502;
DWORD EntriesRead;
DWORD TotalEntries;
DWORD i;
DWORD BestTime;
DWORD BestEntry;
LPWSTR TransportName;
WCHAR UncClientName[UNCLEN+1];
//
// If we're not initialized yet,
// just return
//
if ( NlGlobalTransportCount == 0 ) {
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 ) {
TransportName = NlTransportLookupTransportName(
SessionInfo502[BestEntry].sesi502_transport );
if ( TransportName == 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,
TransportName ));
}
} else {
TransportName = NULL;
}
(VOID) NetApiBufferFree( SessionInfo502 );
return TransportName;
}
VOID
NlTransportClose(
VOID
)
/*++
Routine Description:
Free the list of transports
Arguments:
None
Return Value:
Status of the operation
--*/
{
NetpMemoryFree( NlGlobalTransportList );
NlGlobalTransportList = NULL;
NlGlobalTransportCount = 0;
}
NTSTATUS
NlInitSSI(
VOID
)
/*++
Routine Description:
Allocate and Initialize SSI related data structures. It will
allocate two data structures: one to hold the hash table of pointers
(to linked list of member entries) and another to to serve as memory
pool.
Arguments:
None.
Return Value:
NT Status Code
--*/
{
DWORD i;
NTSTATUS Status;
//
// Initialize the replicator critical section.
//
// InitializeCriticalSection( &NlGlobalReplicatorCritSect );
// InitializeCriticalSection( &NlGlobalTrustListCritSect );
InitializeCriticalSection( &NlGlobalServerSessionTableCritSect );
NlGlobalSSICritSectInit = TRUE;
//
// Allocate NlGlobalServerSessionHashTable on DCs
//
LOCK_SERVER_SESSION_TABLE();
NlGlobalServerSessionHashTable = (PLIST_ENTRY)
NetpMemoryAllocate( sizeof(LIST_ENTRY) *SERVER_SESSION_HASH_TABLE_SIZE);
if ( NlGlobalServerSessionHashTable == NULL ) {
UNLOCK_SERVER_SESSION_TABLE();
return STATUS_NO_MEMORY;
}
for ( i=0; i< SERVER_SESSION_HASH_TABLE_SIZE; i++ ) {
InitializeListHead( &NlGlobalServerSessionHashTable[i] );
}
UNLOCK_SERVER_SESSION_TABLE();
//
// On the PDC,
// Initialize the server session table to contain all the BDCs.
//
if ( NlGlobalRole == RolePrimary ) {
Status = NlBuildLmBdcList();
if ( NT_SUCCESS(Status) ) {
Status = NlBuildNtBdcList();
}
} else {
Status = STATUS_SUCCESS;
}
//
// Build a list of transports for later reference
//
NlTransportOpen();
return Status;
}
PSERVER_SESSION
NlFindNamedServerSession(
IN LPWSTR ComputerName
)
/*++
Routine Description:
Find the specified entry in the Server Session Table.
Enter with the ServerSessionTable Sem locked
Arguments:
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 (NlGlobalServerSessionHashTable == 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 = NlGlobalServerSessionHashTable[Index].Flink ;
ListEntry != &NlGlobalServerSessionHashTable[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;
}
NTSTATUS
NlInsertServerSession(
IN LPWSTR ComputerName,
IN DWORD Flags,
IN ULONG AccountRid,
IN PNETLOGON_CREDENTIAL AuthenticationSeed OPTIONAL,
IN PNETLOGON_CREDENTIAL AuthenticationResponse 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 requests a challenge). 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:
ComputerName - The name of the computer on the client side 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 - If this is a BDC session, this specifies the RID of the
server account.
AuthenticationSeed - Specifies the Initial Authentication Seed
to associate with the entry. Specified only if this call is
being made as result of a challenge request
(e.g. NetrServerRequestChallenge)
AuthenticationResponse - Specifies the Initial Authentication Response from
the remote system to associate with the entry. Specified only if
this call is being made as result of a challenge request
(e.g. NetrServerRequestChallenge)
Return Value:
NT STATUS code.
--*/
{
NTSTATUS Status;
PSERVER_SESSION ServerSession;
LOCK_SERVER_SESSION_TABLE();
//
// If the is no current Server Session table entry,
// allocate one.
//
ServerSession = NlFindNamedServerSession(ComputerName);
if (ServerSession == NULL) {
DWORD Index;
ULONG ComputerNameSize;
//
// Allocate the ServerSession Entry
//
ServerSession = NetpMemoryAllocate( sizeof(SERVER_SESSION) );
if (ServerSession == NULL) {
UNLOCK_SERVER_SESSION_TABLE();
return STATUS_NO_MEMORY;
}
RtlZeroMemory( ServerSession, sizeof(SERVER_SESSION) );
//
// Fill in the fields of the ServerSession entry.
//
ServerSession->SsSecureChannelType = NullSecureChannel;
ServerSession->SsSync = NULL;
InitializeListHead( &ServerSession->SsBdcList );
InitializeListHead( &ServerSession->SsPendingBdcList );
//
// 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 );
UNLOCK_SERVER_SESSION_TABLE();
return Status;
}
ServerSession->SsComputerName[ComputerNameSize] = '\0';
//
// 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( &NlGlobalServerSessionHashTable[Index],
&ServerSession->SsHashList );
//
// Link this entry onto the tail of the Sequential ServerSessionTable.
//
InsertTailList( &NlGlobalServerSessionTable, &ServerSession->SsSeqList );
//
// Beware of server with two concurrent calls outstanding
// (must have rebooted.)
//
} else {
if (ServerSession->SsFlags & SS_LOCKED ) {
UNLOCK_SERVER_SESSION_TABLE();
NlPrint((NL_CRITICAL,
"NlInsertServerSession: Concurrent call detected.\n" ));
return STATUS_ACCESS_DENIED;
}
}
//
// Initialize BDC specific fields.
//
if ( Flags & SS_BDC ) {
//
// If we've already have an account for this BDC,
// Warn that there are multiple accounts.
//
if ( ServerSession->SsFlags & SS_BDC ) {
LPWSTR MsgStrings[1];
NlPrint((NL_CRITICAL,
"NlInsertServerSession: %s: has multiple machine accounts.\n",
ServerSession->SsComputerName ));
MsgStrings[0] = ComputerName;
NlpWriteEventlog(
NELOG_NetlogonDuplicateMachineAccounts,
EVENTLOG_ERROR_TYPE,
NULL,
0,
MsgStrings,
1 );
} else {
//
// Insert this entry at the front of the list of BDCs
//
InsertHeadList( &NlGlobalBdcServerSessionList,
&ServerSession->SsBdcList );
NlGlobalBdcServerSessionCount ++;
}
if ( Flags & SS_LM_BDC ) {
NlAssert( ServerSession->SsLmBdcAccountRid == 0 );
ServerSession->SsLmBdcAccountRid = AccountRid;
} else {
NlAssert( ServerSession->SsNtBdcAccountRid == 0 );
ServerSession->SsNtBdcAccountRid = AccountRid;
}
}
//
// Update the Server Session entry to reflect this new secure channel setup
//
ServerSession->SsCheck = 0;
ServerSession->SsSecureChannelType = NullSecureChannel;
ServerSession->SsNegotiatedFlags = 0;
ServerSession->SsTransportName = NULL;
ServerSession->SsFlags = ((USHORT) Flags) |
(ServerSession->SsFlags & SS_PERMANENT_FLAGS);
if ( AuthenticationSeed != NULL ) {
ServerSession->SsAuthenticationSeed = *AuthenticationSeed;
}
if ( AuthenticationResponse != NULL ) {
NlAssert( sizeof(*AuthenticationResponse) <= sizeof(ServerSession->SsSessionKey ));
RtlCopyMemory( &ServerSession->SsSessionKey,
AuthenticationResponse,
sizeof( *AuthenticationResponse ) );
}
UNLOCK_SERVER_SESSION_TABLE();
return STATUS_SUCCESS;
}
VOID
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:
--*/
{
//
// 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;
NlPrint((NL_SERVER_SESS,
"NlFreeServerSession: %s: Tried to free locked server session\n",
ServerSession->SsComputerName ));
return;
}
//
// If this entry represents a BDC account,
// don't delete the entry until the account is deleted.
//
if ( ServerSession->SsLmBdcAccountRid != 0 ||
ServerSession->SsNtBdcAccountRid != 0 ) {
NlPrint((NL_SERVER_SESS,
"NlFreeServerSession: %s: Didn't delete server session with BDC account.\n",
ServerSession->SsComputerName ));
return;
}
NlPrint((NL_SERVER_SESS,
"NlFreeServerSession: %s: Freed server session\n",
ServerSession->SsComputerName ));
//
// Delink the entry from the hash list.
//
RemoveEntryList( &ServerSession->SsHashList );
//
// 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 );
}
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();
//
// 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) {
(VOID) NtQuerySystemTime( &ServerSession->SsLastPulseTime );
}
UNLOCK_SERVER_SESSION_TABLE();
}
VOID
NlFreeLmBdcServerSession(
IN ULONG ServerRid
)
/*++
Routine Description:
Delete the specified Server Account from the Server Session list.
Arguments:
ServerRid - Rid of server to add to list.
Return Value:
None
--*/
{
PSERVER_SESSION ServerSession;
PLIST_ENTRY ListEntry;
LOCK_SERVER_SESSION_TABLE();
//
// Ensure the ServerSession Table is initialized.
//
if (NlGlobalServerSessionHashTable == NULL) {
return;
}
//
// Loop through the BDC list trying the find the right entry.
//
for ( ListEntry = NlGlobalBdcServerSessionList.Flink ;
ListEntry != &NlGlobalBdcServerSessionList ;
ListEntry = ListEntry->Flink) {
ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsBdcList );
if ( ServerRid == ServerSession->SsLmBdcAccountRid ) {
break;
}
}
if ( ListEntry == &NlGlobalBdcServerSessionList ) {
UNLOCK_SERVER_SESSION_TABLE();
NlPrint((NL_CRITICAL,
"NlFreeLmBdcServerSession: %lx: Couldn't find"
" server session entry for this RID.\n",
ServerRid ));
return;
}
//
// Clear the Account Rid so the ServerSession entry will be deleted
//
ServerSession->SsLmBdcAccountRid = 0;
//
// Actually delete the entry.
//
NlFreeServerSession( ServerSession );
UNLOCK_SERVER_SESSION_TABLE();
}
VOID
NlFreeNamedServerSession(
IN LPWSTR ComputerName,
IN BOOLEAN AccountBeingDeleted
)
/*++
Routine Description:
Frees the specified entry in the ServerSession Table.
Arguments:
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();
//
// Find the entry to delete.
//
ServerSession = NlFindNamedServerSession( ComputerName );
if ( ServerSession == NULL ) {
UNLOCK_SERVER_SESSION_TABLE();
return;
}
//
// If the account is being deleted,
// clear the account RID to allow the session structure to be deleted.
//
// (We might be deleting an workstation or trusted domain account here
// but that doesn't make any difference. In those cases, the account rid
// is already zero.)
//
if ( AccountBeingDeleted ) {
ServerSession->SsNtBdcAccountRid = 0;
}
//
// Actually delete the entry.
//
NlFreeServerSession( ServerSession );
UNLOCK_SERVER_SESSION_TABLE();
}
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( ComputerName, TRUE );
}
VOID
NlServerSessionScavenger(
VOID
)
/*++
Routine Description:
Scavenge the ServerSession Table.
For now, just clean up the SyncContext if a client doesn't use it
for a while.
Arguments:
None.
Return Value:
None.
--*/
{
PLIST_ENTRY ListEntry;
//
// Find the next table entry that needs to be scavenged
//
LOCK_SERVER_SESSION_TABLE();
for ( ListEntry = NlGlobalServerSessionTable.Flink ;
ListEntry != &NlGlobalServerSessionTable ;
) {
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 ) {
NlPrint((NL_SERVER_SESS,
"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 ) {
NlPrint((NL_SERVER_SESS,
"NlServerSessionScavenger: %s: Cleanup Sync context.\n",
ServerSession->SsComputerName ));
CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
NetpMemoryFree( ServerSession->SsSync );
ServerSession->SsSync = NULL;
}
}
}
} // end while
UNLOCK_SERVER_SESSION_TABLE();
}