WindowsXP-SP1/termsrv/winsta/server/helpasst.cpp
2020-09-30 16:53:49 +02:00

796 lines
20 KiB
C++

/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
helpass.c
Abstract:
Salem related function.
Author:
HueiWang 4/26/2000
--*/
#define LSCORE_NO_ICASRV_GLOBALS
#include "precomp.h"
#include <tdi.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include "tsremdsk.h"
#include "sessmgr.h"
#include "sessmgr_i.c"
extern "C"
NTSTATUS
xxxQueryRemoteAddress(
PWINSTATION pWinStation,
PWINSTATIONREMOTEADDRESS pRemoteAddress
);
HRESULT
__LogSalemEvent(
IN IRemoteDesktopHelpSessionMgr* iSessMgr,
IN ULONG eventType,
IN ULONG eventCode,
IN int numStrings,
IN BSTR EventStrings[]
);
//
// Function copied from atlconv.h, we don't include
// any ATL header in termsrv.
//
BSTR A2WBSTR(LPCSTR lp)
{
if (lp == NULL)
return NULL;
BSTR str = NULL;
int nConvertedLen = MultiByteToWideChar(
GetACP(), 0, lp,
-1, NULL, NULL)-1;
str = ::SysAllocStringLen(NULL, nConvertedLen);
if (str != NULL)
{
MultiByteToWideChar(GetACP(), 0, lp, -1,
str, nConvertedLen);
}
return str;
}
NTSTATUS
TSHelpAssistantQueryLogonCredentials(
ExtendedClientCredentials* pCredential
)
/*++
Description:
Retrieve HelpAssistant logon credential, routine first retrieve
infor passed from client and then decrypt password
Parameters:
pWinStation : Pointer to WINSTATION
pCredential : Pointer to ExtendedClientCredentials to receive HelpAssistant
credential.
Returns:
STATUS_SUCCESS or STATUS_INVALID_PARAMETER
--*/
{
LPWSTR pszHelpAssistantPassword = NULL;
NTSTATUS Status;
LPWSTR pszHelpAssistantAccountName = NULL;
LPWSTR pszHelpAssistantAccountDomain = NULL;
if( pCredential )
{
ZeroMemory( pCredential, sizeof(ExtendedClientCredentials) );
Status = TSGetHelpAssistantAccountName(&pszHelpAssistantAccountDomain, &pszHelpAssistantAccountName);
if( ERROR_SUCCESS == Status )
{
// make sure we don't overwrite buffer, length can't be
// more than 255 characters.
lstrcpyn(
pCredential->UserName,
pszHelpAssistantAccountName,
EXTENDED_USERNAME_LEN
);
lstrcpyn(
pCredential->Domain,
pszHelpAssistantAccountDomain,
EXTENDED_DOMAIN_LEN
);
Status = TSGetHelpAssistantAccountPassword( &pszHelpAssistantPassword );
if( ERROR_SUCCESS == Status )
{
ASSERT( lstrlen(pszHelpAssistantPassword) < EXTENDED_PASSWORD_LEN );
if( lstrlen(pszHelpAssistantPassword) < EXTENDED_PASSWORD_LEN )
{
// Password contains encrypted version, overwrite with
// clear text.
lstrcpy( pCredential->Password, pszHelpAssistantPassword );
}
else
{
Status = STATUS_INVALID_PARAMETER;
}
}
}
}
else
{
ASSERT( FALSE );
Status = STATUS_INVALID_PARAMETER;
}
if( NULL != pszHelpAssistantAccountDomain )
{
LocalFree( pszHelpAssistantAccountDomain );
}
if( NULL != pszHelpAssistantAccountName )
{
LocalFree(pszHelpAssistantAccountName);
}
if( NULL != pszHelpAssistantPassword )
{
LocalFree( pszHelpAssistantPassword );
}
return Status;
}
BOOL
TSIsSessionHelpSession(
PWINSTATION pWinStation,
BOOL* pValid
)
/*++
Routine Description:
Determine if a session is HelpAssistant session.
Parameters:
pWinStation : Pointer to WINSTATION structure.
pValid : Optional Pointer to BOOL to receive status of ticket,
TRUE of ticket is valid, FALSE if ticket is invalid or
help is disabled.
Returns:
TRUE/FALSE Funtion return TRUE even if ticket is invalid, caller
should check pValid to determine if ticket is valid or not.
--*/
{
BOOL bReturn;
BOOL bValidHelpSession = FALSE;
if( NULL == pWinStation )
{
ASSERT( NULL != pWinStation );
SetLastError( ERROR_INVALID_PARAMETER );
bReturn = FALSE;
goto CLEANUPANDEXIT;
}
if( pWinStation->Client.ProtocolType != PROTOCOL_RDP )
{
//
// HelpAssistant is RDP specific and not on console
DBGPRINT( ("TermSrv : HelpAssistant protocol type not RDP \n") );
bValidHelpSession = FALSE;
bReturn = FALSE;
}
else if( WSF_ST_HELPSESSION_NOTHELPSESSION & pWinStation->StateFlags )
{
// We are sure that this session is not HelpAssistant Session.
bReturn = FALSE;
bValidHelpSession = FALSE;
}
else if( WSF_ST_HELPSESSION_HELPSESSIONINVALID & pWinStation->StateFlags )
{
// Help assistant logon but password or ticket ID is invalid
bReturn = TRUE;
bValidHelpSession = FALSE;
}
else if( WSF_ST_HELPSESSION_HELPSESSION & pWinStation->StateFlags )
{
// We are sure this is help assistant logon
bReturn = TRUE;
bValidHelpSession = TRUE;
}
else
{
//
// Clear RA state flags.
//
pWinStation->StateFlags &= ~WSF_ST_HELPSESSION_FLAGS;
if( !pWinStation->Client.UserName[0] || !pWinStation->Client.Password[0] ||
!pWinStation->Client.WorkDirectory[0] )
{
bReturn = FALSE;
bValidHelpSession = FALSE;
pWinStation->StateFlags |= WSF_ST_HELPSESSION_NOTHELPSESSION;
}
else
{
//
// TermSrv might call this routine with data send from client,
// client always send hardcoded SALEMHELPASSISTANTACCOUNT_NAME
//
if( lstrcmpi( pWinStation->Client.UserName, SALEMHELPASSISTANTACCOUNT_NAME ) )
{
bReturn = FALSE;
bValidHelpSession = FALSE;
pWinStation->StateFlags |= WSF_ST_HELPSESSION_NOTHELPSESSION;
goto CLEANUPANDEXIT;
}
//
// this is helpassistant login.
//
bReturn = TRUE;
//
// Check if machine policy restrict help or
// in Help mode, deny access if not.
//
if( FALSE == TSIsMachinePolicyAllowHelp() || FALSE == TSIsMachineInHelpMode() )
{
bValidHelpSession = FALSE;
pWinStation->StateFlags |= WSF_ST_HELPSESSION_HELPSESSIONINVALID;
goto CLEANUPANDEXIT;
}
if( TSVerifyHelpSessionAndLogSalemEvent(pWinStation) )
{
bValidHelpSession = TRUE;
pWinStation->StateFlags |= WSF_ST_HELPSESSION_HELPSESSION;
}
else
{
//
// Either ticket is invalid or expired.
//
bValidHelpSession = FALSE;
pWinStation->StateFlags |= WSF_ST_HELPSESSION_HELPSESSIONINVALID;
}
}
}
CLEANUPANDEXIT:
if( pValid )
{
*pValid = bValidHelpSession;
}
return bReturn;
}
DWORD WINAPI
SalemStartupThreadProc( LPVOID ptr )
/*++
Temporary code to start up Salem sessmgr, post B2 need to move sessmgr into svchost
--*/
{
HRESULT hRes = S_OK;
IRemoteDesktopHelpSessionMgr* pISessMgr = NULL;
if( TSIsMachineInSystemRestore() ) {
// Ignore value if we can restore cached LSA key.
// user can always resend ticket again as in XP.
TSSystemRestoreResetValues();
}
//
// Startup sessmgr if there is outstanding ticket and
// we just rebooted from system restore.
//
if( !TSIsMachineInHelpMode() )
{
ExitThread(hRes);
return hRes;
}
hRes = CoInitialize( NULL );
if( FAILED(hRes) )
{
DBGPRINT( ("TermSrv : TSStartupSalem() CoInitialize() failed with 0x%08x\n", hRes) );
// Failed in COM, return FALSE.
goto CLEANUPANDEXIT;
}
hRes = CoCreateInstance(
CLSID_RemoteDesktopHelpSessionMgr,
NULL,
CLSCTX_ALL,
IID_IRemoteDesktopHelpSessionMgr,
(LPVOID *) &pISessMgr
);
if( FAILED(hRes) || NULL == pISessMgr )
{
DBGPRINT( ("TermSrv : TSStartupSalem() CoCreateInstance() failed with 0x%08x\n", hRes) );
// Can't initialize sessmgr
goto CLEANUPANDEXIT;
}
CLEANUPANDEXIT:
if( NULL != pISessMgr )
{
pISessMgr->Release();
}
CoUninitialize();
ExitThread(hRes);
return hRes;
}
void
TSStartupSalem()
{
HANDLE hThread;
hThread = CreateThread( NULL, 0, SalemStartupThreadProc, NULL, 0, NULL );
if( NULL != hThread )
{
CloseHandle( hThread );
}
return;
}
BOOL
TSVerifyHelpSessionAndLogSalemEvent(
PWINSTATION pWinStation
)
/*++
Description:
Verify help session is a valid, non-expired pending help session,
log an event if help session is invalid.
Parameters:
pWinStation : Point to WINSTATION
Returns:
TRUE/FALSE
Note :
WorkDirectory is HelpSessionID and InitialProgram contains
password to pending help session
--*/
{
HRESULT hRes;
IRemoteDesktopHelpSessionMgr* pISessMgr = NULL;
BOOL bSuccess = FALSE;
BSTR bstrHelpSessId = NULL;
BSTR bstrHelpSessPwd = NULL;
WINSTATIONREMOTEADDRESS winstationRemoteAddress;
DWORD dwReturnLength;
NTSTATUS Status;
BSTR bstrExpertIpAddressFromClient = NULL;
BSTR bstrExpertIpAddressFromServer = NULL;
// only have three strings in this event
BSTR bstrEventStrings[3];
hRes = CoInitialize( NULL );
if( FAILED(hRes) )
{
DBGPRINT( ("TermSrv : TSIsHelpSessionValid() CoInitialize() failed with 0x%08x\n", hRes) );
// Failed in COM, return FALSE.
return FALSE;
}
hRes = CoCreateInstance(
CLSID_RemoteDesktopHelpSessionMgr,
NULL,
CLSCTX_ALL,
IID_IRemoteDesktopHelpSessionMgr,
(LPVOID *) &pISessMgr
);
if( FAILED(hRes) || NULL == pISessMgr )
{
DBGPRINT( ("TermSrv : TSIsHelpSessionValid() CoCreateInstance() failed with 0x%08x\n", hRes) );
// Can't initialize sessmgr
goto CLEANUPANDEXIT;
}
//
// Set the security level to impersonate. This is required by
// the session manager.
//
hRes = CoSetProxyBlanket(
(IUnknown *)pISessMgr,
RPC_C_AUTHN_DEFAULT,
RPC_C_AUTHZ_DEFAULT,
NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE
);
if( FAILED(hRes) )
{
DBGPRINT( ("TermSrv : TSIsHelpSessionValid() CoSetProxyBlanket() failed with 0x%08x\n", hRes) );
// can't impersonate, return FALSE
goto CLEANUPANDEXIT;
}
bstrHelpSessId = ::SysAllocString(pWinStation->Client.WorkDirectory);
bstrHelpSessPwd = ::SysAllocString(pWinStation->Client.InitialProgram);
if( NULL == bstrHelpSessId || NULL == bstrHelpSessPwd )
{
// We are so out of memory, treat as error
goto CLEANUPANDEXIT;
}
// Verify help session
hRes = pISessMgr->IsValidHelpSession(
bstrHelpSessId,
bstrHelpSessPwd
);
bSuccess = SUCCEEDED(hRes);
if( FALSE == bSuccess )
{
// Log invalid help ticket event here.
Status = xxxQueryRemoteAddress( pWinStation, &winstationRemoteAddress );
bstrExpertIpAddressFromClient = ::SysAllocString( pWinStation->Client.ClientAddress );
if( !NT_SUCCESS(Status) || AF_INET != winstationRemoteAddress.sin_family )
{
//
// we don't support other than IPV4 now or we failed to retrieve address
// from driver, use what's send in from client.
bstrExpertIpAddressFromServer = ::SysAllocString( pWinStation->Client.ClientAddress );
}
else
{
// refer to in_addr structure.
struct in_addr S;
S.S_un.S_addr = winstationRemoteAddress.ipv4.in_addr;
bstrExpertIpAddressFromServer = A2WBSTR( inet_ntoa(S) );
}
if( !bstrExpertIpAddressFromClient || !bstrExpertIpAddressFromServer )
{
// we are out of memory, can't log event.
goto CLEANUPANDEXIT;
}
bstrEventStrings[0] = bstrExpertIpAddressFromClient;
bstrEventStrings[1] = bstrExpertIpAddressFromServer;
bstrEventStrings[2] = bstrHelpSessId;
__LogSalemEvent(
pISessMgr,
EVENTLOG_INFORMATION_TYPE,
REMOTEASSISTANCE_EVENTLOG_TERMSRV_INVALID_TICKET,
3,
bstrEventStrings
);
}
CLEANUPANDEXIT:
if( NULL != pISessMgr )
{
pISessMgr->Release();
}
if( NULL != bstrHelpSessId )
{
::SysFreeString( bstrHelpSessId );
}
if( NULL != bstrHelpSessPwd )
{
::SysFreeString( bstrHelpSessPwd );
}
if( NULL != bstrExpertIpAddressFromClient )
{
::SysFreeString( bstrExpertIpAddressFromClient );
}
if( NULL != bstrExpertIpAddressFromServer )
{
::SysFreeString( bstrExpertIpAddressFromServer );
}
DBGPRINT( ("TermSrv : TSIsHelpSessionValid() returns 0x%08x\n", hRes) );
CoUninitialize();
return bSuccess;
}
VOID
TSLogSalemReverseConnection(
PWINSTATION pWinStation,
PICA_STACK_ADDRESS pStackAddress
)
/*++
--*/
{
HRESULT hRes;
IRemoteDesktopHelpSessionMgr* pISessMgr = NULL;
BOOL bSuccess = FALSE;
int index;
// Fours string for this event
BSTR bstrEventStrings[3];
ZeroMemory( bstrEventStrings, sizeof(bstrEventStrings) );
hRes = CoInitialize( NULL );
if( FAILED(hRes) )
{
DBGPRINT( ("TermSrv : TSLogSalemReverseConnection() CoInitialize() failed with 0x%08x\n", hRes) );
goto CLEANUPANDEXIT;
}
hRes = CoCreateInstance(
CLSID_RemoteDesktopHelpSessionMgr,
NULL,
CLSCTX_ALL,
IID_IRemoteDesktopHelpSessionMgr,
(LPVOID *) &pISessMgr
);
if( FAILED(hRes) || NULL == pISessMgr )
{
DBGPRINT( ("TermSrv : TSLogSalemReverseConnection() CoCreateInstance() failed with 0x%08x\n", hRes) );
// Can't initialize sessmgr
goto CLEANUPANDEXIT;
}
//
// Set the security level to impersonate. This is required by
// the session manager.
//
hRes = CoSetProxyBlanket(
(IUnknown *)pISessMgr,
RPC_C_AUTHN_DEFAULT,
RPC_C_AUTHZ_DEFAULT,
NULL,
RPC_C_AUTHN_LEVEL_DEFAULT,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE
);
if( FAILED(hRes) )
{
DBGPRINT( ("TermSrv : TSLogSalemReverseConnection() CoSetProxyBlanket() failed with 0x%08x\n", hRes) );
// can't impersonate, return FALSE
goto CLEANUPANDEXIT;
}
//
// sessmgr expect event string in following order
//
// IP address send from client.
// IP address that termsrv connect to, this is part of the expert connect parm.
// Help Session Ticket ID
//
bstrEventStrings[0] = ::SysAllocString( pWinStation->Client.ClientAddress );
{
struct in_addr S;
PTDI_ADDRESS_IP pIpAddress = (PTDI_ADDRESS_IP)&((PCHAR)pStackAddress)[2];
// refer to in_addr structure.
S.S_un.S_addr = pIpAddress->in_addr;
bstrEventStrings[1] = A2WBSTR( inet_ntoa(S) );
}
bstrEventStrings[2] = ::SysAllocString(pWinStation->Client.WorkDirectory);
if( NULL != bstrEventStrings[0] &&
NULL != bstrEventStrings[1] &&
NULL != bstrEventStrings[2] )
{
hRes = __LogSalemEvent(
pISessMgr,
EVENTLOG_INFORMATION_TYPE,
REMOTEASSISTANCE_EVENTLOG_TERMSRV_REVERSE_CONNECT,
3,
bstrEventStrings
);
}
CLEANUPANDEXIT:
if( NULL != pISessMgr )
{
pISessMgr->Release();
}
for(index=0; index < sizeof(bstrEventStrings)/sizeof(bstrEventStrings[0]); index++)
{
if( !bstrEventStrings[index] )
{
::SysFreeString( bstrEventStrings[index] );
}
}
DBGPRINT( ("TermSrv : TSLogSalemReverseConnection() returns 0x%08x\n", hRes) );
CoUninitialize();
return;
}
HRESULT
__LogSalemEvent(
IN IRemoteDesktopHelpSessionMgr* pISessMgr,
IN ULONG eventType,
IN ULONG eventCode,
IN int numStrings,
IN BSTR bstrEventStrings[]
)
/*++
Description:
Create a safearray and pass parameters to sessmgr.
Parameters:
Returns:
S_OK or error code.
--*/
{
HRESULT hRes = S_OK;
VARIANT EventStrings;
int index;
// we only have three string to be included in the event log.
SAFEARRAY* psa = NULL;
SAFEARRAYBOUND bounds;
BSTR* bstrArray = NULL;
bounds.cElements = numStrings;
bounds.lLbound = 0;
VariantInit(&EventStrings);
//
// Create a safearray to pass all event string
//
psa = SafeArrayCreate(VT_BSTR, 1, &bounds);
if( NULL == psa )
{
goto CLEANUPANDEXIT;
}
// Required, lock the safe array
hRes = SafeArrayAccessData(psa, (void **)&bstrArray);
if( SUCCEEDED(hRes) )
{
for(index=0; index < numStrings; index++)
{
bstrArray[index] = bstrEventStrings[index];
}
EventStrings.vt = VT_ARRAY | VT_BSTR;
EventStrings.parray = psa;
hRes = pISessMgr->LogSalemEvent(
eventType,
eventCode,
&EventStrings
);
//
// make sure we clear BSTR array or VariantClear() will invoke
// SafeArrayDestroy() which in term will invoke ::SysFreeString()
// on each BSTR.
//
for(index=0; index < numStrings; index++)
{
bstrArray[index] = NULL;
}
hRes = SafeArrayUnaccessData( psa );
ASSERT( SUCCEEDED(hRes) );
// make sure we don't destroy safe array twice, VariantClear()
// will destroy it.
psa = NULL;
}
CLEANUPANDEXIT:
hRes = VariantClear(&EventStrings);
ASSERT( SUCCEEDED(hRes) );
if( psa != NULL )
{
SafeArrayDestroy(psa);
}
return hRes;
}
HRESULT
TSRemoteAssistancePrepareSystemRestore()
/*++
Routine Description:
Prepare system for RA specific system restore, this includes RA specific encryption key,
registry settings that we need preserve.
Parameters:
None.
Returns:
S_OK or error code.
--*/
{
// Here we have different implementation for XPSP1 and .NET, on .NET, all Salem related
// stuff goes into sessmgr, that is this function will invoke sessmgr's necessay method to
// deal with system restore; however, SP1 installer does not kick off same OCMANAGER setup
// and we will also have to worry about SP1 uinstall issue. When merging two tree on longhorn
// we need to take .NET approach.
return TSSystemRestoreCacheValues();
}