789 lines
24 KiB
C++
789 lines
24 KiB
C++
|
//
|
||
|
// rmigrate.cpp
|
||
|
//
|
||
|
// Implementation of CTscRegMigrate
|
||
|
//
|
||
|
// CTscRegMigrate migrates Tsc settings from the registry
|
||
|
// to .RDP files
|
||
|
//
|
||
|
// Copyright(C) Microsoft Corporation 2000
|
||
|
// Author: Nadim Abdo (nadima)
|
||
|
//
|
||
|
//
|
||
|
|
||
|
#include "stdafx.h"
|
||
|
#define TRC_GROUP TRC_GROUP_UI
|
||
|
#define TRC_FILE "rmigrate.cpp"
|
||
|
#include <atrcapi.h>
|
||
|
|
||
|
#include "rmigrate.h"
|
||
|
#include "autreg.h"
|
||
|
#include "rdpfstore.h"
|
||
|
#include "sh.h"
|
||
|
|
||
|
#ifdef OS_WINCE
|
||
|
#include <ceconfig.h>
|
||
|
#endif
|
||
|
|
||
|
#define TSC_SETTINGS_REG_ROOT TEXT("Software\\Microsoft\\Terminal Server Client\\")
|
||
|
|
||
|
#ifdef OS_WINCE
|
||
|
#define WBT_SETTINGS TEXT("WBT\\Settings")
|
||
|
#endif
|
||
|
|
||
|
CTscRegMigrate::CTscRegMigrate()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
CTscRegMigrate::~CTscRegMigrate()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Migrates all tsc settings to files in szRootDirectory
|
||
|
//
|
||
|
BOOL CTscRegMigrate::MigrateAll(LPTSTR szRootDirectory)
|
||
|
{
|
||
|
DC_BEGIN_FN("MigrateAll");
|
||
|
TRC_ASSERT(szRootDirectory,
|
||
|
(TB,_T("szRootDirectory is NULL")));
|
||
|
TCHAR szFileName[MAX_PATH*2];
|
||
|
TCHAR szKeyName[MAX_PATH+1];
|
||
|
BOOL fCreatedRootDir = FALSE;
|
||
|
|
||
|
if(szRootDirectory)
|
||
|
{
|
||
|
|
||
|
//
|
||
|
// Enumerate and migrate all the TS sessions under HKCU
|
||
|
//
|
||
|
HKEY hRootKey;
|
||
|
LONG rc = RegOpenKeyEx(HKEY_CURRENT_USER,
|
||
|
TSC_SETTINGS_REG_ROOT,
|
||
|
0,
|
||
|
KEY_READ,
|
||
|
&hRootKey);
|
||
|
if(ERROR_SUCCESS == rc && hRootKey)
|
||
|
{
|
||
|
DWORD dwIndex = 0;
|
||
|
for(;;)
|
||
|
{
|
||
|
DWORD cName = sizeof(szKeyName)/sizeof(TCHAR) - 1;
|
||
|
FILETIME ft;
|
||
|
rc = RegEnumKeyEx(hRootKey,
|
||
|
dwIndex,
|
||
|
szKeyName,
|
||
|
&cName,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
NULL,
|
||
|
&ft);
|
||
|
if(ERROR_SUCCESS == rc)
|
||
|
{
|
||
|
//
|
||
|
// Ughh..hackalicious, don't migrate
|
||
|
// the Trace subkey. or the 'Default'
|
||
|
// or LocalDevices
|
||
|
// subkey as default connectoids
|
||
|
// always use new settings
|
||
|
//
|
||
|
if(_tcscmp(szKeyName, TEXT("Trace")) &&
|
||
|
_tcscmp(szKeyName, SH_DEFAULT_REG_SESSION) &&
|
||
|
_tcsicmp(szKeyName, REG_SECURITY_FILTER_SECTION))
|
||
|
{
|
||
|
_tcscpy(szFileName, szRootDirectory);
|
||
|
_tcscat(szFileName, szKeyName);
|
||
|
_tcscat(szFileName, RDP_FILE_EXTENSION);
|
||
|
|
||
|
if (!fCreatedRootDir)
|
||
|
{
|
||
|
//
|
||
|
// Only create RD dir if there are keys to migrate
|
||
|
//
|
||
|
if(CSH::SH_CreateDirectory(szRootDirectory))
|
||
|
{
|
||
|
fCreatedRootDir = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRC_ERR((TB, _T("Error creating directory %s"),szRootDirectory));
|
||
|
RegCloseKey(hRootKey);
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//Carry on migrating whether this migrate
|
||
|
//fails or not
|
||
|
CRdpFileStore rdpf;
|
||
|
if(rdpf.OpenStore( szFileName ) )
|
||
|
{
|
||
|
if(!MigrateSession(szKeyName,
|
||
|
&rdpf,
|
||
|
TRUE))
|
||
|
{
|
||
|
TRC_ERR((TB,
|
||
|
_T("Migrate failed session %s - file %s"),
|
||
|
szKeyName, szFileName));
|
||
|
}
|
||
|
|
||
|
if(rdpf.CommitStore())
|
||
|
{
|
||
|
rdpf.CloseStore();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//next
|
||
|
dwIndex++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//done enum
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
rc = RegCloseKey(hRootKey);
|
||
|
if(ERROR_SUCCESS == rc)
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRC_ERR((TB,_T("RegCloseKey failed - err:%d"),
|
||
|
GetLastError()));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRC_ERR((TB,_T("Error opening tsc reg key")));
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
DC_END_FN();
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Migrates settings in registry to a settings store
|
||
|
// params:
|
||
|
// szSessionName - session to migrate
|
||
|
// pSetStore - settings store to dump the new settings in
|
||
|
// fDeleteUnsafeRegKeys - set to TRUE to delete old regkeys after migration
|
||
|
//
|
||
|
BOOL CTscRegMigrate::MigrateSession(LPTSTR szSessionName, ISettingsStore* pStore,
|
||
|
BOOL fDeleteUnsafeRegKeys)
|
||
|
{
|
||
|
DC_BEGIN_FN("MigrateSession");
|
||
|
TCHAR szRegSection[MAX_PATH];
|
||
|
|
||
|
//
|
||
|
// Sessions are migrated by pulling all the registry settings
|
||
|
// for a session and flattening them into a settings store
|
||
|
//
|
||
|
// In the registry case we used to look for settings under HKCU
|
||
|
// first, if they were not there we'd try HKLM.
|
||
|
//
|
||
|
// The migrate code enumerates all values in the registry under
|
||
|
// a named session, first for HKLM then for HKCU. By writing the HKCU
|
||
|
// settings last, they overwrite any exising HKLM settings, giving
|
||
|
// the correct precedence.
|
||
|
//
|
||
|
// Certain subfolders such as HOTKEYS subfolders and ADDINS are not migrated
|
||
|
// as those values always come from the registry.
|
||
|
//
|
||
|
|
||
|
TRC_ASSERT(szSessionName && pStore,
|
||
|
(TB,_T("Invalid params to MigrateSession")));
|
||
|
TRC_ASSERT(pStore->IsOpenForWrite(),
|
||
|
(TB,_T("Settings store not open for write")));
|
||
|
|
||
|
if(pStore && pStore->IsOpenForWrite() && szSessionName)
|
||
|
{
|
||
|
if(!_tcsicmp(szSessionName,SH_DEFAULT_REG_SESSION))
|
||
|
{
|
||
|
TRC_ALT((TB,_T("Never migrate 'Default' session")));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
_tcscpy(szRegSection, TSC_SETTINGS_REG_ROOT);
|
||
|
_tcsncat(szRegSection, szSessionName, SIZECHAR(szRegSection) -
|
||
|
SIZECHAR(TSC_SETTINGS_REG_ROOT));
|
||
|
//
|
||
|
// Doesn't matter if HKLM migrate fails
|
||
|
// In face it is pretty common because it is usually
|
||
|
// not even present
|
||
|
//
|
||
|
MigrateHiveSettings(HKEY_LOCAL_MACHINE,
|
||
|
szRegSection,
|
||
|
pStore);
|
||
|
|
||
|
if(MigrateHiveSettings(HKEY_CURRENT_USER,
|
||
|
szRegSection,
|
||
|
pStore))
|
||
|
{
|
||
|
#ifdef OS_WINCE
|
||
|
|
||
|
// For a WBT configuration, read some additional registry entries
|
||
|
// which are common for all the sessions.
|
||
|
if (g_CEConfig == CE_CONFIG_WBT)
|
||
|
{
|
||
|
_tcscpy(szRegSection, TSC_SETTINGS_REG_ROOT);
|
||
|
_tcsncat(szRegSection, WBT_SETTINGS, SIZECHAR(szRegSection) -
|
||
|
SIZECHAR(TSC_SETTINGS_REG_ROOT));
|
||
|
|
||
|
//
|
||
|
// Doesn't matter if HKLM migrate fails
|
||
|
// In face it is pretty common because it is usually
|
||
|
// not even present
|
||
|
///
|
||
|
if (!MigrateHiveSettings(HKEY_LOCAL_MACHINE,
|
||
|
szRegSection,
|
||
|
pStore))
|
||
|
{
|
||
|
TRC_ERR((TB,_T("Unable to read the common settings for WBT")));
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
#ifndef OS_WINCE
|
||
|
if (!ConvertPasswordFormat( pStore ))
|
||
|
{
|
||
|
TRC_ERR((TB,_T("ConvertPasswordFormat failed")));
|
||
|
return FALSE;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
//
|
||
|
// Flag controlled as we only want to do this when migrating all settings
|
||
|
// not necessarily when auto-migrating single settings
|
||
|
//
|
||
|
if (fDeleteUnsafeRegKeys) {
|
||
|
//
|
||
|
// After all the migration, delete unsafe entries in the registry
|
||
|
//
|
||
|
RemoveUnsafeRegEntries(HKEY_LOCAL_MACHINE,
|
||
|
szRegSection);
|
||
|
RemoveUnsafeRegEntries(HKEY_CURRENT_USER,
|
||
|
szRegSection);
|
||
|
}
|
||
|
|
||
|
return MungeForWin2kDefaults(pStore);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
DC_END_FN();
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Migrate settings from hKey\szRootName to the settings store (pSto)
|
||
|
// It would have been cool if this function could have been completely
|
||
|
// generic, but it has to have specific knowledge of tsc registry layout
|
||
|
// because of how mstsc5.0 bogusly special cased so many things. E.g
|
||
|
// user name is stored as unicode in a binary blob.
|
||
|
//
|
||
|
BOOL CTscRegMigrate::MigrateHiveSettings(HKEY hKey,
|
||
|
LPCTSTR szRootName,
|
||
|
ISettingsStore* pSto)
|
||
|
{
|
||
|
DC_BEGIN_FN("MigrateHiveSettings");
|
||
|
USES_CONVERSION;
|
||
|
|
||
|
HKEY rootKey;
|
||
|
LONG rc;
|
||
|
BOOL fRet = FALSE;
|
||
|
rc = RegOpenKeyEx( hKey,
|
||
|
szRootName,
|
||
|
0,
|
||
|
KEY_READ | KEY_QUERY_VALUE,
|
||
|
&rootKey);
|
||
|
if(ERROR_SUCCESS == rc)
|
||
|
{
|
||
|
//
|
||
|
// Enumerate all the values under this key
|
||
|
//
|
||
|
DWORD dwIndex = 0;
|
||
|
for(;;)
|
||
|
{
|
||
|
TCHAR szValueName[MAX_PATH];
|
||
|
DWORD dwValueLen = MAX_PATH;
|
||
|
DWORD dwType;
|
||
|
BYTE buf[MAX_PATH];
|
||
|
DWORD dwBufLen = MAX_PATH;
|
||
|
|
||
|
//
|
||
|
// It is important to zero the buf
|
||
|
// because we read some REG_BINARY's that
|
||
|
// are really 0 encoded unicode strings
|
||
|
// but tsc4 and 5 had no trailing 0's.
|
||
|
//
|
||
|
memset(buf, 0, sizeof(buf));
|
||
|
|
||
|
rc =RegEnumValue( rootKey,
|
||
|
dwIndex,
|
||
|
szValueName,
|
||
|
&dwValueLen,
|
||
|
NULL, //reserved
|
||
|
&dwType,
|
||
|
(PBYTE)&buf, //data buffer
|
||
|
&dwBufLen);
|
||
|
if(ERROR_SUCCESS == rc)
|
||
|
{
|
||
|
switch(dwType)
|
||
|
{
|
||
|
case REG_DWORD:
|
||
|
{
|
||
|
// Store as int
|
||
|
UINT value = (UINT)(*((LPDWORD)buf));
|
||
|
fRet = pSto->WriteInt(szValueName,
|
||
|
-1, //default ignored
|
||
|
value,
|
||
|
TRUE); //always write
|
||
|
if(!fRet)
|
||
|
{
|
||
|
DC_QUIT;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case REG_SZ:
|
||
|
{
|
||
|
if(FilterStringMigrate(szValueName))
|
||
|
{
|
||
|
fRet = pSto->WriteString(szValueName,
|
||
|
NULL, //no default
|
||
|
(LPTSTR)buf,
|
||
|
TRUE); //always write
|
||
|
if(!fRet)
|
||
|
{
|
||
|
DC_QUIT;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case REG_BINARY:
|
||
|
{
|
||
|
//
|
||
|
// This is where things get yucky
|
||
|
// some settings e.g UserName are stored
|
||
|
// as 'binary' when they are really unicode strings
|
||
|
// No choice but to look those up.
|
||
|
//
|
||
|
fRet = FALSE;
|
||
|
if(MigrateAsRealBinary(szValueName))
|
||
|
{
|
||
|
fRet = pSto->WriteBinary(szValueName,
|
||
|
(PBYTE)buf,
|
||
|
dwBufLen);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// The binary blob is really a unicode string
|
||
|
//
|
||
|
LPTSTR szString = W2T((LPWSTR)buf);
|
||
|
if( szString)
|
||
|
{
|
||
|
//
|
||
|
// If things weren't yucky enough...
|
||
|
// strip out the " 50" suffix if it is
|
||
|
// present
|
||
|
//
|
||
|
LPTSTR szSuffix = _tcsstr(szValueName,
|
||
|
TEXT(" 50"));
|
||
|
if(szSuffix)
|
||
|
{
|
||
|
*szSuffix = 0;
|
||
|
}
|
||
|
fRet = pSto->WriteString(szValueName,
|
||
|
NULL, //no default
|
||
|
szString,
|
||
|
TRUE); //always write
|
||
|
}
|
||
|
}
|
||
|
if(!fRet)
|
||
|
{
|
||
|
DC_QUIT;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
//Keep enumerating
|
||
|
dwIndex++;
|
||
|
}
|
||
|
else if(ERROR_NO_MORE_ITEMS == rc)
|
||
|
{
|
||
|
fRet = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRC_ERR((TB,_T("RegEnumValue failed - err:%d"),
|
||
|
GetLastError()));
|
||
|
fRet = FALSE;
|
||
|
break;
|
||
|
}
|
||
|
} //for(;;)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRC_ERR((TB,_T("Failed to open reg key - err:%d"),
|
||
|
GetLastError()));
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
DC_EXIT_POINT:
|
||
|
if(ERROR_SUCCESS != RegCloseKey(rootKey))
|
||
|
{
|
||
|
TRC_ERR((TB,_T("RegCloseKey failed - err:%d"),
|
||
|
GetLastError()));
|
||
|
}
|
||
|
|
||
|
DC_END_FN();
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return true if the name (szName) should be migrated as
|
||
|
// a real binary blob.
|
||
|
//
|
||
|
BOOL CTscRegMigrate::MigrateAsRealBinary(LPCTSTR szName)
|
||
|
{
|
||
|
DC_BEGIN_FN("MigrateAsRealBinary");
|
||
|
|
||
|
//
|
||
|
// In Tsc4 and Tsc5, only the password/salt fields
|
||
|
// were real binary blobs, all other REG_BINARY blobs
|
||
|
// are really just unicode strings.
|
||
|
//
|
||
|
if(!_tcscmp(szName, UTREG_UI_PASSWORD50))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if(!_tcscmp(szName, UTREG_UI_PASSWORD))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
else if(!_tcscmp(szName, UTREG_UI_SALT50))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
#ifdef OS_WINCE
|
||
|
else if (!_tcscmp(szName, UI_SETTING_PASSWORD51))
|
||
|
{
|
||
|
return TRUE;
|
||
|
}
|
||
|
#endif
|
||
|
else
|
||
|
{
|
||
|
//
|
||
|
// Don't migrate as binary
|
||
|
//
|
||
|
return FALSE;
|
||
|
}
|
||
|
DC_END_FN();
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Return true if it is ok to migrate the value
|
||
|
// in name
|
||
|
//
|
||
|
BOOL CTscRegMigrate::FilterStringMigrate(LPTSTR szName)
|
||
|
{
|
||
|
if(szName)
|
||
|
{
|
||
|
if(_tcsstr(szName, TEXT("MRU")))
|
||
|
{
|
||
|
if(!_tcscmp(szName, UTREG_UI_SERVER_MRU0))
|
||
|
{
|
||
|
//Translate MRU0 to fulladdress
|
||
|
_tcscpy(szName, UTREG_UI_FULL_ADDRESS);
|
||
|
return TRUE;
|
||
|
}
|
||
|
//Don't migrate any other MRU strings.
|
||
|
//those stay in the registry
|
||
|
return FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//everything else is OK
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Munges the settings in the settings store to be consisten
|
||
|
// with win2k's defaults. Win2k's default values were usually
|
||
|
// deleted on write, this means we would instead use whistler
|
||
|
// defaults but we don't want that. Instead the behavior we
|
||
|
// want is that a migrated connectoid has _exactly_ the same
|
||
|
// settings it used to have for those options that had UI in win2k.
|
||
|
//
|
||
|
// Params -
|
||
|
// pSto - settings store to munge for defaults
|
||
|
// Returns -
|
||
|
// Success flag
|
||
|
//
|
||
|
//
|
||
|
|
||
|
#define TSC_WIN2K_DEFAULT_DESKTOPSIZE 0 //640x480
|
||
|
#define TSC_WIN2K_DEFAULT_FULLSCREENMODE 1 //windowed
|
||
|
#define TSC_WIN2K_DEFAULT_BITMAPCACHE 0 //off
|
||
|
#define TSC_WIN2K_DEFAULT_COMPRESSION 1 //on
|
||
|
BOOL CTscRegMigrate::MungeForWin2kDefaults(ISettingsStore* pSto)
|
||
|
{
|
||
|
DC_BEGIN_FN("MungeForWin2kDefaults");
|
||
|
|
||
|
TRC_ASSERT(pSto,
|
||
|
(TB,_T("pSto is null")));
|
||
|
TRC_ASSERT(pSto->IsOpenForRead() && pSto->IsOpenForWrite(),
|
||
|
(TB,_T("pSto is null")));
|
||
|
|
||
|
//
|
||
|
// Munge settings that had conman UI
|
||
|
// to ensure that we use the user's previous settings (Even
|
||
|
// if they are win2k defaults i.e could be value not specified).
|
||
|
//
|
||
|
|
||
|
// Resolution
|
||
|
if(!pSto->IsValuePresent(UTREG_UI_DESKTOP_SIZEID))
|
||
|
{
|
||
|
//Write out win2k's default desktop size
|
||
|
if(!pSto->WriteInt(UTREG_UI_DESKTOP_SIZEID,
|
||
|
-1, //default ignored
|
||
|
TSC_WIN2K_DEFAULT_DESKTOPSIZE,
|
||
|
TRUE)) //always write
|
||
|
{
|
||
|
TRC_ERR((TB,_T("WriteInt UTREG_UI_DESKTOP_SIZEID failed")));
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Screen mode
|
||
|
if(!pSto->IsValuePresent(UTREG_UI_SCREEN_MODE))
|
||
|
{
|
||
|
//Write out win2k's default screen mode
|
||
|
if(!pSto->WriteInt(UTREG_UI_SCREEN_MODE,
|
||
|
-1, //default ignored
|
||
|
TSC_WIN2K_DEFAULT_FULLSCREENMODE,
|
||
|
TRUE)) //always write
|
||
|
{
|
||
|
TRC_ERR((TB,_T("WriteInt UTREG_UI_SCREEN_MODE failed")));
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Bitmap caching
|
||
|
if(!pSto->IsValuePresent(UTREG_UI_BITMAP_PERSISTENCE))
|
||
|
{
|
||
|
//write out win2k's default bmp persistence option
|
||
|
if(!pSto->WriteInt(UTREG_UI_BITMAP_PERSISTENCE,
|
||
|
-1, //default ignored
|
||
|
TSC_WIN2K_DEFAULT_BITMAPCACHE,
|
||
|
TRUE))
|
||
|
{
|
||
|
TRC_ERR((TB,_T("WriteInt TSC_WIN2K_DEFAULT_BITMAPCACHE failed")));
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Compression
|
||
|
// Compression is special..We've made a decision that it should
|
||
|
// always be on for perf reasons (and there are no drawbacks) so
|
||
|
// change the win2k
|
||
|
//
|
||
|
if(!pSto->IsValuePresent(UTREG_UI_COMPRESS))
|
||
|
{
|
||
|
//write out win2k's default compression option
|
||
|
if(!pSto->WriteInt(UTREG_UI_COMPRESS,
|
||
|
-1, //default ignored
|
||
|
TSC_WIN2K_DEFAULT_COMPRESSION,
|
||
|
TRUE))
|
||
|
{
|
||
|
TRC_ERR((TB,_T("WriteInt TSC_WIN2K_DEFAULT_BITMAPCACHE failed")));
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
DC_END_FN();
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#ifndef OS_WINCE
|
||
|
//
|
||
|
// Convert the password format (if password present)
|
||
|
// i.e if the old style TS5 passwords are present then decrypt
|
||
|
// to plain text and then re-encrypt and save out using CryptoAPI's
|
||
|
//
|
||
|
// On Platforms that don't support Crypto-API we just nuke
|
||
|
// the existing password format as we don't support migrating it
|
||
|
// to the RDP files since it's not a secure format (just a hash).
|
||
|
//
|
||
|
// Start fields in pSto - 'Password 50' + 'Salt 50'
|
||
|
// After conversion - (win2k+) 'Password 51' - binary crypto api password
|
||
|
// After conversion - (less than win2k) = nothing
|
||
|
//
|
||
|
//
|
||
|
BOOL CTscRegMigrate::ConvertPasswordFormat(ISettingsStore* pSto)
|
||
|
{
|
||
|
BOOL bRet = TRUE;
|
||
|
DC_BEGIN_FN("ConvertPasswordFormat");
|
||
|
|
||
|
//Nuke TS4 format
|
||
|
pSto->DeleteValueIfPresent( UTREG_UI_PASSWORD );
|
||
|
|
||
|
if ( CSH::IsCryptoAPIPresent() &&
|
||
|
pSto->IsValuePresent( UTREG_UI_PASSWORD50 ) &&
|
||
|
pSto->IsValuePresent( UTREG_UI_SALT50 ) )
|
||
|
{
|
||
|
BOOL fHavePass = FALSE;
|
||
|
BYTE Password[TSC_MAX_PASSWORD_LENGTH_BYTES];
|
||
|
BYTE Salt[TSC_SALT_LENGTH];
|
||
|
memset( Password, 0, TSC_MAX_PASSWORD_LENGTH_BYTES);
|
||
|
|
||
|
if (pSto->ReadBinary(UTREG_UI_PASSWORD50,
|
||
|
(PBYTE)Password,
|
||
|
sizeof(Password))) //size in bytes
|
||
|
{
|
||
|
fHavePass = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRC_NRM((TB,
|
||
|
_T("ReadBinary for password failed. Maybe password not present")));
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Salt
|
||
|
//
|
||
|
if (!pSto->ReadBinary(UTREG_UI_SALT50,
|
||
|
(PBYTE)Salt,
|
||
|
sizeof(Salt)))
|
||
|
{
|
||
|
fHavePass = FALSE;
|
||
|
TRC_NRM((TB,_T("ReadBinary for salt failed.")));
|
||
|
}
|
||
|
|
||
|
if (fHavePass &&
|
||
|
EncryptDecryptLocalData50( Password,
|
||
|
TSC_WIN2K_PASSWORD_LENGTH_BYTES,
|
||
|
Salt, sizeof(Salt)))
|
||
|
{
|
||
|
//Now we have the clear text pass in Password
|
||
|
//encrypt it with the crypto API and save that back
|
||
|
//out to the store
|
||
|
DATA_BLOB din;
|
||
|
DATA_BLOB dout;
|
||
|
din.cbData = sizeof(Password);
|
||
|
din.pbData = (PBYTE)&Password;
|
||
|
dout.pbData = NULL;
|
||
|
if (CSH::DataProtect( &din, &dout))
|
||
|
{
|
||
|
if (!pSto->WriteBinary(UI_SETTING_PASSWORD51,
|
||
|
dout.pbData,
|
||
|
dout.cbData))
|
||
|
{
|
||
|
bRet = FALSE;
|
||
|
}
|
||
|
LocalFree( dout.pbData );
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bRet = FALSE;
|
||
|
}
|
||
|
|
||
|
// Wipe from stack
|
||
|
SecureZeroMemory( Password, TSC_MAX_PASSWORD_LENGTH_BYTES);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// No longer need the old format so delete them
|
||
|
pSto->DeleteValueIfPresent( UTREG_UI_PASSWORD50 );
|
||
|
pSto->DeleteValueIfPresent( UTREG_UI_SALT50 );
|
||
|
|
||
|
DC_END_FN();
|
||
|
return bRet;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
BOOL
|
||
|
CTscRegMigrate::DeleteRegValue(HKEY hKeyRoot,
|
||
|
LPCTSTR szRootName,
|
||
|
LPCTSTR szValueName)
|
||
|
{
|
||
|
HKEY hKey;
|
||
|
LONG rc;
|
||
|
BOOL fRet = FALSE;
|
||
|
|
||
|
DC_BEGIN_FN("DeleteRegValue");
|
||
|
|
||
|
rc = RegOpenKeyEx( hKeyRoot,
|
||
|
szRootName,
|
||
|
0,
|
||
|
KEY_SET_VALUE, //needed for delete access
|
||
|
&hKey);
|
||
|
if(ERROR_SUCCESS == rc)
|
||
|
{
|
||
|
rc = RegDeleteValue(hKey, szValueName);
|
||
|
if (ERROR_SUCCESS == rc) {
|
||
|
fRet = TRUE;
|
||
|
}
|
||
|
|
||
|
RegCloseKey(hKey);
|
||
|
}
|
||
|
|
||
|
DC_END_FN();
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Remove entries we don't want to keep lying around in the registry
|
||
|
// primarily these are passwords in the old 'insecure' obfuscated formats
|
||
|
//
|
||
|
BOOL
|
||
|
CTscRegMigrate::RemoveUnsafeRegEntries(HKEY hKeyRoot,
|
||
|
LPCTSTR szRootName)
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
|
||
|
DC_BEGIN_FN("RemoveUnsafeRegEntries");
|
||
|
|
||
|
if (!DeleteRegValue(hKeyRoot, szRootName, UTREG_UI_PASSWORD50)) {
|
||
|
TRC_ALT((TB,_T("Failed to delete: %s\\%s"), szRootName,
|
||
|
UTREG_UI_PASSWORD50));
|
||
|
}
|
||
|
|
||
|
if (!DeleteRegValue(hKeyRoot, szRootName, UTREG_UI_PASSWORD)) {
|
||
|
TRC_ALT((TB,_T("Failed to delete: %s\\%s"), szRootName,
|
||
|
UTREG_UI_PASSWORD));
|
||
|
}
|
||
|
|
||
|
if (!DeleteRegValue(hKeyRoot, szRootName, UTREG_UI_SALT50)) {
|
||
|
TRC_ALT((TB,_T("Failed to delete: %s\\%s"), szRootName,
|
||
|
UTREG_UI_SALT50));
|
||
|
}
|
||
|
|
||
|
fRet = TRUE;
|
||
|
|
||
|
DC_END_FN();
|
||
|
|
||
|
return fRet;
|
||
|
}
|