|
|
// 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; }
|