|
|
//+-----------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (c) Microsoft Corporation 1992 - 1995
//
// File: dgutil.cxx
//
// Contents: Server support routines for datagram sockets
//
//
// History: 10-July-1996 MikeSw Created
//
//------------------------------------------------------------------------
#include "kdcsvr.hxx"
#include "sockutil.h"
extern "C" { #include <atq.h>
#include <nlrepl.h>
} #include <issched.hxx>
#include "fileno.h"
#define FILENO FILENO_DGUTIL
#define KDC_KEY "System\\CurrentControlSet\\Services\\kdc"
#define KDC_PARAMETERS_KEY KDC_KEY "\\parameters"
#define KDC_MAX_ACCEPT_BUFFER 5000
#define KDC_MAX_ACCEPT_OUTSTANDING 5
#define KDC_ACCEPT_TIMEOUT 100
#define KDC_LISTEN_BACKLOG 10
#define KDC_CONTEXT_TIMEOUT 50
extern BOOLEAN KdcSocketsInitialized;
typedef struct _KDC_DATAGRAM_ENDPOINT { SOCKADDR LocalAddress; PKDC_GET_TICKET_ROUTINE EndpointFunction; PVOID Endpoint; } KDC_DATAGRAM_ENDPOINT, *PKDC_DATAGRAM_ENDPOINT;
PKDC_DATAGRAM_ENDPOINT DatagramEndpoints = NULL; ULONG DatagramEndpointCount = 0; RTL_RESOURCE DatagramEndpointLock; BOOL DatagramEndpointLockInitialized = FALSE; SOCKET KdcWinsockPnpSocket = INVALID_SOCKET; HANDLE KdcWinsockPnpEvent = NULL; HANDLE KdcPnpEventChangeHandle = NULL;
//+-------------------------------------------------------------------------
//
// Function: KdcAtqDgIoCompletion
//
// Synopsis:
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
VOID KdcAtqDgIoCompletion( IN PVOID Context, IN DWORD BytesWritten, IN DWORD CompletionStatus, IN OVERLAPPED * lpo ) {
PVOID Buffer; SOCKADDR * RemoteAddress = NULL; INT AddressSize; PATQ_CONTEXT AtqContext = (PATQ_CONTEXT) Context; SOCKET NewSocket = INVALID_SOCKET; KERB_MESSAGE_BUFFER InputMessage; KERB_MESSAGE_BUFFER OutputMessage; ULONG_PTR KdcContext; WSABUF SocketBuffer; PKDC_DATAGRAM_ENDPOINT Endpoint;
TRACE(KDC,KdcAtqDgIoCompletion, DEB_FUNCTION);
if (Context == NULL) { return; }
KdcContext = AtqContextGetInfo( AtqContext, ATQ_INFO_COMPLETION_CONTEXT );
//
// If the context is 1, then this is the completion from a write, so close
// this down.
//
if (KdcContext == 1) { lpo = NULL; }
//
// If a client connects and then disconnects gracefully ,we will get a
// completion with zero bytes and success status.
//
if ((BytesWritten == 0) && (CompletionStatus == NO_ERROR)) { CompletionStatus = WSAECONNABORTED; }
if ((CompletionStatus != NO_ERROR) || (lpo == NULL) || !KdcSocketsInitialized) { D_DebugLog((DEB_T_SOCK,"IoCompletion: CompletionStatus = 0x%x\n",CompletionStatus)); D_DebugLog((DEB_T_SOCK,"IoCompletion: lpo = %p\n",lpo)); D_DebugLog((DEB_T_SOCK, "Freeing context %p\n",AtqContext));
if (CompletionStatus == ERROR_OPERATION_ABORTED) { AtqCloseSocket( AtqContext, TRUE ); AtqFreeContext( (PATQ_CONTEXT) AtqContext, FALSE ); return; } else if ( CompletionStatus != ERROR_MORE_DATA ) { AtqFreeContext( (PATQ_CONTEXT) AtqContext, TRUE ); return; } }
//
// Obtain shared access to the global endpoint list
//
RtlAcquireResourceShared( &DatagramEndpointLock , TRUE );
//
// Can not proceed unless there are endpoints registered
//
if ( DatagramEndpointCount == 0 ) { AtqFreeContext( (PATQ_CONTEXT) AtqContext, TRUE ); goto Cleanup; }
AtqGetDatagramAddrs( AtqContext, &NewSocket, &Buffer, (PVOID *) &Endpoint, &RemoteAddress, &AddressSize );
//
// If the remote address is port 88, don't respond, as we don't
// want to be vulnerable to a loopback attack.
//
if ((AddressSize >= sizeof(SOCKADDR_IN) && ((((SOCKADDR_IN *) RemoteAddress)->sin_port == KERB_KDC_PORT) || (((SOCKADDR_IN *) RemoteAddress)->sin_port == KERB_KPASSWD_PORT)))) { //
// Just free up the context so it can be reused.
//
AtqFreeContext( (PATQ_CONTEXT) AtqContext, TRUE ); goto Cleanup; }
//fester
D_DebugLog((DEB_T_SOCK, "Bytes written - %x\n", BytesWritten));
//
// There is a buffer, so use it to do the KDC thang.
//
InputMessage.BufferSize = BytesWritten; InputMessage.Buffer = (PUCHAR) Buffer; OutputMessage.Buffer = NULL;
if ( CompletionStatus != ERROR_MORE_DATA ) { Endpoint->EndpointFunction( NULL, // no atq context for retries
RemoteAddress, &Endpoint->LocalAddress, &InputMessage, &OutputMessage ); } else { //
// For now, if client sends us more data over UDP than we can swallow
// (indicated by a completion status of ERROR_MORE_DATA), just send
// a response of "response too big" back to them, which will cause
// the Windows client (and hopefully a future MIT client) to switch
// over to TCP
//
// In a future release, ATQ should be less timid and give us all the
// UDP data that was received, in which case this logic could be pulled
//
KERB_EXT_ERROR ExtendedError = {0,0};
KerbBuildErrorMessageEx( KRB_ERR_RESPONSE_TOO_BIG, &ExtendedError, SecData.KdcDnsRealmName(), SecData.KdcInternalName(), NULL, NULL, 0, &OutputMessage.BufferSize, &OutputMessage.Buffer ); }
//
// If there is a response, write it back to the sender.
//
if (OutputMessage.Buffer != NULL) { DsysAssert(OutputMessage.BufferSize < max( KdcGlobalMaxDatagramReplySize, KDC_MAX_ACCEPT_BUFFER));
RtlCopyMemory( Buffer, OutputMessage.Buffer, OutputMessage.BufferSize );
KdcFreeEncodedData(OutputMessage.Buffer);
SocketBuffer.buf = (char *) Buffer; SocketBuffer.len = OutputMessage.BufferSize;
AtqContextSetInfo( AtqContext, ATQ_INFO_COMPLETION_CONTEXT, 1 );
if (!AtqWriteDatagramSocket( (PATQ_CONTEXT) AtqContext, &SocketBuffer, 1, // 1 buffer
NULL // no OVERLAPPED
)) { DebugLog((DEB_ERROR,"Datagram write failed for %d bytes: 0x%x\n",OutputMessage.BufferSize,GetLastError())); AtqCloseSocket( AtqContext, TRUE ); AtqFreeContext( (PATQ_CONTEXT) AtqContext, TRUE ); } } else { //
// Just free up the context so it can be reused.
//
AtqFreeContext( (PATQ_CONTEXT) AtqContext, TRUE ); }
Cleanup:
RtlReleaseResource( &DatagramEndpointLock ); }
//+-------------------------------------------------------------------------
//
// Function: KdcCreateDgAtqEndpoint
//
// Synopsis: Sets up a datagram endpoint
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KdcCreateDgAtqEndpoint( IN USHORT Port, IN PVOID EndpointContext, IN ULONG IpAddress, OUT PVOID * Endpoint ) { NTSTATUS Status = STATUS_SUCCESS; ATQ_ENDPOINT_CONFIGURATION EndpointConfig; SOCKET EndpointSocket = INVALID_SOCKET; int RecvBufSize;
//
// Create the endpoint config
//
EndpointConfig.ListenPort = Port; EndpointConfig.IpAddress = IpAddress; EndpointConfig.cbAcceptExRecvBuffer = max( KdcGlobalMaxDatagramReplySize, KDC_MAX_ACCEPT_BUFFER); EndpointConfig.nAcceptExOutstanding = KDC_MAX_ACCEPT_OUTSTANDING; EndpointConfig.AcceptExTimeout = KDC_ACCEPT_TIMEOUT;
EndpointConfig.pfnConnect = NULL; EndpointConfig.pfnConnectEx = KdcAtqDgIoCompletion; EndpointConfig.pfnIoCompletion = KdcAtqDgIoCompletion;
EndpointConfig.fDatagram = TRUE; EndpointConfig.fLockDownPort = TRUE;
EndpointConfig.fReverseQueuing = FALSE; EndpointConfig.cbDatagramWSBufSize = 0; // means use the default.
*Endpoint = AtqCreateEndpoint( &EndpointConfig, EndpointContext ); if (*Endpoint == NULL) { DebugLog((DEB_ERROR,"Failed to create ATQ endpoint\n")); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
//
// Get the socket so we can change the recieve buffer size
//
EndpointSocket = (SOCKET) AtqEndpointGetInfo( *Endpoint, EndpointInfoListenSocket );
RecvBufSize = 0x8000; // 32 k buffers
if (setsockopt( EndpointSocket, SOL_SOCKET, SO_RCVBUF, (const char *) &RecvBufSize, sizeof(int) )) { DebugLog((DEB_ERROR,"Failed to set recv buf size to 32k: 0x%x, %d\n", WSAGetLastError(),WSAGetLastError())); }
//
// Start the endpoint
//
if (!AtqStartEndpoint(*Endpoint)) { DebugLog((DEB_ERROR, "Failed to add ATQ endpoint\n")); Status = STATUS_UNSUCCESSFUL; goto Cleanup; } Cleanup: return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KdcGetAddressListFromWinsock
//
// Synopsis: gets the list of addresses from a winsock ioctl
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KdcGetAddressListFromWinsock( OUT LPSOCKET_ADDRESS_LIST * SocketAddressList ) { ULONG BytesReturned = 150; LPSOCKET_ADDRESS_LIST AddressList = NULL; INT i,j; ULONG NetStatus = STATUS_SUCCESS;
for (;;) {
//
// Allocate a buffer that should be big enough.
//
if ( AddressList != NULL ) { MIDL_user_free( AddressList ); }
AddressList = (LPSOCKET_ADDRESS_LIST) MIDL_user_allocate( BytesReturned );
if ( AddressList == NULL ) { NetStatus = ERROR_NOT_ENOUGH_MEMORY; goto Cleanup; }
//
// Get the list of IP addresses
//
NetStatus = WSAIoctl( KdcWinsockPnpSocket, SIO_ADDRESS_LIST_QUERY, NULL, // No input buffer
0, // No input buffer
(PVOID) AddressList, BytesReturned, &BytesReturned, NULL, // No overlapped,
NULL ); // Not async
if ( NetStatus != 0 ) { NetStatus = WSAGetLastError(); //
// If the buffer isn't big enough, try again.
//
if ( NetStatus == WSAEFAULT ) { continue; }
DebugLog((DEB_ERROR,"LdapUdpPnpBind: Cannot WSAIoctl SIO_ADDRESS_LIST_QUERY %ld %ld\n", NetStatus, BytesReturned)); goto Cleanup; }
break; }
//
// Weed out any zero IP addresses and other invalid addresses
//
for ( i = 0, j = 0; i < AddressList->iAddressCount; i++ ) { PSOCKET_ADDRESS SocketAddress;
//
// Copy this address to the front of the list.
//
AddressList->Address[j] = AddressList->Address[i];
//
// If the address isn't valid,
// skip it.
//
SocketAddress = &AddressList->Address[j];
if ( SocketAddress->iSockaddrLength == 0 || SocketAddress->lpSockaddr == NULL || SocketAddress->lpSockaddr->sa_family != AF_INET || ((PSOCKADDR_IN)(SocketAddress->lpSockaddr))->sin_addr.s_addr == 0 ) {
} else {
//
// Otherwise keep it.
//
j++; } } AddressList->iAddressCount = j; *SocketAddressList = AddressList; AddressList = NULL; Cleanup: if (AddressList != NULL) { MIDL_user_free(AddressList); } if (NetStatus != ERROR_SUCCESS) { return(STATUS_UNSUCCESSFUL); } else { return(STATUS_SUCCESS); } }
//+-------------------------------------------------------------------------
//
// Function: KdcUpdateAddressesWorker
//
// Synopsis: Updates the IP addresses used for datagram sockest by
// stopping the endpoints and then starting them with the
// new addresses
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
ULONG KdcUpdateAddressesWorker( IN PVOID IgnoredParameter ) { ULONG Index; INT IntIndex; NTSTATUS Status = STATUS_SUCCESS; LPSOCKET_ADDRESS_LIST AddressList = NULL; DWORD NetStatus; DWORD BytesReturned ; BOOL DatagramEndpointsLocked = FALSE;
D_DebugLog(( DEB_TRACE, "KdcUpdateAddressesWorker\n" ));
//
// Tell winsock we want address list changes on this socket:
//
if ( KdcWinsockPnpSocket != INVALID_SOCKET ) { NetStatus = WSAIoctl( KdcWinsockPnpSocket, SIO_ADDRESS_LIST_CHANGE, NULL, 0, NULL, 0, &BytesReturned, NULL, NULL );
if ( NetStatus != 0 ) { NetStatus = WSAGetLastError(); if ( NetStatus != WSAEWOULDBLOCK ) { DebugLog((DEB_ERROR,"WSASocket failed with %ld\n", NetStatus )); Status = STATUS_UNSUCCESSFUL; goto Cleanup; } } }
//
// Get the list of socket addresses
//
Status = KdcGetAddressListFromWinsock( &AddressList );
if ( !NT_SUCCESS( Status )) { goto Cleanup; }
//
// Cleanup any old endpoints
//
if (DatagramEndpoints != NULL) { for (Index = 0; Index < DatagramEndpointCount ; Index++ ) { if (DatagramEndpoints[Index].Endpoint != NULL) { //
// Stopping the endpoint prevents future completions
//
while ( FALSE == AtqStopEndpoint( DatagramEndpoints[Index].Endpoint )) { Sleep( 5000 ); }
//
// The endpoint won't close until all outstanding operations on it
// have completed
//
while ( FALSE == AtqCloseEndpoint( DatagramEndpoints[Index].Endpoint )) { Sleep( 5000 ); } } } }
//
// Must acquire the lock now since we're manipulating global variables
// that this lock protects.
//
RtlAcquireResourceExclusive( &DatagramEndpointLock, TRUE ); DatagramEndpointsLocked = TRUE;
MIDL_user_free(DatagramEndpoints); DatagramEndpoints = NULL; DatagramEndpointCount = 0;
//
// Create new endpoints
//
DatagramEndpoints = (PKDC_DATAGRAM_ENDPOINT) MIDL_user_allocate( sizeof(KDC_DATAGRAM_ENDPOINT) * AddressList->iAddressCount * 2 );
if (DatagramEndpoints == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; }
RtlZeroMemory( DatagramEndpoints, sizeof(KDC_DATAGRAM_ENDPOINT) * AddressList->iAddressCount * 2 );
//
// Create an endpoint for the KDC and for KPASSWD for each transport
//
for (IntIndex = 0; IntIndex < AddressList->iAddressCount ; IntIndex++ ) { RtlCopyMemory( &DatagramEndpoints[DatagramEndpointCount].LocalAddress, AddressList->Address[IntIndex].lpSockaddr, sizeof(SOCKADDR_IN) ); DatagramEndpoints[DatagramEndpointCount].EndpointFunction = KdcGetTicket;
Status = KdcCreateDgAtqEndpoint( KERB_KDC_PORT, &DatagramEndpoints[DatagramEndpointCount], ((PSOCKADDR_IN) &DatagramEndpoints[DatagramEndpointCount].LocalAddress)->sin_addr.s_addr, &DatagramEndpoints[DatagramEndpointCount].Endpoint );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
DatagramEndpointCount++;
//
// Create the KPASSWD endpoint
//
RtlCopyMemory( &DatagramEndpoints[DatagramEndpointCount].LocalAddress, AddressList->Address[IntIndex].lpSockaddr, sizeof(SOCKADDR_IN) ); DatagramEndpoints[DatagramEndpointCount].EndpointFunction = KdcChangePassword;
Status = KdcCreateDgAtqEndpoint( KERB_KPASSWD_PORT, &DatagramEndpoints[DatagramEndpointCount], ((PSOCKADDR_IN) &DatagramEndpoints[DatagramEndpointCount].LocalAddress)->sin_addr.s_addr, &DatagramEndpoints[DatagramEndpointCount].Endpoint );
if (!NT_SUCCESS(Status)) { goto Cleanup; }
DatagramEndpointCount++;
}
Cleanup:
if (!NT_SUCCESS(Status)) { if (DatagramEndpoints != NULL) { //
// Allow completion routines to fire while the endpoints are being closed
// by releasing the lock for the duration of the 'stop' operation
//
if ( DatagramEndpointsLocked ) { RtlReleaseResource( &DatagramEndpointLock ); DatagramEndpointsLocked = FALSE; }
for (Index = 0; Index < DatagramEndpointCount ; Index++ ) { if (DatagramEndpoints[Index].Endpoint != NULL) { while ( FALSE == AtqStopEndpoint( DatagramEndpoints[Index].Endpoint )) { Sleep( 5000 ); }
while ( FALSE == AtqCloseEndpoint( DatagramEndpoints[Index].Endpoint )) { Sleep( 5000 ); } } }
//
// Must re-acquire the lock now since we're manipulating global variables
// that this lock protects.
//
RtlAcquireResourceExclusive( &DatagramEndpointLock, TRUE ); DatagramEndpointsLocked = TRUE;
MIDL_user_free(DatagramEndpoints); DatagramEndpoints = NULL; DatagramEndpointCount = 0; } }
if ( DatagramEndpointsLocked ) { RtlReleaseResource( &DatagramEndpointLock ); }
MIDL_user_free(AddressList);
return((ULONG) Status); }
//+-------------------------------------------------------------------------
//
// Function: KdcInitializeSockets
//
// Synopsis: Initializes the KDCs socket handling code
//
// Effects:
//
// Arguments: none
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KdcInitializeDatagramSockets( VOID ) { NTSTATUS Status = STATUS_SUCCESS; DWORD NetStatus;
__try { RtlInitializeResource(&DatagramEndpointLock); } __except( EXCEPTION_EXECUTE_HANDLER ) { Status = GetExceptionCode(); }
if (!NT_SUCCESS(Status)) { goto Cleanup; } else { DatagramEndpointLockInitialized = TRUE; }
//
// Initialize the asynchronous thread queue.
//
if (!AtqInitialize(0)) { DebugLog((DEB_ERROR,"Failed to initialize ATQ\n")); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
//
// Open a socket to get winsock PNP notifications on.
//
KdcWinsockPnpSocket = WSASocket( AF_INET, SOCK_DGRAM, 0, // PF_INET,
NULL, 0, 0 );
if ( KdcWinsockPnpSocket == INVALID_SOCKET ) {
NetStatus = WSAGetLastError(); DebugLog((DEB_ERROR,"WSASocket failed with %ld\n", NetStatus )); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
//
// Open an event to wait on.
//
KdcWinsockPnpEvent = CreateEvent( NULL, // No security ettibutes
FALSE, // Auto reset
FALSE, // Initially not signaled
NULL); // No Name
if ( KdcWinsockPnpEvent == NULL ) { NetStatus = GetLastError(); DebugLog((DEB_ERROR,"Cannot create Winsock PNP event %ld\n", NetStatus )); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
//
// Associate the event with new addresses becoming available on the socket.
//
NetStatus = WSAEventSelect( KdcWinsockPnpSocket, KdcWinsockPnpEvent, FD_ADDRESS_LIST_CHANGE );
if ( NetStatus != 0 ) { NetStatus = WSAGetLastError(); DebugLog((DEB_ERROR,"Can't WSAEventSelect %ld\n", NetStatus )); Status = STATUS_UNSUCCESSFUL; goto Cleanup; }
Status = (NTSTATUS) KdcUpdateAddressesWorker( NULL ); if (!NT_SUCCESS(Status)) { DebugLog((DEB_ERROR,"Failed to udpate datagram addresses\n")); goto Cleanup; }
D_DebugLog((DEB_TRACE, "Successfully started ATQ listening\n"));
if ( KdcPnpEventChangeHandle == NULL ) {
KdcPnpEventChangeHandle = LsaIRegisterNotification( KdcUpdateAddressesWorker, NULL, // no parameter,
NOTIFIER_TYPE_HANDLE_WAIT, 0, // no class
0, // no flags
0, // no interval
KdcWinsockPnpEvent ); if (KdcPnpEventChangeHandle == NULL) { DebugLog((DEB_ERROR,"Failed to register KDC pnp event change handle.\n")); } }
Cleanup:
if (!NT_SUCCESS(Status)) { KdcShutdownSockets(); } return(Status); }
//+-------------------------------------------------------------------------
//
// Function: KdcShutdownDatagramSockets
//
// Synopsis: Shuts down the KDC socket handling code
//
// Effects:
//
// Arguments:
//
// Requires:
//
// Returns:
//
// Notes:
//
//
//--------------------------------------------------------------------------
NTSTATUS KdcShutdownDatagramSockets( VOID ) { ULONG Index;
TRACE(KDC,KdcShutdownSockets, DEB_FUNCTION);
if ( KdcPnpEventChangeHandle != NULL ) { LsaICancelNotification(KdcPnpEventChangeHandle); KdcPnpEventChangeHandle = NULL; }
if ( KdcWinsockPnpEvent != NULL ) { CloseHandle(KdcWinsockPnpEvent); KdcWinsockPnpEvent = NULL; }
if ( KdcWinsockPnpSocket != INVALID_SOCKET ) { closesocket(KdcWinsockPnpSocket); KdcWinsockPnpSocket = INVALID_SOCKET; }
//
// Go through the list of contexts and close them all.
//
if (DatagramEndpoints != NULL) { for (Index = 0; Index < DatagramEndpointCount ; Index++ ) { if (DatagramEndpoints[Index].Endpoint != NULL) { while ( FALSE == AtqStopEndpoint( DatagramEndpoints[Index].Endpoint )) { Sleep( 5000 ); }
while ( FALSE == AtqCloseEndpoint( DatagramEndpoints[Index].Endpoint )) { Sleep( 5000 ); } } }
RtlAcquireResourceExclusive( &DatagramEndpointLock, TRUE );
MIDL_user_free(DatagramEndpoints); DatagramEndpoints = NULL; DatagramEndpointCount = 0;
RtlReleaseResource( &DatagramEndpointLock ); }
if (!AtqTerminate()) { DebugLog((DEB_ERROR, "Failed to terminate ATQ!!!\n")); }
if ( DatagramEndpointLockInitialized ) { RtlDeleteResource(&DatagramEndpointLock); DatagramEndpointLockInitialized = FALSE; }
return(STATUS_SUCCESS); }
|