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.
702 lines
18 KiB
702 lines
18 KiB
/*++
|
|
|
|
Copyright (c) 2001 Microsoft Corporation
|
|
|
|
Module Name :
|
|
w3ssl.cxx
|
|
|
|
Abstract:
|
|
SSL service for W3SVC.
|
|
New SSL service is introduced to IIS6.
|
|
In the dedicated (new process mode) it will run
|
|
in lsass. That should boost ssl performance by eliminating
|
|
context switches and interprocess communication during
|
|
SSL handshakes.
|
|
|
|
In the backward compatibility mode it will not do much.
|
|
Service will register with http for ssl processing, but w3svc
|
|
will register it's strmfilt and http.sys always uses the
|
|
last registered filter so that the one loaded by inetinfo.exe
|
|
will be used.
|
|
|
|
Note: W3SSL service was renamed to HTTPFilter.
|
|
|
|
Author:
|
|
Jaroslav Dunajsky (Jaroslad) 04-16-2001
|
|
|
|
Environment:
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
Stream Filter Worker Process
|
|
--*/
|
|
|
|
|
|
#include "precomp.hxx"
|
|
#include <inetsvcs.h>
|
|
|
|
|
|
HRESULT RunInSameProcessAsIISADMIN (
|
|
BOOL * pfInIISAdminProcess
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
Find out if HttpFilter service executes in the same process
|
|
as IISADMIN service
|
|
|
|
Arguments:
|
|
pfInIISAdminProcess - OUT parameter - set to TRUE current process is
|
|
running in the same process as IISADMIN
|
|
Returns:
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
SC_HANDLE schSCManager = NULL;
|
|
SC_HANDLE schIISADMINService = NULL;
|
|
LPSERVICE_STATUS_PROCESS pServiceStatus = NULL;
|
|
DWORD dwBytesNeeded = 0;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
*pfInIISAdminProcess = FALSE;
|
|
|
|
// Open a handle to the SC Manager database.
|
|
|
|
schSCManager = OpenSCManagerW(
|
|
NULL, // local machine
|
|
NULL, // ServicesActive database
|
|
SC_MANAGER_CONNECT );
|
|
|
|
if ( schSCManager == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
|
|
schIISADMINService = OpenServiceW(
|
|
schSCManager, // SCM database
|
|
L"IISADMIN", // IISADMIN service name
|
|
SERVICE_QUERY_STATUS );
|
|
|
|
if ( schIISADMINService == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// Get the configuration information.
|
|
// - to find out the current image path of the
|
|
// HTTPFilter service
|
|
//
|
|
|
|
|
|
if ( !QueryServiceStatusEx(
|
|
schIISADMINService,
|
|
SC_STATUS_PROCESS_INFO,
|
|
(LPBYTE) pServiceStatus,
|
|
0,
|
|
&dwBytesNeeded ) )
|
|
{
|
|
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
pServiceStatus = reinterpret_cast<LPSERVICE_STATUS_PROCESS> (
|
|
new char[dwBytesNeeded] );
|
|
if ( pServiceStatus == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( ERROR_OUTOFMEMORY );
|
|
goto Finished;
|
|
}
|
|
if ( !QueryServiceStatusEx(
|
|
schIISADMINService,
|
|
SC_STATUS_PROCESS_INFO,
|
|
(LPBYTE) pServiceStatus,
|
|
dwBytesNeeded,
|
|
&dwBytesNeeded ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
*pfInIISAdminProcess = ( pServiceStatus->dwProcessId == GetCurrentProcessId() );
|
|
|
|
hr = S_OK;
|
|
|
|
|
|
|
|
Finished:
|
|
|
|
if ( pServiceStatus != NULL )
|
|
{
|
|
delete [] pServiceStatus;
|
|
pServiceStatus = NULL;
|
|
}
|
|
|
|
if ( schIISADMINService != NULL )
|
|
{
|
|
DBG_REQUIRE( CloseServiceHandle( schIISADMINService ) );
|
|
schIISADMINService = NULL;
|
|
}
|
|
|
|
if ( schSCManager != NULL )
|
|
{
|
|
DBG_REQUIRE( CloseServiceHandle( schSCManager ) );
|
|
schSCManager = NULL;
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Implementation of W3SSL_SERVICE methods
|
|
//
|
|
|
|
//virtual
|
|
HRESULT
|
|
W3SSL_SERVICE::OnServiceStart(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
Initialize HTTPFilter and start service.
|
|
It initializes necessary structures and modules. If there is no error then
|
|
after returning from function HTTPFilter service will be in SERVICE_STARTED state
|
|
|
|
Arguments:
|
|
|
|
Returns:
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
DBG_ASSERT( _InitStatus == INIT_NONE );
|
|
|
|
hr = LoadStrmfilt();
|
|
if ( FAILED ( hr ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
_InitStatus = INIT_STRMFILT_LOADED;
|
|
|
|
//
|
|
// Initialize stream filter
|
|
//
|
|
|
|
hr = _fnStreamFilterInitialize();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in StreamFilterInitialize(). hr = %x\n",
|
|
hr ));
|
|
|
|
goto Finished;
|
|
}
|
|
|
|
_InitStatus = INIT_STREAMFILTER;
|
|
|
|
|
|
//
|
|
// Start listening
|
|
//
|
|
|
|
hr = _fnStreamFilterStart();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in StreamFilterStart(). hr = %x\n",
|
|
hr ));
|
|
|
|
goto Finished;
|
|
}
|
|
_InitStatus = INIT_STREAMFILTER_STARTED;
|
|
|
|
|
|
|
|
//
|
|
// Write current mode of w3ssl
|
|
// ( if it is executing in inetinfo.exe then it supports raw read filters )
|
|
// This setting will be used by WAS to determine what mode to execute in.
|
|
// That way W3SSL and W3SVC will always be running in the same mode
|
|
//
|
|
// CODEWORK: in the case that writing to registry fails we may want to do more
|
|
// than just ignoring it. However to halt startup may not necessarily be the best way
|
|
// to go in that case
|
|
|
|
|
|
DWORD dwRunningInInetinfo = 0;
|
|
BOOL fRunningInInetinfo = FALSE;
|
|
DWORD dwErr = NO_ERROR;
|
|
HKEY hKeyParam;
|
|
|
|
if ( SUCCEEDED( hr = RunInSameProcessAsIISADMIN( &fRunningInInetinfo ) ) )
|
|
{
|
|
if ( ( dwErr = RegOpenKeyExW( HKEY_LOCAL_MACHINE,
|
|
REGISTRY_KEY_HTTPFILTER_PARAMETERS_W,
|
|
0,
|
|
KEY_WRITE,
|
|
&hKeyParam ) ) == NO_ERROR )
|
|
{
|
|
|
|
if ( fRunningInInetinfo )
|
|
{
|
|
dwRunningInInetinfo = 1;
|
|
}
|
|
else
|
|
{
|
|
dwRunningInInetinfo = 0;
|
|
}
|
|
|
|
dwErr = RegSetValueExW( hKeyParam,
|
|
L"CurrentMode",
|
|
NULL,
|
|
REG_DWORD,
|
|
( LPBYTE )&dwRunningInInetinfo,
|
|
sizeof( dwRunningInInetinfo )
|
|
);
|
|
if ( dwErr != NO_ERROR )
|
|
{
|
|
//
|
|
// Ignore the error.
|
|
//
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to write registry value \"RunningInInetinfo\". Win32 = %d\n",
|
|
dwErr ));
|
|
|
|
}
|
|
|
|
RegCloseKey( hKeyParam );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Ignore the error.
|
|
//
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to open registry key %s. Win32 = %d\n",
|
|
REGISTRY_KEY_HTTPFILTER_PARAMETERS_W,
|
|
dwErr ));
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Ignore the error.
|
|
//
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to call RunInSameProcessAsIISADMIN(). hr = %x\n",
|
|
hr ));
|
|
hr = S_OK;
|
|
}
|
|
|
|
|
|
|
|
Finished:
|
|
|
|
if (FAILED ( hr ) )
|
|
{
|
|
//
|
|
// OnServiceStop() will assure proper cleanup
|
|
//
|
|
OnServiceStop();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//virtual
|
|
HRESULT
|
|
W3SSL_SERVICE::OnServiceStop(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
Terminate HTTPFilter service. Performs cleanup
|
|
|
|
_InitStatus is used to determine where to start with cleanup.
|
|
|
|
OnServiceStop() is also used in OnServiceStart() for cleanup in the error case
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
HRESULT - will be reported to SCM
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
//
|
|
// Cases in the switch command don't have "break" command
|
|
// on purpose. _InitStatus indicates how for the initialization has gone
|
|
// and that indicates where cleanup should start
|
|
//
|
|
|
|
switch( _InitStatus )
|
|
{
|
|
|
|
case INIT_STREAMFILTER_STARTED:
|
|
//
|
|
// Stop Listening
|
|
//
|
|
hr = _fnStreamFilterStop();
|
|
if ( FAILED( hr ) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in StreamFilterStop(). hr = %x\n",
|
|
hr ));
|
|
}
|
|
case INIT_STREAMFILTER:
|
|
_fnStreamFilterTerminate();
|
|
|
|
case INIT_STRMFILT_LOADED:
|
|
//
|
|
// time to unload strmfilt
|
|
//
|
|
UnloadStrmfilt();
|
|
|
|
case INIT_NONE:
|
|
break;
|
|
default:
|
|
DBG_ASSERT( FALSE );
|
|
}
|
|
|
|
_InitStatus = INIT_NONE;
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
W3SSL_SERVICE::LoadStrmfilt(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
Dynamically load strmfilt.dll
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
HRESULT
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
DBG_ASSERT( _hStrmfilt == NULL );
|
|
_hStrmfilt = LoadLibraryEx( L"strmfilt.dll",
|
|
NULL,
|
|
0 );
|
|
if ( _hStrmfilt != NULL )
|
|
{
|
|
_fnStreamFilterInitialize = (PFN_STREAM_FILTER_INITIALIZE)
|
|
GetProcAddress(_hStrmfilt, "StreamFilterInitialize");
|
|
if ( _fnStreamFilterInitialize == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in loading strmfilt %s entrypoint(). hr = %x\n",
|
|
"StreamFilterInitialize",
|
|
hr ));
|
|
goto Failed;
|
|
}
|
|
|
|
_fnStreamFilterStart = (PFN_STREAM_FILTER_START)
|
|
GetProcAddress(_hStrmfilt, "StreamFilterStart");
|
|
if ( _fnStreamFilterStart == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in loading strmfilt %s entrypoint(). hr = %x\n",
|
|
"StreamFilterStart",
|
|
hr ));
|
|
goto Failed;
|
|
}
|
|
|
|
_fnStreamFilterStop = (PFN_STREAM_FILTER_STOP)
|
|
GetProcAddress(_hStrmfilt, "StreamFilterStop");
|
|
if ( _fnStreamFilterStop == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in loading strmfilt %s entrypoint(). hr = %x\n",
|
|
"StreamFilterStop",
|
|
hr ));
|
|
goto Failed;
|
|
}
|
|
|
|
_fnStreamFilterTerminate = (PFN_STREAM_FILTER_TERMINATE)
|
|
GetProcAddress(_hStrmfilt, "StreamFilterTerminate");
|
|
if ( _fnStreamFilterTerminate == NULL)
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Error in loading strmfilt %s entrypoint(). hr = %x\n",
|
|
"StreamFilterTerminate",
|
|
hr ));
|
|
goto Failed;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to load strmfilt.dll. hr = %x\n",
|
|
hr ));
|
|
goto Failed;
|
|
}
|
|
|
|
Failed:
|
|
|
|
UnloadStrmfilt();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
W3SSL_SERVICE::UnloadStrmfilt(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
Dynamically load strmfilt.dll
|
|
|
|
Arguments:
|
|
|
|
|
|
Returns:
|
|
HRESULT
|
|
--*/
|
|
|
|
|
|
{
|
|
if ( _hStrmfilt != NULL )
|
|
{
|
|
BOOL fRet = FreeLibrary( _hStrmfilt );
|
|
|
|
_fnStreamFilterInitialize = NULL;
|
|
_fnStreamFilterStart = NULL;
|
|
_fnStreamFilterStop = NULL;
|
|
_fnStreamFilterTerminate = NULL;
|
|
|
|
if ( !fRet )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
}
|
|
_hStrmfilt = NULL;
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
HTTPFilterServiceThreadProc(
|
|
LPVOID /*lpParameter*/
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
This is the "real" proc for the service.
|
|
When HTTPFilterServiceMain is called, to prevent stack overflows,
|
|
it creates a thread that will begin executing this routine.
|
|
|
|
Arguments:
|
|
lpParameter - Not used, should be NULL.
|
|
|
|
Returns:
|
|
Win32 error. Does not return until service is stopped.
|
|
|
|
--*/
|
|
{
|
|
W3SSL_SERVICE * pHTTPFilterServiceInstance;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
|
|
//
|
|
// There is a possibility that after service reported it stopped
|
|
// there is a new request to start the service again.
|
|
// That may cause new thread to enter this function
|
|
// before previous one fully cleaned up and returned.
|
|
//
|
|
|
|
pHTTPFilterServiceInstance = new W3SSL_SERVICE;
|
|
if( pHTTPFilterServiceInstance == NULL )
|
|
{
|
|
//
|
|
// report fatal error to SCM
|
|
// (otherwise service would hang on START_PENDING)
|
|
//
|
|
dwError = ERROR_OUTOFMEMORY;
|
|
SCM_MANAGER::ReportFatalServiceStartupError(
|
|
HTTPFILTER_SERVICE_NAME_W,
|
|
dwError );
|
|
return dwError;
|
|
}
|
|
|
|
pHTTPFilterServiceInstance->RunService();
|
|
|
|
//
|
|
// return value will be ignored.
|
|
// Return value has only informational value
|
|
// if anything failed, SCM would be informed about the error
|
|
// within RunService() call
|
|
//
|
|
|
|
delete pHTTPFilterServiceInstance;
|
|
pHTTPFilterServiceInstance = NULL;
|
|
|
|
return dwError;
|
|
}
|
|
|
|
VOID
|
|
HTTPFilterServiceMain(
|
|
DWORD /*argc*/,
|
|
LPWSTR /*argv*/[]
|
|
)
|
|
/*++
|
|
|
|
Routine:
|
|
This is the "real" entrypoint for the service. When
|
|
the Service Controller dispatcher is requested to
|
|
start a service, it creates a thread that will begin
|
|
executing this routine.
|
|
|
|
Note: HTTPFilterServiceMain name is recognized by lsass as entrypoint
|
|
for HTTPFilter service
|
|
|
|
Arguments:
|
|
argc - Number of command line arguments to this service.
|
|
argv - Pointers to the command line arguments.
|
|
|
|
Returns:
|
|
None. Does not return until service is stopped.
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
HANDLE hThread = NULL;
|
|
DWORD dwThreadId = 0;
|
|
|
|
// Create a separate thread
|
|
hThread = CreateThread( NULL,
|
|
// Big initial size to prevent stack overflows
|
|
IIS_DEFAULT_INITIAL_STACK_SIZE,
|
|
HTTPFilterServiceThreadProc,
|
|
NULL,
|
|
0,
|
|
&dwThreadId );
|
|
|
|
// If failed
|
|
if ( !hThread )
|
|
{
|
|
// Get the error
|
|
dwError=GetLastError();
|
|
|
|
// Convert it to HRESULT
|
|
hr=HRESULT_FROM_WIN32(dwError);
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"CreateThread() failed. hr = %x\n",
|
|
hr ));
|
|
//
|
|
// We have to report the through SCM
|
|
// since otherwise service may end up
|
|
// in START_PENDING forever
|
|
//
|
|
|
|
SCM_MANAGER::ReportFatalServiceStartupError(
|
|
HTTPFILTER_SERVICE_NAME_W,
|
|
dwError );
|
|
|
|
goto exit;
|
|
}
|
|
|
|
// Wait for the service shutdown
|
|
if ( WaitForSingleObject(hThread, INFINITE) == WAIT_FAILED )
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
hr = HRESULT_FROM_WIN32( dwError );
|
|
DBGPRINTF((
|
|
DBG_CONTEXT,
|
|
"WaitForSingleObject() failed. hr = %x\n",
|
|
hr
|
|
));
|
|
|
|
goto exit;
|
|
}
|
|
|
|
// Get the exit code
|
|
if ( !GetExitCodeThread(hThread, &dwError) )
|
|
{
|
|
dwError = GetLastError();
|
|
hr = HRESULT_FROM_WIN32(dwError);
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"GetExitCodeThread() failed. hr = %x\n",
|
|
hr ));
|
|
goto exit;
|
|
}
|
|
|
|
DBG_ASSERT( dwError != STILL_ACTIVE );
|
|
|
|
// If HTTPFilterServiceThreadProc failed
|
|
if ( dwError != ERROR_SUCCESS )
|
|
{
|
|
// Convert it to HRESULT
|
|
hr = HRESULT_FROM_WIN32(dwError);
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"HTTPFilterServiceThreadProc() failed. hr = %x\n",
|
|
hr ));
|
|
}
|
|
|
|
exit:
|
|
if ( hThread )
|
|
{
|
|
// Close the handle
|
|
CloseHandle(hThread);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
ServiceEntry(
|
|
DWORD /*cArgs*/, // unused
|
|
LPSTR /*pArgs[]*/, // unused
|
|
PTCPSVCS_GLOBAL_DATA /*pGlobalData*/ // unused
|
|
|
|
)
|
|
/*++
|
|
Routine:
|
|
IIS services use ServiceEntry for entry point while HTTPFilter has
|
|
HTTPFilterServiceMain name chosen for default entry point by preference of lsass
|
|
Let's support both entry points so that HTTPFilter gets easily loaded
|
|
by both lsass and inetinfo.exe
|
|
|
|
--*/
|
|
{
|
|
HTTPFilterServiceMain( 0,
|
|
NULL );
|
|
}
|