Source code of Windows XP (NT5)
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.

3228 lines
81 KiB

  1. /*++
  2. Copyright (c) 1987-1996 Microsoft Corporation
  3. Module Name:
  4. srvsess.c
  5. Abstract:
  6. Routines for managing the ServerSession structure.
  7. Author:
  8. Ported from Lan Man 2.0
  9. Environment:
  10. User mode only.
  11. Contains NT-specific code.
  12. Requires ANSI C extensions: slash-slash comments, long external names.
  13. Revision History:
  14. 12-Jul-1991 (cliffv)
  15. Ported to NT. Converted to NT style.
  16. --*/
  17. //
  18. // Common include files.
  19. //
  20. #include "logonsrv.h" // Include files common to entire service
  21. #pragma hdrstop
  22. //
  23. // Include files specific to this .c file
  24. //
  25. #include <lmaudit.h>
  26. #include <lmshare.h>
  27. #include <nbtioctl.h>
  28. #include <kerberos.h> // KERB_UPDATE_ADDRESSES_REQUEST
  29. #define MAX_WOC_INTERROGATE 8 // 2 hours
  30. #define KILL_SESSION_TIME (4*4*24) // 4 Days
  31. //
  32. // Registry key where SocketAddressList is saved across reboots
  33. //
  34. #define NETLOGON_KEYWORD_SOCKETADDRESSLIST TEXT("SocketAddressList")
  35. DWORD
  36. NlGetHashVal(
  37. IN LPSTR UpcaseOemComputerName,
  38. IN DWORD HashTableSize
  39. )
  40. /*++
  41. Routine Description:
  42. Generate a HashTable index for the specified ComputerName.
  43. Notice that all sessions for a particular ComputerName hash to the same
  44. value. The ComputerName make a suitable hash key all by itself.
  45. Also, at times we visit all the session entries for a particular
  46. ComputerName. By using only the ComputerName as the hash key, I
  47. can limit my search to the single hash chain.
  48. Arguments:
  49. UpcaseOemComputerName - The upper case OEM name of the computer on
  50. the client side of the secure channel setup.
  51. HashTableSize - Number of entries in the hash table (must be a power of 2)
  52. Return Value:
  53. Returns an index into the HashTable.
  54. --*/
  55. {
  56. UCHAR c;
  57. DWORD value = 0;
  58. while (c = *UpcaseOemComputerName++) {
  59. value += (DWORD) c;
  60. }
  61. return (value & (HashTableSize-1));
  62. }
  63. NTSTATUS
  64. NlGetTdoNameHashVal(
  65. IN PUNICODE_STRING TdoName,
  66. OUT PUNICODE_STRING CanonicalTdoName,
  67. OUT PULONG HashIndex
  68. )
  69. /*++
  70. Routine Description:
  71. Generate a HashTable index for the specified TdoName
  72. Arguments:
  73. TdoName - The name of the TDO this secure channel is for
  74. CanonicalTdoName - Returns the canonical TDO name corresponding to TdoName
  75. The caller must free this buffer using RtlFreeUnicodeString
  76. HashIndex - Returns the index into the DomServerSessionTdoNameHashTable
  77. Return Value:
  78. Status of the operation
  79. --*/
  80. {
  81. NTSTATUS Status;
  82. ULONG Index;
  83. WCHAR c;
  84. DWORD value = 0;
  85. //
  86. // Convert the TdoName to lower case to ensure all versions hash to the same value
  87. //
  88. Status = RtlDowncaseUnicodeString(
  89. CanonicalTdoName,
  90. TdoName,
  91. TRUE );
  92. if ( !NT_SUCCESS(Status) ) {
  93. return Status;
  94. }
  95. //
  96. // Canonicalize the TdoName
  97. //
  98. //
  99. // Ditch the trailing . from DNS names
  100. //
  101. if ( CanonicalTdoName->Length > sizeof(WCHAR) &&
  102. CanonicalTdoName->Buffer[(CanonicalTdoName->Length-sizeof(WCHAR))/sizeof(WCHAR)] == L'.' ) {
  103. CanonicalTdoName->Length -= sizeof(WCHAR);
  104. CanonicalTdoName->MaximumLength -= sizeof(WCHAR);
  105. }
  106. //
  107. // Compute the hash
  108. //
  109. for ( Index=0; Index < (CanonicalTdoName->Length/sizeof(WCHAR)); Index++ ) {
  110. value += (DWORD) CanonicalTdoName->Buffer[Index];
  111. }
  112. *HashIndex = (value & (SERVER_SESSION_TDO_NAME_HASH_TABLE_SIZE-1));
  113. return STATUS_SUCCESS;
  114. }
  115. NTSTATUS
  116. NlCheckServerSession(
  117. IN ULONG ServerRid,
  118. IN PUNICODE_STRING AccountName,
  119. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType
  120. )
  121. /*++
  122. Routine Description:
  123. Create a server session to represent this BDC account.
  124. Arguments:
  125. ServerRid - Rid of server to add to list.
  126. AccountName - Specifies the account name of the account.
  127. SecureChannelType - Specifies the secure channel type of the account.
  128. Return Value:
  129. Status of the operation.
  130. --*/
  131. {
  132. NTSTATUS Status;
  133. WCHAR LocalServerName[CNLEN+1];
  134. LONG LocalServerNameSize;
  135. PSERVER_SESSION ServerSession;
  136. //
  137. // Build a zero terminated server name.
  138. //
  139. // Strip the trailing postfix.
  140. //
  141. // Ignore servers with malformed names. They aren't really DCs so don't
  142. // cloud the issue by failing to start netlogon.
  143. //
  144. LOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  145. LocalServerNameSize = AccountName->Length -
  146. SSI_ACCOUNT_NAME_POSTFIX_LENGTH * sizeof(WCHAR);
  147. if ( LocalServerNameSize < 0 ||
  148. LocalServerNameSize + sizeof(WCHAR) > sizeof(LocalServerName) ) {
  149. NlPrint((NL_SERVER_SESS,
  150. "NlCheckServerSession: %wZ: Skipping add of invalid server name\n",
  151. AccountName ));
  152. Status = STATUS_SUCCESS;
  153. goto Cleanup;
  154. }
  155. if ( AccountName->Buffer[LocalServerNameSize / sizeof(WCHAR)] != SSI_ACCOUNT_NAME_POSTFIX_CHAR ) {
  156. NlPrint((NL_SERVER_SESS,
  157. "NlCheckServerSession: %wZ: Skipping add of server name without $\n",
  158. AccountName ));
  159. Status = STATUS_SUCCESS;
  160. goto Cleanup;
  161. }
  162. RtlCopyMemory( LocalServerName, AccountName->Buffer, LocalServerNameSize );
  163. LocalServerName[ LocalServerNameSize / sizeof(WCHAR) ] = L'\0';
  164. //
  165. // Don't add ourselves to the list.
  166. //
  167. if ( NlNameCompare( LocalServerName,
  168. NlGlobalUnicodeComputerName,
  169. NAMETYPE_COMPUTER ) == 0 ) {
  170. NlPrint((NL_SERVER_SESS,
  171. "NlCheckServerSession: " FORMAT_LPWSTR
  172. ": Skipping add of ourself\n",
  173. LocalServerName ));
  174. Status = STATUS_SUCCESS;
  175. goto Cleanup;
  176. }
  177. //
  178. // Check that any existing secure channel has the secure channel type.
  179. //
  180. ServerSession = NlFindNamedServerSession( NlGlobalDomainInfo, LocalServerName);
  181. if (ServerSession != NULL) {
  182. //
  183. // If the type is wrong,
  184. // ditch the server session.
  185. //
  186. if ( ServerSession->SsSecureChannelType != NullSecureChannel &&
  187. ServerSession->SsSecureChannelType != SecureChannelType ) {
  188. NlPrint((NL_SERVER_SESS,
  189. "NlCheckServerSession: %ws: Server session of type %ld already exists (deleting it)\n",
  190. LocalServerName,
  191. ServerSession->SsSecureChannelType ));
  192. NlFreeNamedServerSession( NlGlobalDomainInfo, LocalServerName, TRUE );
  193. }
  194. }
  195. //
  196. // On a PDC,
  197. // pre-create the server session structure so the PDC can keep track of
  198. // its BDCs.
  199. //
  200. if ( SecureChannelType == ServerSecureChannel &&
  201. NlGlobalDomainInfo->DomRole == RolePrimary ) {
  202. // Always force a pulse to a newly created server.
  203. Status = NlInsertServerSession(
  204. NlGlobalDomainInfo,
  205. LocalServerName,
  206. NULL, // not an interdomain trust account
  207. NullSecureChannel,
  208. SS_FORCE_PULSE | SS_BDC,
  209. ServerRid,
  210. 0, // negotiated flags
  211. NULL, // transport
  212. NULL, // session key
  213. NULL ); // authentication seed
  214. if ( !NT_SUCCESS(Status) ) {
  215. NlPrint((NL_CRITICAL,
  216. "NlCheckServerSession: " FORMAT_LPWSTR
  217. ": Couldn't create server session entry (0x%lx)\n",
  218. LocalServerName,
  219. Status ));
  220. goto Cleanup;
  221. }
  222. NlPrint((NL_SERVER_SESS,
  223. "NlCheckServerSession: " FORMAT_LPWSTR ": Added NT BDC account\n",
  224. LocalServerName ));
  225. }
  226. Status = STATUS_SUCCESS;
  227. Cleanup:
  228. UNLOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  229. return Status;
  230. }
  231. NTSTATUS
  232. NlBuildLmBdcList(
  233. PDOMAIN_INFO DomainInfo
  234. )
  235. /*++
  236. Routine Description:
  237. Get the list of all Lanman DC's in this domain from SAM.
  238. Log an event warning that such DCs are not supported.
  239. Arguments:
  240. DomainInfo - Domain Bdc list is to be enumerated for
  241. Return Value:
  242. Status of the operation.
  243. --*/
  244. {
  245. NTSTATUS Status;
  246. SAMPR_ULONG_ARRAY RelativeIdArray = {0, NULL};
  247. SAMPR_ULONG_ARRAY UseArray = {0, NULL};
  248. RPC_UNICODE_STRING GroupNameString;
  249. SAMPR_HANDLE GroupHandle = NULL;
  250. ULONG ServersGroupRid;
  251. PSAMPR_GET_MEMBERS_BUFFER MembersBuffer = NULL;
  252. ULONG i;
  253. //
  254. // Determine the RID of the Servers group.
  255. //
  256. RtlInitUnicodeString( (PUNICODE_STRING)&GroupNameString,
  257. SSI_SERVER_GROUP_W );
  258. Status = SamrLookupNamesInDomain(
  259. DomainInfo->DomSamAccountDomainHandle,
  260. 1,
  261. &GroupNameString,
  262. &RelativeIdArray,
  263. &UseArray );
  264. if ( !NT_SUCCESS(Status) ) {
  265. RelativeIdArray.Element = NULL;
  266. UseArray.Element = NULL;
  267. // Its OK if the SERVERS group doesn't exist
  268. if ( Status == STATUS_NONE_MAPPED ) {
  269. Status = STATUS_SUCCESS;
  270. }
  271. goto Cleanup;
  272. }
  273. //
  274. // We should get back exactly one entry of info back.
  275. //
  276. NlAssert( UseArray.Count == 1 );
  277. NlAssert( UseArray.Element != NULL );
  278. NlAssert( RelativeIdArray.Count == 1 );
  279. NlAssert( RelativeIdArray.Element != NULL );
  280. if ( UseArray.Element[0] != SidTypeGroup ) {
  281. Status = STATUS_SUCCESS;
  282. goto Cleanup;
  283. }
  284. ServersGroupRid = RelativeIdArray.Element[0];
  285. //
  286. // Open the SERVERS group
  287. //
  288. Status = SamrOpenGroup( DomainInfo->DomSamAccountDomainHandle,
  289. 0, // No desired access
  290. ServersGroupRid,
  291. &GroupHandle );
  292. if ( !NT_SUCCESS(Status) ) {
  293. GroupHandle = NULL;
  294. goto Cleanup;
  295. }
  296. //
  297. // Enumerate members in the SERVERS group.
  298. //
  299. Status = SamrGetMembersInGroup( GroupHandle, &MembersBuffer );
  300. if (!NT_SUCCESS(Status)) {
  301. MembersBuffer = NULL;
  302. goto Cleanup;
  303. }
  304. //
  305. // For each member of the SERVERS group,
  306. // add an entry in the downlevel servers table.
  307. //
  308. if ( MembersBuffer->MemberCount > 0 ) {
  309. LPWSTR MsgStrings[1];
  310. NlPrintDom((NL_CRITICAL, DomainInfo,
  311. "NlBuildLmBdcList: %s: Members of SERVERS group detected (and ignored).\n" ));
  312. MsgStrings[0] = DomainInfo->DomUnicodeDomainName;
  313. NlpWriteEventlog(
  314. NELOG_NetlogonLanmanBdcsNotAllowed,
  315. EVENTLOG_ERROR_TYPE,
  316. NULL,
  317. 0,
  318. MsgStrings,
  319. 1 );
  320. }
  321. //
  322. // Success
  323. //
  324. Status = STATUS_SUCCESS;
  325. //
  326. // Free locally used resources.
  327. //
  328. Cleanup:
  329. SamIFree_SAMPR_ULONG_ARRAY( &RelativeIdArray );
  330. SamIFree_SAMPR_ULONG_ARRAY( &UseArray );
  331. if ( MembersBuffer != NULL ) {
  332. SamIFree_SAMPR_GET_MEMBERS_BUFFER( MembersBuffer );
  333. }
  334. if( GroupHandle != NULL ) {
  335. (VOID) SamrCloseHandle( &GroupHandle );
  336. }
  337. return Status;
  338. }
  339. //
  340. // Number of machine accounts read from SAM on each call
  341. //
  342. #define MACHINES_PER_PASS 250
  343. NTSTATUS
  344. NlBuildNtBdcList(
  345. PDOMAIN_INFO DomainInfo
  346. )
  347. /*++
  348. Routine Description:
  349. Get the list of all Nt Bdc DC's in this domain from SAM.
  350. Arguments:
  351. None
  352. Return Value:
  353. Status of the operation.
  354. --*/
  355. {
  356. NTSTATUS Status;
  357. NTSTATUS SamStatus;
  358. SAMPR_DISPLAY_INFO_BUFFER DisplayInformation;
  359. PDOMAIN_DISPLAY_MACHINE MachineInformation = NULL;
  360. ULONG SamIndex;
  361. BOOL UseDisplayServer = TRUE;
  362. //
  363. // Loop building a list of BDC names from SAM.
  364. //
  365. // On each iteration of the loop,
  366. // get the next several machine accounts from SAM.
  367. // determine which of those names are DC names.
  368. // Merge the DC names into the list we're currently building of all DCs.
  369. //
  370. SamIndex = 0;
  371. DisplayInformation.MachineInformation.Buffer = NULL;
  372. do {
  373. //
  374. // Arguments to SamrQueryDisplayInformation
  375. //
  376. ULONG TotalBytesAvailable;
  377. ULONG BytesReturned;
  378. ULONG EntriesRead;
  379. DWORD i;
  380. //
  381. // Sam is so slow that we want to avoid having the service controller time us out
  382. if ( !GiveInstallHints( FALSE ) ) {
  383. return STATUS_NO_MEMORY;
  384. }
  385. //
  386. // Get the list of machine accounts from SAM
  387. //
  388. NlPrint((NL_SESSION_MORE,
  389. "SamrQueryDisplayInformation with index: %ld\n",
  390. SamIndex ));
  391. if ( UseDisplayServer ) {
  392. SamStatus = SamrQueryDisplayInformation(
  393. DomainInfo->DomSamAccountDomainHandle,
  394. DomainDisplayServer,
  395. SamIndex,
  396. MACHINES_PER_PASS,
  397. 0xFFFFFFFF,
  398. &TotalBytesAvailable,
  399. &BytesReturned,
  400. &DisplayInformation );
  401. // If this PDC is running a registry based SAM (as in the case of
  402. // upgrade from NT 4.0), avoid DomainDisplayServer.
  403. if ( SamStatus == STATUS_INVALID_INFO_CLASS ) {
  404. UseDisplayServer = FALSE;
  405. }
  406. }
  407. if ( !UseDisplayServer ) {
  408. SamStatus = SamrQueryDisplayInformation(
  409. DomainInfo->DomSamAccountDomainHandle,
  410. DomainDisplayMachine,
  411. SamIndex,
  412. MACHINES_PER_PASS,
  413. 0xFFFFFFFF,
  414. &TotalBytesAvailable,
  415. &BytesReturned,
  416. &DisplayInformation );
  417. }
  418. if ( !NT_SUCCESS(SamStatus) ) {
  419. Status = SamStatus;
  420. NlPrint((NL_CRITICAL,
  421. "SamrQueryDisplayInformation failed: 0x%08lx\n",
  422. Status));
  423. goto Cleanup;
  424. }
  425. MachineInformation = (PDOMAIN_DISPLAY_MACHINE)
  426. DisplayInformation.MachineInformation.Buffer;
  427. EntriesRead = DisplayInformation.MachineInformation.EntriesRead;
  428. NlPrint((NL_SESSION_MORE,
  429. "SamrQueryDisplayInformation Completed: 0x%08lx %ld\n",
  430. SamStatus,
  431. EntriesRead ));
  432. //
  433. // Set up for the next call to Sam.
  434. //
  435. if ( SamStatus == STATUS_MORE_ENTRIES ) {
  436. SamIndex = MachineInformation[EntriesRead-1].Index;
  437. }
  438. //
  439. // Loop though the list of machine accounts finding the Server accounts.
  440. //
  441. for ( i=0; i<EntriesRead; i++ ) {
  442. NlPrint((NL_SESSION_MORE,
  443. "%ld %ld %wZ 0x%lx 0x%lx\n",
  444. i,
  445. MachineInformation[i].Index,
  446. &MachineInformation[i].Machine,
  447. MachineInformation[i].AccountControl,
  448. MachineInformation[i].Rid ));
  449. //
  450. // Ensure the machine account is a server account.
  451. //
  452. if ( MachineInformation[i].AccountControl &
  453. USER_SERVER_TRUST_ACCOUNT ) {
  454. //
  455. // Insert the server session.
  456. //
  457. Status = NlCheckServerSession(
  458. MachineInformation[i].Rid,
  459. &MachineInformation[i].Machine,
  460. ServerSecureChannel );
  461. if ( !NT_SUCCESS(Status) ) {
  462. goto Cleanup;
  463. }
  464. }
  465. }
  466. //
  467. // Free the buffer returned from SAM.
  468. //
  469. SamIFree_SAMPR_DISPLAY_INFO_BUFFER( &DisplayInformation,
  470. DomainDisplayMachine );
  471. DisplayInformation.MachineInformation.Buffer = NULL;
  472. } while ( SamStatus == STATUS_MORE_ENTRIES );
  473. //
  474. // Success
  475. //
  476. Status = STATUS_SUCCESS;
  477. //
  478. // Free locally used resources.
  479. //
  480. Cleanup:
  481. SamIFree_SAMPR_DISPLAY_INFO_BUFFER( &DisplayInformation,
  482. DomainDisplayMachine );
  483. return Status;
  484. }
  485. NET_API_STATUS
  486. I_NetLogonGetIpAddresses(
  487. OUT PULONG IpAddressCount,
  488. OUT LPBYTE *IpAddresses
  489. )
  490. /*++
  491. Routine Description:
  492. Returns all of the IP Addresses assigned to this machine.
  493. Arguments:
  494. IpAddressCount - Returns the number of IP addresses assigned to this machine.
  495. IpAddresses - Returns a buffer containing an array of SOCKET_ADDRESS
  496. structures.
  497. This buffer should be freed using I_NetLogonFree().
  498. Return Value:
  499. NO_ERROR - Success
  500. ERROR_NOT_ENOUGH_MEMORY - There was not enough memory to complete the operation.
  501. ERROR_NETLOGON_NOT_STARTED - Netlogon is not started
  502. --*/
  503. {
  504. NET_API_STATUS NetStatus;
  505. ULONG BufferSize;
  506. //
  507. // If caller is calling when the netlogon service isn't running,
  508. // tell it so.
  509. //
  510. if ( !NlStartNetlogonCall() ) {
  511. return ERROR_NETLOGON_NOT_STARTED;
  512. }
  513. //
  514. // Get the IP addresses.
  515. //
  516. *IpAddresses = NULL;
  517. *IpAddressCount = 0;
  518. *IpAddressCount = NlTransportGetIpAddresses(
  519. 0, // No special header,
  520. FALSE, // Return pointers
  521. (PSOCKET_ADDRESS *)IpAddresses,
  522. &BufferSize );
  523. if ( *IpAddressCount == 0 ) {
  524. if ( *IpAddresses != NULL ) {
  525. NetpMemoryFree( *IpAddresses );
  526. }
  527. *IpAddresses = NULL;
  528. }
  529. NetStatus = NO_ERROR;
  530. //
  531. // Indicate that the calling thread has left netlogon.dll
  532. //
  533. NlEndNetlogonCall();
  534. return NetStatus;
  535. }
  536. ULONG
  537. NlTransportGetIpAddresses(
  538. IN ULONG HeaderSize,
  539. IN BOOLEAN ReturnOffsets,
  540. OUT PSOCKET_ADDRESS *RetIpAddresses,
  541. OUT PULONG RetIpAddressSize
  542. )
  543. /*++
  544. Routine Description:
  545. Return all of the IP Addresses assigned to this machine.
  546. Arguments:
  547. HeaderSize - Size (in bytes) of a header to leave at the front of the returned
  548. buffer.
  549. ReturnOffsets - If TRUE, indicates that all returned pointers should
  550. be offsets.
  551. RetIpAddresses - Returns a buffer containing the IP Addresses
  552. This buffer should be freed using NetpMemoryFree().
  553. RetIpAddressSize - Size (in bytes) of RetIpAddresses
  554. Return Value:
  555. Returns the number of IP Addresses returned.
  556. --*/
  557. {
  558. ULONG IpAddressCount;
  559. ULONG IpAddressSize;
  560. ULONG i;
  561. PLIST_ENTRY ListEntry;
  562. PNL_TRANSPORT TransportEntry = NULL;
  563. PSOCKET_ADDRESS SocketAddresses;
  564. LPBYTE OrigBuffer;
  565. LPBYTE Where;
  566. //
  567. // Allocate a buffer that will be large enough.
  568. //
  569. *RetIpAddresses = NULL;
  570. *RetIpAddressSize = 0;
  571. EnterCriticalSection( &NlGlobalTransportCritSect );
  572. if ( HeaderSize + NlGlobalWinsockPnpAddressSize == 0 ) {
  573. LeaveCriticalSection( &NlGlobalTransportCritSect );
  574. return 0;
  575. }
  576. OrigBuffer = NetpMemoryAllocate( HeaderSize + NlGlobalWinsockPnpAddressSize );
  577. if ( OrigBuffer == NULL ) {
  578. LeaveCriticalSection( &NlGlobalTransportCritSect );
  579. return 0;
  580. }
  581. if ( NlGlobalWinsockPnpAddressSize == 0 ) {
  582. LeaveCriticalSection( &NlGlobalTransportCritSect );
  583. *RetIpAddresses = (PSOCKET_ADDRESS)OrigBuffer;
  584. *RetIpAddressSize = HeaderSize;
  585. return 0;
  586. }
  587. SocketAddresses = (PSOCKET_ADDRESS)(OrigBuffer + HeaderSize);
  588. *RetIpAddressSize = HeaderSize + NlGlobalWinsockPnpAddressSize;
  589. Where = (LPBYTE)&SocketAddresses[NlGlobalWinsockPnpAddresses->iAddressCount];
  590. //
  591. // Loop through the list of addresses learned via winsock.
  592. //
  593. IpAddressCount = NlGlobalWinsockPnpAddresses->iAddressCount;
  594. for ( i=0; i<IpAddressCount; i++ ) {
  595. SocketAddresses[i].iSockaddrLength = NlGlobalWinsockPnpAddresses->Address[i].iSockaddrLength;
  596. if ( ReturnOffsets ) {
  597. SocketAddresses[i].lpSockaddr = (PSOCKADDR)(Where-OrigBuffer);
  598. } else {
  599. SocketAddresses[i].lpSockaddr = (PSOCKADDR)Where;
  600. }
  601. RtlCopyMemory( Where,
  602. NlGlobalWinsockPnpAddresses->Address[i].lpSockaddr,
  603. NlGlobalWinsockPnpAddresses->Address[i].iSockaddrLength );
  604. Where += NlGlobalWinsockPnpAddresses->Address[i].iSockaddrLength;
  605. }
  606. LeaveCriticalSection( &NlGlobalTransportCritSect );
  607. *RetIpAddresses = (PSOCKET_ADDRESS)OrigBuffer;
  608. return IpAddressCount;
  609. }
  610. BOOLEAN
  611. NlTransportGetIpAddress(
  612. IN LPWSTR TransportName,
  613. OUT PULONG IpAddress
  614. )
  615. /*++
  616. Routine Description:
  617. Get the IP Address associated with the specified transport.
  618. Arguments:
  619. TransportName - Name of the transport to query.
  620. IpAddress - IP address of the transport.
  621. Zero if the transport currently has no address or
  622. if the transport is not IP.
  623. Return Value:
  624. TRUE: transport is an IP transport
  625. --*/
  626. {
  627. NTSTATUS Status;
  628. BOOLEAN RetVal = FALSE;
  629. IO_STATUS_BLOCK IoStatusBlock;
  630. OBJECT_ATTRIBUTES ObjectAttributes;
  631. UNICODE_STRING TransportNameString;
  632. HANDLE TransportHandle = NULL;
  633. ULONG IpAddresses[NBT_MAXIMUM_BINDINGS+1];
  634. ULONG BytesReturned;
  635. //
  636. // Open the transport device directly.
  637. //
  638. *IpAddress = 0;
  639. RtlInitUnicodeString( &TransportNameString, TransportName );
  640. InitializeObjectAttributes(
  641. &ObjectAttributes,
  642. &TransportNameString,
  643. OBJ_CASE_INSENSITIVE,
  644. NULL,
  645. NULL );
  646. Status = NtOpenFile(
  647. &TransportHandle,
  648. SYNCHRONIZE,
  649. &ObjectAttributes,
  650. &IoStatusBlock,
  651. 0,
  652. 0 );
  653. if (NT_SUCCESS(Status)) {
  654. Status = IoStatusBlock.Status;
  655. }
  656. if (! NT_SUCCESS(Status)) {
  657. NlPrint(( NL_CRITICAL,
  658. "NlTransportGetIpAddress: %ws Cannot NtOpenFile %lx\n",
  659. TransportName,
  660. Status ));
  661. goto Cleanup;
  662. }
  663. //
  664. // Query the IP Address
  665. //
  666. if (!DeviceIoControl( TransportHandle,
  667. IOCTL_NETBT_GET_IP_ADDRS,
  668. NULL,
  669. 0,
  670. IpAddresses,
  671. sizeof(IpAddresses),
  672. &BytesReturned,
  673. NULL)) {
  674. Status = NetpApiStatusToNtStatus(GetLastError());
  675. if ( Status != STATUS_NOT_IMPLEMENTED ) {
  676. NlPrint(( NL_CRITICAL,
  677. "NlTransportGetIpAddress: %ws Cannot DeviceIoControl %lx\n",
  678. TransportName,
  679. Status ));
  680. }
  681. goto Cleanup;
  682. }
  683. //
  684. // Return IP Address
  685. // (Netbt returns the address in host order.)
  686. //
  687. *IpAddress = htonl(*IpAddresses);
  688. RetVal = TRUE;
  689. Cleanup:
  690. if ( TransportHandle != NULL ) {
  691. (VOID) NtClose( TransportHandle );
  692. }
  693. return RetVal;
  694. }
  695. VOID
  696. NlNotifyKerberosOfIpAddresses(
  697. VOID
  698. )
  699. /*++
  700. Routine Description:
  701. Call the Kerberos package to let it know the IP addresses of the machine.
  702. Arguments:
  703. None.
  704. Return Value:
  705. none
  706. --*/
  707. {
  708. PKERB_UPDATE_ADDRESSES_REQUEST UpdateRequest = NULL;
  709. ULONG UpdateRequestSize;
  710. ULONG SocketAddressCount;
  711. UNICODE_STRING KerberosPackageName;
  712. NTSTATUS SubStatus;
  713. PVOID OutputBuffer = NULL;
  714. ULONG OutputBufferSize = 0;
  715. //
  716. // Initialization.
  717. //
  718. RtlInitUnicodeString(
  719. &KerberosPackageName,
  720. MICROSOFT_KERBEROS_NAME_W
  721. );
  722. //
  723. // Grab a copy of the list so we don't call Kerberos with anything
  724. // locked.
  725. //
  726. //
  727. //
  728. SocketAddressCount = NlTransportGetIpAddresses(
  729. offsetof(KERB_UPDATE_ADDRESSES_REQUEST,Addresses),
  730. FALSE,
  731. (PSOCKET_ADDRESS *)&UpdateRequest,
  732. &UpdateRequestSize );
  733. if ( UpdateRequest == NULL ) {
  734. return;
  735. }
  736. //
  737. // Fill in the header.
  738. //
  739. UpdateRequest->MessageType = KerbUpdateAddressesMessage;
  740. UpdateRequest->AddressCount = SocketAddressCount;
  741. //
  742. // Pass them to Kerberos.
  743. //
  744. (VOID) LsaICallPackage(
  745. &KerberosPackageName,
  746. UpdateRequest,
  747. UpdateRequestSize,
  748. &OutputBuffer,
  749. &OutputBufferSize,
  750. &SubStatus );
  751. NetpMemoryFree( UpdateRequest );
  752. }
  753. VOID
  754. NlReadRegSocketAddressList(
  755. VOID
  756. )
  757. /*++
  758. Routine Description:
  759. Read the Socket address list from the registry and save them in the globals
  760. Arguments:
  761. None
  762. Return Value:
  763. None.
  764. --*/
  765. {
  766. NET_API_STATUS NetStatus;
  767. LPSOCKET_ADDRESS_LIST SocketAddressList = NULL;
  768. HKEY ParmHandle = NULL;
  769. ULONG SocketAddressSize = 0;
  770. int i;
  771. DWORD LocalEntryCount;
  772. DWORD RegType;
  773. //
  774. // Open the key for Netlogon\Private
  775. //
  776. ParmHandle = NlOpenNetlogonKey( NL_PRIVATE_KEY );
  777. if (ParmHandle == NULL) {
  778. NlPrint(( NL_CRITICAL,
  779. "Cannot NlOpenNetlogonKey to get socket address list.\n" ));
  780. goto Cleanup;
  781. }
  782. //
  783. // Read the entry from the registry
  784. //
  785. SocketAddressSize = 0;
  786. NetStatus = RegQueryValueExW( ParmHandle,
  787. NETLOGON_KEYWORD_SOCKETADDRESSLIST,
  788. 0, // Reserved
  789. &RegType,
  790. NULL,
  791. &SocketAddressSize );
  792. if ( NetStatus == NO_ERROR || NetStatus == ERROR_MORE_DATA) {
  793. SocketAddressList = LocalAlloc( 0, SocketAddressSize );
  794. if ( SocketAddressList == NULL ) {
  795. goto Cleanup;
  796. }
  797. NetStatus = RegQueryValueExW( ParmHandle,
  798. NETLOGON_KEYWORD_SOCKETADDRESSLIST,
  799. 0, // Reserved
  800. &RegType,
  801. (LPBYTE)SocketAddressList,
  802. &SocketAddressSize );
  803. }
  804. if ( NetStatus != NO_ERROR ) {
  805. if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
  806. NlPrint(( NL_CRITICAL,
  807. "Cannot RegQueryValueExW to get socket address list. %ld\n",
  808. NetStatus ));
  809. }
  810. goto Cleanup;
  811. }
  812. //
  813. // Validate the data.
  814. //
  815. if ( RegType != REG_BINARY ) {
  816. NlPrint(( NL_CRITICAL,
  817. "SocketAddressList isn't REG_BINARY %ld.\n",
  818. RegType ));
  819. goto Cleanup;
  820. }
  821. if ( SocketAddressSize < offsetof(SOCKET_ADDRESS_LIST, Address) ) {
  822. NlPrint(( NL_CRITICAL,
  823. "SocketAddressList is too small %ld.\n",
  824. SocketAddressSize ));
  825. goto Cleanup;
  826. }
  827. if ( SocketAddressList->iAddressCount * sizeof(SOCKET_ADDRESS) >
  828. SocketAddressSize - offsetof(SOCKET_ADDRESS_LIST, Address) ) {
  829. NlPrint(( NL_CRITICAL,
  830. "SocketAddressList size wrong %ld %ld.\n",
  831. SocketAddressList->iAddressCount * sizeof(SOCKET_ADDRESS),
  832. SocketAddressSize - offsetof(SOCKET_ADDRESS_LIST, Address) ));
  833. goto Cleanup;
  834. }
  835. //
  836. // Convert all offsets to pointers
  837. //
  838. for ( i=0; i<SocketAddressList->iAddressCount; i++ ) {
  839. PSOCKET_ADDRESS SocketAddress;
  840. //
  841. // Ensure the offset and lengths are valid
  842. //
  843. SocketAddress = &SocketAddressList->Address[i];
  844. if ( ((DWORD_PTR)SocketAddress->lpSockaddr) >= SocketAddressSize ||
  845. (DWORD)SocketAddress->iSockaddrLength >= SocketAddressSize ||
  846. ((DWORD_PTR)SocketAddress->lpSockaddr)+SocketAddress->iSockaddrLength > SocketAddressSize ) {
  847. NlPrint(( NL_CRITICAL,
  848. "SocketAddressEntry bad %ld %p %ld.\n",
  849. i,
  850. ((DWORD_PTR)SocketAddress->lpSockaddr),
  851. SocketAddress->iSockaddrLength ));
  852. goto Cleanup;
  853. }
  854. SocketAddress->lpSockaddr = (LPSOCKADDR)
  855. (((LPBYTE)SocketAddressList) + ((DWORD_PTR)SocketAddress->lpSockaddr) );
  856. //
  857. // If the address isn't valid,
  858. // blow it away.
  859. //
  860. SocketAddress = &SocketAddressList->Address[i];
  861. if ( SocketAddress->iSockaddrLength == 0 ||
  862. SocketAddress->lpSockaddr == NULL ||
  863. SocketAddress->lpSockaddr->sa_family != AF_INET ||
  864. ((PSOCKADDR_IN)(SocketAddress->lpSockaddr))->sin_addr.s_addr == 0 ) {
  865. NlPrint(( NL_CRITICAL,
  866. "SocketAddressEntry bogus.\n" ));
  867. goto Cleanup;
  868. }
  869. }
  870. //
  871. // Swap the new list into the global.
  872. //
  873. EnterCriticalSection( &NlGlobalTransportCritSect );
  874. NlAssert( NlGlobalWinsockPnpAddresses == NULL );
  875. SocketAddressSize -= offsetof(SOCKET_ADDRESS_LIST, Address);
  876. if ( SocketAddressSize > 0 ) {
  877. NlGlobalWinsockPnpAddresses = SocketAddressList;
  878. SocketAddressList = NULL;
  879. }
  880. NlGlobalWinsockPnpAddressSize = SocketAddressSize;
  881. LeaveCriticalSection( &NlGlobalTransportCritSect );
  882. Cleanup:
  883. if ( SocketAddressList != NULL ) {
  884. LocalFree( SocketAddressList );
  885. }
  886. if ( ParmHandle != NULL ) {
  887. RegCloseKey( ParmHandle );
  888. }
  889. return;
  890. }
  891. BOOLEAN
  892. NlHandleWsaPnp(
  893. VOID
  894. )
  895. /*++
  896. Routine Description:
  897. Handle a WSA PNP event that IP addresses have changed
  898. Arguments:
  899. None
  900. Return Value:
  901. TRUE if the address list has changed
  902. --*/
  903. {
  904. NET_API_STATUS NetStatus;
  905. BOOLEAN RetVal = FALSE;
  906. DWORD BytesReturned;
  907. LPSOCKET_ADDRESS_LIST SocketAddressList = NULL;
  908. HKEY ParmHandle = NULL;
  909. LPSOCKET_ADDRESS_LIST RegBuffer = NULL;
  910. ULONG SocketAddressSize = 0;
  911. int i;
  912. int j;
  913. int MaxAddressCount;
  914. //
  915. // Ask for notification of address changes.
  916. //
  917. if ( NlGlobalWinsockPnpSocket == INVALID_SOCKET ) {
  918. return FALSE;
  919. }
  920. NetStatus = WSAIoctl( NlGlobalWinsockPnpSocket,
  921. SIO_ADDRESS_LIST_CHANGE,
  922. NULL, // No input buffer
  923. 0, // No input buffer
  924. NULL, // No output buffer
  925. 0, // No output buffer
  926. &BytesReturned,
  927. NULL, // No overlapped,
  928. NULL ); // Not async
  929. if ( NetStatus != 0 ) {
  930. NetStatus = WSAGetLastError();
  931. if ( NetStatus != WSAEWOULDBLOCK) {
  932. NlPrint(( NL_CRITICAL,
  933. "NlHandleWsaPnp: Cannot WSAIoctl SIO_ADDRESS_LIST_CHANGE %ld\n",
  934. NetStatus ));
  935. return FALSE;
  936. }
  937. }
  938. //
  939. // Get the list of IP addresses for this machine.
  940. //
  941. BytesReturned = 150; // Initial guess
  942. for (;;) {
  943. //
  944. // Allocate a buffer that should be big enough.
  945. //
  946. if ( SocketAddressList != NULL ) {
  947. LocalFree( SocketAddressList );
  948. }
  949. SocketAddressList = LocalAlloc( 0, BytesReturned );
  950. if ( SocketAddressList == NULL ) {
  951. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  952. NlPrint(( NL_CRITICAL,
  953. "NlHandleWsaPnp: Cannot allocate buffer for WSAIoctl SIO_ADDRESS_LIST_QUERY %ld\n",
  954. NetStatus ));
  955. goto Cleanup;
  956. }
  957. //
  958. // Get the list of IP addresses
  959. //
  960. NetStatus = WSAIoctl( NlGlobalWinsockPnpSocket,
  961. SIO_ADDRESS_LIST_QUERY,
  962. NULL, // No input buffer
  963. 0, // No input buffer
  964. (PVOID) SocketAddressList,
  965. BytesReturned,
  966. &BytesReturned,
  967. NULL, // No overlapped,
  968. NULL ); // Not async
  969. if ( NetStatus != 0 ) {
  970. NetStatus = WSAGetLastError();
  971. //
  972. // If the buffer isn't big enough, try again.
  973. //
  974. if ( NetStatus == WSAEFAULT ) {
  975. continue;
  976. }
  977. NlPrint(( NL_CRITICAL,
  978. "NlHandleWsaPnp: Cannot WSAIoctl SIO_ADDRESS_LIST_QUERY %ld %ld\n",
  979. NetStatus,
  980. BytesReturned ));
  981. goto Cleanup;
  982. }
  983. break;
  984. }
  985. //
  986. // Weed out any zero IP addresses and other invalid addresses
  987. //
  988. EnterCriticalSection( &NlGlobalTransportCritSect );
  989. j=0;
  990. NlPrint(( NL_SERVER_SESS, "Winsock Addrs:" ));
  991. for ( i=0; i<SocketAddressList->iAddressCount; i++ ) {
  992. PSOCKET_ADDRESS SocketAddress;
  993. //
  994. // Copy this address to the front of the list.
  995. //
  996. SocketAddressList->Address[j] = SocketAddressList->Address[i];
  997. //
  998. // If the address isn't valid,
  999. // skip it.
  1000. //
  1001. SocketAddress = &SocketAddressList->Address[j];
  1002. if ( SocketAddress->iSockaddrLength == 0 ||
  1003. SocketAddress->lpSockaddr == NULL ||
  1004. SocketAddress->lpSockaddr->sa_family != AF_INET ||
  1005. ((PSOCKADDR_IN)(SocketAddress->lpSockaddr))->sin_addr.s_addr == 0 ) {
  1006. //
  1007. // Otherwise keep it.
  1008. //
  1009. } else {
  1010. #if NETLOGONDBG
  1011. ULONG IpAddress;
  1012. CHAR IpAddressString[NL_IP_ADDRESS_LENGTH+1];
  1013. IpAddress = ((PSOCKADDR_IN)(SocketAddress->lpSockaddr))->sin_addr.s_addr;
  1014. NetpIpAddressToStr( IpAddress, IpAddressString );
  1015. NlPrint(( NL_SERVER_SESS, " %s", IpAddressString ));
  1016. #endif // NETLOGONDBG
  1017. SocketAddressSize += sizeof(SOCKET_ADDRESS) + SocketAddress->iSockaddrLength;
  1018. j++;
  1019. }
  1020. }
  1021. SocketAddressList->iAddressCount = j;
  1022. NlPrint(( NL_SERVER_SESS, " (%ld) ", j ));
  1023. //
  1024. // See if the list has changed
  1025. //
  1026. if ( NlGlobalWinsockPnpAddresses == NULL ) {
  1027. if ( SocketAddressSize > 0) {
  1028. NlPrint(( NL_SERVER_SESS, "List used to be empty." ));
  1029. RetVal = TRUE;
  1030. }
  1031. } else if ( SocketAddressSize == 0 ) {
  1032. NlPrint(( NL_SERVER_SESS, "List is now empty." ));
  1033. RetVal = TRUE;
  1034. } else if ( NlGlobalWinsockPnpAddresses->iAddressCount !=
  1035. SocketAddressList->iAddressCount ) {
  1036. NlPrint(( NL_SERVER_SESS, "List size changed %ld %ld.",
  1037. NlGlobalWinsockPnpAddresses->iAddressCount,
  1038. SocketAddressList->iAddressCount ));
  1039. RetVal = TRUE;
  1040. } else {
  1041. for ( i=0; i<SocketAddressList->iAddressCount; i++ ) {
  1042. if ( SocketAddressList->Address[i].iSockaddrLength !=
  1043. NlGlobalWinsockPnpAddresses->Address[i].iSockaddrLength ) {
  1044. NlPrint(( NL_SERVER_SESS, "Sockaddrlen changed." ));
  1045. RetVal = TRUE;
  1046. break;
  1047. }
  1048. if ( !RtlEqualMemory(
  1049. SocketAddressList->Address[i].lpSockaddr,
  1050. NlGlobalWinsockPnpAddresses->Address[i].lpSockaddr,
  1051. SocketAddressList->Address[i].iSockaddrLength ) ) {
  1052. NlPrint(( NL_SERVER_SESS, "Address changed." ));
  1053. RetVal = TRUE;
  1054. break;
  1055. }
  1056. }
  1057. }
  1058. NlPrint(( NL_SERVER_SESS, "\n" ));
  1059. //
  1060. // Swap the new list into the global.
  1061. //
  1062. if ( NlGlobalWinsockPnpAddresses != NULL ) {
  1063. LocalFree( NlGlobalWinsockPnpAddresses );
  1064. NlGlobalWinsockPnpAddresses = NULL;
  1065. }
  1066. if ( SocketAddressSize > 0 ) {
  1067. NlGlobalWinsockPnpAddresses = SocketAddressList;
  1068. SocketAddressList = NULL;
  1069. }
  1070. NlGlobalWinsockPnpAddressSize = SocketAddressSize;
  1071. LeaveCriticalSection( &NlGlobalTransportCritSect );
  1072. //
  1073. // Notify Kerberos of the list of addresses.
  1074. //
  1075. if ( RetVal ) {
  1076. NlNotifyKerberosOfIpAddresses();
  1077. }
  1078. //
  1079. // If the list changed,
  1080. // save it in the registry
  1081. //
  1082. if ( RetVal ) {
  1083. ULONG RegBufferSize;
  1084. ULONG RegEntryCount;
  1085. //
  1086. // Grab a copy of the address list with relative offsets and a header.
  1087. //
  1088. RegEntryCount = NlTransportGetIpAddresses(
  1089. offsetof(SOCKET_ADDRESS_LIST, Address),
  1090. TRUE, // Return offsets and not pointers
  1091. (PSOCKET_ADDRESS *)&RegBuffer,
  1092. &RegBufferSize );
  1093. //
  1094. // If we have no IP addresses, NlTransportGetIpAddresses has allocated
  1095. // the header only and returned the size of the header as the size of the
  1096. // allocated buffer. In this case, set the size of the buffer to 0 in
  1097. // order to clean up the registry value.
  1098. //
  1099. if ( RegBufferSize == offsetof(SOCKET_ADDRESS_LIST, Address) ) {
  1100. RegBufferSize = 0;
  1101. }
  1102. if ( RegBuffer != NULL ) {
  1103. //
  1104. // Fill in the header.
  1105. //
  1106. RegBuffer->iAddressCount = RegEntryCount;
  1107. //
  1108. // Open the key for Netlogon\Private
  1109. //
  1110. ParmHandle = NlOpenNetlogonKey( NL_PRIVATE_KEY );
  1111. if (ParmHandle == NULL) {
  1112. NlPrint(( NL_CRITICAL,
  1113. "Cannot NlOpenNetlogonKey to save IP address list.\n" ));
  1114. } else {
  1115. NetStatus = RegSetValueExW( ParmHandle,
  1116. NETLOGON_KEYWORD_SOCKETADDRESSLIST,
  1117. 0, // Reserved
  1118. REG_BINARY,
  1119. (LPBYTE)RegBuffer,
  1120. RegBufferSize );
  1121. if ( NetStatus != ERROR_SUCCESS ) {
  1122. NlPrint(( NL_CRITICAL,
  1123. "Cannot write '%ws' key to registry %ld.\n",
  1124. NETLOGON_KEYWORD_SOCKETADDRESSLIST,
  1125. NetStatus ));
  1126. }
  1127. }
  1128. }
  1129. }
  1130. Cleanup:
  1131. if ( SocketAddressList != NULL ) {
  1132. LocalFree( SocketAddressList );
  1133. }
  1134. if ( ParmHandle != NULL ) {
  1135. RegCloseKey( ParmHandle );
  1136. }
  1137. if ( RegBuffer != NULL ) {
  1138. NetpMemoryFree( RegBuffer );
  1139. }
  1140. return RetVal;
  1141. }
  1142. NET_API_STATUS
  1143. NlTransportOpen(
  1144. VOID
  1145. )
  1146. /*++
  1147. Routine Description:
  1148. Initialize the list of transports
  1149. Arguments:
  1150. None
  1151. Return Value:
  1152. Status of the operation
  1153. --*/
  1154. {
  1155. NET_API_STATUS NetStatus;
  1156. PLMDR_TRANSPORT_LIST TransportList;
  1157. PLMDR_TRANSPORT_LIST TransportEntry;
  1158. //
  1159. // Enumerate the transports supported by the server.
  1160. //
  1161. NetStatus = NlBrowserGetTransportList( &TransportList );
  1162. if ( NetStatus != NERR_Success ) {
  1163. NlPrint(( NL_CRITICAL, "Cannot NlBrowserGetTransportList %ld\n", NetStatus ));
  1164. goto Cleanup;
  1165. }
  1166. //
  1167. // Loop through the list of transports building a local list.
  1168. //
  1169. TransportEntry = TransportList;
  1170. while (TransportEntry != NULL) {
  1171. BOOLEAN IpTransportChanged;
  1172. (VOID) NlTransportAddTransportName(
  1173. TransportEntry->TransportName,
  1174. &IpTransportChanged );
  1175. if (TransportEntry->NextEntryOffset == 0) {
  1176. TransportEntry = NULL;
  1177. } else {
  1178. TransportEntry = (PLMDR_TRANSPORT_LIST)((PCHAR)TransportEntry+
  1179. TransportEntry->NextEntryOffset);
  1180. }
  1181. }
  1182. MIDL_user_free(TransportList);
  1183. //
  1184. // Open a socket to get winsock PNP notifications on.
  1185. //
  1186. NlGlobalWinsockPnpSocket = WSASocket( AF_INET,
  1187. SOCK_DGRAM,
  1188. 0, // PF_INET,
  1189. NULL,
  1190. 0,
  1191. 0 );
  1192. if ( NlGlobalWinsockPnpSocket == INVALID_SOCKET ) {
  1193. NetStatus = WSAGetLastError();
  1194. //
  1195. // If the address family isn't supported,
  1196. // we're done here.
  1197. //
  1198. if ( NetStatus == WSAEAFNOSUPPORT ) {
  1199. NetStatus = NO_ERROR;
  1200. goto Cleanup;
  1201. }
  1202. NlPrint(( NL_CRITICAL, "Can't WSASocket %ld\n", NetStatus ));
  1203. goto Cleanup;
  1204. }
  1205. //
  1206. // Open an event to wait on.
  1207. //
  1208. NlGlobalWinsockPnpEvent = CreateEvent(
  1209. NULL, // No security ettibutes
  1210. FALSE, // Auto reset
  1211. FALSE, // Initially not signaled
  1212. NULL); // No Name
  1213. if ( NlGlobalWinsockPnpEvent == NULL ) {
  1214. NetStatus = GetLastError();
  1215. NlPrint((NL_CRITICAL, "Cannot create Winsock PNP event %ld\n", NetStatus ));
  1216. goto Cleanup;
  1217. }
  1218. //
  1219. // Associate the event with new addresses becoming available on the socket.
  1220. //
  1221. NetStatus = WSAEventSelect( NlGlobalWinsockPnpSocket, NlGlobalWinsockPnpEvent, FD_ADDRESS_LIST_CHANGE );
  1222. if ( NetStatus != 0 ) {
  1223. NetStatus = WSAGetLastError();
  1224. NlPrint(( NL_CRITICAL, "Can't WSAEventSelect %ld\n", NetStatus ));
  1225. goto Cleanup;
  1226. }
  1227. //
  1228. // Grab the addresses from the registry (So we can properly detect if the list changed)
  1229. //
  1230. NlReadRegSocketAddressList();
  1231. //
  1232. // Get the initial list of IP addresses
  1233. //
  1234. if ( NlHandleWsaPnp() ) {
  1235. NlPrint(( NL_CRITICAL, "Address list changed since last boot. (Forget DynamicSiteName.)\n" ));
  1236. //
  1237. // Indicate that we no longer know what site we're in.
  1238. //
  1239. NlSetDynamicSiteName( NULL );
  1240. }
  1241. Cleanup:
  1242. return NetStatus;
  1243. }
  1244. BOOL
  1245. NlTransportAddTransportName(
  1246. IN LPWSTR TransportName,
  1247. OUT PBOOLEAN IpTransportChanged
  1248. )
  1249. /*++
  1250. Routine Description:
  1251. Adds a transport name to the list of transports.
  1252. Arguments:
  1253. TransportName - Name of the transport to add
  1254. IpTransportChanged - Returns TRUE if an IP transport is added or
  1255. the IP address of the transport changes.
  1256. Return Value:
  1257. TRUE - Success
  1258. FALSE - memory allocation failure.
  1259. --*/
  1260. {
  1261. DWORD TransportNameLength;
  1262. PLIST_ENTRY ListEntry;
  1263. PNL_TRANSPORT TransportEntry = NULL;
  1264. ULONG OldIpAddress;
  1265. BOOLEAN WasIpTransport;
  1266. //
  1267. // Initialization.
  1268. //
  1269. *IpTransportChanged = FALSE;
  1270. //
  1271. // If the entry already exists, use it.
  1272. //
  1273. EnterCriticalSection( &NlGlobalTransportCritSect );
  1274. for ( ListEntry = NlGlobalTransportList.Flink ;
  1275. ListEntry != &NlGlobalTransportList ;
  1276. ListEntry = ListEntry->Flink) {
  1277. TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
  1278. if ( _wcsicmp( TransportName, TransportEntry->TransportName ) == 0 ) {
  1279. break;
  1280. }
  1281. TransportEntry = NULL;
  1282. }
  1283. //
  1284. // If there isn't already a transport entry,
  1285. // allocate and initialize one.
  1286. //
  1287. if ( TransportEntry == NULL ) {
  1288. //
  1289. // Allocate a buffer for the new entry.
  1290. //
  1291. TransportNameLength = wcslen( TransportName );
  1292. TransportEntry = LocalAlloc( 0,
  1293. sizeof(NL_TRANSPORT) +
  1294. TransportNameLength * sizeof(WCHAR) );
  1295. if ( TransportEntry == NULL ) {
  1296. LeaveCriticalSection( &NlGlobalTransportCritSect );
  1297. NlPrint(( NL_CRITICAL, "NlTransportAddTransportName: no memory\n" ));
  1298. return FALSE;
  1299. }
  1300. //
  1301. // Build the new entry and link it onto the tail of the list.
  1302. //
  1303. wcscpy( TransportEntry->TransportName, TransportName );
  1304. TransportEntry->IpAddress = 0;
  1305. TransportEntry->IsIpTransport = FALSE;
  1306. TransportEntry->DeviceHandle = INVALID_HANDLE_VALUE;
  1307. //
  1308. // Flag NwLnkIpx since it is poorly behaved.
  1309. //
  1310. // 1) The redir doesn't support it.
  1311. // 2) A datagram sent to it doesn't support the 0x1C name.
  1312. //
  1313. if ( _wcsicmp( TransportName, L"\\Device\\NwlnkIpx" ) == 0 ) {
  1314. TransportEntry->DirectHostIpx = TRUE;
  1315. } else {
  1316. TransportEntry->DirectHostIpx = FALSE;
  1317. }
  1318. InsertTailList( &NlGlobalTransportList, &TransportEntry->Next );
  1319. }
  1320. //
  1321. // Under all circumstances, update the IP address.
  1322. //
  1323. TransportEntry->TransportEnabled = TRUE;
  1324. OldIpAddress = TransportEntry->IpAddress;
  1325. WasIpTransport = TransportEntry->IsIpTransport;
  1326. TransportEntry->IsIpTransport = NlTransportGetIpAddress(
  1327. TransportName,
  1328. &TransportEntry->IpAddress );
  1329. if ( TransportEntry->IsIpTransport ) {
  1330. //
  1331. // If this is a new IP transport,
  1332. // count it.
  1333. //
  1334. if ( !WasIpTransport ) {
  1335. NlGlobalIpTransportCount ++;
  1336. *IpTransportChanged = TRUE;
  1337. }
  1338. //
  1339. // If the transport was just added,
  1340. // Indicate so.
  1341. //
  1342. if ( OldIpAddress == 0 ) {
  1343. #if NETLOGONDBG
  1344. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1345. NetpIpAddressToStr( TransportEntry->IpAddress, IpAddress );
  1346. NlPrint(( NL_SERVER_SESS, "%ws: Transport Added (%s)\n", TransportName, IpAddress ));
  1347. #endif // NETLOGONDBG
  1348. *IpTransportChanged = TRUE;
  1349. //
  1350. // If the IP address hasn't changed,
  1351. // this is simply a superfluous PNP notification.
  1352. //
  1353. } else if ( OldIpAddress == TransportEntry->IpAddress ) {
  1354. #if NETLOGONDBG
  1355. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1356. NetpIpAddressToStr( TransportEntry->IpAddress, IpAddress );
  1357. NlPrint(( NL_SERVER_SESS, "%ws: Transport Address is still (%s)\n", TransportName, IpAddress ));
  1358. #endif // NETLOGONDBG
  1359. //
  1360. // If the IP Address changed,
  1361. // let the caller know.
  1362. //
  1363. } else {
  1364. #if NETLOGONDBG
  1365. CHAR IpAddress[NL_IP_ADDRESS_LENGTH+1];
  1366. CHAR OldIpAddressString[NL_IP_ADDRESS_LENGTH+1];
  1367. NetpIpAddressToStr( OldIpAddress, OldIpAddressString );
  1368. NetpIpAddressToStr( TransportEntry->IpAddress, IpAddress );
  1369. NlPrint(( NL_SERVER_SESS,
  1370. "%ws: Transport Ip Address changed from (%s) to (%s)\n",
  1371. TransportName,
  1372. OldIpAddressString,
  1373. IpAddress ));
  1374. #endif // NETLOGONDBG
  1375. *IpTransportChanged = TRUE;
  1376. }
  1377. //
  1378. // For non-IP transports,
  1379. // there's not much to do.
  1380. //
  1381. } else {
  1382. //
  1383. // If the transport used to be an IP transport,
  1384. // that doesn't seem possible (but) ...
  1385. //
  1386. if ( WasIpTransport ) {
  1387. NlGlobalIpTransportCount --;
  1388. *IpTransportChanged = TRUE;
  1389. }
  1390. NlPrint(( NL_SERVER_SESS, "%ws: Transport Added\n", TransportName ));
  1391. }
  1392. LeaveCriticalSection( &NlGlobalTransportCritSect );
  1393. return TRUE;;
  1394. }
  1395. BOOLEAN
  1396. NlTransportDisableTransportName(
  1397. IN LPWSTR TransportName
  1398. )
  1399. /*++
  1400. Routine Description:
  1401. Disables a transport name on the list of transports.
  1402. The TransportName is never removed thus preventing us from having to
  1403. maintain reference counts.
  1404. Arguments:
  1405. TransportName - Name of the transport to disable.
  1406. Return Value:
  1407. Returns TRUE if an IP transport is disabled.
  1408. --*/
  1409. {
  1410. PLIST_ENTRY ListEntry;
  1411. //
  1412. // Find this transport in the list of transports.
  1413. //
  1414. EnterCriticalSection( &NlGlobalTransportCritSect );
  1415. for ( ListEntry = NlGlobalTransportList.Flink ;
  1416. ListEntry != &NlGlobalTransportList ;
  1417. ListEntry = ListEntry->Flink) {
  1418. PNL_TRANSPORT TransportEntry;
  1419. TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
  1420. if ( TransportEntry->TransportEnabled &&
  1421. _wcsicmp( TransportName, TransportEntry->TransportName ) == 0 ) {
  1422. ULONG OldIpAddress;
  1423. TransportEntry->TransportEnabled = FALSE;
  1424. OldIpAddress = TransportEntry->IpAddress;
  1425. TransportEntry->IpAddress = 0;
  1426. if ( TransportEntry->DeviceHandle != INVALID_HANDLE_VALUE ) {
  1427. NtClose( TransportEntry->DeviceHandle );
  1428. TransportEntry->DeviceHandle = INVALID_HANDLE_VALUE;
  1429. }
  1430. LeaveCriticalSection( &NlGlobalTransportCritSect );
  1431. NlPrint(( NL_SERVER_SESS, "%ws: Transport Removed\n", TransportName ));
  1432. return (OldIpAddress != 0);
  1433. }
  1434. }
  1435. LeaveCriticalSection( &NlGlobalTransportCritSect );
  1436. return FALSE;
  1437. }
  1438. PNL_TRANSPORT
  1439. NlTransportLookupTransportName(
  1440. IN LPWSTR TransportName
  1441. )
  1442. /*++
  1443. Routine Description:
  1444. Returns a transport name equal to the one passed in. However, the
  1445. returned transport name is static and need not be freed.
  1446. Arguments:
  1447. TransportName - Name of the transport to look up
  1448. Return Value:
  1449. NULL - on any error
  1450. Otherwise, returns a pointer to the transport structure.
  1451. --*/
  1452. {
  1453. PLIST_ENTRY ListEntry;
  1454. //
  1455. // If we're not initialized yet,
  1456. // just return
  1457. //
  1458. if ( TransportName == NULL ) {
  1459. return NULL;
  1460. }
  1461. //
  1462. // Find this transport in the list of transports.
  1463. //
  1464. EnterCriticalSection( &NlGlobalTransportCritSect );
  1465. for ( ListEntry = NlGlobalTransportList.Flink ;
  1466. ListEntry != &NlGlobalTransportList ;
  1467. ListEntry = ListEntry->Flink) {
  1468. PNL_TRANSPORT TransportEntry;
  1469. TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
  1470. if ( TransportEntry->TransportEnabled &&
  1471. _wcsicmp( TransportName, TransportEntry->TransportName ) == 0 ) {
  1472. LeaveCriticalSection( &NlGlobalTransportCritSect );
  1473. return TransportEntry;
  1474. }
  1475. }
  1476. LeaveCriticalSection( &NlGlobalTransportCritSect );
  1477. return NULL;
  1478. }
  1479. PNL_TRANSPORT
  1480. NlTransportLookup(
  1481. IN LPWSTR ClientName
  1482. )
  1483. /*++
  1484. Routine Description:
  1485. Determine what transport the specified client is using to access this
  1486. server.
  1487. Arguments:
  1488. ClientName - Name of the client connected to this server.
  1489. Return Value:
  1490. NULL - The client isn't currently connected
  1491. Otherwise, returns a pointer to the transport structure
  1492. --*/
  1493. {
  1494. NET_API_STATUS NetStatus;
  1495. PSESSION_INFO_502 SessionInfo502;
  1496. DWORD EntriesRead;
  1497. DWORD TotalEntries;
  1498. DWORD i;
  1499. DWORD BestTime;
  1500. DWORD BestEntry;
  1501. PNL_TRANSPORT Transport;
  1502. WCHAR UncClientName[UNCLEN+1];
  1503. //
  1504. // Enumerate all the sessions from the particular client.
  1505. //
  1506. UncClientName[0] = '\\';
  1507. UncClientName[1] = '\\';
  1508. wcscpy( &UncClientName[2], ClientName );
  1509. NetStatus = NetSessionEnum(
  1510. NULL, // local
  1511. UncClientName, // Client to query
  1512. NULL, // user name
  1513. 502,
  1514. (LPBYTE *)&SessionInfo502,
  1515. 1024, // PrefMaxLength
  1516. &EntriesRead,
  1517. &TotalEntries,
  1518. NULL ); // No resume handle
  1519. if ( NetStatus != NERR_Success && NetStatus != ERROR_MORE_DATA ) {
  1520. NlPrint(( NL_CRITICAL,
  1521. "NlTransportLookup: " FORMAT_LPWSTR ": Cannot NetSessionEnum %ld\n",
  1522. UncClientName,
  1523. NetStatus ));
  1524. return NULL;
  1525. }
  1526. if ( EntriesRead == 0 ) {
  1527. NlPrint(( NL_CRITICAL,
  1528. "NlTransportLookup: " FORMAT_LPWSTR ": No session exists.\n",
  1529. UncClientName ));
  1530. (VOID) NetApiBufferFree( SessionInfo502 );
  1531. return NULL;
  1532. }
  1533. //
  1534. // Loop through the list of transports finding the best one.
  1535. //
  1536. BestTime = 0xFFFFFFFF;
  1537. for ( i=0; i<EntriesRead; i++ ) {
  1538. #ifdef notdef
  1539. //
  1540. // We're only looking for null sessions
  1541. //
  1542. if ( SessionInfo502[i].sesi502_username != NULL ) {
  1543. continue;
  1544. }
  1545. NlPrint(( NL_SERVER_SESS, "NlTransportLookup: "
  1546. FORMAT_LPWSTR " as " FORMAT_LPWSTR " on " FORMAT_LPWSTR "\n",
  1547. UncClientName,
  1548. SessionInfo502[i].sesi502_username,
  1549. SessionInfo502[i].sesi502_transport ));
  1550. #endif // notdef
  1551. //
  1552. // Find the latest session
  1553. //
  1554. if ( BestTime > SessionInfo502[i].sesi502_idle_time ) {
  1555. // NlPrint(( NL_SERVER_SESS, "NlTransportLookup: Best Entry\n" ));
  1556. BestEntry = i;
  1557. BestTime = SessionInfo502[i].sesi502_idle_time;
  1558. }
  1559. }
  1560. //
  1561. // If an entry was found,
  1562. // Find this transport in the list of transports.
  1563. //
  1564. if ( BestTime != 0xFFFFFFFF ) {
  1565. Transport = NlTransportLookupTransportName(
  1566. SessionInfo502[BestEntry].sesi502_transport );
  1567. if ( Transport == NULL ) {
  1568. NlPrint(( NL_CRITICAL,
  1569. "NlTransportLookup: " FORMAT_LPWSTR ": Transport not found\n",
  1570. SessionInfo502[BestEntry].sesi502_transport ));
  1571. } else {
  1572. NlPrint(( NL_SERVER_SESS,
  1573. "NlTransportLookup: " FORMAT_LPWSTR ": Use Transport " FORMAT_LPWSTR "\n",
  1574. UncClientName,
  1575. Transport->TransportName ));
  1576. }
  1577. } else {
  1578. Transport = NULL;
  1579. }
  1580. (VOID) NetApiBufferFree( SessionInfo502 );
  1581. return Transport;
  1582. }
  1583. VOID
  1584. NlTransportClose(
  1585. VOID
  1586. )
  1587. /*++
  1588. Routine Description:
  1589. Free the list of transports
  1590. Arguments:
  1591. None
  1592. Return Value:
  1593. Status of the operation
  1594. --*/
  1595. {
  1596. PLIST_ENTRY ListEntry;
  1597. PNL_TRANSPORT TransportEntry;
  1598. //
  1599. // Close the winsock PNP socket and event
  1600. //
  1601. EnterCriticalSection( &NlGlobalTransportCritSect );
  1602. if ( NlGlobalWinsockPnpSocket != INVALID_SOCKET ) {
  1603. closesocket( NlGlobalWinsockPnpSocket );
  1604. NlGlobalWinsockPnpSocket = INVALID_SOCKET;
  1605. }
  1606. if ( NlGlobalWinsockPnpEvent != NULL ) {
  1607. (VOID) CloseHandle( NlGlobalWinsockPnpEvent );
  1608. NlGlobalWinsockPnpEvent = NULL;
  1609. }
  1610. if ( NlGlobalWinsockPnpAddresses != NULL ) {
  1611. LocalFree( NlGlobalWinsockPnpAddresses );
  1612. NlGlobalWinsockPnpAddresses = NULL;
  1613. }
  1614. NlGlobalWinsockPnpAddressSize = 0;
  1615. //
  1616. // Delete all of the TransportNames.
  1617. //
  1618. EnterCriticalSection( &NlGlobalTransportCritSect );
  1619. while ( !IsListEmpty( &NlGlobalTransportList )) {
  1620. ListEntry = RemoveHeadList( &NlGlobalTransportList );
  1621. TransportEntry = CONTAINING_RECORD( ListEntry, NL_TRANSPORT, Next );
  1622. if ( TransportEntry->DeviceHandle != INVALID_HANDLE_VALUE ) {
  1623. NtClose( TransportEntry->DeviceHandle );
  1624. TransportEntry->DeviceHandle = INVALID_HANDLE_VALUE;
  1625. }
  1626. LocalFree( TransportEntry );
  1627. }
  1628. NlGlobalIpTransportCount = 0;
  1629. LeaveCriticalSection( &NlGlobalTransportCritSect );
  1630. }
  1631. PSERVER_SESSION
  1632. NlFindNamedServerSession(
  1633. IN PDOMAIN_INFO DomainInfo,
  1634. IN LPWSTR ComputerName
  1635. )
  1636. /*++
  1637. Routine Description:
  1638. Find the specified entry in the Server Session Table.
  1639. Enter with the ServerSessionTable Sem locked
  1640. Arguments:
  1641. DomainInfo - Hosted domain with session to this computer.
  1642. ComputerName - The name of the computer on the client side of the
  1643. secure channel.
  1644. Return Value:
  1645. Returns a pointer to pointer to the found entry. If there is no such
  1646. entry, return a pointer to NULL.
  1647. --*/
  1648. {
  1649. NTSTATUS Status;
  1650. PLIST_ENTRY ListEntry;
  1651. DWORD Index;
  1652. CHAR UpcaseOemComputerName[CNLEN+1];
  1653. ULONG OemComputerNameSize;
  1654. //
  1655. // Ensure the ServerSession Table is initialized.
  1656. //
  1657. if (DomainInfo->DomServerSessionHashTable == NULL) {
  1658. return NULL;
  1659. }
  1660. //
  1661. // Convert the computername to uppercase OEM for easier comparison.
  1662. //
  1663. Status = RtlUpcaseUnicodeToOemN(
  1664. UpcaseOemComputerName,
  1665. sizeof(UpcaseOemComputerName)-1,
  1666. &OemComputerNameSize,
  1667. ComputerName,
  1668. wcslen(ComputerName)*sizeof(WCHAR) );
  1669. if ( !NT_SUCCESS(Status) ) {
  1670. return NULL;
  1671. }
  1672. UpcaseOemComputerName[OemComputerNameSize] = '\0';
  1673. //
  1674. // Loop through this hash chain trying the find the right entry.
  1675. //
  1676. Index = NlGetHashVal( UpcaseOemComputerName, SERVER_SESSION_HASH_TABLE_SIZE );
  1677. for ( ListEntry = DomainInfo->DomServerSessionHashTable[Index].Flink ;
  1678. ListEntry != &DomainInfo->DomServerSessionHashTable[Index] ;
  1679. ListEntry = ListEntry->Flink) {
  1680. PSERVER_SESSION ServerSession;
  1681. ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsHashList );
  1682. //
  1683. // Compare the worstation name
  1684. //
  1685. if ( lstrcmpA( UpcaseOemComputerName,
  1686. ServerSession->SsComputerName ) != 0 ) {
  1687. continue;
  1688. }
  1689. return ServerSession;
  1690. }
  1691. return NULL;
  1692. }
  1693. VOID
  1694. NlSetServerSessionAttributesByTdoName(
  1695. IN PDOMAIN_INFO DomainInfo,
  1696. IN PUNICODE_STRING TdoName,
  1697. IN ULONG TrustAttributes
  1698. )
  1699. /*++
  1700. Routine Description:
  1701. The function sets the specified TrustAttributes on all the server sessions
  1702. from the domain specified by TdoName.
  1703. Arguments:
  1704. DomainInfo - Hosted domain with session to this computer.
  1705. TdoName - The Dns name of the TDO for the secure channel to find.
  1706. TrustAttributes - TrustAttributes to set
  1707. Return Value:
  1708. None
  1709. --*/
  1710. {
  1711. NTSTATUS Status;
  1712. PLIST_ENTRY ListEntry;
  1713. DWORD Index;
  1714. UNICODE_STRING CanonicalTdoName;
  1715. PSERVER_SESSION ServerSession;
  1716. //
  1717. // Initialization
  1718. //
  1719. RtlInitUnicodeString( &CanonicalTdoName, NULL );
  1720. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  1721. //
  1722. // Ensure the ServerSession Table is initialized.
  1723. //
  1724. if (DomainInfo->DomServerSessionTdoNameHashTable == NULL) {
  1725. goto Cleanup;
  1726. }
  1727. //
  1728. // Compute the canonical form of the name and the index into the hash table
  1729. //
  1730. Status = NlGetTdoNameHashVal( TdoName,
  1731. &CanonicalTdoName,
  1732. &Index );
  1733. if ( !NT_SUCCESS(Status) ) {
  1734. goto Cleanup;
  1735. }
  1736. //
  1737. // Loop through this hash chain trying the find the right entries
  1738. //
  1739. for ( ListEntry = DomainInfo->DomServerSessionTdoNameHashTable[Index].Flink ;
  1740. ListEntry != &DomainInfo->DomServerSessionTdoNameHashTable[Index] ;
  1741. ListEntry = ListEntry->Flink) {
  1742. ServerSession = CONTAINING_RECORD( ListEntry, SERVER_SESSION, SsTdoNameHashList );
  1743. //
  1744. // Compare the TDO name
  1745. // A case insensitive compare is done since the names are already canonicalized to compute the hash index
  1746. //
  1747. if ( RtlEqualUnicodeString( &CanonicalTdoName,
  1748. &ServerSession->SsTdoName,
  1749. FALSE ) ) {
  1750. if ( TrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE ) {
  1751. if ( (ServerSession->SsFlags & SS_FOREST_TRANSITIVE) == 0 ) {
  1752. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1753. "%wZ: server session from %s is now cross forest.\n",
  1754. &CanonicalTdoName,
  1755. ServerSession->SsComputerName ));
  1756. }
  1757. ServerSession->SsFlags |= SS_FOREST_TRANSITIVE;
  1758. } else {
  1759. if ( ServerSession->SsFlags & SS_FOREST_TRANSITIVE ) {
  1760. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  1761. "%wZ: server session from %s is now NOT cross forest.\n",
  1762. &CanonicalTdoName,
  1763. ServerSession->SsComputerName ));
  1764. }
  1765. ServerSession->SsFlags &= ~SS_FOREST_TRANSITIVE;
  1766. }
  1767. }
  1768. }
  1769. Cleanup:
  1770. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  1771. RtlFreeUnicodeString( &CanonicalTdoName );
  1772. return;
  1773. }
  1774. NTSTATUS
  1775. NlInsertServerSession(
  1776. IN PDOMAIN_INFO DomainInfo,
  1777. IN LPWSTR ComputerName,
  1778. IN LPWSTR TdoName OPTIONAL,
  1779. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  1780. IN DWORD Flags,
  1781. IN ULONG AccountRid,
  1782. IN ULONG NegotiatedFlags,
  1783. IN PNL_TRANSPORT Transport OPTIONAL,
  1784. IN PNETLOGON_SESSION_KEY SessionKey OPTIONAL,
  1785. IN PNETLOGON_CREDENTIAL AuthenticationSeed OPTIONAL
  1786. )
  1787. /*++
  1788. Routine Description:
  1789. Inserts the described entry into the ServerSession Table.
  1790. The server session entry is created for two reasons: 1) it represents
  1791. the server side of a secure channel, and 2) on a PDC, it represents the
  1792. BDC account for a BDC in the domain. In the first role, it exists for
  1793. the duration of the secure channel (and this routine is called when the
  1794. client gets authenticated). In the second role, it exists as
  1795. long as the machine account exists (and this routine is called during
  1796. netlogon startup for each BDC account).
  1797. If an entry matching this ComputerName already exists
  1798. in the ServerSession Table, that entry will be overwritten.
  1799. Arguments:
  1800. DomainInfo - Hosted domain for this server session
  1801. ComputerName - The name of the computer on the client side of the
  1802. secure channel.
  1803. TdoName - The name of the interdomain trust account. This parameter is
  1804. ignored if the SecureChannelType indicates this is not an uplevel interdomain trust.
  1805. SecureChannelType - The type of the secure channel.
  1806. Flags - Specifies the initial SsFlags to associate with the entry.
  1807. If the SS_BDC bit is set, the structure is considered to represent
  1808. a BDC account in the SAM database.
  1809. AccountRid - Specifies the RID of the client account.
  1810. NegotiatedFlags - Specifies the flags negotiated between the client
  1811. and this server.
  1812. Transport -- If this is a BDC secure channel, specifies the transport
  1813. to use to communicate with the BDC.
  1814. SessionKey -- Specifies th esession key to be used in the secure
  1815. communication with the client.
  1816. AuthenticationSeed - Specifies the Client Credential established as
  1817. a result of the client authentication.
  1818. Return Value:
  1819. NT STATUS code.
  1820. --*/
  1821. {
  1822. NTSTATUS Status;
  1823. PSERVER_SESSION ServerSession = NULL;
  1824. UNICODE_STRING CanonicalTdoName;
  1825. ULONG TdoNameIndex;
  1826. //
  1827. // Initialization
  1828. //
  1829. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  1830. RtlInitUnicodeString( &CanonicalTdoName, NULL );
  1831. //
  1832. // Canonicalize the TdoName
  1833. //
  1834. if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
  1835. UNICODE_STRING TdoNameString;
  1836. //
  1837. // Compute the canonical form of the name and the index into the hash table
  1838. //
  1839. RtlInitUnicodeString( &TdoNameString, TdoName );
  1840. Status = NlGetTdoNameHashVal( &TdoNameString,
  1841. &CanonicalTdoName,
  1842. &TdoNameIndex );
  1843. if ( !NT_SUCCESS(Status) ) {
  1844. goto Cleanup;
  1845. }
  1846. }
  1847. //
  1848. // If we already have a server session for this client,
  1849. // check that the passed info is consistent with the
  1850. // existent one
  1851. //
  1852. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName);
  1853. if ( ServerSession != NULL ) {
  1854. BOOLEAN DeleteExistingSession = FALSE;
  1855. //
  1856. // Beware of server with two concurrent calls outstanding
  1857. // (must have rebooted.)
  1858. // Namely, if the entry is currently locked, return an
  1859. // appropriate error.
  1860. //
  1861. if ( ServerSession->SsFlags & SS_LOCKED ) {
  1862. NlPrint(( NL_CRITICAL,
  1863. "NlInsertServerSession: server session locked for %ws\n",
  1864. ComputerName ));
  1865. Status = STATUS_ACCESS_DENIED;
  1866. goto Cleanup;
  1867. }
  1868. //
  1869. // If there is a mismatch in the type of the account,
  1870. // simply note that fact and override the existing
  1871. // one. The mismatch can happen if
  1872. //
  1873. // * The client has been authenticated as a BDC but the
  1874. // current session is for a non-BDC. This may occur
  1875. // if we haven't yet processed a SAM notification about
  1876. // the new BDC account or SAM hasn't notified us yet.
  1877. // * The client has been authenticated as a non-BDC
  1878. // but the current session is for a BDC. This can
  1879. // occur if a machine had a BDC account and then later
  1880. // has been demoted or converted to a DC in another domain.
  1881. // * This is a insertion of a new BDC account (that is not yet
  1882. // authenticated) but the current session is for a non-BDC.
  1883. // This can happen if the client was an authenticated member
  1884. // server/workstation and has now been promoted to a BDC.
  1885. //
  1886. if ( SecureChannelType == ServerSecureChannel ) {
  1887. //
  1888. // Client comes in as a BDC but the current session
  1889. // is not for a BDC.
  1890. //
  1891. if ( (ServerSession->SsFlags & SS_BDC) == 0 ) {
  1892. NlPrint(( NL_CRITICAL,
  1893. "NlInsertServerSession: BDC connecting on non-BDC channel %ws\n",
  1894. ComputerName ));
  1895. }
  1896. } else if ( ServerSession->SsFlags & SS_BDC ) {
  1897. //
  1898. // Client comes in as a non-BDC but the current session
  1899. // is for a BDC.
  1900. //
  1901. if ( SecureChannelType != NullSecureChannel ) {
  1902. NlPrint(( NL_CRITICAL,
  1903. "NlInsertServerSession: non-BDC %ld connecting on BDC channel %ws\n",
  1904. SecureChannelType,
  1905. ComputerName ));
  1906. }
  1907. }
  1908. //
  1909. // If this is an interdomain secure channel,
  1910. // ensure the existing structure has the correct TDO name.
  1911. //
  1912. if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
  1913. if ( ServerSession->SsSecureChannelType != TrustedDnsDomainSecureChannel ) {
  1914. DeleteExistingSession = TRUE;
  1915. } else {
  1916. if ( !RtlEqualUnicodeString( &CanonicalTdoName,
  1917. &ServerSession->SsTdoName,
  1918. FALSE ) ) {
  1919. DeleteExistingSession = TRUE;
  1920. }
  1921. }
  1922. }
  1923. //
  1924. // If the existing session is inadequate,
  1925. // delete it and create a new one.
  1926. //
  1927. if ( DeleteExistingSession ) {
  1928. if ( !NlFreeServerSession( ServerSession )) {
  1929. NlPrint(( NL_CRITICAL,
  1930. "NlInsertServerSession: server session cannot be freed %ws\n",
  1931. ComputerName ));
  1932. Status = STATUS_ACCESS_DENIED;
  1933. goto Cleanup;
  1934. }
  1935. ServerSession = NULL;
  1936. }
  1937. }
  1938. //
  1939. // If there is no current Server Session table entry,
  1940. // allocate one.
  1941. //
  1942. if ( ServerSession == NULL ) {
  1943. DWORD Index;
  1944. ULONG ComputerNameSize;
  1945. ULONG Size;
  1946. //
  1947. // Allocate the ServerSession Entry
  1948. //
  1949. Size = sizeof(SERVER_SESSION);
  1950. if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
  1951. Size += CanonicalTdoName.Length+sizeof(WCHAR);
  1952. }
  1953. ServerSession = NetpMemoryAllocate( Size );
  1954. if (ServerSession == NULL) {
  1955. Status = STATUS_NO_MEMORY;
  1956. goto Cleanup;
  1957. }
  1958. RtlZeroMemory( ServerSession, Size );
  1959. //
  1960. // Fill in the fields of the ServerSession entry.
  1961. //
  1962. ServerSession->SsSecureChannelType = NullSecureChannel;
  1963. ServerSession->SsSync = NULL;
  1964. InitializeListHead( &ServerSession->SsBdcList );
  1965. InitializeListHead( &ServerSession->SsPendingBdcList );
  1966. ServerSession->SsDomainInfo = DomainInfo;
  1967. //
  1968. // Convert the computername to uppercase OEM for easier comparison.
  1969. //
  1970. Status = RtlUpcaseUnicodeToOemN(
  1971. ServerSession->SsComputerName,
  1972. sizeof(ServerSession->SsComputerName)-1,
  1973. &ComputerNameSize,
  1974. ComputerName,
  1975. wcslen(ComputerName)*sizeof(WCHAR) );
  1976. if ( !NT_SUCCESS(Status) ) {
  1977. NetpMemoryFree( ServerSession );
  1978. goto Cleanup;
  1979. }
  1980. ServerSession->SsComputerName[ComputerNameSize] = '\0';
  1981. //
  1982. // Allocate a hash table if there isn't one yet.
  1983. //
  1984. if ( DomainInfo->DomServerSessionHashTable == NULL ) {
  1985. DWORD i;
  1986. DomainInfo->DomServerSessionHashTable = (PLIST_ENTRY)
  1987. NetpMemoryAllocate( sizeof(LIST_ENTRY) *SERVER_SESSION_HASH_TABLE_SIZE);
  1988. if ( DomainInfo->DomServerSessionHashTable == NULL ) {
  1989. NetpMemoryFree( ServerSession );
  1990. Status = STATUS_NO_MEMORY;
  1991. goto Cleanup;
  1992. }
  1993. for ( i=0; i< SERVER_SESSION_HASH_TABLE_SIZE; i++ ) {
  1994. InitializeListHead( &DomainInfo->DomServerSessionHashTable[i] );
  1995. }
  1996. }
  1997. //
  1998. // Do interdomain trust specific initialization
  1999. //
  2000. if ( SecureChannelType == TrustedDnsDomainSecureChannel ) {
  2001. LPBYTE Where;
  2002. //
  2003. // Copy the TDO name into the buffer.
  2004. // Copy it in canonical form for faster comparison later.
  2005. //
  2006. Where = (LPBYTE)(ServerSession+1);
  2007. ServerSession->SsTdoName.Buffer = (LPWSTR)Where;
  2008. ServerSession->SsTdoName.MaximumLength = CanonicalTdoName.Length + sizeof(WCHAR);
  2009. ServerSession->SsTdoName.Length = CanonicalTdoName.Length;
  2010. RtlCopyMemory( Where, CanonicalTdoName.Buffer, CanonicalTdoName.Length + sizeof(WCHAR) );
  2011. ServerSession->SsTdoName.Buffer[ CanonicalTdoName.Length/sizeof(WCHAR) ] = '\0';
  2012. //
  2013. // Allocate a TdoName hash table if there isn't one yet.
  2014. //
  2015. if ( DomainInfo->DomServerSessionTdoNameHashTable == NULL ) {
  2016. DWORD i;
  2017. DomainInfo->DomServerSessionTdoNameHashTable = (PLIST_ENTRY)
  2018. NetpMemoryAllocate( sizeof(LIST_ENTRY) *SERVER_SESSION_TDO_NAME_HASH_TABLE_SIZE);
  2019. if ( DomainInfo->DomServerSessionTdoNameHashTable == NULL ) {
  2020. NetpMemoryFree( ServerSession );
  2021. Status = STATUS_NO_MEMORY;
  2022. goto Cleanup;
  2023. }
  2024. for ( i=0; i< SERVER_SESSION_TDO_NAME_HASH_TABLE_SIZE; i++ ) {
  2025. InitializeListHead( &DomainInfo->DomServerSessionTdoNameHashTable[i] );
  2026. }
  2027. }
  2028. //
  2029. // Insert the entry in the TDO name hash table.
  2030. //
  2031. InsertHeadList( &DomainInfo->DomServerSessionTdoNameHashTable[TdoNameIndex],
  2032. &ServerSession->SsTdoNameHashList );
  2033. }
  2034. //
  2035. // Link the allocated entry into the head of hash table.
  2036. //
  2037. // The theory is we lookup new entries more frequently than older
  2038. // entries.
  2039. //
  2040. Index = NlGetHashVal( ServerSession->SsComputerName, SERVER_SESSION_HASH_TABLE_SIZE );
  2041. InsertHeadList( &DomainInfo->DomServerSessionHashTable[Index],
  2042. &ServerSession->SsHashList );
  2043. //
  2044. // Link this entry onto the tail of the Sequential ServerSessionTable.
  2045. //
  2046. InsertTailList( &DomainInfo->DomServerSessionTable, &ServerSession->SsSeqList );
  2047. }
  2048. //
  2049. // Initialize BDC specific fields.
  2050. //
  2051. if ( Flags & SS_BDC ) {
  2052. //
  2053. // If we don't yet have this entry on the BDC list,
  2054. // add it.
  2055. //
  2056. if ( (ServerSession->SsFlags & SS_BDC) == 0 ) {
  2057. //
  2058. // Insert this entry at the front of the list of BDCs
  2059. //
  2060. InsertHeadList( &NlGlobalBdcServerSessionList,
  2061. &ServerSession->SsBdcList );
  2062. NlGlobalBdcServerSessionCount ++;
  2063. }
  2064. }
  2065. //
  2066. // Initialize other fields
  2067. //
  2068. ServerSession->SsFlags |= Flags;
  2069. // NlAssert( ServerSession->SsAccountRid == 0 ||
  2070. // ServerSession->SsAccountRid == AccountRid );
  2071. // if ( AccountRid != 0 ) {
  2072. ServerSession->SsAccountRid = AccountRid;
  2073. // }
  2074. //
  2075. // If we're doing a new session setup,
  2076. // set the field that we learned from the session setup.
  2077. //
  2078. if ( AuthenticationSeed != NULL ) {
  2079. ServerSession->SsCheck = 0;
  2080. ServerSession->SsSecureChannelType = SecureChannelType;
  2081. ServerSession->SsNegotiatedFlags = NegotiatedFlags;
  2082. ServerSession->SsTransport = Transport;
  2083. ServerSession->SsFlags = ((USHORT) Flags) |
  2084. (ServerSession->SsFlags & SS_PERMANENT_FLAGS);
  2085. ServerSession->SsAuthenticationSeed = *AuthenticationSeed;
  2086. }
  2087. if ( SessionKey != NULL ) {
  2088. NlAssert( sizeof(*SessionKey) <= sizeof(ServerSession->SsSessionKey) );
  2089. RtlCopyMemory( &ServerSession->SsSessionKey,
  2090. SessionKey,
  2091. sizeof( *SessionKey ) );
  2092. }
  2093. Status = STATUS_SUCCESS;
  2094. Cleanup:
  2095. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2096. RtlFreeUnicodeString( &CanonicalTdoName );
  2097. return Status;
  2098. }
  2099. BOOLEAN
  2100. NlFreeServerSession(
  2101. IN PSERVER_SESSION ServerSession
  2102. )
  2103. /*++
  2104. Routine Description:
  2105. Free the specified Server Session table entry.
  2106. This routine is called with the Server Session table locked.
  2107. Arguments:
  2108. ServerSession - Specifies a pointer to the server session entry
  2109. to delete.
  2110. Return Value:
  2111. TRUE - the structure was deleted now
  2112. FALSE - the structure will be deleted later
  2113. --*/
  2114. {
  2115. //
  2116. // If someone has an outstanding pointer to this entry,
  2117. // delay the deletion for now.
  2118. //
  2119. if ( ServerSession->SsFlags & SS_LOCKED ) {
  2120. ServerSession->SsFlags |= SS_DELETE_ON_UNLOCK;
  2121. NlPrintDom((NL_SERVER_SESS, ServerSession->SsDomainInfo,
  2122. "NlFreeServerSession: %s: Tried to free locked server session\n",
  2123. ServerSession->SsComputerName ));
  2124. return FALSE;
  2125. }
  2126. //
  2127. // If this entry represents a BDC account,
  2128. // don't delete the entry until the account is deleted.
  2129. //
  2130. if ( (ServerSession->SsFlags & SS_BDC) != 0 &&
  2131. (ServerSession->SsFlags & SS_BDC_FORCE_DELETE) == 0 ) {
  2132. NlPrint((NL_SERVER_SESS,
  2133. "NlFreeServerSession: %s: Didn't delete server session with BDC account.\n",
  2134. ServerSession->SsComputerName ));
  2135. return FALSE;
  2136. }
  2137. NlPrintDom((NL_SERVER_SESS, ServerSession->SsDomainInfo,
  2138. "NlFreeServerSession: %s: Freed server session\n",
  2139. ServerSession->SsComputerName ));
  2140. //
  2141. // Delink the entry from the computername hash list.
  2142. //
  2143. RemoveEntryList( &ServerSession->SsHashList );
  2144. //
  2145. // Delink the entry from the TdoName hash list.
  2146. //
  2147. if ( ServerSession->SsSecureChannelType == TrustedDnsDomainSecureChannel ) {
  2148. RemoveEntryList( &ServerSession->SsTdoNameHashList );
  2149. }
  2150. //
  2151. // Delink the entry from the sequential list.
  2152. //
  2153. RemoveEntryList( &ServerSession->SsSeqList );
  2154. //
  2155. // Handle special cleanup for the BDC_SERVER_SESSION
  2156. //
  2157. if ( ServerSession->SsFlags & SS_BDC ) {
  2158. //
  2159. // Remove the entry from the list of BDCs
  2160. //
  2161. RemoveEntryList( &ServerSession->SsBdcList );
  2162. NlGlobalBdcServerSessionCount --;
  2163. //
  2164. // Remove the entry from the list of pending BDCs
  2165. //
  2166. if ( ServerSession->SsFlags & SS_PENDING_BDC ) {
  2167. NlRemovePendingBdc( ServerSession );
  2168. }
  2169. //
  2170. // Clean up an sync context for this entry.
  2171. //
  2172. if ( ServerSession->SsSync != NULL ) {
  2173. CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
  2174. NetpMemoryFree( ServerSession->SsSync );
  2175. }
  2176. }
  2177. //
  2178. // Delete the entry
  2179. //
  2180. NetpMemoryFree( ServerSession );
  2181. return TRUE;
  2182. }
  2183. VOID
  2184. NlUnlockServerSession(
  2185. IN PSERVER_SESSION ServerSession
  2186. )
  2187. /*++
  2188. Routine Description:
  2189. Unlock the specified Server Session table entry.
  2190. Arguments:
  2191. ServerSession - Specifies a pointer to the server session entry to unlock.
  2192. Return Value:
  2193. --*/
  2194. {
  2195. LOCK_SERVER_SESSION_TABLE( ServerSession->SsDomainInfo );
  2196. //
  2197. // Unlock the entry.
  2198. //
  2199. NlAssert( ServerSession->SsFlags & SS_LOCKED );
  2200. ServerSession->SsFlags &= ~SS_LOCKED;
  2201. //
  2202. // If someone wanted to delete the entry while we had it locked,
  2203. // finish the deletion.
  2204. //
  2205. if ( ServerSession->SsFlags & SS_DELETE_ON_UNLOCK ) {
  2206. NlFreeServerSession( ServerSession );
  2207. //
  2208. // Indicate activity from the BDC
  2209. //
  2210. } else if (ServerSession->SsFlags & SS_PENDING_BDC) {
  2211. NlQuerySystemTime( &ServerSession->SsLastPulseTime );
  2212. }
  2213. UNLOCK_SERVER_SESSION_TABLE( ServerSession->SsDomainInfo );
  2214. }
  2215. VOID
  2216. NlFreeNamedServerSession(
  2217. IN PDOMAIN_INFO DomainInfo,
  2218. IN LPWSTR ComputerName,
  2219. IN BOOLEAN AccountBeingDeleted
  2220. )
  2221. /*++
  2222. Routine Description:
  2223. Frees the specified entry in the ServerSession Table.
  2224. Arguments:
  2225. DomainInfo - Hosted domain this session is for
  2226. ComputerName - The name of the computer on the client side of the
  2227. secure channel.
  2228. AccountBeingDeleted - True to indicate that the account for this server
  2229. session is being deleted.
  2230. Return Value:
  2231. An NT status code.
  2232. --*/
  2233. {
  2234. PSERVER_SESSION ServerSession;
  2235. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  2236. //
  2237. // Find the entry to delete.
  2238. //
  2239. ServerSession = NlFindNamedServerSession( DomainInfo, ComputerName );
  2240. if ( ServerSession == NULL ) {
  2241. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2242. return;
  2243. }
  2244. //
  2245. // If the BDC account is being deleted,
  2246. // indicate that it's OK to delete this session structure.
  2247. //
  2248. if ( AccountBeingDeleted &&
  2249. (ServerSession->SsFlags & SS_BDC) != 0 ) {
  2250. ServerSession->SsFlags |= SS_BDC_FORCE_DELETE;
  2251. }
  2252. //
  2253. // Actually delete the entry.
  2254. //
  2255. NlFreeServerSession( ServerSession );
  2256. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2257. }
  2258. VOID
  2259. NlFreeServerSessionForAccount(
  2260. IN PUNICODE_STRING AccountName
  2261. )
  2262. /*++
  2263. Routine Description:
  2264. Frees the specified entry in the ServerSession Table.
  2265. Arguments:
  2266. AccountName - The name of the Account describing trust relationship being
  2267. deleted.
  2268. Return Value:
  2269. None
  2270. --*/
  2271. {
  2272. WCHAR ComputerName[CNLEN+2]; // Extra for $ and \0
  2273. //
  2274. // Convert account name to a computer name by stripping the trailing
  2275. // postfix.
  2276. //
  2277. if ( AccountName->Length + sizeof(WCHAR) > sizeof(ComputerName) ||
  2278. AccountName->Length < SSI_ACCOUNT_NAME_POSTFIX_LENGTH * sizeof(WCHAR)){
  2279. return;
  2280. }
  2281. RtlCopyMemory( ComputerName, AccountName->Buffer, AccountName->Length );
  2282. ComputerName[ AccountName->Length / sizeof(WCHAR) -
  2283. SSI_ACCOUNT_NAME_POSTFIX_LENGTH ] = L'\0';
  2284. //
  2285. // Free the named server session (if any)
  2286. //
  2287. NlFreeNamedServerSession( NlGlobalDomainInfo, ComputerName, TRUE );
  2288. }
  2289. VOID
  2290. NlServerSessionScavenger(
  2291. IN PDOMAIN_INFO DomainInfo
  2292. )
  2293. /*++
  2294. Routine Description:
  2295. Scavenge the ServerSession Table.
  2296. For now, just clean up the SyncContext if a client doesn't use it
  2297. for a while.
  2298. Arguments:
  2299. DomainInfo - Hosted domain to scavenge
  2300. Return Value:
  2301. None.
  2302. --*/
  2303. {
  2304. PLIST_ENTRY ListEntry;
  2305. //
  2306. // Find the next table entry that needs to be scavenged
  2307. //
  2308. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  2309. for ( ListEntry = DomainInfo->DomServerSessionTable.Flink ;
  2310. ListEntry != &DomainInfo->DomServerSessionTable ;
  2311. ) {
  2312. PSERVER_SESSION ServerSession;
  2313. ServerSession =
  2314. CONTAINING_RECORD(ListEntry, SERVER_SESSION, SsSeqList);
  2315. //
  2316. // Grab a pointer to the next entry before deleting this one
  2317. //
  2318. ListEntry = ListEntry->Flink;
  2319. //
  2320. // Increment the number of times this entry has been checked.
  2321. //
  2322. ServerSession->SsCheck ++;
  2323. //
  2324. // If this entry in the Server Session table has been around for many
  2325. // days without the client calling,
  2326. // free it.
  2327. //
  2328. // We wait several days before deleting an old entry. If an entry is
  2329. // deleted, the client has to rediscover us which may cause a lot of
  2330. // net traffic. After several days, that additional traffic isn't
  2331. // significant.
  2332. //
  2333. if (ServerSession->SsCheck > KILL_SESSION_TIME ) {
  2334. NlPrintDom((NL_SERVER_SESS, DomainInfo,
  2335. "NlServerSessionScavenger: %s: Free Server Session.\n",
  2336. ServerSession->SsComputerName ));
  2337. NlFreeServerSession( ServerSession );
  2338. //
  2339. // If this entry in the Server Session table has timed out,
  2340. // Clean up the SAM resources.
  2341. //
  2342. } else if (ServerSession->SsCheck > MAX_WOC_INTERROGATE) {
  2343. //
  2344. // Clean up the SYNC context for this session freeing up
  2345. // the SAM resources.
  2346. //
  2347. // We shouldn't timeout if the ServerSession Entry is locked,
  2348. // but we'll be careful anyway.
  2349. //
  2350. if ( (ServerSession->SsFlags & SS_LOCKED) == 0 &&
  2351. ServerSession->SsFlags & SS_BDC ) {
  2352. if ( ServerSession->SsSync != NULL ) {
  2353. NlPrintDom((NL_SERVER_SESS, DomainInfo,
  2354. "NlServerSessionScavenger: %s: Cleanup Sync context.\n",
  2355. ServerSession->SsComputerName ));
  2356. CLEAN_SYNC_CONTEXT( ServerSession->SsSync );
  2357. NetpMemoryFree( ServerSession->SsSync );
  2358. ServerSession->SsSync = NULL;
  2359. }
  2360. }
  2361. }
  2362. } // end for
  2363. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  2364. }