/*++ 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 #include #include #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(); }