/*++ Copyright (c) 1999-2000 Microsoft Corporation Module Name: policy.cpp Abstract: RDS Policy related function Author: HueiWang 5/2/2000 --*/ #include "stdafx.h" #include "policy.h" #ifndef __WIN9XBUILD__ extern "C" BOOLEAN RegDenyTSConnectionsPolicy(); typedef struct __RDSLevelShadowMap { SHADOWCLASS shadowClass; REMOTE_DESKTOP_SHARING_CLASS rdsLevel; } RDSLevelShadowMap; static const RDSLevelShadowMap ShadowMap[] = { { Shadow_Disable, NO_DESKTOP_SHARING }, // No RDS sharing { Shadow_EnableInputNotify, CONTROLDESKTOP_PERMISSION_REQUIRE }, // Interact with user permission { Shadow_EnableInputNoNotify, CONTROLDESKTOP_PERMISSION_NOT_REQUIRE }, // Interact without user permission { Shadow_EnableNoInputNotify, VIEWDESKTOP_PERMISSION_REQUIRE}, // View with user permission { Shadow_EnableNoInputNoNotify, VIEWDESKTOP_PERMISSION_NOT_REQUIRE } // View without user permission }; DWORD GetPolicyAllowGetHelpSetting( HKEY hKey, LPCTSTR pszKeyName, LPCTSTR pszValueName, IN DWORD* value ) /*++ Routine Description: Routine to query policy registry value. Parameters: hKey : Currently open registry key. pszKeyName : Pointer to a null-terminated string containing the name of the subkey to open. pszValueName : Pointer to a null-terminated string containing the name of the value to query value : Pointer to DWORD to receive GetHelp policy setting. Returns: ERROR_SUCCESS or error code from RegOpenKeyEx(). --*/ { DWORD dwStatus; HKEY hPolicyKey = NULL; DWORD dwType; DWORD cbData; // // Open registry key for system policy // dwStatus = RegOpenKeyEx( hKey, pszKeyName, 0, KEY_READ, &hPolicyKey ); if( ERROR_SUCCESS == dwStatus ) { // query value cbData = 0; dwType = 0; dwStatus = RegQueryValueEx( hPolicyKey, pszValueName, NULL, &dwType, NULL, &cbData ); if( ERROR_SUCCESS == dwStatus ) { if( REG_DWORD == dwType ) { cbData = sizeof(DWORD); // our registry value is REG_DWORD, if different type, // assume not exist. dwStatus = RegQueryValueEx( hPolicyKey, pszValueName, NULL, &dwType, (LPBYTE)value, &cbData ); ASSERT( ERROR_SUCCESS == dwStatus ); } else { // bad registry key type, assume // key does not exist. dwStatus = ERROR_FILE_NOT_FOUND; } } RegCloseKey( hPolicyKey ); } return dwStatus; } SHADOWCLASS MapRDSLevelToTSShadowSetting( IN REMOTE_DESKTOP_SHARING_CLASS RDSLevel ) /*++ Routine Description: Convert TS Shadow settings to our RDS sharing level. Parameter: TSShadowClass : TS Shadow setting. Returns: REMOTE_DESKTOP_SHARING_CLASS --*/ { SHADOWCLASS shadowClass; for( int i=0; i < sizeof(ShadowMap)/sizeof(ShadowMap[0]); i++) { if( ShadowMap[i].rdsLevel == RDSLevel ) { break; } } if( i < sizeof(ShadowMap)/sizeof(ShadowMap[0]) ) { shadowClass = ShadowMap[i].shadowClass; } else { MYASSERT(FALSE); shadowClass = Shadow_Disable; } return shadowClass; } REMOTE_DESKTOP_SHARING_CLASS MapTSShadowSettingToRDSLevel( SHADOWCLASS TSShadowClass ) /*++ Routine Description: Convert TS Shadow settings to our RDS sharing level. Parameter: TSShadowClass : TS Shadow setting. Returns: REMOTE_DESKTOP_SHARING_CLASS --*/ { REMOTE_DESKTOP_SHARING_CLASS level; for( int i=0; i < sizeof(ShadowMap)/sizeof(ShadowMap[0]); i++) { if( ShadowMap[i].shadowClass == TSShadowClass ) { break; } } if( i < sizeof(ShadowMap)/sizeof(ShadowMap[0]) ) { level = ShadowMap[i].rdsLevel; } else { MYASSERT(FALSE); level = NO_DESKTOP_SHARING; } return level; } DWORD MapSessionIdToWinStationName( IN ULONG ulSessionID, OUT PWINSTATIONNAME pWinstationName ) /*++ Routine Description: Find out TS winstation name for the session specified. Parameters: ulSessionID : TS Session ID to query. pWinstationName : Pointer to WINSTATIONNAME to receive name of WinStation for Session ID specified. Returns: ERROR_SUCCESS or Error code. -*/ { BOOL bSuccess; DWORD dwStatus; LPTSTR pBuffer = NULL; DWORD bytesReturned; bSuccess = WTSQuerySessionInformation( WTS_CURRENT_SERVER, ulSessionID, WTSWinStationName, &pBuffer, &bytesReturned ); if( TRUE == bSuccess ) { LPTSTR pszChr; // // WINSTATIONNAME returned from WTSQuerySessionInformation // has '#...' appended to it, we can't use it to query // WINSTATION configuration as RegApi look for exact WINSTATION // name in registry and so it will return default value. // pszChr = _tcschr( pBuffer, _TEXT('#') ); if( NULL != pszChr ) { *pszChr = _TEXT('\0'); } ZeroMemory( pWinstationName, sizeof( WINSTATIONNAME ) ); CopyMemory( pWinstationName, pBuffer, bytesReturned ); dwStatus = ERROR_SUCCESS; } else { dwStatus = GetLastError(); } if( NULL != pBuffer ) { WTSFreeMemory( pBuffer ); } return dwStatus; } DWORD GetWinStationConfig( IN ULONG ulSessionId, OUT WINSTATIONCONFIG2* pConfig ) /*++ Routine Description: Retrieve WINSTATIONCONFIG2 for session specified. Parameters: ulSessionId : TS Session to query. pConfig : Pointer to WINSTATIONCONIF2 to receive result. Returns: ERROR_SUCCESS or error code. --*/ { WINSTATIONNAME WinStationName; DWORD dwStatus; ULONG length = 0; dwStatus = MapSessionIdToWinStationName( ulSessionId, WinStationName ); if( ERROR_SUCCESS == dwStatus ) { dwStatus = RegWinStationQuery( NULL, WinStationName, pConfig, sizeof(WINSTATIONCONFIG2), &length ); } return dwStatus; } #endif BOOL IsUserAllowToGetHelp( IN ULONG ulSessionId, IN LPCTSTR pszUserSid ) /*++ Routine Description: Determine if caller can 'GetHelp' Parameters: ulSessionId : User's TS logon ID. pszUserSid : User's SID in textual form. Returns: TRUE/FALSE Note: Must have impersonate user first. --*/ { BOOL bAllow; DWORD dwStatus; DWORD dwAllow; LPTSTR pszUserHive = NULL; MYASSERT( NULL != pszUserSid ); // // Must be able to GetHelp from machine // bAllow = TSIsMachinePolicyAllowHelp(); if( TRUE == bAllow ) { pszUserHive = (LPTSTR)LocalAlloc( LPTR, sizeof(TCHAR) * (lstrlen(pszUserSid) + lstrlen(RDS_GROUPPOLICY_SUBTREE) + 2 ) ); lstrcpy( pszUserHive, pszUserSid ); lstrcat( pszUserHive, _TEXT("\\") ); lstrcat( pszUserHive, RDS_GROUPPOLICY_SUBTREE ); // // Query user level AllowGetHelp setting. dwStatus = GetPolicyAllowGetHelpSetting( HKEY_USERS, pszUserHive, RDS_ALLOWGETHELP_VALUENAME, &dwAllow ); if( ERROR_SUCCESS == dwStatus ) { bAllow = (POLICY_ENABLE == dwAllow); } else { // no configuration for this user, assume GetHelp // is enabled. bAllow = TRUE; } } if( NULL != pszUserHive ) { LocalFree( pszUserHive ); } return bAllow; } DWORD GetSystemRDSLevel( IN ULONG ulSessionId, OUT REMOTE_DESKTOP_SHARING_CLASS* pSharingLevel ) /*++ Routine Description: Retrieve policy setting for remote desktop sharing level. Parameters: ulSessionId : TS session ID, unuse if TS group policy is set. pSharingLever : Pointer to REMOTE_DESKTOP_SHARING_CLASS to receive machine RDS level. Returns: REMOTE_DESKTOP_SHARING_CLASS --*/ { DWORD dwStatus; #ifndef __WIN9XBUILD__ WINSTATIONCONFIG2 WSConfig; ULONG length = 0; // // TS Group Policy does not have machine level shadow // setting, only TSCC has this setting, and TSCC setting // is based on WINSTATION not entire machine. // // We can't query TS since TS already merger all policy // setting into USERCONFIG which might not be correct // dwStatus = GetWinStationConfig( ulSessionId, &WSConfig ); if( ERROR_SUCCESS == dwStatus ) { if( TRUE == WSConfig.Config.User.fInheritShadow ) { // Shadow config is inherite from user properies // so we query user level setting. dwStatus = GetUserRDSLevel( ulSessionId, pSharingLevel ); } else { *pSharingLevel = MapTSShadowSettingToRDSLevel( WSConfig.Config.User.Shadow ); } } #else // TODO - revisit this for Win9x Build MYASSERT(FALSE); *pSharingLevel = CONTROLDESKTOP_PERMISSION_REQUIRE; dwStatus = ERROR_SUCCESS; #endif return dwStatus; } DWORD GetUserRDSLevel( IN ULONG ulSessionId, OUT REMOTE_DESKTOP_SHARING_CLASS* pLevel ) /*++ same as GetSystemRDSLevel() except it retrieve currently logon user's RDS level. --*/ { DWORD dwStatus; #ifndef __WIN9XBUILD__ BOOL bSuccess; WINSTATIONCONFIG WSConfig; DWORD dwByteReturned; memset( &WSConfig, 0, sizeof(WSConfig) ); // Here we call WInStationQueryInformation() since WTSAPI require // few calls to get the same result bSuccess = WinStationQueryInformation( WTS_CURRENT_SERVER, ulSessionId, WinStationConfiguration, &WSConfig, sizeof( WSConfig ), &dwByteReturned ); if( TRUE == bSuccess ) { dwStatus = ERROR_SUCCESS; *pLevel = MapTSShadowSettingToRDSLevel( WSConfig.User.Shadow ); } else { dwStatus = GetLastError(); } #else // TODO - revisit this for Win9x Build MYASSERT(FALSE); *pLevel = CONTROLDESKTOP_PERMISSION_REQUIRE; dwStatus = ERROR_SUCCESS; #endif return dwStatus; } DWORD ConfigSystemGetHelp( BOOL bEnable ) /*++ Routine Description: Enable/disable 'GetHelp' on local machine. Parameters: bEnable : TRUE to enable, FALSE otherwise. Returns: ERROR_SUCCESS or error code. --*/ { DWORD dwStatus = ERROR_SUCCESS; BOOL bMember; HKEY hKey = NULL; DWORD dwValue; BOOL bAllowHelp; #ifndef __WIN9XBUILD__ dwStatus = IsUserAdmin( &bMember ); if( ERROR_SUCCESS != dwStatus ) { goto CLEANUPANDEXIT; } if( FALSE == bMember ) { dwStatus = ERROR_ACCESS_DENIED; goto CLEANUPANDEXIT; } #endif // We only check if Group Policy has this setting and no // checking on TSCC deny connection setting since // TSCC set WINSTATION deny connection not entire machine // and we can't be sure which connection that connection // is denied, so it is still possible that GetHelp is enabled // but WINSTATION's deny connection is still set to TRUE // in this case, no help is available even this funtion // return SUCCEEDED. // verify no group policy on this dwStatus = GetPolicyAllowGetHelpSetting( HKEY_LOCAL_MACHINE, RDS_GROUPPOLICY_SUBTREE, RDS_ALLOWGETHELP_VALUENAME, &dwValue ); if( ERROR_SUCCESS == dwStatus ) { dwStatus = ERROR_ACCESS_DENIED; } else { // // Write to registry // dwStatus = RegCreateKeyEx( HKEY_LOCAL_MACHINE, RDS_MACHINEPOLICY_SUBTREE, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL ); if( ERROR_SUCCESS == dwStatus ) { dwValue = (bEnable) ? POLICY_ENABLE : POLICY_DISABLE; dwStatus = RegSetValueEx( hKey, RDS_ALLOWGETHELP_VALUENAME, 0, REG_DWORD, (LPBYTE) &dwValue, sizeof(DWORD) ); } MYASSERT( ERROR_SUCCESS == dwStatus ); } #ifndef __WIN9XBUILD__ CLEANUPANDEXIT: #endif if( NULL != hKey ) { RegCloseKey(hKey); } return dwStatus; } DWORD ConfigSystemRDSLevel( IN REMOTE_DESKTOP_SHARING_CLASS level ) /*++ Routine Description: This routine set machine wide RDS level. Parameters: level : new RDS level. Returns: ERROR_SUCCESS or error code. --*/ { // // TODO - revisit this, on Win2K and Whilster, // group policy override this setting via User Properties // or if group policy is not set, TSCC can override this. // and since a user account always has some default value // for this, so we have no way to determine whether this is // a default or else, so we simple return ERROR for Win2K // and Whilster platform. // // // TODO : For legacy platform, Win9x/NT40/TS40, we need to figure // out how/where poledit put this setting, for now, // just return error. // DWORD dwStatus = ERROR_INVALID_FUNCTION; return dwStatus; } DWORD ConfigUserSessionRDSLevel( IN ULONG ulSessionId, IN REMOTE_DESKTOP_SHARING_CLASS level ) /*++ --*/ { #ifndef __WIN9XBUILD__ WINSTATIONCONFIG winstationConfig; SHADOWCLASS shadowClass = MapRDSLevelToTSShadowSetting( level ); BOOL bSuccess; DWORD dwLength; DWORD dwStatus; memset( &winstationConfig, 0, sizeof(winstationConfig) ); bSuccess = WinStationQueryInformation( WTS_CURRENT_SERVER, ulSessionId, WinStationConfiguration, &winstationConfig, sizeof(winstationConfig), &dwLength ); if( TRUE == bSuccess ) { winstationConfig.User.Shadow = shadowClass; bSuccess = WinStationSetInformation( WTS_CURRENT_SERVER, ulSessionId, WinStationConfiguration, &winstationConfig, sizeof(winstationConfig) ); } if( TRUE == bSuccess ) { dwStatus = ERROR_SUCCESS; } else { dwStatus = GetLastError(); } return dwStatus; #else return ERROR_SUCCESS; #endif } #if 0 HRESULT PolicyGetAllowUnSolicitedHelp( BOOL* bAllow ) /*++ --*/ { HRESULT hRes; CComPtr IRegSetting; hRes = IRegSetting.CoCreateInstance( CLSID_RARegSetting ); if( SUCCEEDED(hRes) ) { hRes = IRegSetting->get_AllowUnSolicited(bAllow); } MYASSERT( SUCCEEDED(hRes) ); return hRes; } #endif HRESULT PolicyGetMaxTicketExpiry( LONG* value ) /*++ --*/ { HRESULT hRes; CComPtr IRegSetting; hRes = IRegSetting.CoCreateInstance( CLSID_RARegSetting ); if( SUCCEEDED(hRes) ) { hRes = IRegSetting->get_MaxTicketExpiry(value); } MYASSERT( SUCCEEDED(hRes) ); return hRes; }