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