/*++ Copyright (c) 2001 Microsoft Corporation Module Name : sslconfigprovserver.cxx Abstract: SSL CONFIG PROV server Listens to commands sent from clients and executes SSL parameter lookups in the metabase Author: Jaroslav Dunajsky April-24-2001 Environment: Win32 - User Mode Project: Stream Filter Worker Process --*/ #include "precomp.hxx" //virtual HRESULT SSL_CONFIG_PROV_SERVER::PipeListener( VOID ) /*++ Routine Description: Listens on SslConfigPipe and executes commands Arguments: Return Value: HRESULT --*/ { SSL_CONFIG_PIPE_COMMAND Command; HRESULT hr = E_FAIL; // // Listen on pipe to receive commands // and handle them // while ( TRUE ) { hr = PipeReceiveCommand( &Command ); if ( FAILED( hr ) ) { goto Cleanup; } switch( Command.dwCommandId ) { case CMD_GET_SSL_CONFIGURATION: hr = SendSiteSslConfiguration( Command.dwParameter1 ); if ( FAILED( hr ) ) { goto Cleanup; } break; case CMD_GET_ONE_SITE_SECURE_BINDINGS: hr = SendOneSiteSecureBindings( Command.dwParameter1 ); if ( FAILED( hr ) ) { goto Cleanup; } break; case CMD_GET_ALL_SITES_SECURE_BINDINGS: hr = SendAllSitesSecureBindings(); if ( FAILED( hr ) ) { goto Cleanup; } break; case INVALID_SSL_CONFIGURATION_COMMAND: default: DBG_ASSERT(FALSE); } } Cleanup: return hr; } HRESULT SSL_CONFIG_PROV_SERVER::Initialize( VOID ) /*++ Routine Description: Initialize named pipe Connect to metabase Arguments: Return Value: HRESULT --*/ { HRESULT hr = E_FAIL; HANDLE hEvent = NULL; DBG_ASSERT( _InitStatus == INIT_NONE ); // // Initialize the metabase access (ABO) // hr = CoCreateInstance( CLSID_MSAdminBase, NULL, CLSCTX_SERVER, IID_IMSAdminBase, reinterpret_cast(&_pAdminBase) ); if( FAILED(hr) ) { DBGPRINTF(( DBG_CONTEXT, "Error creating ABO object. hr = %x\n", hr )); goto Cleanup; } _InitStatus = INIT_METABASE; // // Initialize parent - it will create pipe and // start listener thread ( thread will execute PipeListener() ) // hr = SSL_CONFIG_PIPE::PipeInitializeServer( WSZ_SSL_CONFIG_PIPE ); if( FAILED(hr) ) { goto Cleanup; } _InitStatus = INIT_CONFIG_PIPE; hr = S_OK; Cleanup: if ( FAILED( hr ) ) { Terminate(); } return hr; } HRESULT SSL_CONFIG_PROV_SERVER::Terminate( VOID ) /*++ Routine Description: Close named pipe for metabase change notifications Close metabase named pipe Arguments: Return Value: HRESULT --*/ { HRESULT hr = E_FAIL; switch( _InitStatus ) { case INIT_CONFIG_PIPE: // // Terminate Pipe // hr = SSL_CONFIG_PIPE::PipeTerminate(); DBG_ASSERT( SUCCEEDED( hr ) ); case INIT_METABASE: // // Terminate the metabase access (ABO) // if ( _pAdminBase != NULL ) { _pAdminBase->Release(); _pAdminBase = NULL; } } return S_OK; } HRESULT SSL_CONFIG_PROV_SERVER::SendOneSiteSecureBindings( IN DWORD dwSiteId, OPTIONAL IN BOOL fNoResponseOnNotFoundError, OPTIONAL IN MB * pMb ) /*++ Routine Description: Read secure bindings for the site from metabase and send them over named pipe Arguments: dwSiteId - site ID fNoResponseOnNotFoundError - in the case of error don't send response to client it is used for multisite enumeration to not to send MD_ERROR_DATA_NOT_FOUND pMb - already opened metabase (used for multisite enumeration) Return Value: HRESULT --*/ { SSL_CONFIG_PIPE_RESPONSE_HEADER ResponseHeader; HRESULT hr = E_FAIL; MB mb( _pAdminBase ); BOOL fOpenedMetabase = FALSE; MULTISZ mszBindings; WCHAR achSitePath[ METADATA_MAX_NAME_LEN ]; DWORD dwNumberOfStringsInMultisz; DWORD cbBindings; DWORD dwServerState = 0; BOOL fRet = FALSE; ZeroMemory( &ResponseHeader, sizeof( SSL_CONFIG_PIPE_RESPONSE_HEADER ) ); // // Read secure bindings from metabase // if ( pMb == NULL ) { fRet = mb.Open( L"/LM/W3SVC", METADATA_PERMISSION_READ ); if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "Failed to open metabase. hr = %x\n", hr )); goto SendResponse; } pMb = &mb; fOpenedMetabase = TRUE; } _snwprintf( achSitePath, sizeof( achSitePath ) / sizeof( WCHAR ) - 1, L"/%d/", dwSiteId ); fRet = pMb->GetDword( achSitePath, MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwServerState, 0 ); if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "Failed to Server State. hr = %x\n", hr )); goto SendResponse; } if ( dwServerState != MD_SERVER_STATE_STARTED ) { // // When site is not started we will communicate // DATA_NOT_FOUND so that caller doesn't use the site // bindings // hr = MD_ERROR_DATA_NOT_FOUND; goto SendResponse; } fRet = pMb->GetMultisz( achSitePath, MD_SECURE_BINDINGS, IIS_MD_UT_SERVER, &mszBindings, 0 ); if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); if ( hr != MD_ERROR_DATA_NOT_FOUND ) { DBGPRINTF(( DBG_CONTEXT, "Failed to Get Multisz. hr = %x\n", hr )); } goto SendResponse; } hr = S_OK; SendResponse: // // Close metabase handle // if ( fOpenedMetabase ) { mb.Close(); } if ( fNoResponseOnNotFoundError && ( hr == MD_ERROR_DATA_NOT_FOUND || hr == HRESULT_FROM_WIN32( ERROR_PATH_NOT_FOUND ) ) ) { // // MD_ERROR_DATA_NOT_FOUND is OK // it is OK for site not to have secure bindings configured // // ERROR_PATH_NOT_FOUND may happen for example when site ID is greater // than max DWORD. Then conversion from string to DWORD may have truncated the actual Id. // return S_OK; } // // Prepare and send ResponseHeader // if ( FAILED( hr ) ) { cbBindings = 0; } else { cbBindings = sizeof(WCHAR) * ( MULTISZ::CalcLength( reinterpret_cast(mszBindings.QueryPtr()), &dwNumberOfStringsInMultisz ) + 0 /*for Terminating 0*/); } ResponseHeader.dwCommandId = CMD_GET_ONE_SITE_SECURE_BINDINGS; ResponseHeader.dwParameter1 = dwSiteId; ResponseHeader.hrErrorCode = hr; ResponseHeader.dwResponseSize = cbBindings; hr = PipeSendResponseHeader( &ResponseHeader ); if ( FAILED( hr ) ) { return hr; } if ( cbBindings != 0 ) { // // Send Bindings data // hr = PipeSendData( cbBindings, reinterpret_cast(mszBindings.QueryPtr()) ); if ( FAILED( hr ) ) { return hr; } } hr = S_OK; return hr; } HRESULT SSL_CONFIG_PROV_SERVER::SendAllSitesSecureBindings( ) /*++ Routine Description: Enumerate all Secure Bindings and write it to named pipe Arguments: Return Value: HRESULT --*/ { DWORD dwSiteId = 0; MB mb( _pAdminBase ); DWORD dwIndex = 0; WCHAR achSitePath[ METADATA_MAX_NAME_LEN ]; WCHAR * pchEndPtr; HRESULT hr = E_FAIL; HRESULT hrMB = E_FAIL; BOOL fRet = FALSE; SSL_CONFIG_PIPE_RESPONSE_HEADER ResponseHeader; ZeroMemory( &ResponseHeader, sizeof( SSL_CONFIG_PIPE_RESPONSE_HEADER ) ); fRet = mb.Open( L"/LM/W3SVC", METADATA_PERMISSION_READ ); if ( !fRet ) { hrMB = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "Failed to open metabase. hr = %x\n", hrMB )); // Don't do goto Cleanup; // // Before returning from the function // report error over named pipe // } else { hrMB = S_OK; } // // First part of the response // - in the case of previous error this is the only response sent to client // - in the case of success, CMD_GET_ONE_SITE_SECURE_BINDINGS response will // be returned for each site ) // ResponseHeader.dwCommandId = CMD_GET_ALL_SITES_SECURE_BINDINGS; ResponseHeader.dwParameter1 = 0; ResponseHeader.hrErrorCode = hrMB; ResponseHeader.dwResponseSize = 0; // 0 indicates that hr = PipeSendResponseHeader( &ResponseHeader ); if ( FAILED( hr ) || FAILED( hrMB ) ) { goto Cleanup; } // // Enumerate all sites // dwIndex = 0; while ( mb.EnumObjects( L"", achSitePath, dwIndex++ ) ) { // // We only want the sites (w3svc/) // dwSiteId = wcstoul( achSitePath, &pchEndPtr, 10 ); if ( * pchEndPtr != L'\0' ) { // // Correct Site node must consist of digits only // if pchEndPtr is pointing to anything but 0, it means // invalid value. We will ignore continue; } // // Send bindings for current site // hr = SendOneSiteSecureBindings ( dwSiteId, TRUE, /*fNoResponseOnError*/ &mb ); if ( FAILED( hr ) ) { goto Cleanup; } } // // Terminating record // if everything went OK then EnumObjects() returned // ERROR_NO_MORE_ITEMS - That is indication for client // that all data has been retrieved // ResponseHeader.dwCommandId = CMD_GET_ONE_SITE_SECURE_BINDINGS; ResponseHeader.dwParameter1 = 0; ResponseHeader.hrErrorCode = HRESULT_FROM_WIN32( GetLastError() ); ResponseHeader.dwResponseSize = 0; hr = PipeSendResponseHeader( &ResponseHeader ); if ( FAILED( hr ) || FAILED( hrMB ) ) { goto Cleanup; } hr = S_OK; Cleanup: mb.Close(); return hr; } HRESULT SSL_CONFIG_PROV_SERVER::SendSiteSslConfiguration( IN DWORD dwSiteId ) /*++ Routine Description: Get all SSL configuration parameters for specified site Arguments: dwSiteId SiteSslConfiguration Return Value: HRESULT --*/ { SITE_SSL_CONFIGURATION SiteSslConfig; SSL_CONFIG_PIPE_RESPONSE_HEADER ResponseHeader; HRESULT hr = E_FAIL; MB mb( _pAdminBase ); WCHAR achMBSitePath[ 256 ]; WCHAR achMBSiteRootPath[ 256 ]; DWORD dwUseDsMapper = 0; DWORD cbRequired = 0; DWORD dwServerState = 0; BOOL fRet = FALSE; ZeroMemory( &SiteSslConfig, sizeof(SiteSslConfig) ); // // set configuration defaults // SiteSslConfig._dwRevocationFreshnessTime = DEFAULT_REVOCATION_FRESHNESS_TIME; ZeroMemory( &ResponseHeader, sizeof( SSL_CONFIG_PIPE_RESPONSE_HEADER ) ); fRet = mb.Open( L"/LM/W3SVC/", METADATA_PERMISSION_READ ); if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "Failed to open metabase. hr = %x\n", hr )); goto SendResponse; } _snwprintf( achMBSitePath, sizeof( achMBSitePath ) / sizeof( WCHAR ) - 1, L"/%d/", dwSiteId ); _snwprintf( achMBSiteRootPath, sizeof( achMBSiteRootPath ) / sizeof( WCHAR ) - 1, L"/%d/root/", dwSiteId ); fRet = mb.GetDword( achMBSitePath, MD_SERVER_STATE, IIS_MD_UT_SERVER, &dwServerState, 0 ); if ( !fRet ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DBGPRINTF(( DBG_CONTEXT, "Failed to read Server State. hr = %x\n", hr )); goto SendResponse; } if ( dwServerState != MD_SERVER_STATE_STARTED ) { // // When site is not started we will communicate // DATA_NOT_FOUND so that caller doesn't use the site // bindings // hr = MD_ERROR_DATA_NOT_FOUND; goto SendResponse; } // // Lookup MD_SSL_USE_DS_MAPPER // SSLUseDsMapper is global setting that is not inherited to sites (IIS5 legacy) // We have to read it from lm/w3svc // mb.GetDword( L"", MD_SSL_USE_DS_MAPPER, IIS_MD_UT_SERVER, &dwUseDsMapper ); SiteSslConfig._fSslUseDsMapper = !!dwUseDsMapper; // MD_SSL_ACCESS_PERM mb.GetDword( achMBSiteRootPath, MD_SSL_ACCESS_PERM, IIS_MD_UT_FILE, &SiteSslConfig._dwSslAccessPerm ); // Lookup MD_SSL_CERT_HASH SiteSslConfig._cbSslCertHash= sizeof(SiteSslConfig._SslCertHash); hr = ReadMetabaseBinary( &mb, achMBSitePath, MD_SSL_CERT_HASH, &SiteSslConfig._cbSslCertHash, SiteSslConfig._SslCertHash ); // MD_SSL_CERT_STORE_NAME hr = ReadMetabaseString( &mb, achMBSitePath, MD_SSL_CERT_STORE_NAME, sizeof(SiteSslConfig._SslCertStoreName), SiteSslConfig._SslCertStoreName ); // MD_SSL_CERT_CONTAINER hr = ReadMetabaseString( &mb, achMBSitePath, MD_SSL_CERT_CONTAINER, sizeof(SiteSslConfig._SslCertContainer), SiteSslConfig._SslCertContainer ); // MD_SSL_CERT_PROVIDER mb.GetDword( achMBSitePath, MD_SSL_CERT_PROVIDER, IIS_MD_UT_SERVER, &SiteSslConfig._dwSslCertProvider ); // MD_SSL_CERT_OPEN_FLAGS mb.GetDword( achMBSitePath, MD_SSL_CERT_OPEN_FLAGS, IIS_MD_UT_SERVER, &SiteSslConfig._dwSslCertOpenFlags ); // MD_CERT_CHECK_MODE mb.GetDword( achMBSitePath, MD_CERT_CHECK_MODE, IIS_MD_UT_SERVER, &SiteSslConfig._dwCertCheckMode ); // MD_REVOCATION_FRESHNESS_TIME mb.GetDword( achMBSitePath, MD_REVOCATION_FRESHNESS_TIME, IIS_MD_UT_SERVER, &SiteSslConfig._dwRevocationFreshnessTime ); // MD_REVOCATION_URL_RETRIEVAL_TIMEOUT mb.GetDword( achMBSitePath, MD_REVOCATION_URL_RETRIEVAL_TIMEOUT, IIS_MD_UT_SERVER, &SiteSslConfig._dwRevocationUrlRetrievalTimeout ); // MD_SSL_CTL_IDENTIFIER hr = ReadMetabaseString( &mb, achMBSitePath, MD_SSL_CTL_IDENTIFIER, sizeof(SiteSslConfig._SslCtlIdentifier), SiteSslConfig._SslCtlIdentifier ); // MD_SSL_CTL_PROVIDER mb.GetDword( achMBSitePath, MD_SSL_CTL_PROVIDER, IIS_MD_UT_SERVER, &SiteSslConfig._dwSslCtlProvider ); // MD_SSL_CTL_PROVIDER_TYPE mb.GetDword( achMBSitePath, MD_SSL_CTL_PROVIDER_TYPE, IIS_MD_UT_SERVER, &SiteSslConfig._dwSslCtlProviderType ); // MD_SSL_CTL_OPEN_FLAGS mb.GetDword( achMBSitePath, MD_SSL_CTL_OPEN_FLAGS, IIS_MD_UT_SERVER, &SiteSslConfig._dwSslCtlOpenFlags ); // MD_SSL_CTL_STORE_NAME hr = ReadMetabaseString( &mb, achMBSitePath, MD_SSL_CTL_STORE_NAME, sizeof(SiteSslConfig._SslCtlStoreName), SiteSslConfig._SslCtlStoreName ); // MD_SSL_CTL_CONTAINER hr = ReadMetabaseString( &mb, achMBSitePath, MD_SSL_CTL_CONTAINER, sizeof(SiteSslConfig._SslCtlContainerName), SiteSslConfig._SslCtlContainerName ); // MD_SSL_CTL_SIGNER_HASH SiteSslConfig._cbSslCtlSignerHash = sizeof(SiteSslConfig._SslCtlSignerHash); hr = ReadMetabaseBinary( &mb, achMBSitePath, MD_SSL_CTL_SIGNER_HASH, &SiteSslConfig._cbSslCtlSignerHash, SiteSslConfig._SslCtlSignerHash ); hr = S_OK; SendResponse: // // Close metabase handle // mb.Close(); // // Prepare and send ResponseHeader // ResponseHeader.dwCommandId = CMD_GET_SSL_CONFIGURATION; ResponseHeader.dwParameter1 = dwSiteId; ResponseHeader.hrErrorCode = hr; ResponseHeader.dwResponseSize = sizeof(SiteSslConfig); hr = PipeSendResponseHeader( &ResponseHeader ); if ( FAILED( hr ) ) { return hr; } // // Send Bindings data // hr = PipeSendData( ResponseHeader.dwResponseSize, reinterpret_cast(&SiteSslConfig) ); if ( FAILED( hr ) ) { return hr; } return S_OK; } //static HRESULT SSL_CONFIG_PROV_SERVER::ReadMetabaseString( IN MB * pMb, IN WCHAR * pszPath, IN DWORD dwPropId, IN DWORD cchMetabaseString, OUT WCHAR * pszMetabaseString ) { DWORD cbRequired; BOOL fRet = FALSE; cbRequired = cchMetabaseString; fRet = pMb->GetData( pszPath, dwPropId, IIS_MD_UT_SERVER, STRING_METADATA, pszMetabaseString, &cbRequired, METADATA_NO_ATTRIBUTES ); if ( !fRet ) { if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { } return HRESULT_FROM_WIN32( GetLastError() ); } return S_OK; } //static HRESULT SSL_CONFIG_PROV_SERVER::ReadMetabaseBinary( IN MB * pMb, IN WCHAR * pszPath, IN DWORD dwPropId, IN OUT DWORD * pcbMetabaseBinary, OUT BYTE * pbMetabaseBinary ) { BOOL fRet = FALSE; fRet = pMb->GetData( pszPath, dwPropId, IIS_MD_UT_SERVER, BINARY_METADATA, pbMetabaseBinary, pcbMetabaseBinary, METADATA_NO_ATTRIBUTES ); if ( !fRet ) { if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) { } else { *pcbMetabaseBinary = 0; } return HRESULT_FROM_WIN32( GetLastError() ); } return S_OK; }