Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1489 lines
42 KiB

#include "stdafx.h"
#include "rcbdyctl.h"
#include "IMSession.h"
#include "wincrypt.h"
#include "auth.h"
#include "assert.h"
#include "wininet.h"
#include "msgrua.h"
#include "msgrua_i.c"
#include "utils.h"
#include "lock_i.c"
#include "sessions.h"
#include "sessions_i.c"
#include "helpservicetypelib.h"
#include "helpservicetypelib_i.c"
#include "safrcfiledlg.h"
#include "safrcfiledlg_i.c"
/////////////////////////////////////////////////////////////////////////
// CIMSession
// Global help functions declaration.
HWND InitInstance(HINSTANCE hInstance, int nCmdShow);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
DWORD WINAPI HSCInviteThread(LPVOID lpParam);
HRESULT UnlockSession(CIMSession* pThis);
VOID CALLBACK ConnectToExpertCallback(HWND hwnd,UINT uMsg,UINT_PTR idEvent,DWORD dwTime);
UINT_PTR g_timerID;
CIMSession * g_pThis;
HWND g_StatusDlg = NULL;
// Window class name
TCHAR szWindowClass[] = TEXT("Microsoft Remote Assistance Messenger UNLOCK window");
extern HINSTANCE g_hInstance;
#define VIESDESKTOP_PERMISSION_NOT_REQUIRE 0x4
#define SESSION_EXPIRY 305
#define RA_TIMER_UNLOCK_ID 0x1
#define RA_TIMEOUT_UNLOCK 180 * 1000 // 3 minutes.
#define RA_TIMEOUT_USER 1800 * 1000 // 30 minutes
HANDLE g_hLockEvent = NULL;
BOOL g_bActionCancel = FALSE;
HWND g_hWnd = NULL;
LPSTREAM g_spInvitee = NULL;
LPSTREAM g_spStatus = NULL;
CIMSession::CIMSession()
{
m_pSessObj = NULL;
m_pSessMgr = NULL;
m_pMsgrLockKey = NULL;
m_bIsInviter = TRUE;
m_hCryptProv = NULL;
m_hPublicKey = NULL;
m_pSessionEvent = NULL;
m_iState = 0;
m_pfnSessionStatus = NULL;
m_pSessionMgrEvent = NULL;
m_bIsHSC = FALSE;
m_pInvitee = NULL;
m_bLocked = TRUE;
m_bExchangeUser = FALSE;
}
CIMSession::~CIMSession()
{
if (m_pSessObj)
{
if (m_pSessionEvent)
m_pSessionEvent->DispEventUnadvise(m_pSessObj);
m_pSessObj->Release();
}
if (m_pSessionMgrEvent)
m_pSessionMgrEvent->Release();
if (m_pSessionEvent)
m_pSessionEvent->Release();
if (m_pMsgrLockKey)
m_pMsgrLockKey->Release();
if (m_pSessMgr)
m_pSessMgr->Release();
if (m_hPublicKey)
CryptDestroyKey(m_hPublicKey);
if (m_hCryptProv)
CryptReleaseContext(m_hCryptProv, 0);
}
STDMETHODIMP CIMSession::put_OnSessionStatus(IDispatch* pfn)
{
m_pfnSessionStatus = pfn;
return S_OK;
}
STDMETHODIMP CIMSession::get_ReceivedUserTicket(BSTR* pSalemTicket)
{
if (pSalemTicket == NULL)
return E_INVALIDARG;
*pSalemTicket = m_bstrSalemTicket.Copy();
return S_OK;
}
STDMETHODIMP CIMSession::Hook(IMsgrSession*, HWND hWnd)
{
HRESULT hr = S_OK;
m_hWnd = hWnd;
return hr;
}
STDMETHODIMP CIMSession::ContextDataProperty(BSTR pName, BSTR* ppValue)
{
HRESULT hr = S_OK;
CComPtr<IRASetting> cpSetting;
if (*ppValue != NULL)
{
SysFreeString(*ppValue);
*ppValue = NULL;
}
if (m_bstrContextData.Length() == 0)
goto done;
if (pName == NULL || *pName == L'\0')
{
*ppValue = m_bstrContextData.Copy();
goto done;
}
hr = cpSetting.CoCreateInstance( CLSID_RASetting, NULL, CLSCTX_INPROC_SERVER);
if (FAILED_HR(_T("ISetting->CoCreateInstance failed: %s"), hr))
goto done;
cpSetting->get_GetPropertyInBlob(m_bstrContextData, pName, ppValue);
done:
return hr;
}
STDMETHODIMP CIMSession::get_User(IDispatch** ppUser)
{
HRESULT hr = S_OK;
if (ppUser == NULL)
{
hr = E_INVALIDARG;
goto done;
}
if (m_pSessObj)
{
hr = m_pSessObj->get_User(ppUser);
if (FAILED_HR(_T("get_User failed %s"), hr))
goto done;
}
else
{
DEBUG_MSG(_T("No Session found"));
hr = S_OK;
*ppUser = NULL;
}
done:
return hr;
}
STDMETHODIMP CIMSession::get_IsInviter(BOOL* pVal)
{
if (pVal == NULL)
return E_INVALIDARG;
*pVal = m_bIsInviter;
return S_OK;
}
STDMETHODIMP CIMSession::CloseRA()
{
HRESULT hr = S_OK;
if(m_bIsInviter && m_hWnd) // for inviter, this is the last function to call.
SendMessage(m_hWnd, WM_CLOSE, NULL, NULL);
return hr;
}
HRESULT CIMSession::GenEncryptdNoviceBlob(BSTR pPublicKeyBlob, BSTR pSalemTicket, BSTR* pBlob)
{
TraceSpew(_T("Funct: GenEncryptedNoviceBlob"));
HRESULT hr;
if (!pPublicKeyBlob)
return FALSE;
DWORD dwLen, dwBlobLen, dwSessionKeyCount, dwSalemCount;
LPBYTE pBuf =NULL;
HCRYPTKEY hSessKey =NULL;
HCRYPTKEY hPublicKey =NULL;
BSTR pSessionKeyBlob =NULL;
BSTR pSalemBlob =NULL;
TCHAR szHeader[20];
CComBSTR bstrBlob;
if (FAILED(hr = InitCSP(FALSE)))
goto done;
// Import public key
if (FAILED(hr = StringToBinary(pPublicKeyBlob, wcslen(pPublicKeyBlob), &pBuf, &dwLen)))
goto done;
if (!CryptImportKey(m_hCryptProv,
pBuf,
(UINT)dwLen,
0,
0,
&hPublicKey))
{
DEBUG_MSG(_T("Can't import public key"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
// Gen session key.
if (!CryptGenKey(m_hCryptProv, CALG_RC2, CRYPT_EXPORTABLE, &hSessKey))
{
DEBUG_MSG(_T("Create Session key failed"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
DWORD dwBitLength = 40;
if (!CryptSetKeyParam(hSessKey, KP_EFFECTIVE_KEYLEN, (PBYTE) &dwBitLength, 0))
{
DEBUG_MSG(_T("Set KEYLEN failed"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
if (FAILED(hr=GetKeyExportString(hSessKey, hPublicKey, SIMPLEBLOB, &pSessionKeyBlob, &dwSessionKeyCount)))
goto done;
// Encrypt SalemTicket
dwBlobLen = dwLen = wcslen(pSalemTicket) * sizeof(OLECHAR);
if (!CryptEncrypt(hSessKey, NULL, TRUE, 0, NULL, &dwBlobLen, dwLen))
{
DEBUG_MSG(_T("Can't calculate salem ticket buffer length."));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
if (pBuf)
free(pBuf);
if((pBuf = (LPBYTE)malloc(dwBlobLen)) == NULL)
{
hr = E_OUTOFMEMORY;
goto done;
}
ZeroMemory(pBuf, dwBlobLen);
memcpy(pBuf, (LPBYTE)pSalemTicket, dwLen);
if (!CryptEncrypt(hSessKey, NULL, TRUE, 0, pBuf, &dwLen, dwBlobLen))
{
DEBUG_MSG(_T("Can't calculate user ticket length"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
// Need to generate salem ticket blob
if (FAILED(hr=BinaryToString(pBuf, dwBlobLen, &pSalemBlob, &dwSalemCount)))
goto done;
// Generate final Blob
wsprintf(szHeader, _T("%d;S="), dwSessionKeyCount + 2);
bstrBlob = szHeader;
bstrBlob.AppendBSTR(pSessionKeyBlob);
wsprintf(szHeader, _T("%d;U="), dwSalemCount + 2);
bstrBlob.Append(szHeader);
bstrBlob.AppendBSTR(pSalemBlob);
if (!InternetGetConnectedState(&dwLen, 0))
{
DEBUG_MSG(_T("No Internet connection"));
}
else
{
if (dwLen & INTERNET_CONNECTION_MODEM) // connected through Modem
{
bstrBlob.Append("3;L=1");
}
}
*pBlob = bstrBlob.Detach();
done:
if (pBuf)
free(pBuf);
if (pSessionKeyBlob)
SysFreeString(pSessionKeyBlob);
if (pSalemBlob)
SysFreeString(pSalemBlob);
if (hPublicKey)
CryptDestroyKey(hPublicKey);
if (hSessKey)
CryptDestroyKey(hSessKey);
return hr;
}
HRESULT CIMSession::InviterSendSalemTicket(BSTR pContext)
{
TraceSpew(_T("Funct: InviterSendSalemTicket"));
HRESULT hr;
CComPtr<IRASetting> cpSetting;
ISAFRemoteDesktopSession *pRCS = NULL;
BSTR pPublicKeyBlob = NULL;
CComBSTR bstrExpertTicket;
CComBSTR bstrSalemTicket;
CComBSTR bstrBlob, bstrExpertName, bstrUserBlob;
CComPtr<IClassFactory> fact;
CComQIPtr<IPCHUtility> disp;
CComPtr<IDispatch> cpDisp;
CComPtr<IMessengerContact> cpExpert;
TCHAR szHeader[100];
hr = cpSetting.CoCreateInstance( CLSID_RASetting, NULL, CLSCTX_INPROC_SERVER);
if (FAILED_HR(_T("ISetting->CoCreateInstance failed: %s"), hr))
goto done;
// bstrExpertBlob has 2 part: Expert ticket and expert public key. Names: "ET" and "PK"
cpSetting->get_GetPropertyInBlob(pContext, CComBSTR("ET"), &m_bstrExpertTicket);
if (m_bstrExpertTicket.Length() == 0)
{
DEBUG_MSG(_T("No expert ticket"));
goto done;
}
cpSetting->get_GetPropertyInBlob(pContext, CComBSTR("PK"), &pPublicKeyBlob);
// Generate SALEM Ticket.
hr =::CoGetClassObject(CLSID_PCHService, CLSCTX_ALL, NULL, IID_IClassFactory, (void**)&fact );
if (FAILED_HR(_T("CoGetClassObject failed: %s"), hr))
goto done;
// Get Expert name and put it into userblob
hr = m_pSessObj->get_User(&cpDisp);
if (FAILED_HR(_T("get_User failed %s"), hr))
goto done;
hr = cpDisp->QueryInterface(IID_IMessengerContact, (LPVOID*)&cpExpert);
if (FAILED_HR(_T("QI IMsgrUser failed: %s"), hr))
goto done;
hr = cpExpert->get_FriendlyName(&bstrExpertName);
if (FAILED_HR(_T("get_FriendlyName failed %s"), hr))
goto done;
wsprintf(szHeader, _T("%d;EXP_NAME="), bstrExpertName.Length() + 9);
bstrUserBlob = szHeader;
bstrUserBlob.AppendBSTR(bstrExpertName);
bstrUserBlob.Append("4;IM=1");
disp = fact; //... it would run QI automatically.
hr = disp->CreateObject_RemoteDesktopSession(
(REMOTE_DESKTOP_SHARING_CLASS)VIESDESKTOP_PERMISSION_NOT_REQUIRE,
SESSION_EXPIRY, // expired in 5 minutes.
CComBSTR(""),
bstrUserBlob,
&pRCS );
if (FAILED_HR(_T("CreateRemoteDesktopSession failed %s"), hr))
goto done;
hr = pRCS->get_ConnectParms(&bstrSalemTicket);
if (FAILED_HR(_T("GetConnectParms failed: %s"), hr))
goto done;
// Encrypt ticket with the key and send it back.
if (pPublicKeyBlob)
{
if (FAILED(hr = GenEncryptdNoviceBlob(pPublicKeyBlob, bstrSalemTicket, &bstrBlob)))
goto done;
}
else
{
TCHAR sbuf[20];
wsprintf(sbuf, _T("%d;U="), wcslen(bstrSalemTicket) + 2);
bstrBlob = sbuf;
bstrBlob += bstrSalemTicket;
}
hr = m_pSessObj->SendContextData((BSTR)bstrBlob);
if (FAILED_HR(TEXT("Send Context data filed: %s"), hr))
goto done;
done:
if (pRCS)
pRCS->Release();
if (pPublicKeyBlob)
SysFreeString(pPublicKeyBlob);
return hr;
}
#define IM_STATE_GET_TICKET 1
#define IM_STATE_COMPLETE 2
STDMETHODIMP CIMSession::ProcessContext(BSTR pContext)
{
TraceSpewW(L"Funct: ProcessContext %s", (pContext==NULL?L"NULL":pContext));
HRESULT hr = S_OK;
hr = ProcessNotify(pContext); // Is it a notification?
if (SUCCEEDED(hr))
{
goto done;
}
m_iState++;
m_bstrContextData = pContext;
if (m_bIsInviter)
{
switch(m_iState)
{
case IM_STATE_GET_TICKET: // Received Expert ticket
if (!m_bIsHSC)
{
hr = InviterSendSalemTicket(pContext);
if (FAILED(hr))
{
// Need to notify expert side.
Notify(RA_IM_FAILED);
// Also let the local session know the status.
DoSessionStatus(RA_IM_FAILED);
CloseRA(); // close inviter side rcimlby.exe.
}
else
{
if (m_bExchangeUser)
{
// Set a timer to callback and then we call ConnectToExpert.
g_pThis = this;
g_timerID = SetTimer (NULL, NULL, 1000, (TIMERPROC)ConnectToExpertCallback);
if (!g_timerID)
{
// SetTimer failed! This means that we have to bail out of the IM request
// since the call to ConnectToExpert will never happen.
// Need to notify expert side.
Notify(RA_IM_FAILED);
// Also let the local session know the status.
DoSessionStatus(RA_IM_FAILED);
CloseRA(); // close inviter side rcimlby.exe.
}
}
else
{
CComPtr<IClassFactory> fact;
CComQIPtr<IPCHUtility> disp;
LONG lError;
TraceSpew(_T("Connect to Expert"));
hr =::CoGetClassObject(CLSID_PCHService, CLSCTX_ALL, NULL, IID_IClassFactory, (void**)&fact );
if (!FAILED_HR(_T("CoGetClass CLSID_PCHService failed: %s"), hr))
{
disp = fact; //... it would run QI automatically.
hr = disp->ConnectToExpert(m_bstrExpertTicket, 10, &lError);
if (!FAILED_HR(_T("ConnectToExpert failed: %s"), hr))
DoSessionStatus(RA_IM_CONNECTTOEXPERT);
}
CloseRA(); // close inviter side rcimlby.exe
}
}
}
else // Inviter HelpCtr status update.
{
DoSessionStatus(RA_IM_WAITFORCONNECT);
}
break;
#if 0 // Connection complete: currently not used.
case IM_STATE_COMPLETE:
// If host is rcimlby.exe, close it.
if (m_hWnd)
{
DestroyWindow(m_hWnd);
}
else
{
DoSessionStatus(RA_IM_COMPLETE);
}
break;
#endif
default:
// Noise?
break;
}
}
else // Expert side.
{
switch(m_iState)
{
case IM_STATE_GET_TICKET: // Get Novice salem ticket
// Extract this ticket to member variable and signal the call back to let host start to connect.
hr = ExtractSalemTicket(pContext);
if (FAILED(hr))
{
// need to notify Novice that connection failed.
Notify(RA_IM_FAILED);
DoSessionStatus(RA_IM_FAILED);
}
else
{
DoSessionStatus(RA_IM_CONNECTTOSERVER);
}
break;
default:
// Noise?
break;
}
}
done:
return hr;
}
VOID CALLBACK ConnectToExpertCallback(
HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime
)
{
// Kill the Timer
KillTimer(NULL, g_timerID);
HRESULT hr = S_OK;
CComPtr<IClassFactory> fact;
CComQIPtr<IPCHUtility> disp;
LONG lError;
TraceSpew(_T("Connect to Expert"));
hr =::CoGetClassObject(CLSID_PCHService, CLSCTX_ALL, NULL, IID_IClassFactory, (void**)&fact );
if (!FAILED_HR(_T("CoGetClass CLSID_PCHService failed: %s"), hr))
{
disp = fact; //... it would run QI automatically.
hr = disp->ConnectToExpert(g_pThis->m_bstrExpertTicket, 10, &lError);
if (!FAILED_HR(_T("ConnectToExpert failed: %s"), hr))
g_pThis->DoSessionStatus(RA_IM_CONNECTTOEXPERT);
}
g_pThis->CloseRA(); // close inviter side rcimlby.exe.
}
///////////////////////////////////////////////////////////////////////////////////////////
// We can't notify the other party too much time. The context data can be set only 5 times.
HRESULT CIMSession::ProcessNotify(BSTR pContext)
{
TraceSpewW(L"Funct: ProcessNotify %s", (pContext?pContext:L"NULL"));
HRESULT hr = S_OK;
CComPtr<IRASetting> cpSetting;
CComBSTR bstrData;
int lStatus;
hr = cpSetting.CoCreateInstance( CLSID_RASetting, NULL, CLSCTX_INPROC_SERVER);
if (FAILED_HR(_T("ISetting->CoCreateInstance failed: %s"), hr))
goto done;
cpSetting->get_GetPropertyInBlob(pContext, CComBSTR("NOTIFY"), &bstrData);
if (bstrData.Length() == 0)
{
hr = E_FAIL; // Not a notification.
goto done;
}
lStatus = _wtoi((BSTR)bstrData);
switch (lStatus)
{
case RA_IM_COMPLETE:
DoSessionStatus(RA_IM_COMPLETE);
break;
case RA_IM_TERMINATED:
DoSessionStatus(RA_IM_TERMINATED);
break;
case RA_IM_FAILED:
DoSessionStatus(RA_IM_FAILED);
break;
default: // ignore the others.
break;
}
done:
return hr;
}
HRESULT CIMSession::DoSessionStatus(int iState)
{
// Used for trace purpose.
static TCHAR *szMsg[] = { _T("Unknown session status"),
_T("RA_IM_COMPLETE"), // 0x1
_T("RA_IM_WAITFORCONNECT"), // 0x2
_T("RA_IM_CONNECTTOSERVER"), // 0x3
_T("RA_IM_APPSHUTDOWN"), // 0x4
_T("RA_IM_SENDINVITE"), // 0x5
_T("RA_IM_ACCEPTED"), // 0x6
_T("RA_IM_DECLINED"), // 0x7
_T("RA_IM_NOAPP"), // 0x8
_T("RA_IM_TERMINATED"), // 0x9
_T("RA_IM_CANCELLED"), // 0xA
_T("RA_IM_UNLOCK_WAIT"), // 0xB
_T("RA_IM_UNLOCK_FAILED"), // 0xC
_T("RA_IM_UNLOCK_SUCCEED"), // 0xD
_T("RA_IM_UNLOCK_TIMEOUT"), // 0xE
_T("RA_IM_CONNECTTOEXPERT"), // 0xF
_T("RA_IM_EXPERT_TICKET_OUT")// 0x10
};
TCHAR *pMsg = NULL;
if (iState > 0 && iState < (sizeof(szMsg) / sizeof(TCHAR*)))
pMsg = szMsg[iState];
else
pMsg = szMsg[0];
TraceSpew(_T("DoSessionStatus: %s"), pMsg);
if (m_pfnSessionStatus)
{
DISPPARAMS disp;
VARIANTARG varg[1];
disp.rgvarg = varg;
disp.rgdispidNamedArgs = NULL;
disp.cArgs = 1;
disp.cNamedArgs = 0;
varg[0].vt = VT_I4;
varg[0].lVal = iState;
if (m_pfnSessionStatus)
m_pfnSessionStatus->Invoke(0x0, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, NULL, NULL, NULL);
}
else //Jose: If m_pfnSessionStatus is not set send message to the status dialog if it is created
{
if (g_StatusDlg)
::SendMessage(g_StatusDlg,MSG_STATUS,(WPARAM)iState,NULL);
}
if ((iState == RA_IM_TERMINATED || iState == RA_IM_FAILED) && m_bIsInviter && !m_bIsHSC)
{
// need to close inviter RA lobby
CloseRA();
}
return S_OK;
}
HRESULT CIMSession::InitSessionEvent(IMsgrSession* pSessObj)
{
HRESULT hr = S_OK;
if (!m_pSessionEvent)
{
hr = CComObject<CSessionEvent>::CreateInstance(&m_pSessionEvent);
if (FAILED_HR(_T("CreateInstance SessionEvent failed: %s"), hr))
goto done;
m_pSessionEvent->AddRef();
}
m_pSessionEvent->Init(this, pSessObj);
done:
return hr;
}
HRESULT CIMSession::InitCSP(BOOL bGenPublicKey /* = TRUE */)
{
TraceSpew(_T("Funct: InitCSP"));
HRESULT hr = S_OK;
TCHAR szUser[] = _T("RemoteAssistanceIMIntegration");
if (!m_hCryptProv)
{
// 1. If it doesn't exist then create a new one.
if (!CryptAcquireContext(&m_hCryptProv, NULL, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
{
DEBUG_MSG(_T("Create CSP failed"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
}
// Get public key
if(bGenPublicKey &&
!m_hPublicKey &&
!CryptGetUserKey(m_hCryptProv, AT_KEYEXCHANGE, &m_hPublicKey))
{
// Check to see if one needs to be created.
if(GetLastError() == NTE_NO_KEY)
{
// Create an key exchange key pair.
if(!CryptGenKey(m_hCryptProv,AT_KEYEXCHANGE,0,&m_hPublicKey))
{
DEBUG_MSG(_T("Error occurred attempting to create an exchange key."));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
}
else
{
DEBUG_MSG(_T("Error occurred when access Public key"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
}
done:
return hr;
}
HRESULT CIMSession::ExtractSalemTicket(BSTR pContext)
{
TraceSpewW(L"Funct: ExtraceSalemTicket %s", pContext?pContext:L"NULL");
HRESULT hr = S_OK;
// This ContextData could contains S (sessionkey) and U (user=ticket) name pairs.
CComBSTR bstrU, bstrS;
CComPtr<ISetting> cpSetting;
DWORD dwLen;
HCRYPTKEY hSessKey = NULL;
LPBYTE pBuf = NULL;
BSTR pBlob = NULL;
hr = cpSetting.CoCreateInstance(CLSID_Setting, NULL, CLSCTX_INPROC_SERVER);
if (FAILED_HR(_T("ISetting->CoCreateInstance failed: %s"), hr))
goto done;
cpSetting->get_GetPropertyInBlob(pContext, CComBSTR("U"), &bstrU);
cpSetting->get_GetPropertyInBlob(pContext, CComBSTR("S"), &bstrS);
dwLen = bstrS.Length();
if (dwLen > 0)
{
// need to decrypt user ticket
TraceSpewW(L"Decrypt user ticket using Expert's public key...");
if (!m_hCryptProv || !m_hPublicKey)
{
DEBUG_MSG(_T("Can't find Cryptographic handler"));
hr = FALSE;
goto done;
}
if (FAILED(hr = StringToBinary((BSTR)bstrS, dwLen, &pBuf, &dwLen)))
goto done;
if (!CryptImportKey(m_hCryptProv, pBuf, dwLen, m_hPublicKey, 0, &hSessKey))
{
DEBUG_MSG(_T("Can't import Session Key"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
free(pBuf);
pBuf=NULL;
if (FAILED(hr = StringToBinary((BSTR)bstrU, bstrU.Length(), &pBuf, &dwLen)))
goto done;
if (!CryptDecrypt(hSessKey, 0, TRUE, 0, pBuf, &dwLen))
{
DEBUG_MSG(_T("Can't decrypt salem ticket"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
pBlob = SysAllocStringByteLen((char*)pBuf, dwLen);
m_bstrSalemTicket.Attach(pBlob);
}
else
{
TraceSpew(_T("No expert's public key, use plain text to send salem ticket."));
m_bstrSalemTicket = bstrU;
}
done:
if (pBuf)
free(pBuf);
if (hSessKey)
CryptDestroyKey(hSessKey);
return hr;
}
#if 0 // No need for SP1, server or later
DWORD CIMSession::GetExchangeRegValue()
{
CRegKey cKey;
LONG lRet = 0x0;
DWORD dwValue = 0x0;
lRet = cKey.Open(HKEY_LOCAL_MACHINE,
TEXT("SYSTEM\\CurrentControlSet\\Control\\Terminal Server"),
KEY_READ );
if (lRet == ERROR_SUCCESS)
{
lRet = cKey.QueryValue(dwValue,TEXT("UseExchangeIM"));
if (lRet == ERROR_SUCCESS)
{
// Success
}
}
return dwValue;
}
#endif
////////////////////////////////////////////////////////////////////
// This is used for recipient to get his session object.
STDMETHODIMP CIMSession::GetLaunchingSession(LONG lID)
{
HRESULT hr;
IDispatch *pDisp = NULL;
LONG lFlags;
LONG lRet;
CComPtr<IDispatch> cpDispUser;
CComPtr<IMessengerContact> cpMessContact;
CComBSTR bstrServiceId;
CComBSTR bstrNetGUID;
CRegKey cKey;
if (!m_pSessMgr)
{
hr = CoCreateInstance (CLSID_MsgrSessionManager,
NULL,
CLSCTX_LOCAL_SERVER,
IID_IMsgrSessionManager,
(LPVOID*)&m_pSessMgr);
if (FAILED_HR(_T("CoCreate IMsgrSessionManager failed: %s"), hr))
goto done;
}
hr = UnlockSession(this);
if (FAILED(hr))
goto done;
hr = m_pSessMgr->GetLaunchingSession(lID, (IDispatch**)&pDisp);
if (FAILED_HR(TEXT("GetLaunchingSession failed: %s"), hr))
goto done;
hr = pDisp->QueryInterface(IID_IMsgrSession, (LPVOID*)&m_pSessObj);
if (FAILED_HR(_T("QI IID_IMsgrSession failed: %s"), hr))
goto done;
// Grab the user
hr = m_pSessObj->get_User((IDispatch**)&cpDispUser);
if (FAILED_HR(_T("get_User failed: %s"), hr))
goto done;
// QI for the IMessengerContact
hr = cpDispUser->QueryInterface(IID_IMessengerContact, (void **)&cpMessContact);
if (FAILED_HR(_T("QI failed getting IID_IMessengerContact hr=%s"),hr))
goto done;
// Grab the Service ID from the Messenger Contact
hr = cpMessContact->get_ServiceId(&bstrServiceId);
if (FAILED_HR(_T("get_ServiceId failed! hr=%s"),hr))
goto done;
// If the service ID is {9b017612-c9f1-11d2-8d9f-0000f875c541}, then set the
// flag to unlock the API.
// bstrNetGUID = L"{9b017612-c9f1-11d2-8d9f-0000f875c541}"; // Messenger GUID
bstrNetGUID = L"{83D4679E-B6D7-11D2-BF36-00C04FB90A03}"; // Exchange Service GUID
if (bstrNetGUID == bstrServiceId)
{
m_bExchangeUser = TRUE;
}
// Else continue...
// ***************************************************************************
// Hook up everything
if (FAILED(hr = InitSessionEvent(m_pSessObj)))
goto done;
hr = m_pSessObj->get_Flags(&lFlags);
if (FAILED_HR(TEXT("Session Get flags failed: %s"), hr))
goto done;
if (lFlags & SF_INVITEE) // Inviter. Only happened when Messenger UI sends this invitation.
{
m_bIsInviter = FALSE;
}
done:
if (pDisp)
pDisp->Release();
return hr;
}
HRESULT CIMSession::OnLockChallenge(BSTR pChallenge , LONG lCookie)
{
// Send response.
//
// id = [email protected]
// key = L2P3B7C6V9J4T8D5
//
USES_CONVERSION;
HRESULT hr = S_OK;
CComBSTR bstrID = "[email protected]";
CComBSTR bstrResponse;
LPSTR pszKey = "L2P3B7C6V9J4T8D5";
PSTR pszParam1 = NULL;
LPSTR pszResponse = NULL;
pszResponse = CAuthentication::GetAuthentication()->GetMD5Result(W2A(pChallenge), pszKey);
bstrResponse = pszResponse;
hr = m_pMsgrLockKey->SendResponse(bstrID, bstrResponse, lCookie);
if (FAILED_HR(_T("SendResponse failed %s"), hr))
goto done;
done:
if (pszResponse)
delete pszResponse;
return hr;
}
#define WM_APP_LOCKNOTIFY WM_APP + 0x1
#define WM_APP_LOCKNOTIFY_OK WM_APP + 0x2
#define WM_APP_LOCKNOTIFY_FAIL WM_APP + 0x3
#define WM_APP_LOCKNOTIFY_INTHREAD WM_APP + 0x4
HRESULT CIMSession::OnLockResult(BOOL fSucceed, LONG lCookie)
{
// Notify UnlockSession that we've get response..
assert(g_hWnd);
SendNotifyMessage(g_hWnd, WM_APP_LOCKNOTIFY, (WPARAM)fSucceed, NULL);
DoSessionStatus(fSucceed ? RA_IM_UNLOCK_SUCCEED : RA_IM_UNLOCK_FAILED);
return S_OK;
}
///////////////////////////////////////////////////////////////////////
// This method will be used only from inside HSC
STDMETHODIMP CIMSession::HSC_Invite(IDispatch* pUser)
{
// Create a Invitation thread and return.
// Need a lock for user to click cancel.
HRESULT hr = S_OK;
assert(g_hLockEvent == NULL); // If it's not NULL, there is a bug.
g_hLockEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (!g_hLockEvent)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
if (pUser == NULL)
{
hr = E_INVALIDARG;
goto done;
}
CoMarshalInterThreadInterfaceInStream(IID_IDispatch,pUser,&g_spInvitee);
if (this->m_pfnSessionStatus)
{
CoMarshalInterThreadInterfaceInStream(IID_IDispatch, this->m_pfnSessionStatus, &g_spStatus);
this->m_pfnSessionStatus = NULL;
}
if (!CreateThread(NULL, 0, HSCInviteThread, NULL, 0, NULL))
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
//Jose: If m_pfnSessionStatus is not set the component displays its own status dialog.
if (!this->m_pfnSessionStatus)
{
if (IDCANCEL == m_StatusDlg.DoModal())
{
Notify(RA_IM_CANCELLED);
}
}
done:
if (FAILED(hr) && g_hLockEvent != NULL)
{
CloseHandle(g_hLockEvent);
g_hLockEvent = NULL;
}
return hr;
}
DWORD WINAPI HSCInviteThread(LPVOID lpParam)
{
CComObject<CIMSession> *pThis = NULL;
HRESULT hr;
CComBSTR bstrAPPID(C_RA_APPID);
CComPtr<IDispatch> cpDisp;
LockStatus ls=LOCK_NOTINITIALIZED;
hr = CComObject<CIMSession>::CreateInstance(&pThis);
if (FAILED(hr))
{
goto done;
}
if (g_spStatus) // Rebuild StatusCallback
{
CoGetInterfaceAndReleaseStream(g_spStatus,IID_IDispatch,(void**)&pThis->m_pfnSessionStatus);
g_spStatus = NULL;
}
// 1. Create SessionManager
if (!pThis->m_pSessMgr)
{
hr = UnlockSession(pThis);
if (FAILED(hr))
goto done;
}
// Check Lock status
hr = pThis->m_pMsgrLockKey->get_Status(&ls);
if (ls != LOCK_UNLOCKED)
pThis->DoSessionStatus(RA_IM_UNLOCK_SUCCEED);
else
pThis->DoSessionStatus(RA_IM_UNLOCK_FAILED);
if (ls != LOCK_UNLOCKED)
{
pThis->DoSessionStatus(RA_IM_UNLOCK_FAILED);
goto done;
}
// 3. create session object
hr = pThis->m_pSessMgr->CreateSession((IDispatch**)&cpDisp);
if (FAILED(hr))
goto done;
hr = cpDisp->QueryInterface(IID_IMsgrSession, (void **)&pThis->m_pSessObj);
if (FAILED(hr))
goto done;
// Hook up enent sink
if (FAILED(hr = pThis->InitSessionEvent(pThis->m_pSessObj)))
goto done;
// 4. Set session option
hr = pThis->m_pSessObj->put_Application((BSTR)bstrAPPID);
if (FAILED_HR(_T("put_Application failed: %s"), hr))
goto done;
// OK. I'm from HelpCtr.
pThis->m_bIsHSC = TRUE;
// Invite
if (!cpDisp)
cpDisp.Release();
CoGetInterfaceAndReleaseStream(g_spInvitee,IID_IDispatch,(void**)&cpDisp);
g_spInvitee = NULL;
if (g_bActionCancel) // It's cancelled already.
goto done;
if(FAILED(hr = pThis->Invite(cpDisp)))
goto done;
// This loop is only used if user wants to cancel this invitation.
while (1)
{
// User has 10 minutes to click cancel.
// If regular connection doesn't happen in 10 minutes, it timeout too.
DWORD dwWaitState = WaitForSingleObject(g_hLockEvent, RA_TIMEOUT_USER);
if (dwWaitState == WAIT_OBJECT_0 && g_bActionCancel == TRUE) // at this moment, we don't have anyother action.
{
hr = pThis->m_pSessObj->Cancel(MSGR_E_CANCEL, NULL);
}
break; // For now, we always get out of the loop.
}
done:
if (g_hLockEvent)
{
CloseHandle(g_hLockEvent);
g_hLockEvent = NULL;
}
if (pThis)
pThis->Release();
g_bActionCancel = FALSE; //reset this global variable.
return hr;
}
////////////////////////////////////////////////////////////////
// This function only used from inside HSC
HRESULT CIMSession::Invite(IDispatch* pUser)
{
HRESULT hr = S_OK;
if (m_pSessObj == NULL)
{
hr = E_FAIL;
goto done;
}
// Send invitation without ticket. Ticket will be sent from ContextData.
hr = m_pSessObj->Invite(pUser, NULL);
if (FAILED_HR(TEXT("Invite failed %s"), hr))
goto done;
DoSessionStatus(RA_IM_SENDINVITE);
done:
return hr;
}
STDMETHODIMP CIMSession::Notify(int iIMStatus)
{
HRESULT hr = S_OK;
TCHAR szHeader[1024];
CComBSTR bstrData;
if (iIMStatus == RA_IM_CANCELLED || iIMStatus == RA_IM_CLOSE_INVITE_UI) // Doesn't need to use ContextData to notify this msg
{
assert(m_bIsHSC == TRUE); // Only helpctr scenario would do this.
if (g_hLockEvent) // if it's NULL, that means this thread has already terminated itself.
{
g_bActionCancel = (iIMStatus == RA_IM_CANCELLED); // It's possible that user just want to close the UI.
SetEvent(g_hLockEvent);
}
goto done;
}
if (m_pSessObj)
{
wsprintf(szHeader, _T("%d;NOTIFY=%d"), GetDigit(iIMStatus) + 7, iIMStatus);
bstrData = szHeader;
if (bstrData.Length() > 0)
{
hr = m_pSessObj->SendContextData((BSTR)bstrData);
if (FAILED_HR(_T("Notify: SendContextData failed %s"), hr))
goto done;
}
}
done:
return S_OK;
}
////////////////////////////////////////////////////////////////
// This function sends expert ticket to user through ContextData
STDMETHODIMP CIMSession::SendOutExpertTicket(BSTR bstrTicket)
{
TraceSpewW(L"Funct: SendOutExpertTicket %s", bstrTicket?bstrTicket:L"NULL");
HRESULT hr = S_OK;
CComBSTR bstrPublicKeyBlob;
CComBSTR bstrBlob;
DWORD dwCount=0, dwLen;
TCHAR szHeader[100];
if (!m_pSessObj)
return FALSE;
// 1. Get public blob.
if (FAILED(hr = InitCSP()))
goto done;
// 2. Create Blob with predefined format.
if(FAILED(hr = GetKeyExportString(m_hPublicKey, 0, PUBLICKEYBLOB, &bstrPublicKeyBlob, &dwCount)))
goto done;
dwLen = wcslen(bstrTicket);
wsprintf(szHeader, _T("%d;ET="), dwLen+3);
bstrBlob = szHeader;
bstrBlob.AppendBSTR(bstrTicket);
if (dwCount)
{
wsprintf(szHeader, _T("%d;PK="), dwCount+3);
bstrBlob.Append(szHeader);
bstrBlob += bstrPublicKeyBlob;
}
// 3. Send it out.
hr = m_pSessObj->SendContextData((BSTR)bstrBlob);
if (FAILED(hr))
goto done;
DoSessionStatus(RA_IM_EXPERT_TICKET_OUT);
done:
return hr;
}
HRESULT CIMSession::GetKeyExportString(HCRYPTKEY hKey, HCRYPTKEY hExKey, DWORD dwBlobType, BSTR* pBlob, DWORD *pdwCount)
{
HRESULT hr = S_OK;
DWORD dwKeyLen;
LPBYTE pBinBuf = NULL;
if (!pBlob)
return FALSE;
// Calculate how big the destination buffer size we need.
if (!CryptExportKey(hKey, hExKey, dwBlobType, 0, NULL, &dwKeyLen))
{
DEBUG_MSG(_T("Can't calculate public key length"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
pBinBuf = (LPBYTE)malloc(dwKeyLen);
if (!pBinBuf)
{
hr = E_OUTOFMEMORY;
goto done;
}
if (!CryptExportKey(hKey, hExKey, dwBlobType, 0, pBinBuf, &dwKeyLen))
{
DEBUG_MSG(_T("Can't write public key to blob"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
if (FAILED(hr=BinaryToString(pBinBuf, dwKeyLen, pBlob, pdwCount)))
goto done;
done:
if (pBinBuf)
free(pBinBuf);
return hr;
}
HRESULT CIMSession::BinaryToString(LPBYTE pBinBuf, DWORD dwLen, BSTR* pBlob, DWORD *pdwCount)
{
HRESULT hr = S_OK;
TCHAR *pBuf = NULL;
CComBSTR bstrBlob;
if (!pBlob)
return FALSE;
if (!CryptBinaryToString(pBinBuf, dwLen, CRYPT_STRING_BASE64, NULL, pdwCount))
{
DEBUG_MSG(_T("Can't calculate string len for blob converstion"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
if (NULL == (pBuf = (TCHAR*)malloc(*pdwCount * sizeof(TCHAR))))
{
hr = E_OUTOFMEMORY;
goto done;
}
if (!CryptBinaryToString(pBinBuf, dwLen, CRYPT_STRING_BASE64, pBuf, pdwCount))
{
DEBUG_MSG(_T("Can't convert key blob to string"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
bstrBlob.Append(pBuf);
*pBlob = bstrBlob.Detach();
done:
if (pBuf)
free(pBuf);
return hr;
}
HRESULT CIMSession::StringToBinary(BSTR pBlob, DWORD dwCount, LPBYTE *ppBuf, DWORD* pdwLen)
{
HRESULT hr=S_OK;
DWORD dwSkip, dwFlag;
if (!CryptStringToBinary(pBlob, dwCount, CRYPT_STRING_BASE64, NULL, pdwLen, &dwSkip, &dwFlag))
{
DEBUG_MSG(_T("Can't calculate needed binary buffer length"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
*ppBuf = (LPBYTE)malloc(*pdwLen);
if (!CryptStringToBinary(pBlob, dwCount, CRYPT_STRING_BASE64,
*ppBuf, pdwLen, &dwSkip, &dwFlag))
{
DEBUG_MSG(_T("Can't convert to binary blob"));
hr = HRESULT_FROM_WIN32(GetLastError());
goto done;
}
done:
return hr;
}
HRESULT CIMSession::UninitObjects()
{
HRESULT hr = S_OK;
// Make sure we don't follow the null pointer
if (m_pSessMgr && m_pSessionMgrEvent)
{
m_pSessionMgrEvent->Unadvise();
}
return hr;
}
HRESULT UnlockSession(CIMSession* pThis)
{
TraceSpew(_T("Funct: UnlockSession"));
HRESULT hr = S_OK;
MSG msg;
CComPtr<IConnectionPointContainer> cpCPC;
CComPtr<IConnectionPoint> cpCP;
LockStatus ls=LOCK_NOTINITIALIZED;
BOOL bRet;
assert(pThis->m_pSessMgr == NULL);
hr = CoCreateInstance( CLSID_MsgrSessionManager,
NULL,
CLSCTX_LOCAL_SERVER,
IID_IMsgrSessionManager,
(LPVOID*)&pThis->m_pSessMgr);
if (FAILED_HR(_T("CoCreate CLSID_MsgrSessionManager failed: %s"), hr))
goto done;
// 2. Create Lock object
hr = pThis->m_pSessMgr->QueryInterface(IID_IMsgrLock, (LPVOID*)&pThis->m_pMsgrLockKey);
if (FAILED_HR(_T("Can't create MsgrLock object: %s"), hr))
goto done;
// 2. Hook SessionManager events
pThis->m_pSessionMgrEvent = new CSessionMgrEvent(pThis);
if (!pThis->m_pSessionMgrEvent)
{
hr = E_OUTOFMEMORY;
goto done;
}
pThis->m_pSessionMgrEvent->AddRef();
hr = pThis->m_pSessMgr->QueryInterface(IID_IConnectionPointContainer, (void**)&cpCPC);
if (FAILED_HR(_T("QI: IConnectionPointContainer of SessionMgr failed %s"), hr))
goto done;
hr = cpCPC->FindConnectionPoint(DIID_DMsgrSessionManagerEvents, &cpCP);
if (FAILED_HR(_T("FindConnectionPoint DMessengerEvents failed %s"), hr))
goto done;
hr = pThis->m_pSessionMgrEvent->Advise(cpCP);
if (FAILED(hr))
goto done;
g_hWnd = InitInstance(g_hInstance, 0);
// Set up credential with server.
hr = pThis->m_pMsgrLockKey->get_Status(&ls);
if (ls == LOCK_UNLOCKED)
{
hr = S_OK;
goto done;
}
SetTimer(g_hWnd, RA_TIMER_UNLOCK_ID, RA_TIMEOUT_UNLOCK, NULL); // 3 minutes.
// Send challenge
pThis->DoSessionStatus(RA_IM_UNLOCK_WAIT);
hr = pThis->m_pMsgrLockKey->RequestChallenge(70); // Random number: 70
if (FAILED_HR(_T("RequestChallenge failed: %s"), hr))
goto done;
// Wait until permission get granted or timeout.
while (bRet = GetMessage(&msg, NULL, 0, 0))
{
if (msg.message == WM_APP_LOCKNOTIFY_INTHREAD)
{
hr = ((BOOL)msg.wParam)?S_OK:E_FAIL;
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
done:
if (g_hWnd)
{
// kill control window.
DestroyWindow(g_hWnd);
g_hWnd = NULL;
}
TraceSpew(_T("Leave UnlockSession hr=%s"),GetStringFromError(hr));
return hr;
}
HWND InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = NULL;
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = NULL;
RegisterClassEx(&wcex);
hWnd = CreateWindow(szWindowClass, TEXT("Remote Assistance"), WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT, 500, 500, NULL, NULL, hInstance, NULL);
return hWnd;
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
TraceSpew(_T("WndProc: WM_CREATE called"));
g_hWnd = hWnd;
break;
case WM_TIMER:
{
if (wParam == RA_TIMER_UNLOCK_ID)
{
TraceSpew(_T("WndProc: WM_TIMER RA_TIMER_UNLOCK_ID fired"));
PostMessage(NULL, WM_APP_LOCKNOTIFY_INTHREAD, (WPARAM)FALSE, NULL);
}
}
break;
case WM_APP_LOCKNOTIFY:
{
//PostQuitMessage(0); // Used for single thread
TraceSpew(_T("WndProc: WM_APP_LOCKNOTIFY fired"));
KillTimer(g_hWnd, RA_TIMER_UNLOCK_ID);
PostMessage(NULL, WM_APP_LOCKNOTIFY_INTHREAD, wParam, lParam);
}
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}