//+----------------------------------------------------------------------- // // Microsoft Windows // // Copyright (c) Microsoft Corporation 1992 - 1995 // // File: sockutil.cxx // // Contents: Server support routines for sockets // // // History: 10-July-1996 MikeSw Created // //------------------------------------------------------------------------ #include "kdcsvr.hxx" #include "sockutil.h" extern "C" { #include } #include #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 BOOLEAN KdcSocketsInitialized = FALSE; PVOID KdcEndpoint = NULL; PVOID KpasswdEndpoint = NULL; RTL_CRITICAL_SECTION KdcAtqContextLock; LIST_ENTRY KdcAtqContextList; NTSTATUS KdcInitializeDatagramSockets( VOID ); NTSTATUS KdcShutdownDatagramSockets( VOID ); //+------------------------------------------------------------------------- // // Function: KdcAtqCloseSocket // // Synopsis: Wrapper to close socket to avoid socket leaks // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID KdcAtqCloseSocket( IN PKDC_ATQ_CONTEXT Context ) { D_DebugLog ((DEB_T_SOCK, "Closing socket for 0x%x\n", Context)); AtqCloseSocket((PATQ_CONTEXT) Context->AtqContext, TRUE); Context->Flags |= KDC_ATQ_SOCKET_CLOSED; } //+------------------------------------------------------------------------- // // Function: KdcAtqReferenceContext // // Synopsis: References a kdc ATQ context by one // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID KdcAtqReferenceContext( IN PKDC_ATQ_CONTEXT Context ) { D_DebugLog ((DEB_T_SOCK, "Referencing KdcContext 0x%x\n", Context)); RtlEnterCriticalSection(&KdcAtqContextLock); Context->References++; RtlLeaveCriticalSection(&KdcAtqContextLock); } //+------------------------------------------------------------------------- // // Function: KdcAtqDereferenceContext // // Synopsis: Dereferences a context & unlinks & frees it when the // ref count goes to zero // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- BOOLEAN KdcAtqDereferenceContext( IN PKDC_ATQ_CONTEXT * KdcContext ) { PKDC_ATQ_CONTEXT Context = *KdcContext; BOOLEAN Deleted = FALSE; D_DebugLog ((DEB_T_SOCK, "Dereferencing KdcContext 0x%x\n", Context)); if (Context == NULL) { goto Cleanup; } RtlEnterCriticalSection(&KdcAtqContextLock); Context->References--; if (Context->References == 0) { Deleted = TRUE; RemoveEntryList( &Context->Next ); } RtlLeaveCriticalSection(&KdcAtqContextLock); if (Deleted) { if (((Context->Flags & KDC_ATQ_SOCKET_USED) != 0) && ((Context->Flags & KDC_ATQ_SOCKET_CLOSED) == 0)) { KdcAtqCloseSocket( Context ); } D_DebugLog ((DEB_T_SOCK, "Deleting KdcContext 0x%x\n", Context)); AtqFreeContext( (PATQ_CONTEXT) Context->AtqContext, TRUE ); if (Context->WriteBuffer != NULL) { KdcFreeEncodedData(Context->WriteBuffer); } if (Context->Buffer != NULL) { MIDL_user_free(Context->Buffer); } MIDL_user_free(Context); *KdcContext = NULL; } Cleanup: return(Deleted); } //+------------------------------------------------------------------------- // // Function: KdcAtqCreateContext // // Synopsis: Creates & links an ATQ context // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- PKDC_ATQ_CONTEXT KdcAtqCreateContext( IN PATQ_CONTEXT AtqContext, IN PVOID EndpointContext, IN LPOVERLAPPED lpo, IN PSOCKADDR ClientAddress, IN PSOCKADDR ServerAddress ) { PKDC_ATQ_CONTEXT KdcContext; if (!KdcSocketsInitialized) { return(NULL); } KdcContext = (PKDC_ATQ_CONTEXT) MIDL_user_allocate(sizeof(KDC_ATQ_CONTEXT)); if (KdcContext != NULL) { RtlZeroMemory( KdcContext, sizeof(KDC_ATQ_CONTEXT) ); KdcContext->AtqContext = AtqContext; KdcContext->Flags = KDC_ATQ_WRITE_CONTEXT; KdcContext->BufferLength = KERB_MAX_KDC_REQUEST_SIZE; KdcContext->UsedBufferLength = 0; KdcContext->lpo = lpo; KdcContext->EndpointContext = EndpointContext; KdcContext->ExpectedMessageSize = 0; KdcContext->WriteBuffer = NULL; KdcContext->References = 2; // one for the list, one for this copy RtlCopyMemory( &KdcContext->Address, ClientAddress, sizeof(SOCKADDR) ); RtlCopyMemory( &KdcContext->LocalAddress, ServerAddress, sizeof(SOCKADDR) ); KdcContext->Buffer = (PUCHAR) MIDL_user_allocate(KERB_MAX_KDC_REQUEST_SIZE); if (KdcContext->Buffer == NULL) { MIDL_user_free(KdcContext); KdcContext = NULL; } else { RtlEnterCriticalSection( &KdcAtqContextLock ); InsertHeadList(&KdcAtqContextList, &KdcContext->Next); RtlLeaveCriticalSection( &KdcAtqContextLock ); } } D_DebugLog ((DEB_T_SOCK, "Creating KdcContext 0x%x\n", KdcContext)); return(KdcContext); } //+------------------------------------------------------------------------- // // Function: KdcAtqConnectEx // // Synopsis: // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID KdcAtqConnectEx( IN PVOID Context, IN DWORD BytesWritten, IN DWORD CompletionStatus, IN OVERLAPPED * lpo ) { KERBERR KerbErr; PKDC_ATQ_CONTEXT KdcContext = NULL; PATQ_CONTEXT AtqContext = (PATQ_CONTEXT) Context; SOCKADDR * LocalAddress = NULL; SOCKADDR * RemoteAddress = NULL; SOCKET NewSocket = INVALID_SOCKET; KERB_MESSAGE_BUFFER InputMessage; KERB_MESSAGE_BUFFER OutputMessage; PVOID Buffer; PKDC_GET_TICKET_ROUTINE EndpointFunction = NULL; ULONG TotalBytes; TRACE(KDC,KdcAtqConnectEx, DEB_FUNCTION); if ((CompletionStatus != NO_ERROR) || !KdcSocketsInitialized) { D_DebugLog((DEB_T_SOCK," ConnectEx: CompletionStatus = 0x%x\n",CompletionStatus)); AtqCloseSocket( AtqContext, TRUE ); D_DebugLog((DEB_T_SOCK, "Freeing context %p\n",AtqContext)); AtqFreeContext( AtqContext, TRUE ); return; } // // Get the address information including the first write buffer // AtqGetAcceptExAddrs( AtqContext, &NewSocket, &Buffer, (PVOID *) &EndpointFunction, &LocalAddress, &RemoteAddress ); // // Verify that the size is something OK before continuing on // // // Read the number of bytes off the front of the message // if (BytesWritten >= sizeof(ULONG)) { TotalBytes = ntohl(*(PULONG)Buffer); if (TotalBytes >= KDC_MAX_BUFFER_LENGTH) { D_DebugLog((DEB_T_SOCK, "Received huge buffer - %x, bailing out now\n", TotalBytes)); AtqCloseSocket( AtqContext, TRUE ); AtqFreeContext( AtqContext, TRUE ); return; } } else { AtqCloseSocket( AtqContext, TRUE ); AtqFreeContext( AtqContext, TRUE ); return; } // // If the remote address is port 88 or 464, don't respond, as we don't // want to be vulnerable to a loopback attack. // if ((((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. // AtqCloseSocket( AtqContext, TRUE ); AtqFreeContext( AtqContext, TRUE ); return; } // // Set the timeout for future IOs on this context // AtqContextSetInfo( AtqContext, ATQ_INFO_TIMEOUT, KDC_CONTEXT_TIMEOUT ); // // Create a context // KdcContext = KdcAtqCreateContext( AtqContext, EndpointFunction, lpo, RemoteAddress, LocalAddress ); if (KdcContext == NULL) { AtqCloseSocket( AtqContext, TRUE ); AtqFreeContext( AtqContext, TRUE ); return; } AtqContextSetInfo( AtqContext, ATQ_INFO_COMPLETION_CONTEXT, (ULONG_PTR) KdcContext ); // // If we didn't receive all the data, go ahead and read more // KdcContext->ExpectedMessageSize = TotalBytes + sizeof(ULONG); if (KdcContext->ExpectedMessageSize > BytesWritten) { InputMessage.BufferSize = BytesWritten; InputMessage.Buffer = (PUCHAR) Buffer; KerbErr = KdcAtqRetrySocketRead( &KdcContext, &InputMessage ); if (!KERB_SUCCESS(KerbErr)) { DebugLog((DEB_ERROR, "Closing connection due to RetrySocketRead error\n")); DsysAssert(KdcContext->References == 1); } KdcAtqDereferenceContext(&KdcContext); return; } InputMessage.BufferSize = BytesWritten - sizeof(ULONG); InputMessage.Buffer = (PUCHAR) Buffer + sizeof(ULONG); OutputMessage.Buffer = NULL; // // Call either the KdcGetTicket or KdcChangePassword function, based // on which endpoint was used // KerbErr = EndpointFunction( &KdcContext, &KdcContext->Address, &KdcContext->LocalAddress, &InputMessage, &OutputMessage ); if ((KerbErr != KDC_ERR_NONE) || (OutputMessage.BufferSize != 0)) { // // We expect at least some level of message validity before // we'll return anything. // if (KerbErr == KDC_ERR_NO_RESPONSE) { // TBD: Log an "attack" event here. DebugLog((DEB_ERROR, "Bad buffer recieved, closing socket\n")); KdcAtqCloseSocket(KdcContext); KdcAtqDereferenceContext(&KdcContext); } else { ULONG NetworkSize; WSABUF Buffers[2]; NetworkSize = htonl(OutputMessage.BufferSize); Buffers[0].len = sizeof(DWORD); Buffers[0].buf = (PCHAR) &NetworkSize; Buffers[1].len = OutputMessage.BufferSize; Buffers[1].buf = (PCHAR) OutputMessage.Buffer; KdcContext->WriteBufferLength = OutputMessage.BufferSize; KdcContext->WriteBuffer = OutputMessage.Buffer; OutputMessage.Buffer = NULL; // // Reference the context for the read // KdcAtqReferenceContext(KdcContext); if (!AtqWriteSocket( (PATQ_CONTEXT) KdcContext->AtqContext, Buffers, 2, lpo )) { DebugLog((DEB_ERROR,"Failed to write kdc reply to atq: %0x%x\n",GetLastError())); KdcAtqDereferenceContext(&KdcContext); } } } if (OutputMessage.Buffer != NULL) { MIDL_user_free(OutputMessage.Buffer); } KdcAtqDereferenceContext(&KdcContext); } //+------------------------------------------------------------------------- // // Function: KdcAtqIoCompletion // // Synopsis: Callback routine for an io completion on a TCP socket // for the KDC // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID KdcAtqIoCompletion( IN PVOID Context, IN DWORD BytesWritten, IN DWORD CompletionStatus, IN OVERLAPPED * lpo ) { PVOID Buffer; PKDC_ATQ_CONTEXT KdcContext; SOCKET NewSocket = INVALID_SOCKET; KERB_MESSAGE_BUFFER InputMessage; KERB_MESSAGE_BUFFER OutputMessage; PKDC_GET_TICKET_ROUTINE EndpointFunction = NULL; KERBERR KerbErr = KDC_ERR_NONE; TRACE(KDC,KdcAtqIoCompletion, DEB_FUNCTION); if (Context == NULL) { return; } // // 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; } KdcContext = (PKDC_ATQ_CONTEXT) Context; 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)); KdcAtqCloseSocket( KdcContext ); D_DebugLog((DEB_T_SOCK, "Freeing context %p\n",KdcContext->AtqContext)); // // If the overlapped structure is not null, then there is an // outstanding IO that just completed, so dereference the context // to remove that i/o. Otherwise leave the reference there, as we will // probably be called back when the io terminates. // if (lpo != NULL) { KdcAtqDereferenceContext(&KdcContext); } goto Cleanup; } // // NOTE: after reading or writing to a context, the context should // not be touched because a completion may have occurred on another // thread that may delete the context. // if ((KdcContext->Flags & KDC_ATQ_READ_CONTEXT) != 0) { KERBERR KerbErr; ULONG TotalBytes = 0; // // Read the number of bytes off the front of the message // if (KdcContext->UsedBufferLength == 0) { if (BytesWritten >= sizeof(ULONG)) { KdcContext->ExpectedMessageSize = ntohl(*(PULONG)KdcContext->Buffer); } else { DebugLog((DEB_ERROR,"Read completion with no data!\n")); goto Cleanup; } } // // Figure out if we've already read all the data we need // TotalBytes = KdcContext->UsedBufferLength + BytesWritten; if (TotalBytes < KdcContext->ExpectedMessageSize) { InputMessage.BufferSize = BytesWritten ; InputMessage.Buffer = (PUCHAR) KdcContext->Buffer; KerbErr = KdcAtqRetrySocketRead( &KdcContext, &InputMessage ); if (!KERB_SUCCESS(KerbErr)) { //fester DebugLog((DEB_ERROR, "Closing connection due to RetrySocketRead error\n")); DsysAssert(KdcContext->References == 1); } goto Cleanup; } TotalBytes = ntohl(*(PULONG)KdcContext->Buffer); KdcContext->ExpectedMessageSize = TotalBytes + sizeof(ULONG); if (KdcContext->UsedBufferLength + BytesWritten < KdcContext->ExpectedMessageSize) { InputMessage.BufferSize = BytesWritten ; InputMessage.Buffer = (PUCHAR) KdcContext->Buffer; KerbErr = KdcAtqRetrySocketRead( &KdcContext, &InputMessage ); if (!KERB_SUCCESS(KerbErr)) { //fester DebugLog((DEB_ERROR, "Closing connection due to RetrySocketRead error\n")); DsysAssert(KdcContext->References == 1); } goto Cleanup; } // // There is a buffer, so use it to do the KDC thang. // KdcContext->lpo = lpo; InputMessage.BufferSize = (KdcContext->UsedBufferLength + BytesWritten) - sizeof(ULONG); InputMessage.Buffer = KdcContext->Buffer + sizeof(ULONG); OutputMessage.Buffer = NULL; EndpointFunction = (PKDC_GET_TICKET_ROUTINE) KdcContext->EndpointContext; KerbErr = EndpointFunction( &KdcContext, &KdcContext->Address, &KdcContext->LocalAddress, &InputMessage, &OutputMessage ); if ((KerbErr != KDC_ERR_NONE) || (OutputMessage.BufferSize != 0)) { // // We expect at least some level of message validity before // we'll return anything. // if (KerbErr == KDC_ERR_NO_RESPONSE) { // TBD: Log an "attack" event here. KdcAtqCloseSocket(KdcContext); KdcAtqDereferenceContext(&KdcContext); } else { ULONG NetworkSize; WSABUF Buffers[2]; NetworkSize = htonl(OutputMessage.BufferSize); Buffers[0].len = sizeof(DWORD); Buffers[0].buf = (PCHAR) &NetworkSize; Buffers[1].len = OutputMessage.BufferSize; Buffers[1].buf = (PCHAR) OutputMessage.Buffer; KdcContext->WriteBufferLength = OutputMessage.BufferSize; KdcContext->WriteBuffer = OutputMessage.Buffer; OutputMessage.Buffer = NULL; // // If there was no output message, don't send one. // KdcContext->Flags |= KDC_ATQ_WRITE_CONTEXT; KdcContext->Flags &= ~KDC_ATQ_READ_CONTEXT; // // Refernce the context for the write. // KdcAtqReferenceContext(KdcContext); if (!AtqWriteSocket( (PATQ_CONTEXT) KdcContext->AtqContext, Buffers, 2, lpo )) { DebugLog((DEB_ERROR,"Failed to write KDC reply: 0x%x\n",GetLastError())); KdcAtqCloseSocket( KdcContext ); KdcAtqDereferenceContext(&KdcContext); } } if (OutputMessage.Buffer != NULL) { KdcFreeEncodedData(OutputMessage.Buffer); } } } else { KdcContext->Flags |= KDC_ATQ_READ_CONTEXT; KdcContext->Flags &= ~KDC_ATQ_WRITE_CONTEXT; // // Ignore the true size of the buffer // KdcContext->BufferLength = KERB_MAX_KDC_REQUEST_SIZE; KdcContext->UsedBufferLength = 0; KdcContext->ExpectedMessageSize = 0; if (KdcContext->WriteBuffer != NULL) { KdcFreeEncodedData(KdcContext->WriteBuffer); KdcContext->WriteBuffer = NULL; } // // Reference the context for the read // KdcAtqReferenceContext(KdcContext); if (!AtqReadFile( (PATQ_CONTEXT) KdcContext->AtqContext, KdcContext->Buffer, KERB_MAX_KDC_REQUEST_SIZE, lpo )) { DebugLog((DEB_ERROR,"Failed to read file for %d bytes: 0x%x\n",KERB_MAX_KDC_REQUEST_SIZE,GetLastError())); KdcAtqCloseSocket( KdcContext ); // // Dereference the reference we just added // KdcAtqDereferenceContext(&KdcContext); // // Derefernece the reference on the list // KdcAtqDereferenceContext(&KdcContext); } } Cleanup: if (KdcContext != NULL) { KdcAtqDereferenceContext(&KdcContext); } } //+------------------------------------------------------------------------- // // Function: KdcAtqRetrySocketRead // // Synopsis: Retries a read if not all the data was read // // Effects: posts an AtqReadSocket // // Arguments: Context - The KDC context to retry the read on // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- KERBERR KdcAtqRetrySocketRead( IN PKDC_ATQ_CONTEXT * Context, IN PKERB_MESSAGE_BUFFER OldMessage ) { KERBERR KerbErr = KDC_ERR_NONE; PKDC_ATQ_CONTEXT KdcContext = *Context; PBYTE NewBuffer = NULL; ULONG NewBufferLength; D_DebugLog(( DEB_T_SOCK, "RetrySocketRead: Expected size = %#x, current size %#x\n", KdcContext->ExpectedMessageSize, KdcContext->UsedBufferLength)); if (KdcContext->ExpectedMessageSize != 0) { NewBufferLength = KdcContext->ExpectedMessageSize; } else { // // Set max buffer length at 128k // if (KdcContext->BufferLength < KDC_MAX_BUFFER_LENGTH) { NewBufferLength = KdcContext->BufferLength + KERB_MAX_KDC_REQUEST_SIZE; } else { KerbErr = KRB_ERR_GENERIC; goto cleanup; } } if (NewBufferLength > KDC_MAX_BUFFER_LENGTH) { KerbErr = KRB_ERR_GENERIC; goto cleanup; } // // If the expected message size doesn't fit in the current buffer, // allocate a new one. // if (NewBufferLength > KdcContext->BufferLength) { D_DebugLog(( DEB_T_SOCK, "Allocating a new buffer for context %x\n", Context )); NewBuffer = (PBYTE) MIDL_user_allocate( NewBufferLength ); if (NewBuffer == NULL) { KerbErr = KRB_ERR_GENERIC; goto cleanup; } if ( KdcContext->Buffer == OldMessage->Buffer ) { // // we resized while the buffer was in use. Copy the data and touch up // the pointers below // RtlCopyMemory( NewBuffer, OldMessage->Buffer, // same as KdcContext->Buffer OldMessage->BufferSize ); OldMessage->Buffer = NewBuffer ; } MIDL_user_free(KdcContext->Buffer); KdcContext->Buffer = NewBuffer; KdcContext->BufferLength = NewBufferLength; NewBuffer = NULL; } if (KdcContext->Buffer != OldMessage->Buffer) { RtlMoveMemory( KdcContext->Buffer, OldMessage->Buffer, OldMessage->BufferSize ); } KdcContext->UsedBufferLength = KdcContext->UsedBufferLength + OldMessage->BufferSize; KdcContext->Flags |= KDC_ATQ_READ_CONTEXT; KdcContext->Flags &= ~(KDC_ATQ_WRITE_CONTEXT); // // Reference the context for the read // KdcAtqReferenceContext(KdcContext); if (!AtqReadFile( (PATQ_CONTEXT) KdcContext->AtqContext, (PUCHAR) KdcContext->Buffer + KdcContext->UsedBufferLength, KdcContext->BufferLength - KdcContext->UsedBufferLength, KdcContext->lpo )) { DebugLog((DEB_ERROR,"Failed to read file for %d bytes: 0x%x\n",KdcContext->BufferLength - KdcContext->UsedBufferLength, GetLastError)); // // Dereference the reference we just added // KdcAtqDereferenceContext(&KdcContext); KerbErr = KRB_ERR_GENERIC; goto cleanup; } cleanup: if (!KERB_SUCCESS(KerbErr)) { KdcAtqCloseSocket( KdcContext ); KdcAtqDereferenceContext(&KdcContext); } return(KerbErr); } //+------------------------------------------------------------------------- // // Function: KdcAtqConnection // // Synopsis: Connection handling routine for KDC ATQ code // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- VOID KdcAtqConnect( IN SOCKET sNew, IN LPSOCKADDR_IN pSockAddr, IN PVOID EndpointContext, IN PVOID EndpointObject ) { TRACE(KDC,KdcAtqConnect, DEB_FUNCTION); DebugLog((DEB_T_SOCK,"KdcAtqConnect called\n")); } //+------------------------------------------------------------------------- // // Function: KdcInitializeSockets // // Synopsis: Initializes the KDCs socket handling code // // Effects: // // Arguments: none // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KdcInitializeSockets( VOID ) { NTSTATUS Status = STATUS_SUCCESS; ATQ_ENDPOINT_CONFIGURATION EndpointConfig; PATQ_CONTEXT EndpointContext = NULL; BOOLEAN AtqInitCalled = FALSE; TRACE(KDC,KdcInitializeSockets, DEB_FUNCTION); // // Initialize the asynchronous thread queue. // if (!AtqInitialize(0)) { DebugLog((DEB_ERROR,"Failed to initialize ATQ\n")); Status = STATUS_UNSUCCESSFUL; goto Cleanup; } AtqInitCalled = TRUE; Status = RtlInitializeCriticalSection(&KdcAtqContextLock); if (!NT_SUCCESS(Status)) { goto Cleanup; } InitializeListHead(&KdcAtqContextList); // // Create the KDC endpoint // EndpointConfig.ListenPort = KERB_KDC_PORT; EndpointConfig.IpAddress = INADDR_ANY; EndpointConfig.cbAcceptExRecvBuffer = KDC_MAX_ACCEPT_BUFFER; EndpointConfig.nAcceptExOutstanding = KDC_MAX_ACCEPT_OUTSTANDING; EndpointConfig.AcceptExTimeout = KDC_ACCEPT_TIMEOUT; EndpointConfig.pfnConnect = KdcAtqConnect; EndpointConfig.pfnConnectEx = KdcAtqConnectEx; EndpointConfig.pfnIoCompletion = KdcAtqIoCompletion; EndpointConfig.fDatagram = FALSE; EndpointConfig.fLockDownPort = TRUE; KdcEndpoint = AtqCreateEndpoint( &EndpointConfig, KdcGetTicket ); if (KdcEndpoint == NULL) { DebugLog((DEB_ERROR,"Failed to create ATQ endpoint\n")); Status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // Start the endpoint // if (!AtqStartEndpoint(KdcEndpoint)) { DebugLog((DEB_ERROR, "Failed to add ATQ endpoint\n")); Status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // Create the KPASSWD endpoint // EndpointConfig.ListenPort = KERB_KPASSWD_PORT; KpasswdEndpoint = AtqCreateEndpoint( &EndpointConfig, KdcChangePassword ); if (KpasswdEndpoint == NULL) { DebugLog((DEB_ERROR,"Failed to create ATQ endpoint for kpasswd\n")); Status = STATUS_UNSUCCESSFUL; goto Cleanup; } // // Start the endpoint // if (!AtqStartEndpoint(KpasswdEndpoint)) { DebugLog((DEB_ERROR, "Failed to add ATQ endpoint\n")); Status = STATUS_UNSUCCESSFUL; goto Cleanup; } D_DebugLog((DEB_TRACE, "Successfully started ATQ listening for kpasswd\n")); Status = KdcInitializeDatagramSockets( ); if (!NT_SUCCESS(Status)) { goto Cleanup; } KdcSocketsInitialized = TRUE; Cleanup: if (!NT_SUCCESS(Status)) { if (KdcEndpoint != NULL) { (VOID) AtqStopEndpoint( KdcEndpoint ); (VOID) AtqCloseEndpoint( KdcEndpoint ); KdcEndpoint = NULL; } if (KpasswdEndpoint != NULL) { (VOID) AtqStopEndpoint( KpasswdEndpoint ); (VOID) AtqCloseEndpoint( KpasswdEndpoint ); KpasswdEndpoint = NULL; } if (AtqInitCalled) { AtqTerminate(); } } return(Status); } //+------------------------------------------------------------------------- // // Function: KdcShutdownSockets // // Synopsis: Shuts down the KDC socket handling code // // Effects: // // Arguments: // // Requires: // // Returns: // // Notes: // // //-------------------------------------------------------------------------- NTSTATUS KdcShutdownSockets( VOID ) { PKDC_ATQ_CONTEXT Context; PLIST_ENTRY ListEntry; TRACE(KDC,KdcShutdownSockets, DEB_FUNCTION); if (!KdcSocketsInitialized) { return(STATUS_SUCCESS); } // // Go through the list of contexts and close them all. // RtlEnterCriticalSection( &KdcAtqContextLock ); KdcSocketsInitialized = FALSE; for (ListEntry = KdcAtqContextList.Flink; (ListEntry != &KdcAtqContextList) && (ListEntry != NULL) ; ListEntry = ListEntry->Flink ) { Context = CONTAINING_RECORD(ListEntry, KDC_ATQ_CONTEXT, Next); // // If this is a read or write context, free close the associated // socket. (Endpoint contexts don't have sockets). // if (Context->Flags & ( KDC_ATQ_WRITE_CONTEXT | KDC_ATQ_READ_CONTEXT)) { KdcAtqCloseSocket( Context ); } } RtlLeaveCriticalSection( &KdcAtqContextLock ); if (KdcEndpoint != NULL) { (VOID) AtqStopEndpoint( KdcEndpoint ); (VOID) AtqCloseEndpoint( KdcEndpoint ); KdcEndpoint = NULL; } if (KpasswdEndpoint != NULL) { (VOID) AtqStopEndpoint( KpasswdEndpoint ); (VOID) AtqCloseEndpoint( KpasswdEndpoint ); KpasswdEndpoint = NULL; } KdcShutdownDatagramSockets(); if (KdcSocketsInitialized) { if (!AtqTerminate()) { DebugLog((DEB_ERROR, "Failed to terminate ATQ!!!\n")); } RtlDeleteCriticalSection(&KdcAtqContextLock); } return(STATUS_SUCCESS); }