WindowsXP-SP1/termsrv/remdsk/common/remotedesktoputils.cpp
2020-09-30 16:53:49 +02:00

929 lines
20 KiB
C++

/*++
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 <winsock2.h>
#include <RemoteDesktop.h>
#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 <machineAddressList>\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\<module name>,
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;
}