Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1930 lines
51 KiB

/*++
Copyright (c) 1999-2000 Microsoft Corporation
Module Name:
HelpAcc.cpp
Abstract:
Implementation of __HelpAssistantAccount to manage Help Assistant account, this
including creating account, setting various account rights and password.
Author:
HueiWang 06/29/2000
--*/
#include "stdafx.h"
#include "helpacc.h"
#include "resource.h"
#include "policy.h"
#include "cfgbkend.h"
#include "cfgbkend_i.c"
#include "helper.h"
//
// Help account lock
CCriticalSection __HelpAssistantAccount::gm_HelpAccountCS;
// Help Account name and pasword
CComBSTR __HelpAssistantAccount::gm_bstrHelpAccountPwd;
CComBSTR __HelpAssistantAccount::gm_bstrHelpAccountName(HELPASSISTANTACCOUNT_NAME);
CComBSTR __HelpAssistantAccount::gm_bstrHelpAccountDomain;
// Help Account SID
PBYTE __HelpAssistantAccount::gm_pbHelpAccountSid = NULL;
DWORD __HelpAssistantAccount::gm_cbHelpAccountSid = 0;
DWORD __HelpAssistantAccount::gm_dwAccErrCode = ERROR_INVALID_DATA;
///////////////////////////////////////////////////////////////////////////
//
//
//
DWORD
GetGUIDString(
OUT LPTSTR* pszString
)
/*++
--*/
{
UUID uuid;
RPC_STATUS rpcStatus;
LPTSTR pszUuid = NULL;
rpcStatus = UuidCreate( &uuid );
if( rpcStatus != RPC_S_OK &&
rpcStatus != RPC_S_UUID_LOCAL_ONLY &&
rpcStatus != RPC_S_UUID_NO_ADDRESS )
{
goto CLEANUPANDEXIT;
}
rpcStatus = UuidToString( &uuid, &pszUuid );
if( RPC_S_OK == rpcStatus )
{
*pszString = (LPTSTR)LocalAlloc( LPTR, (lstrlen(pszUuid)+1)*sizeof(TCHAR));
if( NULL == *pszString )
{
rpcStatus = GetLastError();
}
else
{
lstrcpy( *pszString, pszUuid );
}
}
CLEANUPANDEXIT:
if( NULL != pszUuid )
{
RpcStringFree(&pszUuid);
}
return rpcStatus;
}
DWORD
GenerateUniqueHelpAssistantName(
IN LPCTSTR pszAccNamePrefix,
OUT CComBSTR& bstrAccName
)
/*++
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
LPTSTR pszString = NULL;
DWORD index;
BOOL bAccEnabled;
for(index =0; index < MAX_UNIQUENAME_RETRY; index++)
{
dwStatus = GetGUIDString( &pszString );
if( ERROR_SUCCESS != dwStatus )
{
break;
}
DWORD dwLen;
DWORD dwAppendStrLen;
LPTSTR pszAppendStr;
// MAX user account name is 20 chars.
bstrAccName = pszAccNamePrefix;
bstrAccName += L"_";
dwLen = bstrAccName.Length();
dwAppendStrLen = lstrlen(pszString);
if( dwAppendStrLen < MAX_USERNAME_LENGTH - dwLen )
{
pszAppendStr = pszString;
}
else
{
pszAppendStr = pszString + dwAppendStrLen - (MAX_USERNAME_LENGTH - dwLen);
}
bstrAccName += pszAppendStr;
if( pszString != NULL )
{
LocalFree( pszString );
}
// check if account name exists.
dwStatus = IsLocalAccountEnabled( bstrAccName, &bAccEnabled );
if( ERROR_SUCCESS != dwStatus )
{
// IsLocalAccountEnabled() return ERROR_SUCCESS if account exist, we want to
// break out when we encounter error.
dwStatus = ERROR_SUCCESS;
break;
}
}
if( index >= MAX_UNIQUENAME_RETRY )
{
// Very unlikely since we try MAX_UNIQUENAME_RETRY to get
// a unique account name, assert to track this
dwStatus = ERROR_USER_EXISTS;
MYASSERT(FALSE);
}
return dwStatus;
}
HRESULT
__HelpAssistantAccount::SetupHelpAccountTSSettings(
BOOL bForce /* FALSE */
)
/*++
Routine Description:
Setup bunch of HelpAssistant account TS settings.
Parameters:
bForce : TRUE to force setup, FALSE otherwise
Returns:
ERROR_SUCCESS or error code
--*/
{
CComBSTR bstrScript;
DWORD dwStatus;
PBYTE pbAlreadySetup = NULL;
DWORD cbAlreadySetup = 0;
HRESULT hRes = S_OK;
CCriticalSectionLocker l(gm_HelpAccountCS);
dwStatus = RetrieveKeyFromLSA(
HELPACCOUNTPROPERLYSETUP,
(PBYTE *)&pbAlreadySetup,
&cbAlreadySetup
);
if( bForce || ERROR_SUCCESS != dwStatus )
{
hRes = GetHelpAccountScript( bstrScript );
if( SUCCEEDED(hRes) )
{
// always config again.
hRes = ConfigHelpAccountTSSettings(
gm_bstrHelpAccountName,
bstrScript
);
if( SUCCEEDED(hRes) )
{
dwStatus = StoreKeyWithLSA(
HELPACCOUNTPROPERLYSETUP,
(PBYTE) &dwStatus,
sizeof(dwStatus)
);
if( ERROR_SUCCESS != dwStatus )
{
hRes = HRESULT_FROM_WIN32(hRes);
}
}
}
}
if( NULL != pbAlreadySetup )
{
LocalFree( pbAlreadySetup );
}
return hRes;
}
HRESULT
__HelpAssistantAccount::LookupHelpAccountSid(
IN LPTSTR pszAccName,
OUT PSID* ppSid,
OUT DWORD* pcbSid
)
/*++
Routine Description:
This routine retrieve help assistant account's SID.
Parameters:
pszAccName : Name of Help Assistant Account.
ppSid : Pointer to PSID to receive account SID.
cbSid : Size of SID return on ppSid
Returns:
S_OK or error code.
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD cbSid = 0;
DWORD cbDomainName = 0;
PSID pAccSid = NULL;
LPTSTR pszDomainName = NULL;
BOOL bSuccess;
SID_NAME_USE SidUse;
// Get buffer size required for SID
bSuccess = LookupAccountName(
NULL,
pszAccName,
NULL,
&cbSid,
NULL,
&cbDomainName,
&SidUse
);
if( TRUE == bSuccess ||
ERROR_INSUFFICIENT_BUFFER == GetLastError() )
{
pAccSid = (PSID)LocalAlloc( LPTR, cbSid );
if( NULL == pAccSid )
{
dwStatus = GetLastError();
goto CLEANUPANDEXIT;
}
// allocate buffer for domain name so LookupAccountName()
// does not return insufficient buffer
pszDomainName = (LPTSTR)LocalAlloc( LPTR, (cbDomainName + 1) * sizeof(TCHAR) );
if( NULL == pszDomainName )
{
dwStatus = GetLastError();
goto CLEANUPANDEXIT;
}
bSuccess = LookupAccountName(
NULL,
pszAccName,
pAccSid,
&cbSid,
pszDomainName,
&cbDomainName,
&SidUse
);
if( FALSE == bSuccess || SidTypeUser != SidUse )
{
//MYASSERT(FALSE);
dwStatus = E_UNEXPECTED;
goto CLEANUPANDEXIT;
}
// Make sure we gets valid SID
bSuccess = IsValidSid( pAccSid );
if( FALSE == bSuccess )
{
dwStatus = E_UNEXPECTED;
}
}
else
{
dwStatus = GetLastError();
}
CLEANUPANDEXIT:
if( dwStatus == ERROR_SUCCESS )
{
*ppSid = pAccSid;
*pcbSid = cbSid;
}
else
{
if( NULL != pAccSid )
{
LocalFree( pAccSid );
}
}
if( NULL != pszDomainName )
{
LocalFree( pszDomainName );
}
return HRESULT_FROM_WIN32(dwStatus);
}
HRESULT
__HelpAssistantAccount::CacheHelpAccountSID()
/*++
Routine Description:
This routine retrieve help assistant account's SID and store
it with LSA, this is so that PTS can verify login user is
the actual Salem Help Assistant Account.
Parameters:
None.
Returns:
S_OK
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
DWORD cbSid = 0;
PSID pAccSid = NULL;
dwStatus = LookupHelpAccountSid(
gm_bstrHelpAccountName,
&pAccSid,
&cbSid
);
if( ERROR_SUCCESS == dwStatus )
{
// Store this with LSA
dwStatus = StoreKeyWithLSA(
HELPASSISTANTACCOUNT_SIDKEY,
(PBYTE)pAccSid,
cbSid
);
}
if( NULL != pAccSid )
{
LocalFree( pAccSid );
}
return HRESULT_FROM_WIN32(dwStatus);
}
HRESULT
__HelpAssistantAccount::GetHelpAccountScript(
OUT CComBSTR& bstrScript
)
/*++
Routine Description:
Routine to retrieve logon script for help assistant account.
Parameters:
bstrScript : Reference to CComBSTR, on return, this parameter contains
full path to the logon script.
Returns:
ERROR_SUCCESS or error code from GetSystemDirectory
NOTE:
TODO - Need to get the actual path/name.
--*/
{
DWORD dwStatus = ERROR_SUCCESS;
TCHAR szScript[MAX_PATH + 1];
dwStatus = (DWORD)GetSystemDirectory( szScript, MAX_PATH );
if( 0 == dwStatus )
{
//MYASSERT(FALSE);
dwStatus = GetLastError();
}
else
{
bstrScript = szScript;
bstrScript += _TEXT("\\");
bstrScript += RDSADDINEXECNAME;
dwStatus = ERROR_SUCCESS;
}
return HRESULT_FROM_WIN32(dwStatus);
}
HRESULT
__HelpAssistantAccount::Initialize(
BOOL bVerifyPassword /* = TRUE */
)
/*++
Routine Description:
Initialize HelpAssistantAccount structure global variables.
Parameters:
None.
Returns:
S_OK or error code.
--*/
{
HRESULT hRes = S_OK;
LPTSTR pszOldPassword = NULL;
DWORD cbOldPassword = 0;
DWORD dwStatus;
BOOL bStatus;
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+2];
DWORD cbComputerName = MAX_COMPUTERNAME_LENGTH+1;
PSID pCachedHelpAccSid = NULL;
DWORD cbCachedHelpAccSid = 0;
BOOL bAccountEnable = TRUE;
LPTSTR rights[1];
LPTSTR pszHelpAcctName = NULL;
LPTSTR pszHelpAccDomain = NULL;
CCriticalSectionLocker l(gm_HelpAccountCS);
if( FALSE == GetComputerName( szComputerName, &cbComputerName ) )
{
//MYASSERT(FALSE);
gm_dwAccErrCode = GetLastError();
goto CLEANUPANDEXIT;
}
//
// Load password from LSA
//
dwStatus = RetrieveKeyFromLSA(
HELPASSISTANTACCOUNT_PASSWORDKEY,
(PBYTE *)&pszOldPassword,
&cbOldPassword
);
if( ERROR_SUCCESS != dwStatus )
{
//
// Account is not properly setup
gm_dwAccErrCode = dwStatus;
goto CLEANUPANDEXIT;
}
// Load account SID
dwStatus = RetrieveKeyFromLSA(
HELPASSISTANTACCOUNT_SIDKEY,
(PBYTE *)&pCachedHelpAccSid,
&cbCachedHelpAccSid
);
if( ERROR_SUCCESS != dwStatus )
{
gm_dwAccErrCode = dwStatus;
goto CLEANUPANDEXIT;
}
dwStatus = TSGetHelpAssistantAccountName(
&pszHelpAccDomain,
&pszHelpAcctName
);
if( ERROR_SUCCESS != dwStatus )
{
gm_dwAccErrCode = dwStatus;
goto CLEANUPANDEXIT;
}
gm_bstrHelpAccountDomain = pszHelpAccDomain;
gm_bstrHelpAccountName = pszHelpAcctName;
DebugPrintf(
_TEXT("HelpAssistant account name : %s\n"),
gm_bstrHelpAccountName
);
//
// Check if account is enable, if not enable it or
// LogonUser() will failed with error 1331
//
dwStatus = IsLocalAccountEnabled(
gm_bstrHelpAccountName,
&bAccountEnable
);
if( ERROR_SUCCESS != dwStatus )
{
// critical error, account might not exist.
gm_dwAccErrCode = ERROR_INVALID_DATA;
goto CLEANUPANDEXIT;
}
//
// Everything is OK, cache values.
//
gm_bstrHelpAccountPwd = pszOldPassword;
gm_pbHelpAccountSid = (PBYTE)pCachedHelpAccSid;
gm_cbHelpAccountSid = cbCachedHelpAccSid;
pCachedHelpAccSid = NULL;
//
// Setup/upgrade on DC will try to validate password but
// since account on DC goes to ADS, server might not be
// available, we will reset password on start up so we don't
// need to validate password on upgrade.
//
if( TRUE == bVerifyPassword )
{
if( FALSE == bAccountEnable )
{
// enable the account so we can check password
dwStatus = EnableHelpAssistantAccount( TRUE );
if( ERROR_SUCCESS != dwStatus )
{
//
// Can't enable the account, critical error
//
gm_dwAccErrCode = dwStatus;
goto CLEANUPANDEXIT;
}
}
rights[0] = SE_NETWORK_LOGON_NAME;
//
// Enable network logon rights to validate password
//
dwStatus = EnableAccountRights(
TRUE,
1,
rights
);
if( ERROR_SUCCESS != dwStatus )
{
DebugPrintf(
_TEXT("EnableAccountRights() returns 0x%08x\n"),
dwStatus
);
gm_dwAccErrCode = dwStatus;
//
// Error code path, restore account status
//
if( FALSE == bAccountEnable )
{
// non-critical error.
EnableHelpAssistantAccount( bAccountEnable );
}
goto CLEANUPANDEXIT;
}
// valid password
bStatus = ValidatePassword(
gm_bstrHelpAccountName,
L".",
pszOldPassword
);
if( FALSE == bStatus )
{
// mismatch password, force password change
dwStatus = ChangeLocalAccountPassword(
gm_bstrHelpAccountName,
pszOldPassword,
pszOldPassword
);
DebugPrintf(
_TEXT("ChangeLocalAccountPassword() returns %d\n"),
dwStatus
);
if( ERROR_SUCCESS != dwStatus )
{
gm_dwAccErrCode = ERROR_LOGON_FAILURE;
}
else
{
bStatus = ValidatePassword(
gm_bstrHelpAccountName,
L".",
pszOldPassword
);
}
}
//
// Disable network interactive rights
//
dwStatus = EnableAccountRights(
FALSE,
1,
rights
);
MYASSERT( ERROR_SUCCESS == dwStatus );
//
// Restore account status
//
if( FALSE == bAccountEnable )
{
// non-critical error.
EnableHelpAssistantAccount( bAccountEnable );
}
}
//
// No checking on dwStatus from disabling account rights,
// security risk but not affecting our operation
//
gm_dwAccErrCode = dwStatus;
CLEANUPANDEXIT:
FreeMemory(pszHelpAcctName);
FreeMemory( pszOldPassword );
FreeMemory( pCachedHelpAccSid );
FreeMemory( pszHelpAccDomain );
return HRESULT_FROM_WIN32( gm_dwAccErrCode );
}
HRESULT
__HelpAssistantAccount::DeleteHelpAccount()
/*++
Routine Description:
Delete Help Assistant Account.
Parameters:
None.
Returns:
S_OK or error code.
--*/
{
DWORD dwStatus;
BOOL bStatus;
BOOL bEnable;
CCriticalSectionLocker l(gm_HelpAccountCS);
if( ERROR_SUCCESS == IsLocalAccountEnabled(gm_bstrHelpAccountName, &bEnable) )
{
//
// remove all TS rights or it will shows up
// as unknown SID string on TSCC permission page
//
SetupHelpAccountTSRights(
TRUE,
FALSE,
FALSE,
WINSTATION_ALL_ACCESS
);
// don't need to verify password
(void) Initialize(FALSE);
// Always delete interactive right or there will
// be lots of entries in local security
(void) EnableRemoteInteractiveRight(FALSE);
}
//
// Delete NT account
//
dwStatus = NetUserDel(
NULL,
gm_bstrHelpAccountName
);
if( ERROR_ACCESS_DENIED == dwStatus )
{
// We don't have priviledge, probably can't
// touch accunt, get out.
// MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
dwStatus = ERROR_SUCCESS;
//
// Overwrite password stored with LSA
//
StoreKeyWithLSA(
HELPASSISTANTACCOUNT_PASSWORDKEY,
NULL,
0
);
//
// Overwrite Help Assistant Account SID store in LSA
//
StoreKeyWithLSA(
HELPASSISTANTACCOUNT_SIDKEY,
NULL,
0
);
// Not yet setup Help Assistant account
StoreKeyWithLSA(
HELPACCOUNTPROPERLYSETUP,
NULL,
0
);
CLEANUPANDEXIT:
return HRESULT_FROM_WIN32(dwStatus);
}
HRESULT
__HelpAssistantAccount::CreateHelpAccount(
IN LPCTSTR pszPassword
)
/*++
Routine Description:
Create Help Assistant Account.
Parameters:
pszPassword : Suggested password.
Returns:
S_OK or error code.
Note:
1) Routine should only be invoked during setup.
2) Password parameter might not be honor in future so
it is only a suggestion.
--*/
{
HRESULT hRes = S_OK;
BOOL bStatus;
DWORD dwStatus;
CComBSTR AccFullName;
CComBSTR AccDesc;
CComBSTR AccName;
CComBSTR bstrNewHelpAccName;
TCHAR newAssistantAccountPwd[MAX_HELPACCOUNT_PASSWORD + 1];
CComBSTR bstrScript;
BOOL bPersonalOrProMachine;
CCriticalSectionLocker l(gm_HelpAccountCS);
bStatus = AccName.LoadString(IDS_HELPACCNAME);
if( FALSE == bStatus )
{
hRes = E_UNEXPECTED;
return hRes;
}
bStatus = AccFullName.LoadString(
IDS_HELPACCFULLNAME
);
if( FALSE == bStatus )
{
hRes = E_UNEXPECTED;
return hRes;
}
bStatus = AccDesc.LoadString(
IDS_HELPACCDESC
);
if( FALSE == bStatus )
{
hRes = E_UNEXPECTED;
return hRes;
}
bPersonalOrProMachine = IsPersonalOrProMachine();
//
// Verify help assistant account exist and don't check
// password, on service startup, we will verify password
// if mismatch, service startup will reset the password.
//
hRes = Initialize( FALSE );
if( SUCCEEDED(hRes) )
{
// Account already exists, check if this is the hardcoded HelpAssistant,
// if so, rename to whatever in resource, we only need to rename
// account if
// 1) existing account is HelpAssistant - administrator has not rename it.
// 2) account name in resource is not HelpAssistant.
// 3) We are running on server or above SKU.
if( (FALSE == bPersonalOrProMachine) ||
(gm_bstrHelpAccountName == HELPASSISTANTACCOUNT_NAME &&
AccName != HELPASSISTANTACCOUNT_NAME) )
{
if( FALSE == bPersonalOrProMachine )
{
// on server or above SKU, we rename it to unique name.
dwStatus = GenerateUniqueHelpAssistantName( AccName, bstrNewHelpAccName );
}
else
{
dwStatus = ERROR_SUCCESS;
bstrNewHelpAccName = AccName;
}
if( ERROR_SUCCESS == dwStatus )
{
dwStatus = RenameLocalAccount( gm_bstrHelpAccountName, bstrNewHelpAccName );
}
if( ERROR_SUCCESS == dwStatus )
{
// cache the new help assistant account name.
gm_bstrHelpAccountName = bstrNewHelpAccName;
}
else
{
// force a delete and reload.
hRes = HRESULT_FROM_WIN32( dwStatus );
}
}
//
// Account already exist, change the description,
// if failed, force delete and re-create account again
//
if( SUCCEEDED(hRes) )
{
//
// Update account description
//
dwStatus = UpdateLocalAccountFullnameAndDesc(
gm_bstrHelpAccountName,
AccFullName,
AccDesc
);
if( ERROR_SUCCESS != dwStatus )
{
hRes = HRESULT_FROM_WIN32(dwStatus);
}
}
}
if( FAILED(hRes) )
{
//
// Either password mismatch or account not exists,...
// delete account and re-create one
//
(void)DeleteHelpAccount();
// generate password if NULL or zero length string
if( NULL == pszPassword || 0 == lstrlen(pszPassword) )
{
ZeroMemory(newAssistantAccountPwd, sizeof(newAssistantAccountPwd)/sizeof(newAssistantAccountPwd[0]));
dwStatus = CreatePassword(newAssistantAccountPwd, sizeof(newAssistantAccountPwd)/sizeof(newAssistantAccountPwd[0])-1);
if( ERROR_SUCCESS != dwStatus )
{
hRes = HRESULT_FROM_WIN32(dwStatus);
goto CLEANUPANDEXIT;
}
}
else
{
memset(
newAssistantAccountPwd,
0,
sizeof(newAssistantAccountPwd)
);
_tcsncpy(
newAssistantAccountPwd,
pszPassword,
min(lstrlen(pszPassword), MAX_HELPACCOUNT_PASSWORD)
);
}
hRes = GetHelpAccountScript( bstrScript );
if( FAILED(hRes) )
{
goto CLEANUPANDEXIT;
}
//
// On personal or pro machine, we use what's in resorce.
// server or advance server, we use HelpAssist_<Random string>
//
if( FALSE == bPersonalOrProMachine )
{
dwStatus = GenerateUniqueHelpAssistantName(
AccName,
gm_bstrHelpAccountName
);
if( ERROR_SUCCESS != dwStatus )
{
hRes = HRESULT_FROM_WIN32(dwStatus);
goto CLEANUPANDEXIT;
}
}
else
{
gm_bstrHelpAccountName = AccName;
}
if( SUCCEEDED(hRes) )
{
BOOL bAccExist;
//
// Create local account will enables it if account is disabled
//
dwStatus = CreateLocalAccount(
gm_bstrHelpAccountName,
newAssistantAccountPwd,
AccFullName,
AccDesc,
NULL,
bstrScript,
&bAccExist
);
if( ERROR_SUCCESS == dwStatus )
{
if( FALSE == bAccExist )
{
DebugPrintf( _TEXT("%s account is new\n"), gm_bstrHelpAccountName );
//
// Store the actual Help Assistant Account's SID with LSA
// so TermSrv can verify this SID
//
hRes = CacheHelpAccountSID();
}
else
{
DebugPrintf( _TEXT("%s account exists\n") );
hRes = ResetHelpAccountPassword(newAssistantAccountPwd);
}
if( SUCCEEDED(hRes) )
{
dwStatus = StoreKeyWithLSA(
HELPASSISTANTACCOUNT_PASSWORDKEY,
(PBYTE)newAssistantAccountPwd,
sizeof(newAssistantAccountPwd)
);
hRes = HRESULT_FROM_WIN32( dwStatus );
}
if( SUCCEEDED(hRes) )
{
// reload global variable here, don't need to
// verify password, DC ADS might not be available.
hRes = Initialize( FALSE );
}
//
// TODO - need to fix CreateLocalAccount() on SRV SKU
// too riskly for client release.
//
UpdateLocalAccountFullnameAndDesc(
gm_bstrHelpAccountName,
AccFullName,
AccDesc
);
// Always disable the account.
EnableHelpAssistantAccount( FALSE );
}
else
{
hRes = HRESULT_FROM_WIN32( dwStatus );
}
}
}
if( SUCCEEDED(hRes) )
{
// remove network and interactive logon rights from account.
LPTSTR rights[1];
DWORD dwStatus;
rights[0] = SE_NETWORK_LOGON_NAME;
dwStatus = EnableAccountRights( FALSE, 1, rights );
//
// Just for backward compatible, ignore error
//
rights[0] = SE_INTERACTIVE_LOGON_NAME;
dwStatus = EnableAccountRights( FALSE, 1, rights );
//
// Just for backward compatible, ignore error
//
hRes = S_OK;
}
if( SUCCEEDED(hRes) )
{
//
// TS setup always overwrite default security on upgrade.
//
//
// Give user all rights except SeRemoteInterativeRights, Whilster does
// not use WINSTATION_CONNECT any more.
hRes = SetupHelpAccountTSRights(
FALSE, // Not deleting, refer to ModifyUserAccess()
TRUE, // enable TS rights
TRUE, // delete existing entry if exist.
WINSTATION_ALL_ACCESS
);
}
CLEANUPANDEXIT:
return hRes;
}
HRESULT
__HelpAssistantAccount::ConfigHelpAccountTSSettings(
IN LPTSTR pszUserName,
IN LPTSTR pszInitProgram
)
/*++
Routine Description:
This routine configurate TS specific settings
for user account.
Parameters:
pszUserName : Name of user account to configurate.
pszInitProgram : Full path to init. program when user
login.
Returns:
ERROR_SUCCESS or error code
--*/
{
BOOL bStatus;
HRESULT hRes = S_OK;
HMODULE hWtsapi32 = NULL;
PWTSSetUserConfigW pConfig = NULL;
BOOL bManualSetConsole = TRUE;
BOOL bEnable;
DWORD dwStatus;
//DebugPrintf( _TEXT("SetupHelpAccountTSSettings...\n") );
CCriticalSectionLocker l(gm_HelpAccountCS);
dwStatus = IsLocalAccountEnabled(
pszUserName,
&bEnable
);
if( ERROR_SUCCESS != dwStatus )
{
//MYASSERT(FALSE);
hRes = HRESULT_FROM_WIN32( dwStatus );
return hRes;
}
hWtsapi32 = LoadLibrary( _TEXT("wtsapi32.dll") );
if( NULL != hWtsapi32 )
{
pConfig = (PWTSSetUserConfigW)GetProcAddress(
hWtsapi32,
"WTSSetUserConfigW"
);
if( NULL != pConfig )
{
DWORD dwSettings;
//
// Set WTSUserConfigfAllowLogonTerminalServer
//
dwSettings = TRUE;
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigfAllowLogonTerminalServer,
(LPWSTR)&dwSettings,
sizeof(dwSettings)
);
if( FALSE == bStatus )
{
DebugPrintf( _TEXT("WTSUserConfigfAllowLogonTerminalServer return %d\n"), GetLastError() );
bStatus = TRUE;
}
// MYASSERT( TRUE == bStatus );
if( TRUE == bStatus )
{
//
// Ignore all error and continue on setting values
// catch error at the calling routine
//
dwSettings = TRUE;
// Reset connection when connection broken
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigBrokenTimeoutSettings,
(LPWSTR)&dwSettings,
sizeof(dwSettings)
);
dwSettings = FALSE;
// initial program
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigfInheritInitialProgram,
(LPWSTR)&dwSettings,
sizeof(dwSettings)
);
dwSettings = FALSE;
// No re-connect.
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigReconnectSettings,
(LPWSTR)&dwSettings,
sizeof(dwSettings)
);
dwSettings = FALSE;
// No drive mapping
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigfDeviceClientDrives,
(LPWSTR)&dwSettings,
sizeof(dwSettings)
);
dwSettings = FALSE;
// No printer.
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigfDeviceClientPrinters,
(LPWSTR)&dwSettings,
sizeof(dwSettings)
);
dwSettings = FALSE;
// No defaultPrinter
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigfDeviceClientDefaultPrinter,
(LPWSTR)&dwSettings,
sizeof(dwSettings)
);
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigInitialProgram,
pszInitProgram,
wcslen(pszInitProgram)
);
TCHAR path_buffer[MAX_PATH+1];
TCHAR drive[_MAX_DRIVE + 1];
TCHAR dir[_MAX_DIR + 1];
memset( path_buffer, 0, sizeof(path_buffer) );
_tsplitpath( pszInitProgram, drive, dir, NULL, NULL );
wsprintf( path_buffer, L"%s%s", drive, dir );
bStatus = (pConfig)(
NULL,
pszUserName,
WTSUserConfigWorkingDirectory,
path_buffer,
wcslen(path_buffer)
);
}
if( FALSE == bStatus )
{
hRes = HRESULT_FROM_WIN32( GetLastError() );
}
} // end (pConfig != NULL)
}
if( NULL != hWtsapi32 )
{
FreeLibrary( hWtsapi32 );
}
//DebugPrintf( _TEXT("SetupHelpAccountTSSettings() ended...\n") );
return hRes;
}
HRESULT
__HelpAssistantAccount::SetupHelpAccountTSRights(
IN BOOL bDel,
IN BOOL bEnable,
IN BOOL bDeleteExisting,
IN DWORD dwPermissions
)
/*++
Routine Description:
This routine configurate TS specific settings
for user account.
Parameters:
pszUserName : Name of user account to configurate.
bDel : TRUE to delete account, FALSE otherwise.
bEnable : TRUE if enable, FALSE otherwise.
dwPermissions : Permission to be enable or disable
Returns:
ERROR_SUCCESS or error code
Note:
Refer to cfgbkend.idl for bDel and bEnable parameter.
--*/
{
BOOL bStatus;
HRESULT hRes = S_OK;
CComPtr<ICfgComp> tsccICfgComp;
IUserSecurity* tsccIUserSecurity = NULL;
DWORD dwNumWinStations = 0;
DWORD dwWinStationSize = 0;
PWS pWinStationList = NULL;
DWORD index;
DWORD dwCfgStatus = ERROR_SUCCESS;
BOOL bManualSetConsole = TRUE;
ULONG cbSecDescLen;
CCriticalSectionLocker l(gm_HelpAccountCS);
CoInitialize(NULL);
//hRes = CoInitializeEx(NULL, COINIT_MULTITHREADED);
hRes = tsccICfgComp.CoCreateInstance( CLSID_CfgComp );
if( FAILED(hRes) )
{
DebugPrintf( _TEXT("CoCreateInstance() failed with error code 0x%08x\n"), hRes );
//MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
hRes = tsccICfgComp->Initialize();
if( FAILED(hRes) )
{
DebugPrintf( _TEXT("tsccICfgComp->Initialize() failed with error code 0x%08x\n"), hRes );
// MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
hRes = tsccICfgComp->QueryInterface(
IID_IUserSecurity,
reinterpret_cast<void **>(&tsccIUserSecurity)
);
if( FAILED(hRes) || NULL == tsccIUserSecurity)
{
DebugPrintf( _TEXT("QueryInterface() failed with error code 0x%08x\n"), hRes );
// MYASSERT(FALSE);
goto CLEANUPANDEXIT;
}
//
// Setting Default security shadow permission
//
hRes = tsccIUserSecurity->ModifyDefaultSecurity(
L"",
gm_bstrHelpAccountName,
dwPermissions,
bDel,
bEnable,
FALSE,
&dwCfgStatus
);
if( FAILED(hRes) || ERROR_SUCCESS != dwCfgStatus )
{
DebugPrintf(
_TEXT("ModifyDefaultSecurity on default security return 0x%08x, dwCfgStatus = %d\n"),
hRes,
dwCfgStatus
);
// MYASSERT(FALSE);
//
// Continue on to setting wtsapi32, we still can
// RDS on non-console winstation
//
hRes = S_OK;
dwCfgStatus = ERROR_SUCCESS;
}
// retrieve a list of winstation name
hRes = tsccICfgComp->GetWinstationList(
&dwNumWinStations,
&dwWinStationSize,
&pWinStationList
);
if( FAILED(hRes) )
{
DebugPrintf( _TEXT("QueryInterface() failed with error code 0x%08x\n"), hRes );
goto CLEANUPANDEXIT;
}
//
// Set TS Logon permission on all winstation
//
for( index = 0; index < dwNumWinStations && ERROR_SUCCESS == dwCfgStatus && SUCCEEDED(hRes); index ++ )
{
if( 0 == _tcsicmp( pWinStationList[index].Name, L"Console" ) )
{
bManualSetConsole = FALSE;
}
dwCfgStatus = 0;
//DebugPrintf( _TEXT("Name of Winstation : %s\n"), pWinStationList[index].Name );
// check if custom security exist for this winstation
dwCfgStatus = RegWinStationQuerySecurity(
SERVERNAME_CURRENT,
pWinStationList[index].Name,
NULL,
0,
&cbSecDescLen
);
if( ERROR_INSUFFICIENT_BUFFER == dwCfgStatus )
{
DebugPrintf( _TEXT("Winstation : %s has custom security\n"), pWinStationList[index].Name );
// From TS Setup, Insufficient buffer means the winstation has custom security
hRes = tsccIUserSecurity->ModifyUserAccess(
pWinStationList[index].Name,
gm_bstrHelpAccountName,
dwPermissions,
bDel,
bEnable,
bDeleteExisting,
FALSE,
&dwCfgStatus
);
if( FAILED(hRes) || ERROR_SUCCESS != dwCfgStatus )
{
DebugPrintf(
_TEXT("ModifyUserAccess return 0x%08x, dwCfgStatus = %d\n"),
hRes,
dwCfgStatus
);
// MYASSERT(FALSE);
continue;
}
}
else if( ERROR_FILE_NOT_FOUND == dwCfgStatus )
{
// no custom security for this winstation
dwCfgStatus = ERROR_SUCCESS;
}
else
{
DebugPrintf(
_TEXT("RegWinStationQuerySecurity returns %d\n"),
dwCfgStatus
);
// MYASSERT(FALSE);
}
}
if( ERROR_SUCCESS != dwCfgStatus || FAILED(hRes) )
{
DebugPrintf(
_TEXT("ModifyUserAccess() Loop failed - 0x%08x, %d...\n"),
hRes, dwCfgStatus
);
goto CLEANUPANDEXIT;
}
if( TRUE == bManualSetConsole )
{
//
// Setting Console shadow permission, we don't know when GetWinstationList()
// will return us Console so...
//
hRes = tsccIUserSecurity->ModifyUserAccess(
L"Console",
gm_bstrHelpAccountName,
dwPermissions,
bDel,
bEnable,
bDeleteExisting,
FALSE,
&dwCfgStatus
);
if( FAILED(hRes) || ERROR_SUCCESS != dwCfgStatus )
{
DebugPrintf(
_TEXT("ModifyUserAccess on console return 0x%08x, dwCfgStatus = %d\n"),
hRes,
dwCfgStatus
);
// MYASSERT(FALSE);
//
// Continue on to setting wtsapi32, we still can
// RDS on non-console winstation
//
hRes = S_OK;
dwCfgStatus = ERROR_SUCCESS;
// goto CLEANUPANDEXIT;
}
}
if( SUCCEEDED(hRes) )
{
// Force TermSrv to reload default security for console and winstation
// if termsrv is running, it's OK to fail since we do ForceUpdate().
// ForceUpdate() only force termsrv to reload custom security for
// winstation not default security.
DebugPrintf(_TEXT("_WinStationReInitializeSecurity...\n"));
if( _WinStationReInitializeSecurity( SERVERNAME_CURRENT ) == FALSE )
{
DebugPrintf(_TEXT("_WinStationReInitializeSecurity failed with error code %d...\n"), GetLastError() );
}
tsccICfgComp->ForceUpdate();
}
CLEANUPANDEXIT:
if( NULL != pWinStationList )
{
CoTaskMemFree( pWinStationList );
}
if( NULL != tsccIUserSecurity )
{
tsccIUserSecurity->Release();
}
if( tsccICfgComp )
{
tsccICfgComp.Release();
}
DebugPrintf( _TEXT("SetupHelpAccountTSRights() ended...\n") );
CoUninitialize();
return hRes;
}
HRESULT
__HelpAssistantAccount::ResetHelpAccountPassword(
IN LPCTSTR pszPassword
)
/*++
Routine Description:
This routine change help assistant account password and
store corresponding password to LSA.
Parameters:
None.
Returns:
ERROR_SUCCESS or error code.
Note:
If help account is disable or not present on local machine,
we assume no help can be done on local machine.
--*/
{
DWORD dwStatus;
BOOL bEnabled;
TCHAR szNewPassword[MAX_HELPACCOUNT_PASSWORD+1];
CCriticalSectionLocker l(gm_HelpAccountCS);
memset(
szNewPassword,
0,
sizeof(szNewPassword)
);
//
// Check if help assistant account is enabled.
//
dwStatus = IsLocalAccountEnabled(
gm_bstrHelpAccountName,
&bEnabled
);
if( ERROR_SUCCESS != dwStatus )
{
dwStatus = SESSMGR_E_HELPACCOUNT;
goto CLEANUPANDEXIT;
}
//
// Account is disable, don't reset password
//
if( FALSE == bEnabled )
{
// help account is disabled, no help is available from this box
DebugPrintf(
_TEXT("Account is disabled...\n")
);
dwStatus = SESSMGR_E_HELPACCOUNT;
goto CLEANUPANDEXIT;
}
//
// if account is enabled, re-set password
//
if( NULL == pszPassword || 0 == lstrlen(pszPassword) )
{
// we are asked to generate a random password,
// bail out if can't create random password
ZeroMemory( szNewPassword, sizeof(szNewPassword) / sizeof(szNewPassword[0]) );
dwStatus = CreatePassword( szNewPassword, sizeof(szNewPassword)/sizeof(szNewPassword[0])-1 );
if( ERROR_SUCCESS != dwStatus )
{
MYASSERT( FALSE );
goto CLEANUPANDEXIT;
}
}
else
{
memset(
szNewPassword,
0,
sizeof(szNewPassword)
);
_tcsncpy(
szNewPassword,
pszPassword,
min(lstrlen(pszPassword), MAX_HELPACCOUNT_PASSWORD)
);
}
//
// Change the password and cache with LSA, if caching failed
// reset password back to what we have before.
//
dwStatus = ChangeLocalAccountPassword(
gm_bstrHelpAccountName,
gm_bstrHelpAccountPwd,
szNewPassword
);
if( ERROR_SUCCESS == dwStatus )
{
//
// save the password with LSA
//
dwStatus = StoreKeyWithLSA(
HELPASSISTANTACCOUNT_PASSWORDKEY,
(PBYTE) szNewPassword,
(lstrlen(szNewPassword)+1) * sizeof(TCHAR)
);
if( ERROR_SUCCESS != dwStatus )
{
DWORD dwStatus1;
//
// something wrong with storing password, reset password
// back so we can recover next time.
//
dwStatus1 = ChangeLocalAccountPassword(
gm_bstrHelpAccountName,
szNewPassword,
gm_bstrHelpAccountPwd
);
if( ERROR_SUCCESS != dwStatus1 )
{
//
// we have a big problem here, should we delete the account
// and recreate one again?
//
}
}
else
{
//
// make a copy of new password.
//
gm_bstrHelpAccountPwd = szNewPassword;
}
}
CLEANUPANDEXIT:
return HRESULT_FROM_WIN32(dwStatus);
}
DWORD
__HelpAssistantAccount::EnableAccountRights(
BOOL bEnable,
DWORD dwNumRights,
LPTSTR* rights
)
/*++
--*/
{
DWORD dwStatus;
LSA_UNICODE_STRING UserRightString[1];
LSA_HANDLE PolicyHandle = NULL;
//
// create an lsa policy for it
dwStatus = OpenPolicy(
NULL,
POLICY_ALL_ACCESS,
&PolicyHandle
);
if( ERROR_SUCCESS == dwStatus )
{
for( DWORD i=0; i < dwNumRights && ERROR_SUCCESS == dwStatus ; i++ )
{
DebugPrintf(
_TEXT("%s Help Assistant rights %s\n"),
(bEnable) ? _TEXT("Enable") : _TEXT("Disable"),
rights[i]
);
// Remote interactive right
InitLsaString(
UserRightString,
rights[i]
);
if( bEnable )
{
dwStatus = LsaAddAccountRights(
PolicyHandle,
gm_pbHelpAccountSid,
UserRightString,
1
);
}
else
{
dwStatus = LsaRemoveAccountRights(
PolicyHandle,
gm_pbHelpAccountSid,
FALSE,
UserRightString,
1
);
}
DebugPrintf(
_TEXT("\tEnable/disable account rights %s returns 0x%08x\n"),
rights[i],
dwStatus
);
if( dwStatus == STATUS_NO_SUCH_PRIVILEGE )
{
dwStatus = ERROR_SUCCESS;
}
}
LsaClose(PolicyHandle);
}
return dwStatus;
}
HRESULT
__HelpAssistantAccount::EnableRemoteInteractiveRight(
IN BOOL bEnable
)
/*++
Routine Description:
Routine to enable/disable Help Assistant account remote interactive
logon rights.
Parameters:
bEnable : TRUE to enable, FALSE to disable.
Returns:
S_OK or error code.
--*/
{
LPTSTR rights[1];
DWORD dwStatus;
rights[0] = SE_REMOTE_INTERACTIVE_LOGON_NAME;
dwStatus = EnableAccountRights( bEnable, 1, rights );
return HRESULT_FROM_WIN32(dwStatus);
}
BOOL
__HelpAssistantAccount::IsAccountHelpAccount(
IN PBYTE pbSid,
IN DWORD cbSid
)
/*++
Routine Description:
Check if a user is Help Assistant.
Parameters:
pbSid : Pointer to user SID to checked.
cbSid : Size of user SID.
Returns:
TRUE/FALSE
--*/
{
BOOL bSuccess = FALSE;
if( NULL != pbSid )
{
// make sure it is a valid sid.
bSuccess = IsValidSid( (PSID)pbSid );
if( FALSE == bSuccess )
{
SetLastError( ERROR_INVALID_SID );
}
else
{
bSuccess = EqualSid( gm_pbHelpAccountSid, pbSid );
if( FALSE == bSuccess )
{
SetLastError( ERROR_INVALID_DATA );
}
}
}
return bSuccess;
}
HRESULT
__HelpAssistantAccount::EnableHelpAssistantAccount(
BOOL bEnable
)
/*++
--*/
{
DWORD dwStatus;
dwStatus = EnableLocalAccount( gm_bstrHelpAccountName, bEnable );
DebugPrintf(
_TEXT("%s %s returns %d\n"),
gm_bstrHelpAccountName,
(bEnable) ? _TEXT("Enable") : _TEXT("Disable"),
dwStatus
);
return HRESULT_FROM_WIN32( dwStatus );
}