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