|
|
//Copyright (c) 1998 - 1999 Microsoft Corporation
#include "stdafx.h"
#include "connode.h"
#include "resource.h"
#include "license.h"
#include "tssec.h"
#include "defaults.h"
#include "wincrypt.h"
#define NO_PASSWORD_VALUE_LEN 4
#define NO_PASSWORD_VALUE 0x45628275
CConNode::CConNode() { m_bConnected = FALSE; m_bConnectionInitialized = FALSE;
m_szServer[0] = NULL; m_szDescription[0] = NULL;
m_szUserName[0] = NULL; m_szDomain[0] = NULL;
m_bSavePassword = FALSE;
m_resType = SCREEN_RES_FILL_MMC; m_Width = DEFAULT_RES_WIDTH; m_Height = DEFAULT_RES_HEIGHT;
m_szProgramPath[0] = NULL; m_szProgramStartIn[0] = NULL;
m_pMhostCtl = NULL; m_pTsClientCtl = NULL;
m_bConnectToConsole = FALSE; m_bRedirectDrives = FALSE; m_pIComponent = NULL; m_fPasswordSpecified = FALSE;
_blobEncryptedPassword.cbData = 0; _blobEncryptedPassword.pbData = 0; }
CConNode::~CConNode() { if (m_pIComponent) { m_pIComponent->Release(); m_pIComponent = NULL; }
if (_blobEncryptedPassword.pbData && _blobEncryptedPassword.cbData) { LocalFree(_blobEncryptedPassword.pbData); _blobEncryptedPassword.pbData = NULL; _blobEncryptedPassword.cbData = 0; } }
BOOL CConNode::SetServerName( LPTSTR szServerName) { ASSERT(szServerName); if (szServerName != NULL) { lstrcpy(m_szServer, szServerName); } else { m_szServer[0] = NULL; } return TRUE; }
BOOL CConNode::SetDescription( LPTSTR szDescription) { ASSERT(szDescription); if (szDescription != NULL) { lstrcpy(m_szDescription, szDescription); } else { m_szDescription[0] = NULL; } return TRUE; }
BOOL CConNode::SetUserName( LPTSTR szUserName) { ASSERT(szUserName); if (szUserName != NULL) { lstrcpy(m_szUserName, szUserName); } else { m_szUserName[0] = NULL; }
return TRUE; }
BOOL CConNode::SetDomain(LPTSTR szDomain) { ASSERT(szDomain); if (szDomain != NULL) { lstrcpy(m_szDomain, szDomain); } else { m_szDomain[0] = NULL; } return TRUE; }
//
// DataProtect
// Protect data for persistence using data protection API
// params:
// pInData - (in) input bytes to protect
// cbLen - (in) length of pInData in bytes
// ppOutData - (out) output bytes
// pcbOutLen - (out) length of output
// returns: bool status
//
BOOL CConNode::DataProtect(PBYTE pInData, DWORD cbLen, PBYTE* ppOutData, PDWORD pcbOutLen) { DATA_BLOB DataIn; DATA_BLOB DataOut; ASSERT(pInData && cbLen); ASSERT(ppOutData); ASSERT(pcbOutLen); if (pInData && cbLen && ppOutData && pcbOutLen) { DataIn.pbData = pInData; DataIn.cbData = cbLen;
if (CryptProtectData( &DataIn, TEXT("ps"), // DESCRIPTION STRING.
NULL, // optional entropy
NULL, // reserved
NULL, // NO prompting
CRYPTPROTECT_UI_FORBIDDEN, //don't pop UI
&DataOut )) { *ppOutData = (PBYTE)LocalAlloc(LPTR, DataOut.cbData); if (*ppOutData) { //copy the output data
memcpy(*ppOutData, DataOut.pbData, DataOut.cbData); *pcbOutLen = DataOut.cbData; LocalFree(DataOut.pbData); return TRUE; } else { LocalFree(DataOut.pbData); return FALSE; } } else { DWORD dwLastErr = GetLastError(); DBGMSG( L"CryptProtectData FAILED error:%d\n",dwLastErr); return FALSE; } } else { return FALSE; } }
//
// DataUnprotect
// UnProtect persisted out data using data protection API
// params:
// pInData - (in) input bytes to UN protect
// cbLen - (in) length of pInData in bytes
// ppOutData - (out) output bytes
// pcbOutLen - (out) length of output
// returns: bool status
//
//
BOOL CConNode::DataUnprotect(PBYTE pInData, DWORD cbLen, PBYTE* ppOutData, PDWORD pcbOutLen) { DATA_BLOB DataIn; DATA_BLOB DataOut; ASSERT(pInData && cbLen && ppOutData && pcbOutLen); if (pInData && cbLen && ppOutData && pcbOutLen) { DataIn.pbData = pInData; DataIn.cbData = cbLen;
if (CryptUnprotectData( &DataIn, NULL, // NO DESCRIPTION STRING
NULL, // optional entropy
NULL, // reserved
NULL, // NO prompting
CRYPTPROTECT_UI_FORBIDDEN, //don't pop UI
&DataOut )) { *ppOutData = (PBYTE)LocalAlloc(LPTR, DataOut.cbData); if (*ppOutData) { //copy the output data
memcpy(*ppOutData, DataOut.pbData, DataOut.cbData); *pcbOutLen = DataOut.cbData; LocalFree(DataOut.pbData); return TRUE; } else { LocalFree(DataOut.pbData); return FALSE; } } else { DWORD dwLastErr = GetLastError(); DBGMSG( L"CryptUnprotectData FAILED error:%d\n",dwLastErr); return FALSE; } } else { return FALSE; } }
HRESULT CConNode::PersistToStream( IStream* pStm) { //
// Persist the data of this connection node to the stream
//
// The data is persisted started at the current seek position
// of the stream.
HRESULT hr; ULONG cbWritten;
ASSERT(pStm); if (!pStm) { return E_FAIL; }
//
//Persist info version
//
int persist_ver = CONNODE_PERSIST_INFO_VERSION; hr = pStm->Write( &persist_ver, sizeof(persist_ver), &cbWritten); HR_RET_IF_FAIL(hr);
//
//server
//
hr = pStm->Write( &m_szServer, sizeof(m_szServer), &cbWritten); HR_RET_IF_FAIL(hr);
//
//description
//
hr = pStm->Write( &m_szDescription, sizeof(m_szDescription), &cbWritten); HR_RET_IF_FAIL(hr);
//
//user name
//
hr = pStm->Write( &m_szUserName, sizeof(m_szUserName), &cbWritten); HR_RET_IF_FAIL(hr);
//
//encrypted password
//
//Intentional ignore of failure code as crypto may fail
hr = WriteProtectedPassword( pStm); //
//domain
//
hr = pStm->Write( &m_szDomain, sizeof(m_szDomain), &cbWritten); HR_RET_IF_FAIL(hr);
//
//Password specified flag
//
BOOL fWritePassword = GetPasswordSpecified() && GetSavePassword(); hr = pStm->Write( &fWritePassword, sizeof(fWritePassword), &cbWritten); HR_RET_IF_FAIL(hr);
//
// Screen resolution
//
hr = pStm->Write( &m_resType, sizeof(m_resType), &cbWritten); HR_RET_IF_FAIL(hr);
hr = pStm->Write( &m_Width, sizeof(m_Width), &cbWritten); HR_RET_IF_FAIL(hr);
hr = pStm->Write( &m_Height, sizeof(m_Height), &cbWritten); HR_RET_IF_FAIL(hr);
//
// Start program
//
hr = pStm->Write( &m_szProgramPath, sizeof(m_szProgramPath), &cbWritten); HR_RET_IF_FAIL(hr);
//
// Work dir
//
hr = pStm->Write( &m_szProgramStartIn, sizeof(m_szProgramStartIn), &cbWritten); HR_RET_IF_FAIL(hr);
//
// Connect to console
//
hr = pStm->Write( &m_bConnectToConsole, sizeof(m_bConnectToConsole), &cbWritten); HR_RET_IF_FAIL(hr);
hr = pStm->Write( &m_bRedirectDrives, sizeof(m_bRedirectDrives), &cbWritten); HR_RET_IF_FAIL(hr);
//
// PADDING for future extension
//
DWORD dwPad = (DWORD)-1; hr = pStm->Write( &dwPad, sizeof(dwPad), &cbWritten); HR_RET_IF_FAIL(hr); hr = pStm->Write( &dwPad, sizeof(dwPad), &cbWritten); HR_RET_IF_FAIL(hr); hr = pStm->Write( &dwPad, sizeof(dwPad), &cbWritten); HR_RET_IF_FAIL(hr);
return S_OK; }
HRESULT CConNode::InitFromStream( IStream* pStm) { //
// Reads in the data for this connection node from the stream
// starting at the current seek position in the stream.
//
HRESULT hr; ULONG cbRead;
ASSERT(pStm); if (!pStm) { return E_FAIL; }
int persist_info_version; //
//Persist info version
//
hr = pStm->Read( &persist_info_version, sizeof(persist_info_version), &cbRead); HR_RET_IF_FAIL(hr);
if (persist_info_version <= CONNODE_PERSIST_INFO_VERSION_TSAC_BETA) { //
// Unsupported perist version
//
return E_FAIL; }
//
//server
//
hr = pStm->Read( &m_szServer, sizeof(m_szServer), &cbRead); m_szServer[sizeof(m_szServer) / sizeof(TCHAR) - 1] = NULL; HR_RET_IF_FAIL(hr);
//
//description
//
hr = pStm->Read( &m_szDescription, sizeof(m_szDescription), &cbRead); m_szDescription[sizeof(m_szDescription) / sizeof(TCHAR) - 1] = NULL; HR_RET_IF_FAIL(hr);
//
//user name
//
hr = pStm->Read( &m_szUserName, sizeof(m_szUserName), &cbRead); m_szUserName[sizeof(m_szUserName) / sizeof(TCHAR) - 1] = NULL; HR_RET_IF_FAIL(hr);
//
// Password
//
BOOL fGotPassword = FALSE; if (CONNODE_PERSIST_INFO_VERSION_TSAC_BETA == persist_info_version) { //
// We drop the password if it's in TSAC format as we dropped
// support for that format
//
//
// Just seek past the correct number of bytes
//
LARGE_INTEGER seekDelta = {0, CL_OLD_PASSWORD_LENGTH + CL_SALT_LENGTH}; hr = pStm->Seek(seekDelta, STREAM_SEEK_CUR, NULL); HR_RET_IF_FAIL(hr); } else if (persist_info_version <= CONNODE_PERSIST_INFO_VERSION_DOTNET_BETA3) { //
// We drop support for the legacy DPAPI+Control obfuscation formats
//
DWORD cbSecureLen = 0; //
//encrypted bytes length
//
hr = pStm->Read( &cbSecureLen, sizeof(cbSecureLen), &cbRead); HR_RET_IF_FAIL(hr);
//
// Just seek ahead in the stream
//
LARGE_INTEGER seekDelta; seekDelta.LowPart = cbSecureLen; seekDelta.HighPart = 0; hr = pStm->Seek(seekDelta, STREAM_SEEK_CUR, NULL); HR_RET_IF_FAIL(hr); } else { //Read the new more secure format
hr = ReadProtectedPassword(pStm); if(SUCCEEDED(hr)) { fGotPassword = TRUE; } else { ODS(TEXT("Failed to ReadProtectedPassword\n")); } } //
//domain
//
if(persist_info_version >= CONNODE_PERSIST_INFO_VERSION_DOTNET_BETA3) { hr = pStm->Read( &m_szDomain, sizeof(m_szDomain), &cbRead); m_szDomain[sizeof(m_szDomain) / sizeof(TCHAR) - 1] = NULL; HR_RET_IF_FAIL(hr); } else { //Old length for domain
hr = pStm->Read( &m_szDomain, CL_OLD_DOMAIN_LENGTH * sizeof(TCHAR), &cbRead); m_szDomain[CL_OLD_DOMAIN_LENGTH - 1] = NULL; HR_RET_IF_FAIL(hr); }
//
//Password specified flag
//
hr = pStm->Read( &m_fPasswordSpecified, sizeof(m_fPasswordSpecified), &cbRead); HR_RET_IF_FAIL(hr);
//
// Override the autologon flag if we failed to
// get a password, e.g if we were unable to decrypt
// it because the current user doesn't match credentials
//
if(!fGotPassword) { m_fPasswordSpecified = FALSE; }
//
// If the password was specified in the file
// it means we want it saved
//
m_bSavePassword = m_fPasswordSpecified;
//
// Screen resolution
//
hr = pStm->Read( &m_resType, sizeof(m_resType), &cbRead); HR_RET_IF_FAIL(hr);
hr = pStm->Read( &m_Width, sizeof(m_Width), &cbRead); HR_RET_IF_FAIL(hr);
hr = pStm->Read( &m_Height, sizeof(m_Height), &cbRead); HR_RET_IF_FAIL(hr);
//
// Start program
//
hr = pStm->Read( &m_szProgramPath, sizeof(m_szProgramPath), &cbRead); m_szProgramPath[sizeof(m_szProgramPath) / sizeof(TCHAR) - 1] = NULL; HR_RET_IF_FAIL(hr);
//
// Work dir
//
hr = pStm->Read( &m_szProgramStartIn, sizeof(m_szProgramStartIn), &cbRead); m_szProgramStartIn[sizeof(m_szProgramStartIn) / sizeof(TCHAR) - 1] = NULL; HR_RET_IF_FAIL(hr);
if(persist_info_version >= CONNODE_PERSIST_INFO_VERSION_WHISTLER_BETA1) { //
// Connect to console
//
hr = pStm->Read( &m_bConnectToConsole, sizeof(m_bConnectToConsole), &cbRead); HR_RET_IF_FAIL(hr);
hr = pStm->Read( &m_bRedirectDrives, sizeof(m_bRedirectDrives), &cbRead); HR_RET_IF_FAIL(hr); //
// PADDING for future extension
//
DWORD dwPad; hr = pStm->Read( &dwPad, sizeof(dwPad), &cbRead); HR_RET_IF_FAIL(hr); hr = pStm->Read( &dwPad, sizeof(dwPad), &cbRead); HR_RET_IF_FAIL(hr); hr = pStm->Read( &dwPad, sizeof(dwPad), &cbRead); HR_RET_IF_FAIL(hr); }
return S_OK; }
HRESULT CConNode::ReadProtectedPassword(IStream* pStm) { HRESULT hr = E_FAIL; ULONG cbRead; if (pStm) { //
// NOTE: About password encryption
// at runtime the password is passed around in DPAPI form
//
// Legacy formats had the password first encrypted with the
// control's password obfuscation - we got rid of those.
//
// persistence format is
// -DWORD giving size of encrypted data field
// -Data protection ENCRYPTED BYTES of encryptedpass+salt concatenation
//
DWORD cbSecureLen = 0; //
//encrypted bytes length
//
hr = pStm->Read( &cbSecureLen, sizeof(cbSecureLen), &cbRead); HR_RET_IF_FAIL(hr); if (cbSecureLen == 0) { return E_FAIL; }
PBYTE pEncryptedBytes = (PBYTE) LocalAlloc(LPTR, cbSecureLen); if (!pEncryptedBytes) { return E_OUTOFMEMORY; }
//
//read in the encrypted pass+salt combo
//
hr = pStm->Read( pEncryptedBytes, cbSecureLen, &cbRead); HR_RET_IF_FAIL(hr); if (cbSecureLen != cbRead) { LocalFree(pEncryptedBytes); return E_FAIL; }
if (cbSecureLen == NO_PASSWORD_VALUE_LEN) { ODS(TEXT("Read cbSecurLen of NO_PASSWORD_VALUE_LEN. No password.")); LocalFree(pEncryptedBytes); return E_FAIL; } //
// DPAPI decrypt the persisted secure bytes to test if the decryption
// succeeds
//
PBYTE pUnSecureBytes; DWORD cbUnSecureLen; if (!DataUnprotect( (PBYTE)pEncryptedBytes, cbSecureLen, &pUnSecureBytes, &cbUnSecureLen)) { //DPAPI Password encryption failed
ODS(TEXT("DataUnProtect encryption FAILED\n")); LocalFree(pEncryptedBytes); return E_FAIL; }
//
// Free any existing data in the blob
//
if (_blobEncryptedPassword.pbData && _blobEncryptedPassword.cbData) { LocalFree(_blobEncryptedPassword.pbData); _blobEncryptedPassword.pbData = NULL; _blobEncryptedPassword.cbData = 0; }
//
// Do not free the encrypted bytes, they are kept around
// in the data blob in DPAPI format - ConNode will take care
// of correctly freeing the bytes when they are no longer needed.
//
_blobEncryptedPassword.pbData = pEncryptedBytes; _blobEncryptedPassword.cbData = cbSecureLen;
SecureZeroMemory(pUnSecureBytes, cbUnSecureLen); LocalFree(pUnSecureBytes); return hr; } else { return E_INVALIDARG; } }
//
// Write a DPAPI protected password out to the stream
//
HRESULT CConNode::WriteProtectedPassword(IStream* pStm) { HRESULT hr = E_FAIL; ULONG cbWritten; if (pStm) { //
// NOTE: About password encryption
// at runtime the password is passed around in DPAPI form
//
// Save the password/salt in the following format
// -DWORD giving size of encrypted data field
// -Data protection ENCRYPTED BYTES of encryptedpass+salt concatenation
//
PBYTE pSecureBytes = NULL; DWORD cbSecureLen = NULL; BOOL fFreeSecureBytes = FALSE;
DWORD dwDummyBytes = NO_PASSWORD_VALUE;
//
// Don't save the password if the setting is not selected or if there
// isn't any data to save.
//
if (!GetSavePassword() || 0 == _blobEncryptedPassword.cbData) { //
// User chose not to save the password, write 4 bytes
//
cbSecureLen = 4; pSecureBytes = (PBYTE)&dwDummyBytes; }
//
//encrypted bytes length
//
cbSecureLen = _blobEncryptedPassword.cbData; hr = pStm->Write(&cbSecureLen, sizeof(cbSecureLen), &cbWritten);
//
//write out secured bytes
//
if (SUCCEEDED(hr)) { pSecureBytes = _blobEncryptedPassword.pbData; hr = pStm->Write(pSecureBytes, cbSecureLen, &cbWritten); } } else { hr = E_INVALIDARG; } return hr; }
IMsRdpClient* CConNode::GetTsClient() { if (m_pTsClientCtl) { m_pTsClientCtl->AddRef(); } return m_pTsClientCtl; }
void CConNode::SetTsClient(IMsRdpClient* pTs) { if (NULL == pTs) { if (m_pTsClientCtl) { m_pTsClientCtl->Release(); m_pTsClientCtl = NULL; } } else { m_pTsClientCtl = pTs; m_pTsClientCtl->AddRef(); } }
IMstscMhst* CConNode::GetMultiHostCtl() { if (m_pMhostCtl) { m_pMhostCtl->AddRef(); } return m_pMhostCtl; }
void CConNode::SetMultiHostCtl(IMstscMhst* pMhst) { if (NULL == pMhst) { if (m_pMhostCtl) { m_pMhostCtl->Release(); } m_pMhostCtl = NULL; } else { m_pMhostCtl = pMhst; m_pMhostCtl->AddRef(); } }
IComponent* CConNode::GetView() { if (m_pIComponent) { m_pIComponent->AddRef(); } return m_pIComponent; }
void CConNode::SetView(IComponent* pView) { if (!pView) { if (m_pIComponent) { m_pIComponent->Release(); m_pIComponent = NULL; } } else { if (m_pIComponent) { ODS( L"Clobbering IComponent interface, could be leaking\n" ); }
pView->AddRef(); m_pIComponent = pView; } }
//
// Store a clear text password in encrypted form
//
HRESULT CConNode::SetClearTextPass(LPCTSTR szClearPass) { HRESULT hr = E_FAIL;
DATA_BLOB din; din.cbData = _tcslen(szClearPass) * sizeof(TCHAR); din.pbData = (PBYTE)szClearPass; if (_blobEncryptedPassword.pbData) { LocalFree(_blobEncryptedPassword.pbData); _blobEncryptedPassword.pbData = NULL; _blobEncryptedPassword.cbData = 0; } if (din.cbData) { if (CryptProtectData(&din, TEXT("ps"), // DESCRIPTION STRING.
NULL, // optional entropy
NULL, // reserved
NULL, // NO prompting
CRYPTPROTECT_UI_FORBIDDEN, //don't pop UI
&_blobEncryptedPassword)) { hr = S_OK; } else { ODS((_T("DataProtect failed"))); hr = E_FAIL; } } else { ODS((_T("0 length password, not encrypting"))); hr = S_OK; }
return hr; }
//
// Retrieve a clear text password
//
//
// Params
// [out] szBuffer - receives decrypted password
// [int] cbLen - length of szBuffer
//
HRESULT CConNode::GetClearTextPass(LPTSTR szBuffer, INT cbLen) { HRESULT hr = E_FAIL;
DATA_BLOB dout; dout.cbData = 0; dout.pbData = NULL; if (_blobEncryptedPassword.cbData) { memset(szBuffer, 0, cbLen); if (CryptUnprotectData(&_blobEncryptedPassword, NULL, // NO DESCRIPTION STRING
NULL, // optional entropy
NULL, // reserved
NULL, // NO prompting
CRYPTPROTECT_UI_FORBIDDEN, //don't pop UI
&dout)) { memcpy(szBuffer, dout.pbData, min( dout.cbData,(UINT)cbLen-sizeof(TCHAR)));
//
// Nuke the original copy
//
SecureZeroMemory(dout.pbData, dout.cbData); LocalFree( dout.pbData ); hr = S_OK; } else { ODS((_T("DataUnprotect failed"))); hr = E_FAIL; } } else { ODS(_T("0 length encrypted pass, not decrypting"));
//
// Just reset the output buffer
//
memset(szBuffer, 0, cbLen); hr = S_OK; }
return hr; }
|