windows-nt/Source/XPSP1/NT/inetsrv/iis/ui/itools/w3key/mdkey.cpp

673 lines
18 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
// This file is the metadata version of the key storage object for the w3 server.
// it knows nothing about the LSA storage and only interacts with the metabase. Likewise,
// it does not convert old keyset.exe keys into a newer format. Any old LSA keys should have
// automatically converted to the new metabase format by the setup utility.
// file created 4/1/1997 by BoydM
#include "stdafx.h"
#include "KeyObjs.h"
#include "iiscnfgp.h"
#include "wrapmb.h"
#include "cmnkey.h"
#include "mdkey.h"
#include "mdserv.h"
#include "crackcrt.h"
#include "resource.h"
#include "ListRow.h"
#include "bindsdlg.h"
IMPLEMENT_DYNCREATE(CMDKey, CKey);
#define MAX_LEN (METADATA_MAX_NAME_LEN * sizeof(WCHAR))
#define DEFAULT_PORT 443
extern HINSTANCE g_hInstance;
//--------------------------------------------------------
CMDKey::CMDKey() :
m_fUpdateKeys( FALSE ),
m_fUpdateFriendlyName( FALSE ),
m_fUpdateIdent( FALSE ),
m_fUpdateBindings( FALSE ),
m_pService( NULL )
{}
//--------------------------------------------------------
CMDKey::CMDKey(CMDKeyService* pService) :
m_fUpdateKeys( FALSE ),
m_fUpdateFriendlyName( FALSE ),
m_fUpdateIdent( FALSE ),
m_fUpdateBindings( FALSE ),
m_pService( pService )
{}
//--------------------------------------------------------
CMDKey::~CMDKey()
{}
//-------------------------------------------------------------
void CMDKey::SetName( CString &szNewName )
{
CCmnKey::SetName( szNewName );
m_fUpdateFriendlyName = TRUE;
}
//-------------------------------------------------------------
// update the key's caption
void CMDKey::UpdateCaption( void )
{
// specify the resources to use
HINSTANCE hOldRes = AfxGetResourceHandle();
AfxSetResourceHandle( g_hInstance );
CString sz;
// the caption is based on the name of the key
CString szCaption = m_szName;
// now we tack on info about the server it is attached to
szCaption += _T(" <");
switch( m_rgbszBindings.GetSize() )
{
case 0: // there are no bindings, do nothing
sz.Empty();
break;
case 1: // if there is only one binding, use it in the brackets
sz = m_rgbszBindings[0];
// actually, we need to see if it is the non-localized default string
if ( sz == MDNAME_DEFAULT )
sz.LoadString( IDS_DEFAULT ); // load the localized default string
break;
default: // there are multiple bindings, say so
sz.LoadString( IDS_MULTIPLE_BINDINGS );
break;
};
// close the brackets
szCaption += sz;
szCaption += _T(">");
// and setup the caption
m_szItemName = szCaption;
FSetCaption(szCaption);
// update the icon too
UpdateIcon();
// restore the resources
AfxSetResourceHandle( hOldRes );
}
//-------------------------------------------------------------
// make a copy of the key
//-------------------------------------------------------------
CKey* CMDKey::PClone( void )
{
CMDKey* pClone = NULL;
// TRY to make a new key object
try
{
pClone = new CMDKey(m_pService);
// copy over all the data
pClone->CopyDataFrom( this );
}
catch( CException e )
{
// if the object had been made, delete it
if ( pClone )
delete pClone;
return NULL;
}
return (CKey*)pClone;
}
//-------------------------------------------------------------
// copy the members from a key into this key
//-------------------------------------------------------------
void CMDKey::CopyDataFrom( CKey* pKey )
{
// copy over the base data
CKey::CopyDataFrom( pKey );
// if the key we are copying from is a MD key, copy over
// the w3MD specific information as well
if ( pKey->IsKindOf(RUNTIME_CLASS(CMDKey)) )
{
CMDKey* pMDKey = (CMDKey*)pKey;
m_szName = pMDKey->m_szName;
}
else
{
m_szName = pKey->m_szItemName;
}
}
//-------------------------------------------------------------
void CMDKey::OnProperties()
{
// specify the resources to use
HINSTANCE hOldRes = AfxGetResourceHandle();
AfxSetResourceHandle( g_hInstance );
// if this key does not have a signed certificate, do not allow the user
// to make any bindings to it
if ( !m_pCertificate )
{
AfxMessageBox( IDS_DONT_BIND_UNSIGNED );
// restore the resources
AfxSetResourceHandle( hOldRes );
return;
}
// the properties of the w3 key invove its ip address relationship
CBindingsDlg dlg( m_pService->m_pszwMachineName );
// give it this key
dlg.m_pKey = this;
// set the instance members of the dialog
// run the dialog
if ( dlg.DoModal() == IDOK )
{
// cause the name to rebuild
UpdateCaption();
// set it dirty
SetDirty( TRUE );
}
// restore the resources
AfxSetResourceHandle( hOldRes );
}
//--------------------------------------------------------
// add a binding to the binding list
void CMDKey::AddBinding( LPCSTR psz )
{
CString szBinding = psz;
// filter out disabled or incomplete key bindings
if ( (szBinding.Find(MDNAME_INCOMPLETE) >= 0) ||
(szBinding.Find(MDNAME_DISABLED) >= 0) )
{
return;
}
// add the binding to the list
m_rgbszBindings.Add( psz );
// update the caption if we need to
switch( m_rgbszBindings.GetSize() )
{
case 1: // the display changes on these
case 2:
UpdateCaption();
};
}
//--------------------------------------------------------
// is a given binding already associated with the key
void CMDKey::RemoveBinding( CString szBinding )
{
DWORD nItems, iItem;
// scan the binding list
nItems = m_rgbszBindings.GetSize();
for ( iItem = 0; iItem < nItems; iItem++ )
{
// look for the binding
if ( szBinding == m_rgbszBindings[iItem] )
{
// found it!
m_rgbszBindings.RemoveAt( iItem );
m_fUpdateBindings = TRUE;
// update the caption if we need to
switch( m_rgbszBindings.GetSize() )
{
case 0: // the display changes on these
case 1:
UpdateCaption();
};
}
}
}
//--------------------------------------------------------
// is a given binding already associated with the key
BOOL CMDKey::FContainsBinding( CString szBinding )
{
DWORD nItems, iItem;
// scan the binding list
nItems = m_rgbszBindings.GetSize();
for ( iItem = 0; iItem < nItems; iItem++ )
{
// look for the binding
if ( szBinding == m_rgbszBindings[iItem] )
{
// found it!
return TRUE;
}
}
// we did not find the binding
return FALSE;
}
//--------------------------------------------------------
BOOL CMDKey::FGetIdentString( CString &szIdent )
{
// make sure the cert is there
if ( !m_pCertificate || !m_cbCertificate )
return FALSE;
return FGetIdentString( m_pCertificate, m_cbCertificate, szIdent );
}
//--------------------------------------------------------
BOOL CMDKey::FGetIdentString( PVOID pCert, DWORD cbCert, CString &szIdent )
{
// declare the cracker object
CCrackedCert cracker;
// crack the cert
if ( cracker.CrackCert( (PUCHAR)pCert, cbCert ) )
{
DWORD* pdw = cracker.PGetSerialNumber();
szIdent.Format( "%d:%d:%d:%d", pdw[0], pdw[1], pdw[2], pdw[3] );
// success
return TRUE;
}
return FALSE;
}
//--------------------------------------------------------
// does a given key name exist already in the metabase?
BOOL CMDKey::FGetIdentString( CWrapMetaBase* pWrap, PCHAR pszObj, CString &szIdent )
{
BOOL fAnswer = FALSE;
DWORD cbData;
PVOID pData;
CString sz;
// if this is an incomplete key, fail
if ( _tcsncmp(MDNAME_INCOMPLETE, pszObj, _tcslen(MDNAME_INCOMPLETE)) == 0 )
return FALSE;
// try and ready the cached ident directly.
BOOL f = pWrap->GetString( pszObj, MD_SSL_IDENT, IIS_MD_UT_SERVER,
sz.GetBuffer(MAX_LEN), MAX_LEN);
sz.ReleaseBuffer();
if ( f )
{
// good. It was cached.
szIdent = sz;
m_szIdent = szIdent;
return TRUE;
}
// drat. We haven't cached the ident for this key before. we need to get it. This
// means loading the certificate - and cracking it to get the serial number. If there
// is no certificate (an incomplete key) we return false.
pData = pWrap->GetData( pszObj, MD_SSL_PUBLIC_KEY, IIS_MD_UT_SERVER,
BINARY_METADATA, &cbData, 0 );
// we got the certificate and can now crack it
if ( pData )
{
m_fUpdateIdent = FGetIdentString( (PUCHAR)pData, cbData, szIdent );
fAnswer = m_fUpdateIdent;
// cache the ident in memory. It will get written out on Commit
m_szIdent = szIdent;
/*
// declare the cracker object
CCrackedCert cracker;
// crack the cert
if ( cracker.CrackCert( (PUCHAR)pData, cbData ) )
{
DWORD* pdw = cracker.PGetSerialNumber();
szIdent.Format( "%d:%d:%d:%d", pdw[0], pdw[1], pdw[2], pdw[3] );
// cache the ident in memory. It will get written out on Commit
m_szIdent = szIdent;
m_fUpdateIdent = TRUE;
// success
fAnswer = TRUE;
}
*/
// free the buffer
pWrap->FreeWrapData( pData );
}
else
{
// we did not get the certificate - return FALSE
// fAnswer is already set to false
}
// return the answer;
return fAnswer;
}
//--------------------------------------------------------
BOOL CMDKey::FWriteKey( CWrapMetaBase* pWrap, DWORD iKey, CStringArray* prgbszTracker )
{
BOOL f;
DWORD nBindings = m_rgbszBindings.GetSize();
CString szBinding;
BOOL fUpdateAll = FALSE;
// if there are no assigned bindings, the key still gets stored with a object
// name in the format of "disabled{iKey}". and if it is incomplete, then store
// it with the name "incomplete{iKey}" Because the iKey can change and we don't
// want any conflicts, re-write them
if ( nBindings == 0 )
{
// build the binding name as appropriate
if ( m_pCertificate )
szBinding.Format( "%s%d", MDNAME_DISABLED, iKey );
else
szBinding.Format( "%s%d", MDNAME_INCOMPLETE, iKey );
// set the update flag
m_fUpdateBindings = TRUE;
}
// NOTE: pWrap has already been opened to /LM/W3Svc/SSLKeys
// if the key is not dirty, its easy
if ( !m_fUpdateKeys && !m_fUpdateFriendlyName && !m_fUpdateIdent && !m_fUpdateBindings && !FGetDirty() )
{
// add names of its bindings so it doesn't get deleted
DWORD iBinding;
for ( iBinding = 0; iBinding < nBindings; iBinding++ )
prgbszTracker->Add( m_rgbszBindings[iBinding] );
return TRUE;
}
// handle no bindings as a special case first
if ( nBindings == 0 )
{
// tell the server about it
prgbszTracker->Add((LPCSTR)szBinding);
// ok. Create the key in the metabase.
f = pWrap->AddObject( szBinding );
// and save the data
f = FWriteData( pWrap, szBinding, TRUE );
// clear the dirty flag and exit
SetDirty( FALSE );
return TRUE;
}
// there are bindings to be saved... loop though them and update each
DWORD iBinding;
for ( iBinding = 0; iBinding < nBindings; iBinding++ )
{
// get the binding name
szBinding = m_rgbszBindings[iBinding];
// test code
if ( szBinding.IsEmpty() )
AfxMessageBox( "Empty Binding Alert!" );
// now that we know where to save it, add the name to list of saved
// objects being kept track of by the server object - (This is so that
// the server knows what's been added)
prgbszTracker->Add((LPCSTR)szBinding);
// ok. Create the key in the metabase. Really, we may only need to do this
// if m_fUpdateBindings is set - if the object is new - update all the data
fUpdateAll = pWrap->AddObject( szBinding ) || m_fUpdateBindings;
// write out the data
FWriteData( pWrap, szBinding, fUpdateAll );
}
// clear the flags
m_fUpdateKeys = FALSE;
m_fUpdateFriendlyName = FALSE;
m_fUpdateIdent = FALSE;
m_fUpdateBindings = FALSE;
// clear the dirty flag
SetDirty( FALSE );
return TRUE;
}
//--------------------------------------------------------
// write out the data portion to a particular binding
BOOL CMDKey::FWriteData( CWrapMetaBase* pWrap, CString szBinding, BOOL fWriteAll )
{
BOOL f;
// write all the parts of the key - start with the certificate
// start with the secure parts
if ( m_fUpdateKeys || fWriteAll )
{
if ( m_pCertificate )
f = pWrap->SetData( szBinding, MD_SSL_PUBLIC_KEY, IIS_MD_UT_SERVER, BINARY_METADATA,
m_pCertificate, m_cbCertificate,
METADATA_SECURE );
// write out the private key
if ( m_pPrivateKey )
f = pWrap->SetData( szBinding, MD_SSL_PRIVATE_KEY, IIS_MD_UT_SERVER, BINARY_METADATA,
m_pPrivateKey, m_cbPrivateKey,
METADATA_SECURE );
// write out the password - treat is ast secure binary data
if ( !m_szPassword.IsEmpty() )
f = pWrap->SetData( szBinding, MD_SSL_KEY_PASSWORD, IIS_MD_UT_SERVER, BINARY_METADATA,
(PVOID)(LPCSTR)m_szPassword, m_szPassword.GetLength()+1,
METADATA_SECURE );
// write out the request
if ( m_pCertificateRequest )
f = pWrap->SetData( szBinding, MD_SSL_KEY_REQUEST, IIS_MD_UT_SERVER, BINARY_METADATA,
m_pCertificateRequest, m_cbCertificateRequest,
METADATA_SECURE );
}
// write out the cached serial number
if ( m_fUpdateIdent || m_fUpdateKeys || fWriteAll )
if ( !m_szIdent.IsEmpty() )
{
f = pWrap->SetString( szBinding, MD_SSL_IDENT, IIS_MD_UT_SERVER,
m_szIdent, METADATA_SECURE );
}
// write out the friendly name of the key
if ( m_fUpdateFriendlyName || fWriteAll )
f = pWrap->SetString( szBinding, MD_SSL_FRIENDLY_NAME, IIS_MD_UT_SERVER,
m_szName, 0 );
return TRUE;
}
//--------------------------------------------------------
BOOL CMDKey::FLoadKey( CWrapMetaBase* pWrap, PCHAR pszObj )
{
DWORD cbData;
PVOID pData;
// start with the public key
pData = pWrap->GetData( pszObj, MD_SSL_PUBLIC_KEY, IIS_MD_UT_SERVER,
BINARY_METADATA, &cbData, 0 );
if ( pData )
{
// set the data into place
m_pCertificate = (PVOID)GlobalAlloc( GPTR, cbData );
// if we got the pointer, copy the rest of the data into place
if( m_pCertificate)
{
m_cbCertificate = cbData;
CopyMemory( m_pCertificate, pData, cbData );
}
// free the buffer
pWrap->FreeWrapData( pData );
}
// now the private key
pData = pWrap->GetData( pszObj, MD_SSL_PRIVATE_KEY, IIS_MD_UT_SERVER,
BINARY_METADATA, &cbData, 0 );
if ( pData )
{
// set the data into place
m_pPrivateKey = (PVOID)GlobalAlloc( GPTR, cbData );
// if we got the pointer, copy the rest of the data into place
if( m_pPrivateKey)
{
m_cbPrivateKey = cbData;
CopyMemory( m_pPrivateKey, pData, cbData );
}
// free the buffer
pWrap->FreeWrapData( pData );
}
// now the password key
pData = pWrap->GetData( pszObj, MD_SSL_KEY_PASSWORD, IIS_MD_UT_SERVER,
BINARY_METADATA, &cbData, 0 );
if ( pData )
{
// set the data into place - relatively easy in this case
m_szPassword = (LPCSTR)pData;
// free the buffer
pWrap->FreeWrapData( pData );
}
// now the request
pData = pWrap->GetData( pszObj, MD_SSL_KEY_REQUEST, IIS_MD_UT_SERVER,
BINARY_METADATA, &cbData, 0 );
if ( pData )
{
// set the data into place
m_pCertificateRequest = (PVOID)GlobalAlloc( GPTR, cbData );
// if we got the pointer, copy the rest of the data into place
if( m_pCertificateRequest)
{
m_cbCertificateRequest = cbData;
CopyMemory( m_pCertificateRequest, pData, cbData );
}
// free the buffer
pWrap->FreeWrapData( pData );
}
// finally, retrieve the friendly name
BOOL f = pWrap->GetString( pszObj, MD_SSL_FRIENDLY_NAME, IIS_MD_UT_SERVER,
m_szName.GetBuffer(MAX_LEN), MAX_LEN, 0);
m_szName.ReleaseBuffer();
if ( !f )
m_szName.Empty();
// make this item's metabase name the first name in the list
AddBinding( pszObj );
// Success
return TRUE;
}
//-------------------------------------------------------------
// install a cert
BOOL CMDKey::FInstallCertificate( PVOID pCert, DWORD cbCert, CString &szPass )
{
// first, we should test that the certificate and password are valid
// for this particular key
// cache the old certificate in case the new one fails
DWORD old_cbCertificate = m_cbCertificate;
PVOID old_pCertificate = m_pCertificate;
// set the new one into place
m_cbCertificate = cbCert;
m_pCertificate = pCert;
// verify the password - verify password puts up any error dialogs
if ( !FVerifyValidPassword(szPass) )
{
// resore the old values
m_cbCertificate = old_cbCertificate;
m_pCertificate = old_pCertificate;
// dispose of the new stuff
GlobalFree( pCert );
// return false
return FALSE;
}
// now we need to see if this key has already been installed
// get the identification string
CString szIdentThis;
if ( !FGetIdentString( pCert, cbCert, szIdentThis ) )
return FALSE;
// scan the existing keys, looking for one with the same ident string
// if one is found, tell the user that it already exists and fail
CString szIdentTest;
CMDKey* pTestKey = m_pService->GetFirstMDKey();
while ( pTestKey )
{
// if we are testing against this key, continue
if ( pTestKey == this )
goto GETNEXTKEY;
// get the test ident string
if ( !pTestKey->FGetIdentString( pTestKey->m_pCertificate,
pTestKey->m_cbCertificate, szIdentTest ) )
goto GETNEXTKEY;
// test the ident strings
if ( szIdentThis == szIdentTest )
{
// the key already exists
AfxMessageBox( IDS_DUPLICATE_CERT );
return FALSE;
}
GETNEXTKEY:
// get the next key for the loop
pTestKey = m_pService->GetNextMDKey(pTestKey);
}
// run the default action
BOOL fDefault = CKey::FInstallCertificate(pCert, cbCert, szPass);
// set the update keys flag
m_fUpdateKeys = TRUE;
// if everything worked so far then check to see if there is a key
// on this service with the default binding. If there isn't, then
// set this key to have the default binding.
if ( fDefault )
{
// load the default binding string
CString szBinding;
szBinding = MDNAME_DEFAULT;
// if no key has the default binding, then make it so
if ( !m_pService->FIsBindingInUse(szBinding) )
{
m_rgbszBindings.Add( MDNAME_DEFAULT );
}
}
// if it worked, force the icon to change
if ( fDefault )
UpdateIcon();
// return the default answer
return fDefault;
}