/************************************************************************* * * winset.c * * Window station set APS * * Copyright Microsoft Corporation, 1998 * * *************************************************************************/ /* * Includes */ #include "precomp.h" #pragma hdrstop #include "conntfy.h" // for SetLockedState /* * External Procedures */ NTSTATUS xxxWinStationSetInformation( ULONG, WINSTATIONINFOCLASS, PVOID, ULONG ); VOID _ReadUserProfile( PWCHAR, PWCHAR, PUSERCONFIG ); extern BOOL IsCallerSystem( VOID ); extern BOOL IsCallerAdmin( VOID ); /* * Internal Procedures used */ NTSTATUS _SetConfig( PWINSTATION, PWINSTATIONCONFIG, ULONG ); NTSTATUS _SetPdParams( PWINSTATION, PPDPARAMS, ULONG ); NTSTATUS _SetBeep( PWINSTATION, PBEEPINPUT, ULONG ); NTSTATUS WinStationShadowChangeMode( PWINSTATION, PWINSTATIONSHADOW, ULONG ); NTSTATUS FlushVirtualInput( PWINSTATION, VIRTUALCHANNELCLASS, ULONG ); NTSTATUS RpcCheckClientAccess( PWINSTATION pWinStation, ACCESS_MASK DesiredAccess, BOOLEAN AlreadyImpersonating ); NTSTATUS CheckWireBuffer(WINSTATIONINFOCLASS InfoClass, PVOID WireBuf, ULONG WireBufLen, PVOID *ppLocalBuf, PULONG pLocalBufLen); NTSTATUS ValidateInputConfig( PWINSTATION pWinStation, PWINSTATIONCONFIG pConfig ); /* * Global data */ typedef ULONG_PTR (*PFN)(); HMODULE ghNetApiDll = NULL; PFN pNetGetAnyDCName = NULL; PFN pNetApiBufferFree = NULL; /* * External data */ NTSTATUS _CheckCallerLocalAndSystem() /*++ Checking caller is calling from local and also is running under system context --*/ { NTSTATUS Status; BOOL bRevert = FALSE; UINT LocalFlag; Status = RpcImpersonateClient( NULL ); if( Status != RPC_S_OK ) { DBGPRINT((" RpcImpersonateClient() failed : 0x%x\n",Status)); Status = STATUS_CANNOT_IMPERSONATE; goto CLEANUPANDEXIT; } bRevert = TRUE; // // Inquire if local RPC call // Status = I_RpcBindingIsClientLocal( 0, // Active RPC call we are servicing &LocalFlag ); if( Status != RPC_S_OK ) { DBGPRINT((" I_RpcBindingIsClientLocal() failed : 0x%x\n",Status)); Status = STATUS_ACCESS_DENIED; goto CLEANUPANDEXIT; } if( !LocalFlag ) { DBGPRINT((" Not a local client call\n")); Status = STATUS_ACCESS_DENIED; goto CLEANUPANDEXIT; } Status = (IsCallerSystem()) ? STATUS_SUCCESS : STATUS_ACCESS_DENIED; CLEANUPANDEXIT: if( TRUE == bRevert ) { RpcRevertToSelf(); } return Status; } /******************************************************************************* * * xxxWinStationSetInformation * * set window station information (worker routine) * * ENTRY: * pWinStation (input) * pointer to citrix window station structure * WinStationInformationClass (input) * Specifies the type of information to set at the specified window * station object. * pWinStationInformation (input) * A pointer to a buffer that contains information to set for the * specified window station. The format and contents of the buffer * depend on the specified information class being set. * WinStationInformationLength (input) * Specifies the length in bytes of the window station information * buffer. * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS xxxWinStationSetInformation( ULONG LogonId, WINSTATIONINFOCLASS WinStationInformationClass, PVOID pWinStationInformation, ULONG WinStationInformationLength ) { NTSTATUS Status = STATUS_SUCCESS; PWINSTATION pWinStation; ULONG cbReturned; WINSTATION_APIMSG msg; PWINSTATIONCONFIG pConfig; ULONG ConfigLength; PPDPARAMS pPdParams; ULONG PdParamsLength; RPC_STATUS RpcStatus; TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationSetInformation LogonId=%d, Class=%d\n", LogonId, (ULONG)WinStationInformationClass )); /* * Find the WinStation * Return error if not found or currently terminating. */ pWinStation = FindWinStationById( LogonId, FALSE ); if ( !pWinStation ) return( STATUS_CTX_WINSTATION_NOT_FOUND ); if ( pWinStation->Terminating ) { ReleaseWinStation( pWinStation ); return( STATUS_CTX_CLOSE_PENDING ); } /* * Verify that client has SET access */ Status = RpcCheckClientAccess( pWinStation, WINSTATION_SET, FALSE ); if ( !NT_SUCCESS( Status ) ) { ReleaseWinStation( pWinStation ); return( Status ); } switch ( WinStationInformationClass ) { case WinStationPdParams : Status = CheckWireBuffer(WinStationInformationClass, pWinStationInformation, WinStationInformationLength, &pPdParams, &PdParamsLength); if ( !NT_SUCCESS(Status) ) { break; } if ( pWinStation->hStack ) { // Check for availability if ( pWinStation->pWsx && pWinStation->pWsx->pWsxIcaStackIoControl ) { Status = pWinStation->pWsx->pWsxIcaStackIoControl( pWinStation->pWsxContext, pWinStation->hIca, pWinStation->hStack, IOCTL_ICA_STACK_SET_PARAMS, pPdParams, PdParamsLength, NULL, 0, NULL ); } else { Status = STATUS_INVALID_INFO_CLASS; } } LocalFree((PVOID)pPdParams); break; case WinStationConfiguration : Status = CheckWireBuffer(WinStationInformationClass, pWinStationInformation, WinStationInformationLength, &pConfig, &ConfigLength); if ( !NT_SUCCESS(Status) ) { break; } Status = _SetConfig( pWinStation, pConfig, ConfigLength ); LocalFree((PVOID)pConfig); break; case WinStationTrace : RpcStatus = RpcImpersonateClient( NULL ); if( RpcStatus != RPC_S_OK ) { Status = STATUS_CANNOT_IMPERSONATE; break; } if (!IsCallerAdmin() && !IsCallerSystem()) { Status = STATUS_ACCESS_DENIED; } RpcRevertToSelf(); if (!NT_SUCCESS(Status)) { break; } if ( WinStationInformationLength < sizeof(ICA_TRACE) ) { Status = STATUS_BUFFER_TOO_SMALL; break; } if ( pWinStation->hIca ) { Status = IcaIoControl( pWinStation->hIca, IOCTL_ICA_SET_TRACE, pWinStationInformation, WinStationInformationLength, NULL, 0, NULL ); } break; case WinStationSystemTrace : RpcStatus = RpcImpersonateClient( NULL ); if( RpcStatus != RPC_S_OK ) { Status = STATUS_CANNOT_IMPERSONATE; break; } if (!IsCallerAdmin() && !IsCallerSystem()) { Status = STATUS_ACCESS_DENIED; } RpcRevertToSelf(); if (!NT_SUCCESS(Status)) { break; } if ( WinStationInformationLength < sizeof(ICA_TRACE) ) { Status = STATUS_BUFFER_TOO_SMALL; break; } /* * Open ICA device driver */ if ( hTrace == NULL ) { Status = IcaOpen( &hTrace ); if ( !NT_SUCCESS(Status) ) hTrace = NULL; } if ( hTrace ) { Status = IcaIoControl( hTrace, IOCTL_ICA_SET_SYSTEM_TRACE, pWinStationInformation, WinStationInformationLength, NULL, 0, NULL ); } break; case WinStationPrinter : break; case WinStationBeep : if (WinStationInformationLength < sizeof(BEEPINPUT)) { Status = STATUS_BUFFER_TOO_SMALL ; break; } Status = _SetBeep( pWinStation, (PBEEPINPUT) pWinStationInformation, WinStationInformationLength ); break; case WinStationEncryptionOff : if ( pWinStation->hStack ) { // Check for availability if ( pWinStation->pWsx && pWinStation->pWsx->pWsxIcaStackIoControl ) { Status = pWinStation->pWsx->pWsxIcaStackIoControl( pWinStation->pWsxContext, pWinStation->hIca, pWinStation->hStack, IOCTL_ICA_STACK_ENCRYPTION_OFF, pWinStationInformation, WinStationInformationLength, NULL, 0, NULL ); } else { Status = STATUS_INVALID_INFO_CLASS; } } break; case WinStationEncryptionPerm : if ( pWinStation->hStack ) { // Check for availability if ( pWinStation->pWsx && pWinStation->pWsx->pWsxIcaStackIoControl ) { Status = pWinStation->pWsx->pWsxIcaStackIoControl( pWinStation->pWsxContext, pWinStation->hIca, pWinStation->hStack, IOCTL_ICA_STACK_ENCRYPTION_PERM, pWinStationInformation, WinStationInformationLength, NULL, 0, NULL ); } else { Status = STATUS_INVALID_INFO_CLASS; } } break; case WinStationSecureDesktopEnter : if ( pWinStation->hStack ) { // Check for availability if ( pWinStation->pWsx && pWinStation->pWsx->pWsxIcaStackIoControl ) { Status = pWinStation->pWsx->pWsxIcaStackIoControl( pWinStation->pWsxContext, pWinStation->hIca, pWinStation->hStack, IOCTL_ICA_STACK_ENCRYPTION_ENTER, pWinStationInformation, WinStationInformationLength, NULL, 0, NULL ); } else { Status = STATUS_INVALID_INFO_CLASS; } } break; case WinStationSecureDesktopExit : if ( pWinStation->hStack ) { // Check for availability if ( pWinStation->pWsx && pWinStation->pWsx->pWsxIcaStackIoControl ) { Status = pWinStation->pWsx->pWsxIcaStackIoControl( pWinStation->pWsxContext, pWinStation->hIca, pWinStation->hStack, IOCTL_ICA_STACK_ENCRYPTION_EXIT, pWinStationInformation, WinStationInformationLength, NULL, 0, NULL ); } else { Status = STATUS_INVALID_INFO_CLASS; } } break; /* * Give focus to winlogon security desktop * -- used by progman.exe */ case WinStationNtSecurity : /* * Tell the WinStation to Send Winlogon the CTR-ALT-DEL message */ msg.ApiNumber = SMWinStationNtSecurity; Status = SendWinStationCommand( pWinStation, &msg, 0 ); break; case WinStationClientData : // // Handles multiple client data items. The data buffer // format is: // ULONG // Length of next data item // WINSTATIONCLIENTDATA // Including variable length part // ULONG // Length of next data item // WINSTATIONCLIENTDATA // Including variable length part // etc // // WinStationInformationLength is the length of the entire // data buffer. Keep processing client data items until // the buffer is exhausted. // if ( WinStationInformationLength < sizeof(ULONG) + sizeof(WINSTATIONCLIENTDATA) ) { Status = STATUS_INFO_LENGTH_MISMATCH; break; } if ( pWinStation->hStack ) { // Check for availability if ( pWinStation->pWsx && pWinStation->pWsx->pWsxIcaStackIoControl ) { ULONG CurLen; ULONG LenUsed =0; PBYTE CurPtr = (PBYTE)pWinStationInformation; while (LenUsed + sizeof(ULONG) < WinStationInformationLength) { CurLen = *(ULONG UNALIGNED *)CurPtr; LenUsed += sizeof(ULONG); CurPtr += sizeof(ULONG); if ( (LenUsed + CurLen >= LenUsed) && (LenUsed + CurLen <= WinStationInformationLength)) { Status = pWinStation->pWsx->pWsxIcaStackIoControl( pWinStation->pWsxContext, pWinStation->hIca, pWinStation->hStack, IOCTL_ICA_STACK_SET_CLIENT_DATA, CurPtr, CurLen, NULL, 0, NULL ); LenUsed += CurLen; CurPtr += CurLen; }else { Status = STATUS_INVALID_USER_BUFFER; break; } } } else { Status = STATUS_INVALID_INFO_CLASS; } } break; case WinStationInitialProgram : /* * Identify first program, non-consoles only */ if ( LogonId != 0 ) { /* * Tell the WinStation this is the initial program */ msg.ApiNumber = SMWinStationInitialProgram; Status = SendWinStationCommand( pWinStation, &msg, 0 ); } break; case WinStationShadowInfo: Status = _CheckCallerLocalAndSystem(); if( NT_SUCCESS(Status) ) { Status = WinStationShadowChangeMode( pWinStation, (PWINSTATIONSHADOW) pWinStationInformation, WinStationInformationLength ); } break; case WinStationLockedState: { BOOL bLockedState; if (WinStationInformationLength == sizeof(bLockedState)) { bLockedState = * (LPBOOL) pWinStationInformation; Status = SetLockedState (pWinStation, bLockedState); } else { Status = STATUS_INFO_LENGTH_MISMATCH; } break; } case WinStationDisallowAutoReconnect: { RpcStatus = RpcImpersonateClient( NULL ); if( RpcStatus != RPC_S_OK ) { Status = STATUS_CANNOT_IMPERSONATE; break; } if (!IsCallerSystem()) { Status = STATUS_ACCESS_DENIED; } RpcRevertToSelf(); if (Status != STATUS_SUCCESS) { break; } if (WinStationInformationLength == sizeof(BOOLEAN)) { pWinStation->fDisallowAutoReconnect = * (PBOOLEAN) pWinStationInformation; } else { Status = STATUS_INFO_LENGTH_MISMATCH; } break; } case WinStationMprNotifyInfo: { Status = _CheckCallerLocalAndSystem(); if (Status != STATUS_SUCCESS) { break; } if (WinStationInformationLength == sizeof(ExtendedClientCredentials)) { pExtendedClientCredentials pMprInfo ; pMprInfo = (pExtendedClientCredentials) pWinStationInformation; wcsncpy(g_MprNotifyInfo.Domain, pMprInfo->Domain, EXTENDED_DOMAIN_LEN); g_MprNotifyInfo.Domain[EXTENDED_DOMAIN_LEN] = L'\0'; wcsncpy(g_MprNotifyInfo.UserName, pMprInfo->UserName, EXTENDED_USERNAME_LEN); g_MprNotifyInfo.UserName[EXTENDED_USERNAME_LEN] = L'\0'; wcsncpy(g_MprNotifyInfo.Password, pMprInfo->Password, EXTENDED_PASSWORD_LEN); g_MprNotifyInfo.Password[EXTENDED_PASSWORD_LEN] = L'\0'; } else { Status = STATUS_INFO_LENGTH_MISMATCH; } break; } case WinStationExecSrvSystemPipe: RpcStatus = RpcImpersonateClient( NULL ); if( RpcStatus != RPC_S_OK ) { Status = STATUS_CANNOT_IMPERSONATE; break; } if (!IsCallerSystem()) { Status = STATUS_ACCESS_DENIED; } RpcRevertToSelf(); if (Status != STATUS_SUCCESS) { break; } if ( WinStationInformationLength <= ( EXECSRVPIPENAMELEN * sizeof(WCHAR) ) ) { memcpy( pWinStation->ExecSrvSystemPipe, pWinStationInformation, WinStationInformationLength ); } break; default: /* * Fail the call */ Status = STATUS_INVALID_INFO_CLASS; break; } ReleaseWinStation( pWinStation ); TRACE((hTrace,TC_ICASRV,TT_API2,"TERMSRV: WinStationSetInformation LogonId=%d, Class=%d, Status=0x%x\n", LogonId, (ULONG)WinStationInformationClass, Status)); return( Status ); } /******************************************************************************* * * _SetConfig * * Set window station configuration * This API does not do a user policy or user pref merge between source and destination, * the caller of this API ( assuming the Set privilage is set) will be able to change the * config data of an already active session, although only the shadow value has an impact (in practise). * The rest of the values are only used upon login, nothing happens after login. The reason * Shadow is different is becasue the shadow thread which uses the shadow info only start * (and reads config data) when shadowing, so as logn as shadow session has not started, you * can use this api to change the shadow value and have an impact on this session's behavior in * as far as shadow is concerned. * * * ENTRY: * pWinStation (input) * pointer to citrix window station structure * pConfig (input) * pointer to configuration structure * Length (input) * length of configuration structure * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS _SetConfig( PWINSTATION pWinStation, PWINSTATIONCONFIG pConfig, ULONG Length ) { USERCONFIG UserConfig; NTSTATUS Status; /* * Validate length */ if ( Length < sizeof(WINSTATIONCONFIG) ) return( STATUS_BUFFER_TOO_SMALL ); /* * Check the input configuration */ Status = ValidateInputConfig(pWinStation, pConfig ); if (Status != STATUS_SUCCESS) { return Status; } /* * Copy structure */ pWinStation->Config.Config = *pConfig; /* * Merge client data into winstation structure */ if ( pWinStation->pWsx && pWinStation->pWsx->pWsxInitializeUserConfig ) { pWinStation->pWsx->pWsxInitializeUserConfig( pWinStation->pWsxContext, pWinStation->hStack, pWinStation->hIcaThinwireChannel, &pWinStation->Config.Config.User, &pWinStation->Client.HRes, &pWinStation->Client.VRes, &pWinStation->Client.ColorDepth); } #if NT2195 // in win2k, this could accomplish nothing since all of userconfigw data was already consumed // by various TS modules at login time, changing them while a session was active had no impact. // The same is true when you use TSCC to make a change, it warns you that changes will not affect // live sessions, etc. /* * If user is logged on -> merge user profile data */ if ( pWinStation->UserName[0] ) { /* * Read user profile data */ _ReadUserProfile( pWinStation->Domain, pWinStation->UserName, &UserConfig ); /* * Merge user config data into the winstation */ MergeUserConfigData( pWinStation, &UserConfig ); } #endif /* * Convert any "published app" to absolute path */ if ( pWinStation->pWsx && pWinStation->pWsx->pWsxConvertPublishedApp ) { (void) pWinStation->pWsx->pWsxConvertPublishedApp( pWinStation->pWsxContext, &pWinStation->Config.Config.User); } return( STATUS_SUCCESS ); } NTSTATUS ValidateInputConfig( PWINSTATION pWinStation, PWINSTATIONCONFIG pConfig ) { NTSTATUS Status = STATUS_SUCCESS; pConfig->User.UserName[ USERNAME_LENGTH ] = L'\0'; pConfig->User.Domain[ DOMAIN_LENGTH ] = L'\0'; pConfig->User.Password[ PASSWORD_LENGTH ] = L'\0'; pConfig->User.WorkDirectory[ DIRECTORY_LENGTH ] = L'\0'; pConfig->User.InitialProgram[ INITIALPROGRAM_LENGTH] = L'\0'; pConfig->User.CallbackNumber[ CALLBACK_LENGTH ] = L'\0'; /* fInheritSecurity */ //BYTE MinEncryptionLevel; pConfig->User.NWLogonServer[ NASIFILESERVER_LENGTH] = L'\0'; /* WinFrame Profile Path - Overrides standard profile path */ pConfig->User.WFProfilePath[ DIRECTORY_LENGTH ] = L'\0'; /* WinFrame Home Directory - Overrides standard Home Directory */ pConfig->User.WFHomeDir[ DIRECTORY_LENGTH ] = L'\0'; return Status; } /******************************************************************************* * * _ReadUserProfile * * This routine reads the user profile data from the registry * * ENTRY: * pDomain (input) * domain of user * pUserName (input) * user name to read * pUserConfig (output) * address to return user profile data * * EXIT: * None. * ******************************************************************************/ VOID _ReadUserProfile( PWCHAR pDomain, PWCHAR pUserName, PUSERCONFIG pUserConfig ) { PWCHAR pServerName; ULONG Length; LONG Error; /* * Get Domain Controller name and userconfig data. * If no userconfig data for user then get default values. */ if ( ghNetApiDll == NULL ) { ghNetApiDll = LoadLibrary( L"NETAPI32" ); if ( ghNetApiDll ) { pNetGetAnyDCName = GetProcAddress( ghNetApiDll, "NetGetAnyDCName" ); pNetApiBufferFree = GetProcAddress( ghNetApiDll, "NetApiBufferFree" ); } } /* * Check to make sure we got a server name */ if ( pNetGetAnyDCName == NULL || pNetGetAnyDCName( NULL, pDomain, (LPBYTE *)&pServerName ) != ERROR_SUCCESS ) pServerName = NULL; /* * Read user profile data */ Error = RegUserConfigQuery( pServerName, pUserName, pUserConfig, sizeof(USERCONFIG), &Length ); TRACE((hTrace,TC_ICASRV,TT_API1, "RegUserConfigQuery: \\\\%S\\%S, server %S, Error=%u\n", pDomain, pUserName, pServerName, Error )); if ( Error != ERROR_SUCCESS ) { Error = RegDefaultUserConfigQuery( pServerName, pUserConfig, sizeof(USERCONFIG), &Length ); TRACE((hTrace,TC_ICASRV,TT_ERROR, "RegDefaultUserConfigQuery, Error=%u\n", Error )); } /* * Free memory */ if ( pServerName && pNetApiBufferFree ) pNetApiBufferFree( pServerName ); } /******************************************************************************* * * _SetBeep * * Beep the WinStation * * ENTRY: * pWinStation (input) * pointer to citrix window station structure * pBeepInput (input) * pointer to Beep input structure * Length (input) * length of Beep input structure * * EXIT: * STATUS_SUCCESS - no error * ******************************************************************************/ NTSTATUS _SetBeep( PWINSTATION pWinStation, PBEEPINPUT pBeepInput, ULONG Length) { NTSTATUS Status = STATUS_SUCCESS; BEEP_SET_PARAMETERS BeepParameters; IO_STATUS_BLOCK IoStatus; /* * Do the regular Beep, so you can support fancy Beeps from * sound cards. */ if ( pWinStation->LogonId == 0 ) { if ( MessageBeep( pBeepInput->uType ) ) return( STATUS_SUCCESS ); else return( STATUS_UNSUCCESSFUL ); } BeepParameters.Frequency = 440; BeepParameters.Duration = 125; if ( pWinStation->hIcaBeepChannel ) { Status = NtDeviceIoControlFile( pWinStation->hIcaBeepChannel, NULL, NULL, NULL, &IoStatus, IOCTL_BEEP_SET, &BeepParameters, sizeof( BeepParameters ), NULL, 0 ); } return( STATUS_SUCCESS ); }