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.

764 lines
18 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  5. //
  6. // File: sockets.cxx
  7. //
  8. // Contents: Code for kerberos client sockets
  9. //
  10. // Classes:
  11. //
  12. // Functions:
  13. //
  14. // History: 26-Jul-1996 MikeSw Created
  15. //
  16. //----------------------------------------------------------------------------
  17. #ifdef WIN32_CHICAGO
  18. #include <kerb.hxx>
  19. #include <kerbp.h>
  20. #endif // WIN32_CHICAGO
  21. #ifndef WIN32_CHICAGO
  22. extern "C"
  23. {
  24. #include <nt.h>
  25. #include <ntrtl.h>
  26. #include <nturtl.h>
  27. #ifndef WIN32_CHICAGO
  28. #include <winsock2.h>
  29. #else // WIN32_CHICAGO
  30. #include <winsock.h>
  31. #endif // WIN32_CHICAGO
  32. #include <dsgetdc.h>
  33. }
  34. #include <kerbcomm.h>
  35. #include <kerberr.h>
  36. #include <kerbcon.h>
  37. #include <midles.h>
  38. #include <authen.hxx>
  39. #include <tostring.hxx>
  40. #include "debug.h"
  41. #else // WIN32_CHICAGO
  42. extern "C"
  43. {
  44. #include <winsock.h>
  45. }
  46. #endif // WIN32_CHICAGO
  47. LONG SocketStarts = -1;
  48. ULONG TcpFragLength = 0x7fffffff ;
  49. ULONG TcpFragDelay = 0 ;
  50. //+-------------------------------------------------------------------------
  51. //
  52. // Function: KerbInitializeSockets
  53. //
  54. // Synopsis:
  55. //
  56. // Effects:
  57. //
  58. // Arguments:
  59. //
  60. // Requires:
  61. //
  62. // Returns:
  63. //
  64. // Notes:
  65. //
  66. //
  67. //--------------------------------------------------------------------------
  68. NTSTATUS
  69. KerbInitializeSockets(
  70. IN ULONG VersionRequired,
  71. IN ULONG MinSockets,
  72. OUT BOOLEAN *TcpNotInstalled
  73. )
  74. {
  75. NTSTATUS Status = STATUS_SUCCESS;
  76. int Error;
  77. WSADATA SocketData;
  78. #ifndef WIN32_CHICAGO
  79. WSAPROTOCOL_INFO *lpProtocolBuf = NULL;
  80. DWORD dwBufLen = 0;
  81. INT protocols[2];
  82. int nRet = 0;
  83. #endif // WIN32_CHICAGO
  84. //
  85. // Initialze sockets
  86. //
  87. *TcpNotInstalled = FALSE;
  88. if (InterlockedIncrement(&SocketStarts) != 0)
  89. {
  90. return(STATUS_SUCCESS);
  91. }
  92. Error = WSAStartup((int) VersionRequired, &SocketData);
  93. if (Error != 0)
  94. {
  95. DebugLog((DEB_ERROR,"WSAStartup failed: 0x%x\n",Error));
  96. Status = STATUS_NO_LOGON_SERVERS;
  97. goto Cleanup;
  98. }
  99. //
  100. // Make sure the version is high enough for us
  101. //
  102. if ((LOBYTE(SocketData.wVersion) < HIBYTE(VersionRequired)) ||
  103. (((LOBYTE(SocketData.wVersion) == HIBYTE(VersionRequired)) &&
  104. (HIBYTE(SocketData.wVersion) < LOBYTE(VersionRequired)))))
  105. {
  106. DebugLog((DEB_ERROR,"Invalid socket version: wanted 0x%x, got 0x%x\n",
  107. VersionRequired, SocketData.wVersion));
  108. Status = STATUS_NO_LOGON_SERVERS;
  109. goto Cleanup;
  110. }
  111. if (SocketData.iMaxSockets < MinSockets)
  112. {
  113. DebugLog((DEB_ERROR,"Not enough sockets available: wanted %d, got %d\n",
  114. MinSockets, SocketData.iMaxSockets ));
  115. Status = STATUS_NO_LOGON_SERVERS;
  116. goto Cleanup;
  117. }
  118. #ifndef WIN32_CHICAGO
  119. //
  120. // Check if TCP is an available xport
  121. //
  122. protocols[0] = IPPROTO_TCP;
  123. protocols[1] = NULL;
  124. nRet = WSAEnumProtocols(protocols, lpProtocolBuf, &dwBufLen);
  125. if (nRet == 0)
  126. {
  127. //
  128. // Tcp is not installed as a xport.
  129. //
  130. D_DebugLog((DEB_T_SOCK,"WSAEnumProtocols returned 0x%x.\n", nRet));
  131. *TcpNotInstalled = TRUE;
  132. }
  133. #endif // WIN32_CHICAGO
  134. Cleanup:
  135. if (!NT_SUCCESS(Status))
  136. {
  137. if (SocketStarts != -1)
  138. {
  139. WSACleanup();
  140. InterlockedDecrement(&SocketStarts);
  141. }
  142. }
  143. return(Status);
  144. }
  145. //+-------------------------------------------------------------------------
  146. //
  147. // Function: KerbCleanupSockets
  148. //
  149. // Synopsis: Cleansup socket handling code
  150. //
  151. // Effects: calls WSACleanup()
  152. //
  153. // Arguments:
  154. //
  155. // Requires:
  156. //
  157. // Returns:
  158. //
  159. // Notes:
  160. //
  161. //
  162. //--------------------------------------------------------------------------
  163. VOID
  164. KerbCleanupSockets(
  165. )
  166. {
  167. if (InterlockedDecrement(&SocketStarts) < 0)
  168. {
  169. WSACleanup();
  170. }
  171. }
  172. //+-------------------------------------------------------------------------
  173. //
  174. // Function: KerbCloseSocket
  175. //
  176. // Synopsis: Closes a socket binding handle
  177. //
  178. // Effects: calls closesocket on the handle
  179. //
  180. // Arguments: SocketHandle - handle to close
  181. //
  182. // Requires:
  183. //
  184. // Returns: none
  185. //
  186. // Notes:
  187. //
  188. //
  189. //--------------------------------------------------------------------------
  190. VOID
  191. KerbCloseSocket(
  192. IN SOCKET SocketHandle
  193. )
  194. {
  195. int SockError;
  196. if (SocketHandle != 0)
  197. {
  198. SockError = closesocket(SocketHandle);
  199. if (SockError != 0)
  200. {
  201. DebugLog((DEB_ERROR,"CloseSocket failed: last error = %d\n",WSAGetLastError()));
  202. }
  203. }
  204. }
  205. //+-------------------------------------------------------------------------
  206. //
  207. // Function: KerbBindSocketByAddress
  208. //
  209. // Synopsis: Binds to the KDC socket on the specified address
  210. //
  211. // Effects:
  212. //
  213. // Arguments: Address - Address to bind to
  214. // AddressType - Address type, as specified by DC locator
  215. // ContextHandle - Receives bound socket
  216. //
  217. // Requires:
  218. //
  219. // Returns:
  220. //
  221. // Notes:
  222. //
  223. //
  224. //--------------------------------------------------------------------------
  225. NTSTATUS
  226. KerbBindSocketByAddress(
  227. IN PUNICODE_STRING Address,
  228. IN ULONG AddressType,
  229. IN BOOLEAN UseDatagram,
  230. IN USHORT PortNumber,
  231. OUT SOCKET * ContextHandle
  232. )
  233. {
  234. NTSTATUS Status = STATUS_SUCCESS;
  235. SOCKET ClientSocket = INVALID_SOCKET;
  236. struct sockaddr_in ServerAddress;
  237. struct sockaddr_in ClientAddress;
  238. LPHOSTENT ServerInfo = NULL;
  239. STRING AnsiAddress = {0};
  240. AnsiAddress.Buffer = NULL;
  241. Status = RtlUnicodeStringToAnsiString(
  242. &AnsiAddress,
  243. Address,
  244. TRUE
  245. );
  246. if (!NT_SUCCESS(Status))
  247. {
  248. goto Cleanup;
  249. }
  250. ClientSocket = socket(
  251. PF_INET,
  252. (UseDatagram ? SOCK_DGRAM : SOCK_STREAM),
  253. 0
  254. );
  255. if (ClientSocket == INVALID_SOCKET)
  256. {
  257. DebugLog((DEB_ERROR,"Failed to create socket: %d\n",WSAGetLastError()));
  258. Status = STATUS_NO_LOGON_SERVERS;
  259. goto Cleanup;
  260. }
  261. if (UseDatagram)
  262. {
  263. //
  264. // Bind client socket to any local interface and port
  265. //
  266. ClientAddress.sin_family = AF_INET;
  267. ClientAddress.sin_addr.s_addr = INADDR_ANY;
  268. ClientAddress.sin_port = 0; // no specific port
  269. if (bind(
  270. ClientSocket,
  271. (LPSOCKADDR) &ClientAddress,
  272. sizeof(ClientAddress)
  273. ) == SOCKET_ERROR )
  274. {
  275. DebugLog((DEB_ERROR,"Failed to bind client socket: %d\n",WSAGetLastError()));
  276. Status = STATUS_NO_LOGON_SERVERS;
  277. goto Cleanup;
  278. }
  279. }
  280. if (AddressType == DS_INET_ADDRESS)
  281. {
  282. ULONG InetAddress;
  283. //
  284. // Get the address of the server
  285. //
  286. InetAddress = inet_addr(AnsiAddress.Buffer);
  287. if (InetAddress == SOCKET_ERROR)
  288. {
  289. DebugLog((DEB_ERROR,"Failed to convert %Z to address: %d\n", &AnsiAddress, WSAGetLastError()));
  290. Status = STATUS_NO_LOGON_SERVERS;
  291. goto Cleanup;
  292. }
  293. ServerAddress.sin_family = AF_INET;
  294. RtlCopyMemory(
  295. &ServerAddress.sin_addr,
  296. &InetAddress,
  297. sizeof(ULONG)
  298. );
  299. }
  300. else
  301. {
  302. //
  303. // Get the address of the server
  304. //
  305. ServerInfo = gethostbyname(AnsiAddress.Buffer);
  306. if (ServerInfo == NULL)
  307. {
  308. DebugLog((DEB_ERROR,"Failed to get host %Z by name: %d\n", &AnsiAddress, WSAGetLastError()));
  309. Status = STATUS_NO_LOGON_SERVERS;
  310. goto Cleanup;
  311. }
  312. ServerAddress.sin_family = ServerInfo->h_addrtype;
  313. RtlCopyMemory(
  314. &ServerAddress.sin_addr,
  315. ServerInfo->h_addr,
  316. sizeof(ULONG)
  317. );
  318. }
  319. ServerAddress.sin_port = htons(PortNumber);
  320. if (connect(
  321. ClientSocket,
  322. (LPSOCKADDR) &ServerAddress,
  323. sizeof(ServerAddress)
  324. ) == SOCKET_ERROR)
  325. {
  326. DebugLog((DEB_ERROR,"Failed to connect to server %Z: %d\n",&AnsiAddress, WSAGetLastError()));
  327. Status = STATUS_NO_LOGON_SERVERS;
  328. goto Cleanup;
  329. }
  330. *ContextHandle = ClientSocket;
  331. D_DebugLog((DEB_TRACE,"Successfully bound to %Z\n",&AnsiAddress));
  332. Cleanup:
  333. if (AnsiAddress.Buffer != NULL)
  334. {
  335. RtlFreeAnsiString(&AnsiAddress);
  336. }
  337. if (!NT_SUCCESS(Status))
  338. {
  339. if (ClientSocket != INVALID_SOCKET)
  340. {
  341. closesocket(ClientSocket);
  342. }
  343. }
  344. return(Status);
  345. }
  346. //+-------------------------------------------------------------------------
  347. //
  348. // Function: KerbCallKdc
  349. //
  350. // Synopsis: Socket client stub for calling the KDC.
  351. //
  352. // Effects:
  353. //
  354. // Arguments:
  355. //
  356. // Requires:
  357. //
  358. // Returns:
  359. //
  360. // Notes:
  361. //
  362. //
  363. //--------------------------------------------------------------------------
  364. NTSTATUS
  365. KerbCallKdc(
  366. IN PUNICODE_STRING KdcAddress,
  367. IN ULONG AddressType,
  368. IN ULONG Timeout,
  369. IN BOOLEAN UseDatagram,
  370. IN USHORT PortNumber,
  371. IN PKERB_MESSAGE_BUFFER Input,
  372. OUT PKERB_MESSAGE_BUFFER Output
  373. )
  374. {
  375. NTSTATUS Status = STATUS_SUCCESS;
  376. ULONG Bytes;
  377. int NumberReady;
  378. SOCKET Socket = 0;
  379. PUCHAR RemainingBuffer;
  380. ULONG RemainingSize;
  381. ULONG SendSize ;
  382. fd_set ReadHandles;
  383. struct timeval TimeoutTime;
  384. ULONG NetworkSize;
  385. BOOLEAN RetriedOnce = FALSE;
  386. #ifndef WIN32_CHICAGO
  387. WSABUF Buffers[2] = {0};
  388. LPWSABUF SendBuffers = NULL;
  389. ULONG BufferCount = 0;
  390. int SendStatus;
  391. #endif // WIN32_CHICAGO
  392. //
  393. // Start out by binding to the KDC
  394. //
  395. DebugLog((DEB_TRACE, "Calling KDC: %S\n", KdcAddress->Buffer));
  396. Status = KerbBindSocketByAddress(
  397. KdcAddress,
  398. AddressType,
  399. UseDatagram,
  400. PortNumber,
  401. &Socket
  402. );
  403. if (!NT_SUCCESS(Status))
  404. {
  405. goto Cleanup;
  406. }
  407. RemainingBuffer = Input->Buffer;
  408. RemainingSize = Input->BufferSize;
  409. #ifndef WIN32_CHICAGO
  410. //
  411. // Use winsock2
  412. //
  413. Buffers[0].len = sizeof(ULONG);
  414. NetworkSize = htonl(RemainingSize);
  415. Buffers[0].buf = (PCHAR) &NetworkSize;
  416. Buffers[1].len = Input->BufferSize;
  417. Buffers[1].buf = (PCHAR) Input->Buffer;
  418. if (UseDatagram)
  419. {
  420. BufferCount = 1;
  421. SendBuffers = &Buffers[1];
  422. RemainingSize = Buffers[1].len;
  423. }
  424. else
  425. {
  426. BufferCount = 2;
  427. SendBuffers = &Buffers[0];
  428. RemainingSize = Buffers[0].len + Buffers[1].len;
  429. }
  430. RetrySend:
  431. SendStatus = WSASend(
  432. Socket,
  433. SendBuffers,
  434. BufferCount,
  435. &Bytes,
  436. 0, // no flags
  437. NULL, // no overlapped
  438. NULL // no completion routine
  439. );
  440. if ((SendStatus != 0) || (Bytes == 0))
  441. {
  442. DsysAssert(SendStatus == SOCKET_ERROR);
  443. DebugLog((DEB_ERROR,"Failed to send data: %d\n",WSAGetLastError()));
  444. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  445. goto Cleanup;
  446. }
  447. if (Bytes < RemainingSize)
  448. {
  449. RemainingSize -= Bytes;
  450. if (Bytes > SendBuffers->len)
  451. {
  452. //
  453. // We sent the whole of a buffer, so move on to the next
  454. //
  455. Bytes -= SendBuffers->len;
  456. DsysAssert(BufferCount > 1);
  457. BufferCount--;
  458. SendBuffers++;
  459. SendBuffers->len -= Bytes;
  460. SendBuffers->buf += Bytes;
  461. }
  462. else
  463. {
  464. SendBuffers->len -= Bytes;
  465. SendBuffers->buf += Bytes;
  466. }
  467. goto RetrySend;
  468. }
  469. #else // WIN32_CHICAGO
  470. //
  471. // Use winsock1 for win9x
  472. //
  473. //
  474. // For TCP, send length first
  475. //
  476. RetrySend:
  477. if (!UseDatagram)
  478. {
  479. NetworkSize = htonl(RemainingSize);
  480. Bytes = send(Socket, (char *)&NetworkSize,sizeof(ULONG), 0);
  481. if (Bytes != sizeof(ULONG) )
  482. {
  483. DebugLog((DEB_ERROR,"Failed to send TCP packet length: bytes sent = %d, last err = %d\n",
  484. Bytes,
  485. WSAGetLastError()));
  486. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  487. goto Cleanup;
  488. }
  489. }
  490. do
  491. {
  492. if (!UseDatagram)
  493. {
  494. if ( RemainingSize > TcpFragLength )
  495. {
  496. SendSize = TcpFragLength ;
  497. }
  498. else
  499. {
  500. SendSize = RemainingSize ;
  501. }
  502. if ( TcpFragDelay )
  503. {
  504. Sleep( TcpFragDelay );
  505. }
  506. }
  507. else
  508. {
  509. SendSize = RemainingSize ;
  510. }
  511. D_DebugLog(( DEB_T_SOCK, "Sending %x bytes to %wZ\n",
  512. SendSize, KdcAddress ));
  513. Bytes = send(Socket, (char *) RemainingBuffer, SendSize, 0);
  514. if (Bytes == SOCKET_ERROR)
  515. {
  516. DebugLog((DEB_ERROR,"Failed to send data: %d\n",WSAGetLastError()));
  517. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  518. goto Cleanup;
  519. }
  520. if (Bytes != SendSize)
  521. {
  522. DebugLog((DEB_ERROR,"Failed to send all data - only send %d out of %d\n",
  523. Bytes, RemainingSize ));
  524. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  525. goto Cleanup;
  526. }
  527. RemainingBuffer += Bytes;
  528. RemainingSize -= Bytes;
  529. } while ((Bytes != 0) && (RemainingSize != 0));
  530. #endif
  531. //
  532. // Now select on the socket and wait for a response
  533. // ReadHandles and TimeoutTime must be reset each time, cause winsock
  534. // zeroes them out in case of error
  535. ReadHandles.fd_count = 1;
  536. ReadHandles.fd_array[0] = Socket;
  537. TimeoutTime.tv_sec = Timeout;
  538. TimeoutTime.tv_usec = 0;
  539. D_DebugLog(( DEB_T_SOCK, "Socket being used for select is 0x%x\n", ReadHandles.fd_array[0] ));
  540. NumberReady = select(
  541. 1,
  542. &ReadHandles,
  543. NULL,
  544. NULL,
  545. &TimeoutTime
  546. );
  547. if ((NumberReady == SOCKET_ERROR) ||
  548. (NumberReady == 0))
  549. {
  550. DebugLog((DEB_ERROR,"Failed to select on response on socket 0x%x from kdc: %d\n", ReadHandles.fd_array[0], WSAGetLastError()));
  551. DebugLog((DEB_ERROR,"select returned %d\n",NumberReady));
  552. //
  553. // Retry again and wait.
  554. //
  555. if ((NumberReady == 0) && (!RetriedOnce))
  556. {
  557. RetriedOnce = TRUE;
  558. goto RetrySend;
  559. }
  560. Status = STATUS_NO_LOGON_SERVERS;
  561. goto Cleanup;
  562. }
  563. //
  564. // Now receive the data
  565. //
  566. if (UseDatagram)
  567. {
  568. Output->BufferSize = KERB_MAX_KDC_RESPONSE_SIZE;
  569. Output->Buffer = (PUCHAR) MIDL_user_allocate(KERB_MAX_KDC_RESPONSE_SIZE);
  570. if (Output->Buffer == NULL)
  571. {
  572. Status = STATUS_INSUFFICIENT_RESOURCES;
  573. goto Cleanup;
  574. }
  575. Bytes = recv(
  576. Socket,
  577. (char *) Output->Buffer,
  578. Output->BufferSize,
  579. 0
  580. );
  581. if ((Bytes == SOCKET_ERROR) || (Bytes == 0))
  582. {
  583. DebugLog((DEB_ERROR,"Failed to receive socket data: %d\n",WSAGetLastError()));
  584. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  585. goto Cleanup;
  586. }
  587. Output->BufferSize = Bytes;
  588. }
  589. else
  590. {
  591. Bytes = recv(
  592. Socket,
  593. (char *) &NetworkSize,
  594. sizeof(ULONG),
  595. 0
  596. );
  597. if (Bytes != sizeof(ULONG) )
  598. {
  599. DebugLog((DEB_ERROR,"Failed to receive socket data: %d\n",WSAGetLastError()));
  600. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  601. goto Cleanup;
  602. }
  603. RemainingSize = ntohl(NetworkSize);
  604. Output->BufferSize = RemainingSize;
  605. Output->Buffer = (PUCHAR) MIDL_user_allocate(RemainingSize);
  606. if (Output->Buffer == NULL)
  607. {
  608. Status = STATUS_INSUFFICIENT_RESOURCES;
  609. goto Cleanup;
  610. }
  611. while (RemainingSize != 0)
  612. {
  613. //
  614. // Make sure there is data ready
  615. //
  616. D_DebugLog(( DEB_T_SOCK, "Socket being used for select is 0x%x\n", ReadHandles.fd_array[0] ));
  617. NumberReady = select(
  618. 1,
  619. &ReadHandles,
  620. NULL,
  621. NULL,
  622. &TimeoutTime
  623. );
  624. if ((NumberReady == SOCKET_ERROR) ||
  625. (NumberReady == 0))
  626. {
  627. DebugLog((DEB_ERROR,"Failed to select on response on socket 0x%x from kdc: %d\n", ReadHandles.fd_array[0], WSAGetLastError()));
  628. DebugLog((DEB_ERROR,"select returned %d\n",NumberReady));
  629. Status = STATUS_NO_LOGON_SERVERS;
  630. goto Cleanup;
  631. }
  632. //
  633. // Receive the data
  634. //
  635. Bytes = recv(
  636. Socket,
  637. (char *) Output->Buffer + Output->BufferSize - RemainingSize,
  638. RemainingSize,
  639. 0
  640. );
  641. if ((Bytes == SOCKET_ERROR) || (Bytes == 0))
  642. {
  643. DebugLog((DEB_ERROR,"Failed to receive socket data: %d\n",WSAGetLastError()));
  644. Status = SEC_E_NO_AUTHENTICATING_AUTHORITY;
  645. goto Cleanup;
  646. }
  647. RemainingSize -= Bytes;
  648. }
  649. }
  650. Cleanup:
  651. if (Socket != 0)
  652. {
  653. KerbCloseSocket(Socket);
  654. }
  655. if (!NT_SUCCESS(Status))
  656. {
  657. if (Output->Buffer != NULL)
  658. {
  659. MIDL_user_free(Output->Buffer);
  660. Output->Buffer = NULL;
  661. }
  662. }
  663. return(Status);
  664. }