Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3079 lines
81 KiB

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