#include "stdafx.h" #include "KeyObjs.h" #include "CmnKey.h" #include "W3Key.h" #include "W3Serv.h" #include "resource.h" #include "kmlsa.h" // the service image index extern int g_iServiceImage; extern HINSTANCE g_hInstance; //-------------------------------------------------------- CW3KeyService::CW3KeyService(): m_pszwMachineName( NULL ) { // set the icon id m_iImage = (WORD)g_iServiceImage; // load the service name m_szItemName.LoadString( IDS_SERV_NAME ); } //-------------------------------------------------------- CW3KeyService::~CW3KeyService() { // if the machine name has been cached, release it if ( m_pszwMachineName ) { delete m_pszwMachineName; m_pszwMachineName = NULL; } } //-------------------------------------------------------- void CW3KeyService::LoadKeys( CMachine* pMachine ) { // specify the resources to use HINSTANCE hOldRes = AfxGetResourceHandle(); AfxSetResourceHandle( g_hInstance ); HANDLE hPolicy; DWORD err; // since we use the machine name several times, set it up only once if ( !m_pszwMachineName ) { // get the normal name CString szName; pMachine->GetMachineName( szName ); // allocate the cache for the machine name m_pszwMachineName = new WCHAR[MAX_PATH]; if ( !m_pszwMachineName ) goto cleanup; // unicodize the name MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, szName, -1, m_pszwMachineName, MAX_PATH ); } // attempt to open an LSA policy on the target machine hPolicy = HOpenLSAPolicy( m_pszwMachineName, &err ); // if we did not open a policy, fail if ( !hPolicy ) goto cleanup; // load the keys that were previously saved by key manager FRestoreNormalKeys( hPolicy ); // load any keys that were created by keyset - not keymanager FLoadKeySetKeys( hPolicy ); // close the policy FCloseLSAPolicy( hPolicy, &err ); // restore the resources cleanup: AfxSetResourceHandle( hOldRes ); } //---------------------------------------------------------------- CW3Key* CW3KeyService::PGetDefaultKey( void ) { // get the first key CW3Key* pKey = GetFirstW3Key(); // loop through the keys, looking for the default while( pKey ) { // test the key if ( pKey->FIsDefault() ) return pKey; // get the next key pKey = GetNextW3Key( pKey ); } // we did not find the default key, return NULL return NULL; } //---------------------------------------------------------------- // the plan to commit the machine takes place in several steps // First we write out the current keys to the secrets. // The keys are written out using a structured naming sequense. // namely, Key1, Key2, Key3, etc... // After writing out all the keys, any keys that are still in the // secrets but are not current keys (i.e. they were deleted) are // removed from the secrets. // Next, we build the mapping between the keys and the servers. This part // is basically what KeySet did. BOOL CW3KeyService::FCommitChangesNow( void ) { HANDLE hPolicy; DWORD err; // if there is nothing to do, then do nothing if ( !m_fDirty ) return TRUE; // this can take a few seconds, so set the hourglass cursor CWaitCursor waitcursor; // open a policy to the target machine hPolicy = HOpenLSAPolicy( m_pszwMachineName, &err ); // make sure it worked if( !hPolicy ) { WriteSecretMessageBox(); return FALSE; } // commit the changes here if ( !DeleteAllW3Keys(hPolicy) ) { FCloseLSAPolicy( hPolicy, &err ); WriteSecretMessageBox(); // even though we failed to delete all the old keys, write out the new // ones anyway. } // commit the changes here if ( !FWriteOutKeys(hPolicy) ) { FCloseLSAPolicy( hPolicy, &err ); WriteSecretMessageBox(); return FALSE; } // record the new current number of keys in the registry // now if all the keys are deleted, the right number of // resources will be purged from the registry m_nNumKeysRead = GetChildCount(); // close the policy FCloseLSAPolicy( hPolicy, &err ); // clear the dirty flag SetDirty( FALSE ); return TRUE; } //---------------------------------------------------------------- void CW3KeyService::WriteSecretMessageBox( void ) { CString szMessage; // get the message string szMessage.LoadString( IDS_COMMIT_ERROR ); // tack on the name of the server szMessage += m_szItemName; // tack on some punctuation szMessage += _T("."); AfxMessageBox( szMessage ); } //---------------------------------------------------------------- // looks for keys that were installed by keyset, not key manager. These // keys only exist directly in w3 server form and are unnamed. BOOL CW3KeyService::FLoadKeySetKeys( HANDLE hPolicy ) { PLSA_UNICODE_STRING pLSAData; DWORD err; BOOL fFoundKeySetKeys = FALSE; // get the W3 server links data from the secrets database pLSAData = FRetrieveLSASecret( hPolicy, KEYSET_LIST, &err ); // if we get lucky, there won't be any keys to test if ( !pLSAData ) return TRUE; // allocate the name buffer PWCHAR pWName = (PWCHAR)GlobalAlloc( GPTR, (MAX_PATH+1) * sizeof(WCHAR) ); ASSERT( pWName ); if ( !pWName ) { AfxThrowMemoryException(); return FALSE; } // No such luck. Now we have to walk the list and delete all those secrets WCHAR* pszAddress = (WCHAR*)(pLSAData->Buffer); WCHAR* pchKeys; // loop the items in the list, deleting the associated items while( pchKeys = wcschr(pszAddress, L',') ) { // ignore empty segments if ( *pszAddress != L',' ) { *pchKeys = L'\0'; // put the wide name into a cstring CString szTestAddress = pszAddress; // see if we need to check for the default key BOOL fCheckForDefault = (wcscmp(pszAddress, KEYSET_DEFAULT) == 0); // search the keys, looking for the one that matches this CW3Key* pKey = GetFirstW3Key(); while( pKey ) { // if it is the default key, check for that if ( fCheckForDefault ) { if ( pKey->FIsDefault() ) { // this is a keyman key goto incrementKeyList; } } else { // otherwise, check the actual ip address if ( pKey->m_szIPAddress == szTestAddress ) { // this is a keyman key goto incrementKeyList; } } // get the next key pKey = GetNextW3Key( pKey ); } // if we get here, then we have found a keyset key fFoundKeySetKeys = TRUE; // create a new key pKey = new CW3Key; // initialize it from the wide address of the key if ( pKey->FInitKey( hPolicy, pszAddress ) ) { // add the key to the service pKey->FAddToTree( this ); // mark the machine object as dirty SetDirty( TRUE ); } } incrementKeyList: // increment the pointers pchKeys++; pszAddress = pchKeys; } // free the buffer for the names GlobalFree( (HANDLE)pWName ); // delete the list key itself // free the list key itself if ( pLSAData ) DisposeLSAData( pLSAData ); // if we found any keyset keys, tell the user what to expect if ( fFoundKeySetKeys ) AfxMessageBox( IDS_FOUND_KEYSET_KEYS, MB_OK|MB_ICONINFORMATION ); return TRUE; } //---------------------------------------------------------------- // similar to the routine "DeleteAll" in the KeySet utility BOOL CW3KeyService::DeleteAllW3Keys( HANDLE hPolicy ) { DWORD err; PLSA_UNICODE_STRING pLSAData; // get the secret list of keys pLSAData = FRetrieveLSASecret( hPolicy, KEYSET_LIST, &err ); // if we get lucky, there won't be any keys to get rid of if ( !pLSAData ) return TRUE; // allocate the name buffer PWCHAR pWName = (PWCHAR)GlobalAlloc( GPTR, (MAX_PATH+1) * sizeof(WCHAR) ); ASSERT( pWName ); if ( !pWName ) { AfxThrowMemoryException(); return FALSE; } // No such luck. Now we have to walk the list and delete all those secrets WCHAR* pszAddress = (WCHAR*)(pLSAData->Buffer); WCHAR* pchKeys; // loop the items in the list, deleting the associated items while( pchKeys = wcschr(pszAddress, L',') ) { // ignore empty segments if ( *pszAddress != L',' ) { *pchKeys = L'\0'; // Nuke the secrets, one at a time swprintf( pWName, KEYSET_PUB_KEY, pszAddress ); FStoreLSASecret( hPolicy, pWName, NULL, 0, &err ); swprintf( pWName, KEYSET_PRIV_KEY, pszAddress ); FStoreLSASecret( hPolicy, pWName, NULL, 0, &err ); swprintf( pWName, KEYSET_PASSWORD, pszAddress ); FStoreLSASecret( hPolicy, pWName, NULL, 0, &err ); } // increment the pointers pchKeys++; pszAddress = pchKeys; } // delete the list key itself FStoreLSASecret( hPolicy, KEYSET_LIST, NULL, 0, &err ); // free the buffer for the names GlobalFree( (HANDLE)pWName ); // free the info we originally retrieved from the secret if ( pLSAData ) DisposeLSAData( pLSAData ); // return success return TRUE; } //---------------------------------------------------------------- // utiltiy to append data to an existing handle, thus growing it BOOL CW3KeyService::FExpandoHandle( HANDLE* ph, PVOID pData, DWORD cbData ) { HANDLE hNew; HANDLE hOld = *ph; ASSERT( ph && *ph && pData && cbData ); if ( !ph || !*ph ) return FALSE; if ( !pData || !cbData ) return TRUE; // calculate the new size of the handle SIZE_T cbSizeOld = GlobalSize( *ph ); SIZE_T cbSizeNew = cbSizeOld + cbData; // allocate a new handle at the new size hNew = GlobalAlloc( GHND, cbSizeNew ); // if it didn't work, throw if ( !hNew ) { AfxThrowMemoryException(); return FALSE; } // lock down the handles and copy over the existing data PCHAR pNew = (PCHAR)GlobalLock( hNew ); // only copy over the old data if there is some if ( cbSizeOld > 0 ) { PCHAR pOld = (PCHAR)GlobalLock( hOld ); CopyMemory( pNew, pOld, cbSizeOld ); GlobalUnlock( hOld ); } // advance the new pointer and copy in the new data pNew += cbSizeOld; CopyMemory( pNew, pData, cbData ); // unlock the new handle GlobalUnlock( hNew ); // it did work, so set the handle *ph = hNew; // finally, dispose of the old handle GlobalFree( hOld ); // return success return TRUE; } //---------------------------------------------------------------- BOOL CW3KeyService::FWriteOutKeys( HANDLE hPolicy ) { WORD iKey; DWORD err; HANDLE hList = GlobalAlloc( GHND, 0 ); LONG cch; BOOL fSomethingInList = FALSE; ASSERT( hPolicy ); ASSERT( hList ); if ( !hList ) AfxThrowMemoryException(); // get the buffer for the wide name PWCHAR pWName = (PWCHAR)GlobalAlloc( GPTR, (MAX_PATH+1) * sizeof(WCHAR) ); // for each key in the machine, write its data out to the secrets iKey = 0; CW3Key* pKey = GetFirstW3Key(); while( pKey ) { // tell the key to store itself if ( !pKey->WriteKey(hPolicy, iKey, pWName) ) return FALSE; // if this key has a link, add it to the w3 links list cch = wcslen( pWName ); if ( cch ) { wcscat( pWName, L"," ); cch++; FExpandoHandle( &hList, pWName, cch * sizeof(WCHAR) ); // terminates later fSomethingInList = TRUE; } // get the next key pKey = GetNextW3Key( pKey ); iKey++; } // save the contents of the list as a secret if ( fSomethingInList ) { WORD word = 0; // terminate the list of named keys FExpandoHandle( &hList, &word, sizeof(WORD) ); ASSERT( GlobalSize(hList) < 0xFFFF ); PVOID p = GlobalLock(hList); FStoreLSASecret( hPolicy, KEYSET_LIST, p, (WORD)GlobalSize(hList), &err ); GlobalUnlock(hList); } // free the handle for the list GlobalFree( hList ); hList = NULL; // once we get here we have already written out all our keys. However, the case could exist where // we now have fewer keys than we started with. This means that there are keys in the secrets database // that are no longer needed. If this is the case, get rid of them WORD numKeys = GetChildCount(); if ( numKeys < m_nNumKeysRead ) { PCHAR pName = (PCHAR)GlobalAlloc( GPTR, MAX_PATH+1 ); // make sure we got the name buffers ASSERT( pName && pWName ); if ( pName && pWName ) for ( iKey = numKeys; iKey < m_nNumKeysRead; iKey++ ) { // prepare the name of the secret. - Base name plus the number+1 sprintf( pName, "%s%d", KEY_NAME_BASE, iKey+1 ); // unicodize the name MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pName, -1, pWName, MAX_PATH+1 ); // remove the secret FStoreLSASecret( hPolicy, pWName, NULL, 0, &err ); } // free the string buffers GlobalFree( (HANDLE)pName ); } // free the string buffers GlobalFree( (HANDLE)pWName ); // return success return TRUE; } //---------------------------------------------------------------- BOOL CW3KeyService::FRestoreNormalKeys( HANDLE hPolicy ) { DWORD iKey = 0; PCHAR pName = (PCHAR)GlobalAlloc( GPTR, MAX_PATH+1 ); PWCHAR pWName = (PWCHAR)GlobalAlloc( GPTR, (MAX_PATH+1) * sizeof(WCHAR) ); PLSA_UNICODE_STRING pLSAData; DWORD err; // make sure we got the name buffers ASSERT( pName && pWName ); if ( !pName || !pWName ) return FALSE; // clear the number of keys read (we haven't read any yet!) m_nNumKeysRead = 0; // load keys until we have loaded them all while(TRUE) { // increment the key counter iKey++; // build the key secret name sprintf( pName, "%s%d", KEY_NAME_BASE, iKey ); // unicodize the name MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pName, -1, pWName, MAX_PATH+1 ); // get the secret pLSAData = FRetrieveLSASecret( hPolicy, pWName, &err ); // if we didn't get anything, leave the loop if ( !pLSAData ) break; // there is a key here, so count it m_nNumKeysRead++; // ah, but we did get something. Make a new key and add it to the key list CW3Key* pKey = new CW3Key; if ( pKey->FInitKey(pLSAData->Buffer, pLSAData->Length) ) { pKey->FAddToTree( this ); } else { // failed to init key delete pKey; } // dispose of the lsa buffer now that we have loaded from it if ( pLSAData ) DisposeLSAData( pLSAData ); pLSAData = NULL; } // free the buffers GlobalFree( (HANDLE)pName ); GlobalFree( (HANDLE)pWName ); if ( pLSAData ) DisposeLSAData( pLSAData ); return TRUE; } //------------------------------------------------------------- void DisposeLSAData( PVOID pData ) { PLSA_UNICODE_STRING pDataLSA = (PLSA_UNICODE_STRING)pData; if ( !pDataLSA || !pDataLSA->Buffer ) return; GlobalFree(pDataLSA); }