windows-nt/Source/XPSP1/NT/inetsrv/iis/setup/osrc/lsakeys.cpp
2020-09-26 16:20:57 +08:00

670 lines
18 KiB
C++

// This class is to help setup retrieve the old-style LSA keys and convert them
// into the new MetaData keys.
// created by BoydM 4/2/97
#include "stdafx.h"
#include "LSAKeys.h"
#ifndef _CHICAGO_
// it is assumed that #include "ntlsa.h" is included in stdafx.h
#define KEYSET_LIST L"W3_KEY_LIST"
#define KEYSET_PUB_KEY L"W3_PUBLIC_KEY_%s"
#define KEYSET_PRIV_KEY L"W3_PRIVATE_KEY_%s"
#define KEYSET_PASSWORD L"W3_KEY_PASSWORD_%s"
#define KEYSET_DEFAULT L"Default"
#define KEY_NAME_BASE "W3_KEYMAN_KEY_"
#define KEY_LINKS_SECRET_W L"W3_KEYMAN_LINKS"
#define KEYMAN_LINK_DEFAULT "DEFAULT"
#define KEY_VERSION 0x102 // version we are converting from
#define MDNAME_INCOMPLETE "incomplete"
#define MDNAME_DISABLED "disabled"
#define MDNAME_DEFAULT "default"
#define MDNAME_PORT ":443" // use the default SSL port
//----------------------------------------------------------------------
// construction
CLSAKeys::CLSAKeys():
m_cbPublic(0),
m_pPublic(NULL),
m_cbPrivate(0),
m_pPrivate(NULL),
m_cbPassword(0),
m_pPassword(NULL),
m_cbRequest(0),
m_pRequest(NULL),
m_hPolicy(NULL)
{
}
//----------------------------------------------------------------------
CLSAKeys::~CLSAKeys()
{
DWORD err;
// clear out the last loaded key
UnloadKey();
// if it is opehn, close the LSA policy
if ( m_hPolicy )
FCloseLSAPolicy( m_hPolicy, &err );
};
//----------------------------------------------------------------------
// clean up the currently loaded key
void CLSAKeys::UnloadKey()
{
// unload the public key
if ( m_cbPublic && m_pPublic )
{
GlobalFree( m_pPublic );
m_cbPublic = 0;
m_pPublic = NULL;
}
// unload the private key
if ( m_cbPrivate && m_pPrivate )
{
GlobalFree( m_pPrivate );
m_cbPrivate = 0;
m_pPrivate = NULL;
}
// unload the password
if ( m_cbPassword && m_pPassword )
{
GlobalFree( m_pPassword );
m_cbPassword = 0;
m_pPassword = NULL;
}
// unload the key request
if ( m_cbRequest && m_pRequest )
{
GlobalFree( m_pRequest );
m_cbRequest = 0;
m_pRequest = NULL;
}
// empty the strings too
m_szFriendlyName[0] = 0;
m_szMetaName[0] = 0;
}
//----------------------------------------------------------------------
// DeleteAllLSAKeys deletes ALL remenents of the LSA keys in the Metabase.
// (not including, of course anything written out there in the future as part
// of some backup scheme when uninstalling). Call this only AFTER ALL the keys
// have been converted to the metabase. They will no longer be there after
// this routine is used.
// NOTE: this also blows away any really-old KeySet keys because they look
// like the KeyMan keys. And we have to kill both the keyset keys and the
// generic storage used by the server.
DWORD CLSAKeys::DeleteAllLSAKeys()
{
DWORD err;
// first, delete the KeyManager type keys.
err = DeleteKMKeys();
if ( err != KEYLSA_SUCCESS )
return err;
// second, delete the keyset style keys. - this also removes the ones
// that the server uses and any keyset keys.
return DeleteServerKeys();
}
//----------------------------------------------------------------------
DWORD CLSAKeys::DeleteKMKeys()
{
PCHAR pName = (PCHAR)GlobalAlloc( GPTR, MAX_PATH+1 );
PWCHAR pWName = (PWCHAR)GlobalAlloc( GPTR, (MAX_PATH+1) * sizeof(WCHAR) );
PLSA_UNICODE_STRING pLSAData;
DWORD err;
if ( !pName || !pWName )
return ERROR_NOT_ENOUGH_MEMORY;
// reset the index so we get the first key
m_iKey = 0;
// loop through the keys, deleting each in turn
while( TRUE )
{
// increment the index
m_iKey++;
// build the key secret name
sprintf( pName, "%s%d", KEY_NAME_BASE, m_iKey );
// unicodize the name
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pName, -1, pWName, MAX_PATH+1 );
// get the secret
pLSAData = FRetrieveLSASecret( m_hPolicy, pWName, &err );
// if we don't get the secret, exit
if ( !pLSAData )
{
break;
}
// The secret is there. Clean up first
DisposeLSAData( pLSAData );
// now delete the secret
FStoreLSASecret( m_hPolicy, pWName, NULL, 0, &err );
};
return KEYLSA_SUCCESS;
}
//----------------------------------------------------------------------
DWORD CLSAKeys::DeleteServerKeys()
{
DWORD err;
PLSA_UNICODE_STRING pLSAData;
// get the secret list of keys
pLSAData = FRetrieveLSASecret( m_hPolicy, KEYSET_LIST, &err );
// if we get lucky, there won't be any keys to get rid of
if ( !pLSAData )
return KEYLSA_SUCCESS;
// allocate the name buffer
PWCHAR pWName = (PWCHAR)GlobalAlloc( GPTR, (MAX_PATH+1) * sizeof(WCHAR) );
ASSERT( pWName );
if ( !pWName )
{
return 0xFFFFFFFF;
}
// 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( m_hPolicy, pWName, NULL, 0, &err );
swprintf( pWName, KEYSET_PRIV_KEY, pszAddress );
FStoreLSASecret( m_hPolicy, pWName, NULL, 0, &err );
swprintf( pWName, KEYSET_PASSWORD, pszAddress );
FStoreLSASecret( m_hPolicy, pWName, NULL, 0, &err );
}
// increment the pointers
pchKeys++;
pszAddress = pchKeys;
}
// delete the list key itself
FStoreLSASecret( m_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 KEYLSA_SUCCESS;
}
//----------------------------------------------------------------------
// loading the keys
// LoadFirstKey loads the first key on the specified target machine. Until
// this method is called, the data values in the object are meaningless
// this method works by preparing the list of keys to load. Then it calls
// LoadNextKey to start the process
// Unfortunately, the whole process of saving keys in the LSA registry was a bit
// of a mess because they all had to be on the same level.
DWORD CLSAKeys::LoadFirstKey( PWCHAR pszwTargetMachine )
{
DWORD err;
// open the policy on the target machine being administered
m_hPolicy = HOpenLSAPolicy( pszwTargetMachine, &err );
if ( !m_hPolicy ) return KEYLSA_UNABLE_TO_OPEN_POLICY;
// tell it to load the first key. The first key's index is actually 1,
// but LoadNextKey impliess that it is ++LoadNextKey, so start it at 0
m_iKey = 0;
// load that first key and return the response
return LoadNextKey();
}
//----------------------------------------------------------------------
// LoadNextKey loads the next key on the target machine specified in LoadFirstKey
// LoadNextKey automatically cleans up the memory used by the previous key.
DWORD CLSAKeys::LoadNextKey()
{
// the very first thing we do is - get rid of any previously loaded key
UnloadKey();
PCHAR pName = (PCHAR)GlobalAlloc( GPTR, MAX_PATH+1 );
PWCHAR pWName = (PWCHAR)GlobalAlloc( GPTR, (MAX_PATH+1) * sizeof(WCHAR) );
PLSA_UNICODE_STRING pLSAData = NULL;
DWORD err = 0xFFFFFFFF;
PUCHAR pSrc;
WORD cbSrc;
DWORD dword, version, i;
DWORD cbChar;
PUCHAR p;
CHAR szIPAddress[256];
BOOL fDefault;
if ( !pName || !pWName )
return err;
// increment the index so we get the next key
m_iKey++;
// build the key secret name
sprintf( pName, "%s%d", KEY_NAME_BASE, m_iKey );
// unicodize the name
MultiByteToWideChar( CP_ACP, MB_PRECOMPOSED, pName, -1, pWName, MAX_PATH+1 );
// get the secret
pLSAData = FRetrieveLSASecret( m_hPolicy, pWName, &err );
// if we don't get the secret, exit with the error
if ( !pLSAData )
{
err = KEYLSA_NO_MORE_KEYS;
goto cleanup;
}
// we have the data from the secret. Now we parse it out into the components we desire
// this probably could have been done cleaner the first time - but now it doesn't matter
// anyway because the MetaBase takes care of storing all the individual pieces of info
// anyway. It should also be way faster too.
// This part of the routine is pretty much lifted out of CW3Key::InitializeFromPointer
// from the w3key.dll. The appropriate sections have been either commented out or changed.
pSrc = (PUCHAR)pLSAData->Buffer;
cbSrc = pLSAData->Length;
cbChar = sizeof(TCHAR);
p = pSrc;
//========================== start from CW3Key::InitializeFromPointer
ASSERT(pSrc && cbSrc);
// get the version of the data - just put it into dword for now
version = *((UNALIGNED DWORD*)p);
// check the version for validity
// if ( version > KEY_VERSION )
// {
// return FALSE;
// }
p += sizeof(DWORD);
// anything below version 0x101 is BAD. Do not accept it
if ( version < 0x101 )
{
err = KEYLSA_INVALID_VERSION;
goto cleanup;
}
// get the bits and the complete flag
// no longer used
p += sizeof(DWORD);
p += sizeof(BOOL);
ASSERT( p < (pSrc + cbSrc) );
// get the reserved dword - (acutally, just skip over it)
p += sizeof(DWORD);
// now the strings......
// for each string, first get the size of the string, then the data from the string
// get the reserved string - (actually, just skip over it)
dword = *((UNALIGNED DWORD*)p);
p += sizeof(DWORD);
p += dword;
// get the name
dword = *((UNALIGNED DWORD*)p);
p += sizeof(DWORD);
strcpy( m_szFriendlyName, (PCHAR)p );
p += dword;
ASSERT( p < (pSrc + cbSrc) );
// get the password
dword = *((UNALIGNED DWORD*)p);
p += sizeof(DWORD);
// if there is no password, don't worry, just skip it
if ( dword )
{
// make a new pointer for it
m_cbPassword = dword;
m_pPassword = (PVOID)GlobalAlloc( GPTR, m_cbPassword );
if ( !m_pPassword )
{
err = 0xFFFFFFFF;
goto cleanup;
}
// put in the private key
CopyMemory( m_pPassword, p, m_cbPassword );
p += dword;
ASSERT( p < (pSrc + cbSrc) );
}
// get the organization
// no longer used - skip the DN info
for ( i = 0; i < 6; i++ )
{
dword = *((UNALIGNED DWORD*)p);
p += sizeof(DWORD);
p += dword;
ASSERT( p < (pSrc + cbSrc) );
}
// get the ip addres it is attached to
dword = *((UNALIGNED DWORD*)p);
p += sizeof(DWORD);
// szIPAddress = p;
strcpy( szIPAddress, (PCHAR)p );
p += dword;
ASSERT( p < (pSrc + cbSrc) );
// get the default flag
fDefault = *((UNALIGNED BOOL*)p);
p += sizeof(BOOL);
// now put get the number of bytes in the private key
m_cbPrivate = *((UNALIGNED DWORD*)p);
p += sizeof(DWORD);
ASSERT( p < (pSrc + cbSrc) );
// make a new pointer for it
m_pPrivate = (PVOID)GlobalAlloc( GPTR, m_cbPrivate );
if ( !m_pPrivate )
{
err = 0xFFFFFFFF;
goto cleanup;
}
// put in the private key
CopyMemory( m_pPrivate, p, m_cbPrivate );
p += m_cbPrivate;
ASSERT( p < (pSrc + cbSrc) );
// now put get the number of bytes in the certificate
m_cbPublic = *((UNALIGNED DWORD*)p);
p += sizeof(DWORD);
ASSERT( p < (pSrc + cbSrc) );
// only make a certificate pointer if m_cbCertificate is greater than zero
m_pPublic = NULL;
if ( m_cbPublic )
{
m_pPublic = (PVOID)GlobalAlloc( GPTR, m_cbPublic );
if ( !m_pPublic )
{
err = 0xFFFFFFFF;
goto cleanup;
}
// put in the private key
CopyMemory( m_pPublic, p, m_cbPublic );
p += m_cbPublic;
if ( version >= KEY_VERSION ) {
ASSERT( p < (pSrc + cbSrc) );
} else {
ASSERT( p == (pSrc + cbSrc) );
}
}
// added near the end
if ( version >= KEY_VERSION )
{
// now put get the number of bytes in the certificte request
m_cbRequest = *((UNALIGNED DWORD*)p);
p += sizeof(DWORD);
ASSERT( p < (pSrc + cbSrc) );
// only make a certificate pointer if m_cbCertificate is greater than zero
m_pRequest = NULL;
if ( m_cbRequest )
{
m_pRequest = (PVOID)GlobalAlloc( GPTR, m_cbRequest );
if ( !m_pRequest )
{
err = 0xFFFFFFFF;
goto cleanup;
}
// put in the private key
CopyMemory( m_pRequest, p, m_cbRequest );
p += m_cbRequest;
ASSERT( p < (pSrc + cbSrc) );
}
}
else
{
m_cbRequest = 0;
m_pRequest = NULL;
}
//========================== end from CW3Key::InitializeFromPointer
// now we figure out the appropriate metabase name for this key
// this isn't too bad. If the targets a specific address, then the title
// is the in the form of {IP}:{PORT}. Since there were no ports in the old
// version, we will assume an appropriate default number. If it is the
// default key, then the name is "default". If it is a disabled key, then
// the name is "disabled". If it is an incomplete key, then the name is
// "incomplete". Of course, it takes a little logic to tell the difference
// between some of these.
// first, see if it is an incomplete key. - test for the public portion
if ( !m_pPublic )
{
// there may be multiple incomplete keys, so make sure they have unique names
// m_szMetaName.Format( _T("%s%d"), MDNAME_INCOMPLETE, m_iKey );
sprintf( m_szMetaName, "%s%d", MDNAME_INCOMPLETE, m_iKey );
}
// now test if it is the default key
else if ( fDefault )
{
// m_szMetaName = MDNAME_DEFAULT;
strcpy( m_szMetaName, MDNAME_DEFAULT );
}
// test for a disabled key
else if ( szIPAddress[0] == 0 )
{
// there may be multiple disabled keys, so make sure they have unique names
// m_szMetaName.Format( _T("%s%d"), MDNAME_DISABLED, m_iKey );
sprintf( m_szMetaName, "%s%d", MDNAME_DISABLED, m_iKey );
}
else
{
// it is a regular old IP targeted key
// m_szMetaName = szIPAddress;
// add on the default port specification
// m_szMetaName += MDNAME_PORT;
// sprintf( m_szMetaName, "%s%s", szIPAddress, MDNAME_PORT );
strcpy(m_szMetaName, szIPAddress);
}
// free the buffers
cleanup:
GlobalFree( (HANDLE)pName );
GlobalFree( (HANDLE)pWName );
if ( pLSAData )
DisposeLSAData( pLSAData );
return err;
}
//============================================= LSA Utility routines
//-------------------------------------------------------------
// pass in a NULL pszwServer name to open the local machine
HANDLE CLSAKeys::HOpenLSAPolicy( WCHAR *pszwServer, DWORD *pErr )
{
NTSTATUS ntStatus;
LSA_OBJECT_ATTRIBUTES objectAttributs;
LSA_HANDLE hPolicy;
LSA_UNICODE_STRING unicodeServer;
// prepare the object attributes
InitializeObjectAttributes( &objectAttributs, NULL, 0L, NULL, NULL );
// prepare the lsa_unicode name of the server
if ( pszwServer )
{
unicodeServer.Buffer = pszwServer;
unicodeServer.Length = wcslen(pszwServer) * sizeof(WCHAR);
unicodeServer.MaximumLength = unicodeServer.Length + sizeof(WCHAR);
}
// attempt to open the policy
ntStatus = LsaOpenPolicy( pszwServer ? &unicodeServer : NULL,
&objectAttributs, POLICY_ALL_ACCESS, &hPolicy );
// check for an error
if ( !NT_SUCCESS(ntStatus) )
{
*pErr = LsaNtStatusToWinError( ntStatus );
return NULL;
}
// success, so return the policy handle as a regular handle
*pErr = 0;
return hPolicy;
}
//-------------------------------------------------------------
BOOL CLSAKeys::FCloseLSAPolicy( HANDLE hPolicy, DWORD *pErr )
{
NTSTATUS ntStatus;
// close the policy
ntStatus = LsaClose( hPolicy );
// check for an error
if ( !NT_SUCCESS(ntStatus) )
{
*pErr = LsaNtStatusToWinError( ntStatus );
return FALSE;
}
// success, so return the policy handle as a regular handle
*pErr = 0;
return TRUE;
}
//-------------------------------------------------------------
// passing NULL in for pvData deletes the secret
BOOL CLSAKeys::FStoreLSASecret( HANDLE hPolicy, WCHAR* pszwSecretName, void* pvData, WORD cbData, DWORD *pErr )
{
LSA_UNICODE_STRING unicodeSecretName;
LSA_UNICODE_STRING unicodeData;
NTSTATUS ntStatus;
// make sure we have a policy and a secret name
if ( !hPolicy || !pszwSecretName )
{
*pErr = 1;
return FALSE;
}
// prepare the lsa_unicode name of the server
unicodeSecretName.Buffer = pszwSecretName;
unicodeSecretName.Length = wcslen(pszwSecretName) * sizeof(WCHAR);
unicodeSecretName.MaximumLength = unicodeSecretName.Length + sizeof(WCHAR);
// prepare the unicode data record
if ( pvData )
{
unicodeData.Buffer = (WCHAR*)pvData;
unicodeData.Length = cbData;
unicodeData.MaximumLength = cbData;
}
// it is now time to store the secret
ntStatus = LsaStorePrivateData( hPolicy, &unicodeSecretName, pvData ? &unicodeData : NULL );
// check for an error
if ( !NT_SUCCESS(ntStatus) )
{
*pErr = LsaNtStatusToWinError( ntStatus );
return FALSE;
}
// success, so return the policy handle as a regular handle
*pErr = 0;
return TRUE;
}
//-------------------------------------------------------------
// passing NULL in for pvData deletes the secret
PLSA_UNICODE_STRING CLSAKeys::FRetrieveLSASecret( HANDLE hPolicy, WCHAR* pszwSecretName, DWORD *pErr )
{
LSA_UNICODE_STRING unicodeSecretName;
LSA_UNICODE_STRING* pUnicodeData = NULL;
NTSTATUS ntStatus;
// make sure we have a policy and a secret name
if ( !hPolicy || !pszwSecretName )
{
*pErr = 1;
return FALSE;
}
// prepare the lsa_unicode name of the server
unicodeSecretName.Buffer = pszwSecretName;
unicodeSecretName.Length = wcslen(pszwSecretName) * sizeof(WCHAR);
unicodeSecretName.MaximumLength = unicodeSecretName.Length + sizeof(WCHAR);
// it is now time to store the secret
ntStatus = LsaRetrievePrivateData( hPolicy, &unicodeSecretName, &pUnicodeData );
// check for an error
if ( !NT_SUCCESS(ntStatus) )
{
*pErr = LsaNtStatusToWinError( ntStatus );
return NULL;
}
// success, so return the policy handle as a regular handle
*pErr = 0;
return pUnicodeData;
}
//-------------------------------------------------------------
void CLSAKeys::DisposeLSAData( PVOID pData )
{
PLSA_UNICODE_STRING pDataLSA = (PLSA_UNICODE_STRING)pData;
if ( !pDataLSA || !pDataLSA->Buffer ) return;
GlobalFree(pDataLSA);
}
#endif //_CHICAGO_