/*++ Copyright (c) 1999-2000 Microsoft Corporation Module Name: RemoteDesktopUtils Abstract: Misc. RD Utils Author: Tad Brockway 02/00 Revision History: --*/ #ifdef TRC_FILE #undef TRC_FILE #endif #define TRC_FILE "_rdsutl" #include #include #include "RemoteDesktopUtils.h" #include "base64.h" //#include "RemoteDesktopDBG.h" int GetClientmachineAddressList( OUT CComBSTR& clientmachineAddressList ) /*++ --*/ { char hostname[MAX_PATH+1]; int errCode = 0; struct hostent* pHostEnt; if( gethostname(hostname, sizeof(hostname)) != 0 ) { errCode = WSAGetLastError(); } else { pHostEnt = gethostbyname( hostname ); if( NULL != pHostEnt ) { clientmachineAddressList = pHostEnt->h_name; } else { errCode = WSAGetLastError(); } } return errCode; } BSTR CreateConnectParmsString( IN DWORD protocolType, IN CComBSTR &machineAddressList, IN CComBSTR &assistantAccount, IN CComBSTR &assistantAccountPwd, IN CComBSTR &helpSessionID, IN CComBSTR &helpSessionName, IN CComBSTR &helpSessionPwd, IN CComBSTR &protocolSpecificParms ) /*++ Routine Description: Create a connect parms string. Format is: "protocolType,machineAddressList,assistantAccount,assistantAccountPwd,helpSessionName,helpSessionPwd,protocolSpecificParms" Arguments: protocolType - Identifies the protocol type. See RemoteDesktopChannels.h machineAddressList - Identifies network address of server machine. assistantAccountName - Account name for initial log in to server machine, ignore for Whistler assistantAccountNamePwd - Password for assistantAccountName helpSessionID - Help session identifier. helpSessionName - Help session name. helpSessionPwd - Password to help session once logged in to server machine. protocolSpecificParms - Parameters specific to a particular protocol. Return Value: --*/ { CComBSTR result; WCHAR buf[256]; UNREFERENCED_PARAMETER(assistantAccount); // // Add a version stamp for our connect parm. wsprintf(buf, TEXT("%ld"), SALEM_CURRENT_CONNECTPARM_VERSION); result = buf; result += TEXT(","); wsprintf(buf, TEXT("%ld"), protocolType); result += buf; result += TEXT(","); result += machineAddressList; result += TEXT(","); result += assistantAccountPwd; result += TEXT(","); result += helpSessionID; result += TEXT(","); result += helpSessionName; result += TEXT(","); result += helpSessionPwd; if (protocolSpecificParms.Length() > 0) { result += TEXT(","); result += protocolSpecificParms; } return result.Detach(); } DWORD ParseHelpAccountName( IN BSTR helpAccount, OUT CComBSTR& machineAddressList, OUT CComBSTR& AccountName ) /*++ HelpAccount in connection parameter is \HelpAssistant. --*/ { DC_BEGIN_FN("ParseHelpAccountName"); BSTR tmp; WCHAR *tok; DWORD result = ERROR_SUCCESS; DWORD len; // // Make a copy of the input string so we can parse it. // tmp = SysAllocString(helpAccount); if (tmp == NULL) { TRC_ERR((TB, TEXT("Can't allocate parms string."))); result = ERROR_OUTOFMEMORY; goto CLEANUPANDEXIT; } machineAddressList = L""; AccountName = L""; // // Machine Name // tok = wcstok(tmp, L"\\"); if (tok != NULL) { machineAddressList = tok; } else { // // for backward compatible. // machineAddressList = L""; AccountName = helpAccount; goto CLEANUPANDEXIT; } // // Actual help assistant account name. // len = wcslen(helpAccount); if (tok < (tmp + len)) { tok += wcslen(tok); tok += 1; if (*tok != L'\0') { AccountName = tok; } } // // Help Assistant accout name must be in the string or // this is critical error. // if( AccountName.Length() == 0 ) { result = ERROR_INVALID_PARAMETER; } CLEANUPANDEXIT: if (tmp != NULL) { SysFreeString(tmp); } DC_END_FN(); return result; } DWORD ParseConnectParmsString( IN BSTR parmsString, OUT DWORD* pdwConnParmVersion, OUT DWORD *protocolType, OUT CComBSTR &machineAddressList, OUT CComBSTR &assistantAccount, OUT CComBSTR &assistantAccountPwd, OUT CComBSTR &helpSessionID, OUT CComBSTR &helpSessionName, OUT CComBSTR &helpSessionPwd, OUT CComBSTR &protocolSpecificParms ) /*++ Routine Description: Parse a connect string created by a call to CreateConnectParmsString. Arguments: Return Value: ERROR_SUCCESS on success. Otherwise, an error code is returned. --*/ { DC_BEGIN_FN("ParseConnectParmsString"); BSTR tmp; WCHAR *tok; DWORD result = ERROR_SUCCESS; DWORD len; DWORD dwVersion = 0; UNREFERENCED_PARAMETER(assistantAccount); // // Make a copy of the input string so we can parse it. // tmp = SysAllocString(parmsString); if (tmp == NULL) { TRC_ERR((TB, TEXT("Can't allocate parms string."))); result = ERROR_OUTOFMEMORY; goto CLEANUPANDEXIT; } // // Retrieve connect parm version stamp, Whistler beta 1 // connect parm does not have version stamp, bail out, // sessmgr/termsrv will wipe out pending help. // tok = wcstok(tmp, L","); if (tok != NULL) { dwVersion = _wtol(tok); } else { result = ERROR_INVALID_USER_BUFFER; goto CLEANUPANDEXIT; } if( dwVersion < SALEM_FIRST_VALID_CONNECTPARM_VERSION ) { // // connect parm is whistler beta 1 // result = ERROR_NOT_SUPPORTED; goto CLEANUPANDEXIT; } *pdwConnParmVersion = dwVersion; // // We have no use for version at this time, // future update on connect parm should // take make the necessary change // // // Protocol. // tok = wcstok(NULL, L","); if (tok != NULL) { *protocolType = _wtoi(tok); } // // Machine Name // tok = wcstok(NULL, L","); if (tok != NULL) { machineAddressList = tok; } else { result = ERROR_INVALID_USER_BUFFER; goto CLEANUPANDEXIT; } // // Assistant Account Password // tok = wcstok(NULL, L","); if (tok != NULL) { assistantAccountPwd = tok; } else { result = ERROR_INVALID_USER_BUFFER; goto CLEANUPANDEXIT; } // // Help Session ID // tok = wcstok(NULL, L","); if (tok != NULL) { helpSessionID = tok; } else { result = ERROR_INVALID_USER_BUFFER; goto CLEANUPANDEXIT; } // // Help Session Name // tok = wcstok(NULL, L","); if (tok != NULL) { helpSessionName = tok; } else { result = ERROR_INVALID_USER_BUFFER; goto CLEANUPANDEXIT; } // // Help Session Password // tok = wcstok(NULL, L","); if (tok != NULL) { helpSessionPwd = tok; } else { result = ERROR_INVALID_USER_BUFFER; goto CLEANUPANDEXIT; } // // Protocol-Specific Parms // len = wcslen(parmsString); if (tok < (tmp + len)) { tok += wcslen(tok); tok += 1; if (*tok != L'\0') { protocolSpecificParms = tok; } else { protocolSpecificParms = L""; } } else { protocolSpecificParms = L""; } CLEANUPANDEXIT: if (result != ERROR_SUCCESS) { TRC_ERR((TB, TEXT("Error parsing %s"), parmsString)); } if (tmp != NULL) { SysFreeString(tmp); } DC_END_FN(); return result; } BSTR ReallocBSTR( IN BSTR origStr, IN DWORD requiredByteLen ) /*++ Routine Description: Realloc a BSTR Arguments: Return Value: The realloc'd string on success. Otherwise, NULL is returned. --*/ { DC_BEGIN_FN("ReallocBSTR"); BSTR tmp; DWORD len; DWORD origLen; // // Allocate the new string. // tmp = SysAllocStringByteLen(NULL, requiredByteLen); if (tmp == NULL) { TRC_ERR((TB, TEXT("Failed to allocate %ld bytes."), requiredByteLen)); goto CLEANUPANDEXIT; } // // Copy data from the original string. // origLen = SysStringByteLen(origStr); len = origLen <= requiredByteLen ? origLen : requiredByteLen; memcpy(tmp, origStr, len); // // Release the old string. // SysFreeString(origStr); CLEANUPANDEXIT: DC_END_FN(); return tmp; } DWORD CreateSystemSid( PSID *ppSystemSid ) /*++ Routine Description: Create a SYSTEM SID. Arguments: Return Value: ERROR_SUCCESS on success. Otherwise, an error code is returned. --*/ { DC_BEGIN_FN("CreateSystemSid"); DWORD dwStatus = ERROR_SUCCESS; PSID pSid; SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; TRC_ASSERT(ppSystemSid != NULL, (TB, L"ppSystemSid != NULL")); if( TRUE == AllocateAndInitializeSid( &SidAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSid ) ) { *ppSystemSid = pSid; } else { dwStatus = GetLastError(); } DC_END_FN(); return dwStatus; } BOOL IsSystemToken( HANDLE TokenHandle, PSID pSystemSid ) /*++ Routine Description: Returns whether the current token is running under SYSTEM security. Arguments: TokenHandle - Param1 Thread or process token pSystemSid - System SID. Return Value: TRUE if System token. FALSE otherwise. --*/ { DC_BEGIN_FN("IsSystemToken"); BOOL Result = FALSE; ULONG ReturnLength, BufferLength; DWORD dwStatus; PTOKEN_USER pTokenUser = NULL; TRC_ASSERT(NULL != pSystemSid, (TB, L"NULL != pSystemSid")); // Get user SID. ReturnLength = 0; Result = GetTokenInformation( TokenHandle, TokenUser, NULL, 0, &ReturnLength ); if( ReturnLength == 0 ) { TRC_ERR((TB, L"GetTokenInformation: %08X", GetLastError())); Result = FALSE; CloseHandle( TokenHandle ); goto CLEANUPANDEXIT; } BufferLength = ReturnLength; pTokenUser = (PTOKEN_USER)LocalAlloc( LPTR, BufferLength ); if( pTokenUser == NULL ) { TRC_ERR((TB, L"LocalAlloc: %08X", GetLastError())); Result = FALSE; CloseHandle( TokenHandle ); goto CLEANUPANDEXIT; } Result = GetTokenInformation( TokenHandle, TokenUser, pTokenUser, BufferLength, &ReturnLength ); CloseHandle( TokenHandle ); if( TRUE == Result ) { Result = EqualSid( pTokenUser->User.Sid, pSystemSid); } else { TRC_ERR((TB, L"GetTokenInformation: %08X", GetLastError())); } CLEANUPANDEXIT: if( pTokenUser ) { LocalFree( pTokenUser ); } DC_END_FN(); return Result; } BOOL IsCallerSystem( PSID pSystemSid ) /*++ Routine Description: Returns whether the current thread is running under SYSTEM security. NOTE: Caller should be impersonated prior to invoking this function. Arguments: pSystemSid - System SID. Return Value: TRUE if System. FALSE otherwise. --*/ { DC_BEGIN_FN("IsCallerSystem"); BOOL Result; HANDLE TokenHandle; // // Open the thread token and check if System token. // Result = OpenThreadToken( GetCurrentThread(), TOKEN_QUERY, FALSE, // Use impersonation &TokenHandle ); if( TRUE == Result ) { // // This token should not be released. This function does not leak // handles. // Result = IsSystemToken(TokenHandle, pSystemSid); } else { TRC_ERR((TB, L"OpenThreadToken: %08X", GetLastError())); } DC_END_FN(); return Result; } void AttachDebugger( LPCTSTR pszDebugger ) /*++ Routine Description: Attach debugger to our process or process hosting our DLL. Parameters: pszDebugger : Debugger command, e.g. ntsd -d -g -G -p %d Returns: None. Note: Must have "-p %d" since we don't know debugger's parameter for process. --*/ { // // Attach debugger // if( !IsDebuggerPresent() ) { TCHAR szCommand[256]; PROCESS_INFORMATION ProcessInfo; STARTUPINFO StartupInfo; // // ntsd -d -g -G -p %d // wsprintf( szCommand, pszDebugger, GetCurrentProcessId() ); ZeroMemory(&StartupInfo, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); if (!CreateProcess(NULL, szCommand, NULL, NULL, FALSE, 0, NULL, NULL, &StartupInfo, &ProcessInfo)) { return; } else { CloseHandle(ProcessInfo.hProcess); CloseHandle(ProcessInfo.hThread); while (!IsDebuggerPresent()) { Sleep(500); } } } else { DebugBreak(); } return; } void AttachDebuggerIfAsked(HINSTANCE hInst) /*++ Routine Description: Check if debug enable flag in our registry HKLM\Software\Microsoft\Remote Desktop\, if enable, attach debugger to running process. Parameter : hInst : instance handle. Returns: None. --*/ { CRegKey regKey; DWORD dwStatus; TCHAR szModuleName[MAX_PATH+1]; TCHAR szFileName[MAX_PATH+1]; CComBSTR bstrRegKey(_TEXT("Software\\Microsoft\\Remote Desktop\\")); TCHAR szDebugCmd[256]; DWORD cbDebugCmd = sizeof(szDebugCmd)/sizeof(szDebugCmd[0]); dwStatus = GetModuleFileName( hInst, szModuleName, MAX_PATH+1 ); if( 0 == dwStatus ) { // // Can't attach debugger with name. // return; } _tsplitpath( szModuleName, NULL, NULL, szFileName, NULL ); bstrRegKey += szFileName; // // Check if we are asked to attach/break into debugger // dwStatus = regKey.Open( HKEY_LOCAL_MACHINE, bstrRegKey ); if( 0 != dwStatus ) { return; } dwStatus = regKey.QueryValue( szDebugCmd, _TEXT("Debugger"), &cbDebugCmd ); if( 0 != dwStatus || cbDebugCmd > 200 ) { // 200 chars is way too much for debugger command. return; } AttachDebugger( szDebugCmd ); return; } DWORD HashSecurityData( IN PBYTE const pbData, IN DWORD cbData, OUT CComBSTR& bstrHashedData ) /*++ Routine Description: Hash a blob of data and return hased data in BSTR Parameters: pbData : Pointer to data to be hashed. cbData : Size of data to be hashed. bstrHashedData : Return hashed data in BSTR form. Returns: ERROR_SUCCESS or error code. --*/ { DC_BEGIN_FN("HashSecurityData"); DWORD dwStatus; LPSTR pbEncodedData = NULL; DWORD cbEncodedData = 0; PBYTE pbHashedData = NULL; DWORD cbHashedData = 0; DWORD dwSize; HCRYPTPROV hCryptProv = NULL; HCRYPTHASH hHash = NULL; BOOL bSuccess; bSuccess = CryptAcquireContext( &hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ); if( FALSE == bSuccess ) { dwStatus = GetLastError(); TRC_ERR((TB, L"CryptAcquireContext: %08X", dwStatus)); goto CLEANUPANDEXIT; } bSuccess = CryptCreateHash( hCryptProv, CALG_SHA1, 0, 0, &hHash ); if( FALSE == bSuccess ) { dwStatus = GetLastError(); TRC_ERR((TB, L"CryptCreateHash: %08X", dwStatus)); goto CLEANUPANDEXIT; } bSuccess = CryptHashData( hHash, pbData, cbData, 0 ); if( FALSE == bSuccess ) { dwStatus = GetLastError(); TRC_ERR((TB, L"CryptHashData: %08X", dwStatus)); goto CLEANUPANDEXIT; } dwSize = sizeof( cbHashedData ); bSuccess = CryptGetHashParam( hHash, HP_HASHSIZE, (PBYTE)&cbHashedData, &dwSize, 0 ); if( FALSE == bSuccess ) { dwStatus = GetLastError(); TRC_ERR((TB, L"CryptGetHashParam with HP_HASHSIZE : %08X", dwStatus)); goto CLEANUPANDEXIT; } pbHashedData = (PBYTE)LocalAlloc(LPTR, cbHashedData); if( NULL == pbHashedData ) { dwStatus = GetLastError(); goto CLEANUPANDEXIT; } bSuccess = CryptGetHashParam( hHash, HP_HASHVAL, pbHashedData, &cbHashedData, 0 ); if( FALSE == bSuccess ) { dwStatus = GetLastError(); TRC_ERR((TB, L"CryptGetHashParam with HP_HASHVAL : %08X", dwStatus)); goto CLEANUPANDEXIT; } // // Hash data and convert to string form. // dwStatus = LSBase64EncodeA( pbHashedData, cbHashedData, NULL, &cbEncodedData ); if( ERROR_SUCCESS != dwStatus ) { TRC_ERR((TB, L"LSBase64EncodeA : %08X", dwStatus)); goto CLEANUPANDEXIT; } pbEncodedData = (LPSTR) LocalAlloc( LPTR, cbEncodedData+1 ); if( NULL == pbEncodedData ) { dwStatus = GetLastError(); goto CLEANUPANDEXIT; } dwStatus = LSBase64EncodeA( pbHashedData, cbHashedData, pbEncodedData, &cbEncodedData ); if( ERROR_SUCCESS == dwStatus ) { // // Base64 encoding always add '\r', '\n' at the end, // remove it // if( pbEncodedData[cbEncodedData - 1] == '\n' && pbEncodedData[cbEncodedData - 2] == '\r' ) { pbEncodedData[cbEncodedData - 2] = 0; cbEncodedData -= 2; } bstrHashedData = pbEncodedData; } else { TRC_ERR((TB, L"LSBase64EncodeA : %08X", dwStatus)); } CLEANUPANDEXIT: if( NULL != pbEncodedData ) { LocalFree( pbEncodedData ); } if( NULL != pbHashedData ) { LocalFree( pbHashedData ); } if( NULL != hHash ) { CryptDestroyHash( hHash ); } if( NULL != hCryptProv ) { CryptReleaseContext( hCryptProv, 0 ); } DC_END_FN(); return dwStatus; }