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.
 
 
 
 
 
 

844 lines
25 KiB

//-----------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation, 1997 - 1999
//
// rpcisapi.cxx
//
// IIS ISAPI extension part of the RPC proxy over HTTP.
//
// Exports:
//
// BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer )
//
// Returns the version of the spec that this server was built with.
//
// BOOL WINAPI HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB )
//
// This function does all of the work.
//
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Includes:
//-----------------------------------------------------------------------------
#include <sysinc.h>
#include <winsock2.h>
#include <rpc.h>
#include <rpcdcep.h>
#include <rpcerrp.h>
#include <httpfilt.h>
#include <httpext.h>
#include <mbstring.h>
#include <ecblist.h>
#include <filter.h>
#include <olist.h>
#include <server.h>
#include <RpcIsapi.h>
#include <registry.h>
#include <StrSafe.h>
//-----------------------------------------------------------------------------
// Globals:
//-----------------------------------------------------------------------------
extern SERVER_INFO *g_pServerInfo;
extern BOOL g_fIsIIS6;
//-----------------------------------------------------------------------------
// GetExtensionVersion()
//
//-----------------------------------------------------------------------------
BOOL WINAPI GetExtensionVersion( HSE_VERSION_INFO *pVer )
{
HRESULT hr;
pVer->dwExtensionVersion = MAKELONG( HSE_VERSION_MINOR, HSE_VERSION_MAJOR );
ASSERT( sizeof(EXTENSION_DESCRIPTION) <= HSE_MAX_EXT_DLL_NAME_LEN );
hr = StringCchCopyNA (pVer->lpszExtensionDesc,
sizeof(pVer->lpszExtensionDesc),
EXTENSION_DESCRIPTION,
sizeof(EXTENSION_DESCRIPTION)
);
// this cannot fail as the strings are constant
ASSERT(SUCCEEDED(hr));
// if the filter data structures are not initialized yet, initialize them
if (g_pServerInfo == NULL)
{
if (InitializeGlobalDataStructures(FALSE /* IsFromFilter */) == FALSE)
{
return FALSE;
}
}
LogEventStartupSuccess (fIsIISInCompatibilityMode ? "5" : "6");
return TRUE;
}
//-----------------------------------------------------------------------------
// ReplyToClient()
//
//-----------------------------------------------------------------------------
BOOL ReplyToClient( EXTENSION_CONTROL_BLOCK *pECB,
const char *pBuffer,
DWORD *pdwSize,
DWORD *pdwStatus )
{
DWORD dwFlags = (HSE_IO_SYNC | HSE_IO_NODELAY);
if (!pECB->WriteClient(pECB->ConnID, (char *)pBuffer, pdwSize, dwFlags))
{
*pdwStatus = GetLastError();
#ifdef DBG_ERROR
DbgPrint("ReplyToClient(): failed: %d\n",*pdwStatus);
#endif
return FALSE;
}
*pdwStatus = HSE_STATUS_SUCCESS;
return TRUE;
}
DWORD
ReplyToClientWithStatus (
IN EXTENSION_CONTROL_BLOCK *pECB,
IN RPC_STATUS RpcStatus
)
/*++
Routine Description:
Sends a reply to the client with the given error code as error.
Arguments:
pECB - extension control block
RpcStatus - error code to be returned to client
Return Value:
Return value appropriate for return to IIS (i.e. HSE_STATUS_*)
--*/
{
// size is the error string + 20 space for the error code
char Buffer[sizeof(ServerErrorString) + 20];
ULONG Size;
ULONG Status;
BOOL Result;
HRESULT hr;
hr = StringCbPrintfA (Buffer,
sizeof(Buffer),
ServerErrorString,
RpcStatus
);
// this should never fail as the strings are constant
ASSERT(SUCCEEDED(hr));
Size = strlen(Buffer);
Result = ReplyToClient (
pECB,
Buffer,
&Size,
&Status
);
return Status;
}
//-----------------------------------------------------------------------------
// ParseQueryString()
//
// The query string is in the format:
//
// <Index_of_pOverlapped>
//
// Where the index is in ASCII Hex. The index is read and converted back
// to a DWORD then used to locate the SERVER_OVERLAPPED. If its found,
// return TRUE, else return FALSE.
//
// NOTE: "&" is the parameter separator if multiple parameters are passed.
//-----------------------------------------------------------------------------
BOOL ParseQueryString( unsigned char *pszQuery,
SERVER_OVERLAPPED **ppOverlapped,
DWORD *pdwStatus )
{
DWORD dwIndex = 0;
pszQuery = AnsiHexToDWORD(pszQuery,&dwIndex,pdwStatus);
if (!pszQuery)
{
return FALSE;
}
*ppOverlapped = GetOverlapped(dwIndex);
if (*ppOverlapped == NULL)
{
return FALSE;
}
return TRUE;
}
BOOL
ParseHTTP2QueryString (
IN char *Query,
IN OUT USHORT *ServerAddressBuffer,
IN ULONG ServerAddressBufferLength,
IN OUT USHORT *ServerPortBuffer,
IN ULONG ServerPortBufferLength
)
/*++
Routine Description:
Parses an HTTP2 format query string into a server address and
server port.
Arguments:
Query - the raw query string as received by the extension
ServerAddressBuffer - the buffer where the server address will be
stored. Undefined upon failure. Upon success it will be 0 terminated.
ServerAddressBufferLengh - the length of ServerAddressBuffer in unicode chars.
ServerPortBuffer - the buffer where the server port will be
stored. Undefined upon failure. Upon success it will be 0 terminated.
ServerPortBufferLengh - the length of ServerAddressBuffer in unicode chars.
Return Value:
non-zero for success and 0 for failure.
--*/
{
char *ColonPosition;
int CharactersConverted;
ColonPosition = _mbschr(Query, ServerAddressAndPortSeparator);
if (ColonPosition == NULL)
return FALSE;
CharactersConverted = MultiByteToWideChar (
CP_ACP,
MB_ERR_INVALID_CHARS,
Query,
ColonPosition - Query,
ServerAddressBuffer,
ServerAddressBufferLength
);
// did we convert successfully?
if (CharactersConverted == 0)
return FALSE;
// do we have space for the terminating null?
if (CharactersConverted + 1 > ServerAddressBufferLength)
return FALSE;
// null terminate the server address string. Since we gave the length
// explicitly to MultiByteToWideChar it will not add null for us
ServerAddressBuffer[CharactersConverted] = 0;
CharactersConverted = MultiByteToWideChar (
CP_ACP,
MB_ERR_INVALID_CHARS,
ColonPosition + 1,
-1, // have MultiByteToWideChar calculate the length
ServerPortBuffer,
ServerPortBufferLength
);
// did we convert successfully?
if (CharactersConverted == 0)
return FALSE;
// since we had MultiByteToWideChar calculate the string length,
// it has null terminated the resulting string. We're all done.
return TRUE;
}
BOOL
GetRemoteUserString (
IN EXTENSION_CONTROL_BLOCK *pECB,
OUT char **FinalRemoteUser
)
/*++
Routine Description:
Gets the remote user name from IIS. If anonymous, an empty
string will be returned.
Arguments:
pECB - the extension control block given to us by IIS.
FinalRemoteUser - on output a pointer to the remote user name allocated by this
routine. Muts be freed by caller using MemFree. Undefined on failure.
Return Value:
non-zero for success or 0 for failure/
--*/
{
ULONG ActualServerVariableLength;
char *TempRemoteUser;
BOOL Result;
ActualServerVariableLength = 0;
TempRemoteUser = NULL;
Result = pECB->GetServerVariable(pECB->ConnID,
"REMOTE_USER",
TempRemoteUser,
&ActualServerVariableLength
);
ASSERT(Result == FALSE);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
return FALSE;
}
TempRemoteUser = (char *)MemAllocate(ActualServerVariableLength + 1);
if (TempRemoteUser == NULL)
{
return FALSE;
}
Result = pECB->GetServerVariable(pECB->ConnID,
"REMOTE_USER",
TempRemoteUser,
&ActualServerVariableLength
);
if (Result == FALSE)
{
MemFree(TempRemoteUser);
return FALSE;
}
*FinalRemoteUser = TempRemoteUser;
return TRUE;
}
RPC_STATUS
RPC_ENTRY I_RpcProxyIsValidMachine (
IN char *pszMachine,
IN char *pszDotMachine,
IN ULONG dwPortNumber
)
{
BOOL Result;
Result = HttpProxyIsValidMachine(g_pServerInfo,
pszMachine,
pszDotMachine,
dwPortNumber
);
if (Result)
return RPC_S_OK;
else
return RPC_S_ACCESS_DENIED;
}
RPC_STATUS
RPC_ENTRY I_RpcProxyGetClientAddress (
IN void *Context,
OUT char *Buffer,
OUT ULONG *BufferLength
)
{
BOOL Result;
EXTENSION_CONTROL_BLOCK *pECB = (EXTENSION_CONTROL_BLOCK *) Context;
Result = pECB->GetServerVariable(pECB->ConnID,
"REMOTE_ADDR",
Buffer,
BufferLength
);
if (Result)
return RPC_S_OK;
else
return GetLastError();
}
RPC_STATUS
RPC_ENTRY I_RpcProxyGetConnectionTimeout (
OUT ULONG *ConnectionTimeout
)
{
*ConnectionTimeout = IISConnectionTimeout;
return RPC_S_OK;
}
const I_RpcProxyCallbackInterface ProxyCallbackInterface =
{
I_RpcProxyIsValidMachine,
I_RpcProxyGetClientAddress,
I_RpcProxyGetConnectionTimeout
};
// uncomment this to see why connections are being rejected
// by AllowAnonymous
// #define DEBUG_ALLOW_ANONYMOUS
//-----------------------------------------------------------------------------
// HttpExtensionProc()
//
//-----------------------------------------------------------------------------
DWORD WINAPI HttpExtensionProc( EXTENSION_CONTROL_BLOCK *pECB )
{
DWORD dwStatus;
DWORD dwFlags;
DWORD dwSize;
SERVER_INFO *pServerInfo = g_pServerInfo;
SERVER_OVERLAPPED *pOverlapped = NULL;
HSE_SEND_HEADER_EX_INFO HeaderEx;
CHAR *pLegacyVerb;
RPC_STATUS RpcStatus;
ULONG VerbLength;
USHORT ServerAddress[MaxServerAddressLength];
USHORT ServerPort[MaxServerPortLength];
BOOL Result;
ULONG ProxyType;
BOOL ConnectionEstablishmentRequest;
char EchoResponse[sizeof(EchoResponseHeader2) + 2]; // 2 is for
// the size of the Echo RTS which goes instead of ContentLength
int BytesWritten;
ULONG BufferSize;
const BYTE *EchoRTS;
char ServerVariable[20];
ULONG ActualServerVariableLength;
ULONG NumContentLength;
BOOL AnonymousConnection;
char *RemoteUser = NULL;
RPC_CHAR *ActualServerName;
DWORD ExtensionProcResult;
DWORD HttpStatusCode;
char *DestinationEnd;
HRESULT hr;
if (g_pServerInfo->dwEnabled == FALSE)
{
dwSize = sizeof(STATUS_PROXY_DISABLED) - 1; // don't want to count trailing 0.
ReplyToClient(pECB,STATUS_PROXY_DISABLED, &dwSize, &dwStatus);
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
if (g_pServerInfo->AllowAnonymous == FALSE)
{
// if we are not allowing anonymous connections, check that a secure
// channel and authentication are used
// assume anonymous for now
AnonymousConnection = TRUE;
ActualServerVariableLength = sizeof(ServerVariable);
Result = pECB->GetServerVariable(pECB->ConnID,
"HTTPS",
ServerVariable,
&ActualServerVariableLength
);
if (!Result)
{
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
if (RpcpStringCompareIntA(ServerVariable, "on") == 0)
{
Result = GetRemoteUserString (pECB,
&RemoteUser
);
if (!Result)
{
#ifdef DEBUG_ALLOW_ANONYMOUS
DbgPrint("Connection rejected getting the remote user failed\n");
#endif // #ifdef DEBUG_ALLOW_ANONYMOUS
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
// if non-empty string, it is authenticated
if (RemoteUser[0] != 0)
{
AnonymousConnection = FALSE;
#ifdef DEBUG_ALLOW_ANONYMOUS
DbgPrint("Connection rejected because user could not be retrieved\n");
#endif // #ifdef DEBUG_ALLOW_ANONYMOUS
}
else
{
#ifdef DEBUG_ALLOW_ANONYMOUS
DbgPrint("Connection accepted for user %s\n", RemoteUser);
#endif // #ifdef DEBUG_ALLOW_ANONYMOUS
}
}
else
{
#ifdef DEBUG_ALLOW_ANONYMOUS
DbgPrint("Connection rejected because it is not SSL\n");
#endif // #ifdef DEBUG_ALLOW_ANONYMOUS
}
// if empty string, it is not authenticated
if (AnonymousConnection)
{
dwSize = sizeof(AnonymousAccessNotAllowedString) - 1; // don't want to count trailing 0.
ReplyToClient(pECB, AnonymousAccessNotAllowedString, &dwSize, &dwStatus);
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = dwStatus;
goto AbortAndExit;
}
}
pECB->dwHttpStatusCode = STATUS_CONNECTION_OK;
if (g_fIsIIS6)
{
pLegacyVerb = RPC_CONNECT;
}
else
{
pLegacyVerb = POST_STR;
}
VerbLength = strlen(pECB->lpszMethod);
ConnectionEstablishmentRequest = FALSE;
// get the verb and depending on that we will determine our course of
// action
if ((VerbLength == InChannelEstablishmentMethodLength)
&& (lstrcmpi(pECB->lpszMethod, InChannelEstablishmentMethod) == 0))
{
ConnectionEstablishmentRequest = TRUE;
ProxyType = RPC_PROXY_CONNECTION_TYPE_IN_PROXY;
}
else if ((VerbLength == OutChannelEstablishmentMethodLength)
&& (lstrcmpi(pECB->lpszMethod, OutChannelEstablishmentMethod) == 0))
{
ConnectionEstablishmentRequest = TRUE;
ProxyType = RPC_PROXY_CONNECTION_TYPE_OUT_PROXY;
}
if (ConnectionEstablishmentRequest)
{
// check if this is echo request or a true connection
// establishment
ActualServerVariableLength = sizeof(ServerVariable);
Result = pECB->GetServerVariable(pECB->ConnID,
"CONTENT_LENGTH",
ServerVariable,
&ActualServerVariableLength
);
if (!Result)
{
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
NumContentLength = atol(ServerVariable);
if (NumContentLength <= MaxEchoRequestSize)
{
// too small for a channel. Must be an echo
EchoRTS = GetEchoRTS(&BufferSize);
// Echo back the RTS packet + headers
hr = StringCbPrintfExA(EchoResponse,
sizeof(EchoResponse),
&DestinationEnd, // ppszDestEnd
NULL, // pcbRemaining
0, // Flags
EchoResponseHeader2,
BufferSize
);
if (FAILED(hr))
{
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
// calculate the number of bytes written as the
// distance from the end of the written buffer to the
// beginning
BytesWritten = DestinationEnd - EchoResponse;
// send back headers
dwSize = sizeof(HeaderEx);
dwFlags = 0;
memset(&HeaderEx, 0, dwSize);
HeaderEx.fKeepConn = TRUE;
HeaderEx.pszStatus = EchoResponseHeader1;
HeaderEx.cchStatus = sizeof(EchoResponseHeader1);
HeaderEx.pszHeader = EchoResponse;
HeaderEx.cchHeader = BytesWritten;
Result = pECB->ServerSupportFunction(pECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER_EX,
&HeaderEx,
&dwSize,
&dwFlags);
if (!Result)
{
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
// send back Echo RTS
dwSize = BufferSize;
dwFlags = 0;
Result = pECB->WriteClient(pECB->ConnID, (char *)EchoRTS, &dwSize, dwFlags);
if (!Result)
{
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
return HSE_STATUS_SUCCESS;
}
else
{
// a channel
Result = ParseHTTP2QueryString (
pECB->lpszQueryString,
ServerAddress,
sizeof(ServerAddress) / sizeof(ServerAddress[0]),
ServerPort,
sizeof(ServerPort) / sizeof(ServerPort[0])
);
if (Result == FALSE)
{
dwSize = sizeof(CannotParseQueryString) - 1; // don't want to count trailing 0.
ReplyToClient(pECB, CannotParseQueryString, &dwSize, &dwStatus);
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = dwStatus;
goto AbortAndExit;
}
if (g_pServerInfo->RpcNewHttpProxyChannel)
{
// get the remote user (if not available yet) and call
// the redirector dll
if (RemoteUser == NULL)
{
Result = GetRemoteUserString (pECB,
&RemoteUser
);
if (Result == FALSE)
{
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
}
ASSERT(g_pServerInfo->RpcHttpProxyFreeString);
RpcStatus = g_pServerInfo->RpcNewHttpProxyChannel (
ServerAddress,
ServerPort,
RemoteUser,
&ActualServerName
);
if (RpcStatus != RPC_S_OK)
{
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
}
else
ActualServerName = ServerAddress;
RpcStatus = I_RpcProxyNewConnection (
ProxyType,
ActualServerName,
ServerPort,
pECB,
(I_RpcProxyCallbackInterface *)&ProxyCallbackInterface
);
if (g_pServerInfo->RpcNewHttpProxyChannel
&& (ActualServerName != ServerAddress))
{
// get the remote user and call the redirector dll
ASSERT(g_pServerInfo->RpcHttpProxyFreeString);
g_pServerInfo->RpcHttpProxyFreeString(ActualServerName);
}
if (RpcStatus != RPC_S_OK)
{
ReplyToClientWithStatus(pECB, RpcStatus);
HttpStatusCode = STATUS_CONNECTION_OK;
ExtensionProcResult = HSE_STATUS_SUCCESS;
goto AbortAndExit;
}
#if DBG
DbgPrint("RPCPROXY: %d: Connection type %d to %S:%S\n", GetCurrentProcessId(),
ProxyType, ServerAddress, ServerPort);
#endif
HttpStatusCode = STATUS_CONNECTION_OK;
ExtensionProcResult = HSE_STATUS_PENDING;
goto AbortAndExit;
}
}
//
// The RPC request must be a post (or RPC_CONNECT for 6.0):
//
if (_mbsicmp(pECB->lpszMethod,pLegacyVerb))
{
dwSize = sizeof(STATUS_MUST_BE_POST_STR) - 1; // don't want to count trailing 0.
ReplyToClient(pECB,STATUS_MUST_BE_POST_STR,&dwSize,&dwStatus);
HttpStatusCode = STATUS_CONNECTION_OK;
ExtensionProcResult = HSE_STATUS_SUCCESS;
goto AbortAndExit;
}
//
// Make sure there is no data from the initial BIND in the ECB data buffer:
//
// ASSERT(pECB->cbTotalBytes == 0);
//
// Get the connect parameters:
//
if (!ParseQueryString(pECB->lpszQueryString,&pOverlapped,&dwStatus))
{
dwSize = sizeof(STATUS_POST_BAD_FORMAT_STR) - 1; // don't want to count trailing 0.
ReplyToClient(pECB,STATUS_POST_BAD_FORMAT_STR,&dwSize,&dwStatus);
HttpStatusCode = STATUS_CONNECTION_OK;
ExtensionProcResult = HSE_STATUS_SUCCESS;
goto AbortAndExit;
}
pOverlapped->pECB = pECB;
//
// Add the new ECB to the Active ECB List:
//
if (!AddToECBList(g_pServerInfo->pActiveECBList,pECB))
{
#ifdef DBG_ERROR
DbgPrint("HttpExtensionProc(): AddToECBList() failed\n");
#endif
FreeOverlapped(pOverlapped);
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
//
// Submit the first client async read:
//
if (!StartAsyncClientRead(pECB,pOverlapped->pConn,&dwStatus))
{
#ifdef DBG_ERROR
DbgPrint("HttpExtensionProc(): StartAsyncClientRead() failed %d\n",dwStatus);
#endif
FreeOverlapped(pOverlapped);
CleanupECB(pECB);
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
//
// Post the first server read on the new socket:
//
IncrementECBRefCount(pServerInfo->pActiveECBList,pECB);
if (!SubmitNewRead(pServerInfo,pOverlapped,&dwStatus))
{
#ifdef DBG_ERROR
DbgPrint("HttpExtensionProc(): SubmitNewRead() failed %d\n",dwStatus);
#endif
FreeOverlapped(pOverlapped);
CleanupECB(pECB);
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
//
// Make sure the server receive thread is up and running:
//
if (!CheckStartReceiveThread(g_pServerInfo,&dwStatus))
{
#ifdef DBG_ERROR
DbgPrint("HttpExtensionProc(): CheckStartReceiveThread() failed %d\n",dwStatus);
#endif
FreeOverlapped(pOverlapped);
CleanupECB(pECB);
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
//
// Send back connection Ok to client, and also set fKeepConn to FALSE.
//
dwSize = sizeof(HeaderEx);
dwFlags = 0;
memset(&HeaderEx,0,dwSize);
HeaderEx.fKeepConn = FALSE;
if (!pECB->ServerSupportFunction(pECB->ConnID,
HSE_REQ_SEND_RESPONSE_HEADER_EX,
&HeaderEx,
&dwSize,
&dwFlags))
{
#ifdef DBG_ERROR
DbgPrint("HttpExtensionProc(): SSF(HSE_REQ_SEND_RESPONSE_HEADER_EX) failed %d\n",dwStatus);
#endif
FreeOverlapped(pOverlapped);
CleanupECB(pECB);
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_ERROR;
goto AbortAndExit;
}
HttpStatusCode = STATUS_SERVER_ERROR;
ExtensionProcResult = HSE_STATUS_PENDING;
// fall through
AbortAndExit:
if (RemoteUser)
MemFree(RemoteUser);
pECB->dwHttpStatusCode = HttpStatusCode;
return ExtensionProcResult;
}