/*++ 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 // Include files common to entire service #undef INITSSI_ALLOCATE // // Include files specific to this .c file // #include #include #include #include #include #include // 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 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(); }