731 lines
17 KiB
C++
731 lines
17 KiB
C++
/*++
|
|
|
|
Copyright (c) 1998 Microsoft Corporation
|
|
|
|
Module Name :
|
|
servercert.cxx
|
|
|
|
Abstract:
|
|
Server Certificate wrapper
|
|
|
|
Author:
|
|
Bilal Alam (BAlam) 29-March-2000
|
|
|
|
Environment:
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
Stream Filter Worker Process
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
SERVER_CERT_HASH * SERVER_CERT::sm_pServerCertHash;
|
|
|
|
SERVER_CERT::SERVER_CERT(
|
|
CREDENTIAL_ID * pCredentialId
|
|
) : _pCredentialId( pCredentialId ),
|
|
_pCertContext( NULL ),
|
|
_pCertStore( NULL ),
|
|
_cRefs( 1 ),
|
|
_usPublicKeySize( 0 )
|
|
{
|
|
_dwSignature = SERVER_CERT_SIGNATURE;
|
|
}
|
|
|
|
SERVER_CERT::~SERVER_CERT()
|
|
{
|
|
if ( _pCertContext != NULL )
|
|
{
|
|
CertFreeCertificateContext( _pCertContext );
|
|
_pCertContext = NULL;
|
|
}
|
|
|
|
if ( _pCertStore != NULL )
|
|
{
|
|
_pCertStore->DereferenceStore();
|
|
_pCertStore = NULL;
|
|
}
|
|
|
|
_dwSignature = SERVER_CERT_SIGNATURE_FREE;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
SERVER_CERT::Initialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize server certificate globals
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
sm_pServerCertHash = new SERVER_CERT_HASH();
|
|
if ( sm_pServerCertHash == NULL )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//static
|
|
VOID
|
|
SERVER_CERT::Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Cleanup server certificate globals
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( sm_pServerCertHash != NULL )
|
|
{
|
|
delete sm_pServerCertHash;
|
|
sm_pServerCertHash = NULL;
|
|
}
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
SERVER_CERT::GetServerCertificate(
|
|
DWORD dwSiteId,
|
|
SERVER_CERT ** ppServerCert
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Find a suitable server certificate for use with the site represnented by
|
|
given site id
|
|
|
|
Arguments:
|
|
|
|
dwSiteId - Site to find cert for
|
|
ppServerCert - Filled with a pointer to server certificate
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
SERVER_CERT * pServerCert = NULL;
|
|
CREDENTIAL_ID * pCredentialId = NULL;
|
|
HRESULT hr = NO_ERROR;
|
|
LK_RETCODE lkrc;
|
|
STACK_STRU( strMBPath, 64 );
|
|
WCHAR achNum[ 64 ];
|
|
DWORD cchTrunc = 0;
|
|
|
|
if ( ppServerCert == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
*ppServerCert = NULL;
|
|
|
|
//
|
|
// First build up a Crednential ID to use in looking up in our
|
|
// server cert cache
|
|
//
|
|
|
|
pCredentialId = new CREDENTIAL_ID;
|
|
if ( pCredentialId == NULL )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
//
|
|
// First try the site level configuration
|
|
//
|
|
|
|
hr = strMBPath.Copy( L"/LM/W3SVC/" );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
cchTrunc = strMBPath.QueryCCH();
|
|
|
|
_ultow( dwSiteId, achNum, 10 );
|
|
|
|
hr = strMBPath.Append( achNum );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
hr = SERVER_CERT::BuildCredentialId( strMBPath,
|
|
pCredentialId );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
//
|
|
// OK. If we failed because of not found, then look up at service
|
|
// level
|
|
//
|
|
|
|
if ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) )
|
|
{
|
|
DBG_ASSERT( cchTrunc != 0 );
|
|
|
|
strMBPath.SetLen( cchTrunc );
|
|
|
|
hr = SERVER_CERT::BuildCredentialId( strMBPath,
|
|
pCredentialId );
|
|
}
|
|
|
|
if ( FAILED( hr ) )
|
|
{
|
|
//
|
|
// Regardless of error, we are toast because we couldn't find
|
|
// a server cert
|
|
//
|
|
|
|
delete pCredentialId;
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
DBG_ASSERT( sm_pServerCertHash != NULL );
|
|
|
|
lkrc = sm_pServerCertHash->FindKey( pCredentialId,
|
|
&pServerCert );
|
|
if ( lkrc == LK_SUCCESS )
|
|
{
|
|
//
|
|
// Server already contains a credential ID
|
|
//
|
|
|
|
delete pCredentialId;
|
|
|
|
*ppServerCert = pServerCert;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Ok. It wasn't in our case, we need to it there
|
|
//
|
|
|
|
pServerCert = new SERVER_CERT( pCredentialId );
|
|
if ( pServerCert == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
|
|
delete pCredentialId;
|
|
|
|
return hr;
|
|
}
|
|
|
|
hr = pServerCert->SetupCertificate( strMBPath );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
//
|
|
// Server certificate owns the reference to pCredentialId now
|
|
//
|
|
|
|
delete pServerCert;
|
|
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Now try to add cert to hash.
|
|
//
|
|
|
|
lkrc = sm_pServerCertHash->InsertRecord( pServerCert );
|
|
|
|
//
|
|
// Ignore the error. If it didn't get added then we will naturally
|
|
// clean it up on the callers dereference
|
|
//
|
|
|
|
*ppServerCert = pServerCert;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
SERVER_CERT::BuildCredentialId(
|
|
STRU & strMBPath,
|
|
CREDENTIAL_ID * pCredentialId
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Read the configured server cert and CTL hash. This forms the identifier
|
|
for the credentials we need for this site
|
|
|
|
Arguments:
|
|
|
|
strMBPath - Metabase path
|
|
pCredentialId - Filled with credential ID
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
MB mb( g_pStreamFilter->QueryMDObject() );
|
|
BOOL fRet;
|
|
DWORD cbRequired;
|
|
BYTE abBuff[ 64 ];
|
|
BUFFER buff( abBuff, sizeof( abBuff ) );
|
|
HRESULT hr = NO_ERROR;
|
|
|
|
if ( pCredentialId == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
fRet = mb.Open( strMBPath.QueryStr(),
|
|
METADATA_PERMISSION_READ );
|
|
if ( !fRet )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
//
|
|
// Find the server certificate hash
|
|
//
|
|
|
|
cbRequired = buff.QuerySize();
|
|
|
|
fRet = mb.GetData( NULL,
|
|
MD_SSL_CERT_HASH,
|
|
IIS_MD_UT_SERVER,
|
|
BINARY_METADATA,
|
|
buff.QueryPtr(),
|
|
&cbRequired,
|
|
METADATA_NO_ATTRIBUTES );
|
|
if ( !fRet )
|
|
{
|
|
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
if ( !buff.Resize( cbRequired ) )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
fRet = mb.GetData( NULL,
|
|
MD_SSL_CERT_HASH,
|
|
IIS_MD_UT_SERVER,
|
|
BINARY_METADATA,
|
|
buff.QueryPtr(),
|
|
&cbRequired,
|
|
METADATA_NO_ATTRIBUTES );
|
|
if ( !fRet )
|
|
{
|
|
DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
|
|
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
}
|
|
|
|
//
|
|
// No server cert. Then we can't setup SSL
|
|
//
|
|
|
|
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND );
|
|
}
|
|
|
|
//
|
|
// Add to our credential ID
|
|
//
|
|
|
|
hr = pCredentialId->Append( (PBYTE) buff.QueryPtr(),
|
|
cbRequired );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// Look for an optional CTL to add to ID
|
|
//
|
|
|
|
cbRequired = buff.QuerySize();
|
|
|
|
fRet = mb.GetData( NULL,
|
|
MD_SSL_CTL_IDENTIFIER,
|
|
IIS_MD_UT_SERVER,
|
|
STRING_METADATA,
|
|
buff.QueryPtr(),
|
|
&cbRequired,
|
|
METADATA_NO_ATTRIBUTES );
|
|
if ( !fRet )
|
|
{
|
|
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
if ( !buff.Resize( cbRequired ) )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
fRet = mb.GetData( NULL,
|
|
MD_SSL_CTL_IDENTIFIER,
|
|
IIS_MD_UT_SERVER,
|
|
STRING_METADATA,
|
|
buff.QueryPtr(),
|
|
&cbRequired,
|
|
METADATA_NO_ATTRIBUTES );
|
|
}
|
|
}
|
|
|
|
if ( fRet )
|
|
{
|
|
hr = pCredentialId->Append( (PBYTE) buff.QueryPtr(),
|
|
cbRequired );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
HRESULT
|
|
SERVER_CERT::SetupCertificate(
|
|
STRU & strMBPath
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Do the SChannel go to setup the credentials for the server certificate
|
|
setup at the metabase path specified
|
|
|
|
Arguments:
|
|
|
|
strMBPath - Metabase path for SSL configuration.
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
HRESULT hr = NO_ERROR;
|
|
MB mb( g_pStreamFilter->QueryMDObject() );
|
|
BYTE abBuff[ 128 ];
|
|
BUFFER buff( abBuff, sizeof( abBuff ) );
|
|
DWORD cbRequired = 0;
|
|
DWORD cbHash = 0;
|
|
STACK_STRU( strStoreName, 256 );
|
|
CERT_STORE * pCertStore = NULL;
|
|
CRYPT_HASH_BLOB hashBlob;
|
|
PCERT_PUBLIC_KEY_INFO pPublicKey;
|
|
DWORD cbX500Name = 0;
|
|
BOOL fRet;
|
|
|
|
|
|
if ( !mb.Open( strMBPath.QueryStr() ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// Get the required server certificate hash
|
|
//
|
|
|
|
cbRequired = buff.QuerySize();
|
|
if ( !mb.GetData( NULL,
|
|
MD_SSL_CERT_HASH,
|
|
IIS_MD_UT_SERVER,
|
|
BINARY_METADATA,
|
|
buff.QueryPtr(),
|
|
&cbRequired,
|
|
METADATA_NO_ATTRIBUTES ) )
|
|
{
|
|
if ( GetLastError() == ERROR_INSUFFICIENT_BUFFER )
|
|
{
|
|
//
|
|
// Try again with a bigger buffer
|
|
//
|
|
|
|
if ( !buff.Resize( cbRequired ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
|
|
if ( !mb.GetData( NULL,
|
|
MD_SSL_CERT_HASH,
|
|
IIS_MD_UT_SERVER,
|
|
BINARY_METADATA,
|
|
buff.QueryPtr(),
|
|
&cbRequired,
|
|
METADATA_NO_ATTRIBUTES ) )
|
|
{
|
|
DBG_ASSERT( GetLastError() != ERROR_INSUFFICIENT_BUFFER );
|
|
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
}
|
|
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
|
|
cbHash = cbRequired;
|
|
DBG_ASSERT( cbHash != 0 );
|
|
|
|
//
|
|
// Get the required certificate store
|
|
//
|
|
|
|
if ( !mb.GetStr( NULL,
|
|
MD_SSL_CERT_STORE_NAME,
|
|
IIS_MD_UT_SERVER,
|
|
&strStoreName,
|
|
METADATA_NO_ATTRIBUTES,
|
|
NULL ) )
|
|
{
|
|
//
|
|
// No store, no play
|
|
//
|
|
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// OK. We are ready to retrieve the certificate using CAPI APIs
|
|
//
|
|
|
|
//
|
|
// First get the desired store and store it away for later!
|
|
//
|
|
|
|
hr = CERT_STORE::OpenStore( strStoreName,
|
|
&pCertStore );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
DBG_ASSERT( pCertStore != NULL );
|
|
_pCertStore = pCertStore;
|
|
|
|
//
|
|
// Now find the certificate hash in the store
|
|
//
|
|
|
|
hashBlob.cbData = cbHash;
|
|
hashBlob.pbData = (PBYTE) buff.QueryPtr();
|
|
|
|
_pCertContext = CertFindCertificateInStore( _pCertStore->QueryStore(),
|
|
X509_ASN_ENCODING,
|
|
0,
|
|
CERT_FIND_SHA1_HASH,
|
|
(VOID*) &hashBlob,
|
|
NULL );
|
|
if ( _pCertContext == NULL )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
|
|
|
|
//
|
|
// Get certificate public key size
|
|
//
|
|
|
|
DBG_ASSERT( _usPublicKeySize == 0 );
|
|
|
|
pPublicKey = &(_pCertContext->pCertInfo->SubjectPublicKeyInfo);
|
|
|
|
_usPublicKeySize = (USHORT) CertGetPublicKeyLength( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
pPublicKey );
|
|
|
|
if ( _usPublicKeySize == 0 )
|
|
{
|
|
//
|
|
// Failed to receive public key size
|
|
//
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
|
|
}
|
|
|
|
//
|
|
// Get issuer string
|
|
//
|
|
|
|
DBG_ASSERT( _pCertContext->pCertInfo != NULL );
|
|
|
|
//
|
|
// First find out the size of buffer required for issuer
|
|
//
|
|
|
|
cbX500Name = CertNameToStrA( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
&_pCertContext->pCertInfo->Issuer,
|
|
CERT_X500_NAME_STR,
|
|
NULL,
|
|
0);
|
|
if(!buff.Resize(cbX500Name))
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
|
|
cbX500Name = CertNameToStrA( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
&_pCertContext->pCertInfo->Issuer,
|
|
CERT_X500_NAME_STR,
|
|
(LPSTR) buff.QueryPtr(),
|
|
buff.QuerySize() );
|
|
|
|
hr = _strIssuer.Copy( (LPSTR) buff.QueryPtr() );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
//
|
|
// Get subject string
|
|
//
|
|
|
|
//
|
|
// First find out the size of buffer required for subject
|
|
//
|
|
|
|
cbX500Name = CertNameToStrA( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
&_pCertContext->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR,
|
|
NULL,
|
|
0);
|
|
if(!buff.Resize(cbX500Name))
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
goto Finished;
|
|
}
|
|
cbX500Name = CertNameToStrA( PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
|
|
&_pCertContext->pCertInfo->Subject,
|
|
CERT_X500_NAME_STR,
|
|
(LPSTR) buff.QueryPtr(),
|
|
buff.QuerySize() );
|
|
|
|
hr = _strSubject.Copy( (LPSTR) buff.QueryPtr() );
|
|
if ( FAILED( hr ) )
|
|
{
|
|
goto Finished;
|
|
}
|
|
|
|
Finished:
|
|
|
|
return hr;
|
|
}
|
|
|
|
//static
|
|
LK_PREDICATE
|
|
SERVER_CERT::CertStorePredicate(
|
|
SERVER_CERT * pServerCert,
|
|
void * pvState
|
|
)
|
|
/*++
|
|
|
|
Description:
|
|
|
|
DeleteIf() predicate used to find items which reference the
|
|
CERT_STORE pointed to by pvState
|
|
|
|
Arguments:
|
|
|
|
pServerCert - Server cert
|
|
pvState - Points to CERT_STORE
|
|
|
|
Returns:
|
|
|
|
LK_PREDICATE - LKP_PERFORM indicates removing the current
|
|
token from token cache
|
|
|
|
LKP_NO_ACTION indicates doing nothing.
|
|
|
|
--*/
|
|
{
|
|
LK_PREDICATE lkpAction;
|
|
CERT_STORE * pCertStore;
|
|
|
|
DBG_ASSERT( pServerCert != NULL );
|
|
|
|
pCertStore = (CERT_STORE*) pvState;
|
|
DBG_ASSERT( pCertStore != NULL );
|
|
|
|
if ( pServerCert->_pCertStore == pCertStore )
|
|
{
|
|
//
|
|
// Before we delete the cert, flush any site which is referencing
|
|
// it
|
|
//
|
|
|
|
SITE_CONFIG::FlushByServerCert( pServerCert );
|
|
|
|
lkpAction = LKP_PERFORM;
|
|
}
|
|
else
|
|
{
|
|
lkpAction = LKP_NO_ACTION;
|
|
}
|
|
|
|
return lkpAction;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
SERVER_CERT::FlushByStore(
|
|
CERT_STORE * pCertStore
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flush any server certs which reference the given store
|
|
|
|
Arguments:
|
|
|
|
pCertStore - Cert store to check
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( sm_pServerCertHash != NULL );
|
|
|
|
sm_pServerCertHash->DeleteIf( SERVER_CERT::CertStorePredicate,
|
|
pCertStore );
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|