MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.IO;5using System.Management.Automation.Internal;6using System.Management.Automation.Remoting.Server;7using System.Management.Automation.Runspaces;8using System.Threading;9 10using Dbg = System.Management.Automation.Diagnostics;11 12namespace System.Management.Automation.Remoting13{14 /// <summary>15 /// By design, on the server side, each remote connection is represented by16 /// a ServerRemoteSession object, which contains one instance of this class.17 ///18 /// This class holds 4 pieces of information.19 /// 1. Client capability: This is the capability received during the negotiation process.20 /// 2. Server capability: This comes from default parameters.21 /// 3. Client configuration: This holds the remote session related configuration parameters that22 /// the client sent to the server. This parameters can be changed and resent after the connection23 /// is established.24 /// 4. Server configuration: this holds the server sider configuration parameters.25 ///26 /// All these together define the connection level parameters.27 /// </summary>28 internal class ServerRemoteSessionContext29 {30 #region Constructors31 32 /// <summary>33 /// The constructor instantiates a server capability object and a server configuration34 /// using default values.35 /// </summary>36 internal ServerRemoteSessionContext()37 {38 ServerCapability = RemoteSessionCapability.CreateServerCapability();39 }40 41 #endregion Constructors42 43 /// <summary>44 /// This property represents the capability that the server receives from the client.45 /// </summary>46 internal RemoteSessionCapability ClientCapability { get; set; }47 48 /// <summary>49 /// This property is the server capability generated on the server side.50 /// </summary>51 internal RemoteSessionCapability ServerCapability { get; set; }52 53 /// <summary>54 /// True if negotiation from client is succeeded...in which case ClientCapability55 /// is the capability that server agreed with.56 /// </summary>57 internal bool IsNegotiationSucceeded { get; set; }58 }59 60 /// <summary>61 /// This class is designed to be the server side controller of a remote connection.62 ///63 /// It contains a static entry point that the PowerShell server process will get into64 /// the server mode. At this entry point, a runspace configuration is passed in. This runspace65 /// configuration is used to instantiate a server side runspace.66 ///67 /// This class controls a remote connection by using a Session data structure handler, which68 /// in turn contains a Finite State Machine, and a transport mechanism.69 /// </summary>70 internal class ServerRemoteSession : RemoteSession71 {72 [TraceSource("ServerRemoteSession", "ServerRemoteSession")]73 private static readonly PSTraceSource s_trace = PSTraceSource.GetTracer("ServerRemoteSession", "ServerRemoteSession");74 75 private readonly PSSenderInfo _senderInfo;76 private readonly string _configProviderId;77 private readonly string _initParameters;78 private string _initScriptForOutOfProcRS;79 private PSSessionConfiguration _sessionConfigProvider;80 81 // used to apply quotas on command and session transportmanagers.82 private int? _maxRecvdObjectSize;83 private int? _maxRecvdDataSizeCommand;84 85 private ServerRunspacePoolDriver _runspacePoolDriver;86 private readonly PSRemotingCryptoHelperServer _cryptoHelper;87 88 // Specifies an optional endpoint configuration for out-of-proc session use.89 // Creates a pushed remote runspace session created with this configuration name.90 private string _configurationName;91 92 // Specifies an optional .pssc configuration file path for out-of-proc session use.93 // The .pssc file is used to configure the runspace for the endpoint session.94 private string _configurationFile;95 96 // Specifies an initial location of the powershell session.97 private string _initialLocation;98 99 #region Events100 /// <summary>101 /// Raised when session is closed.102 /// </summary>103 internal EventHandler<RemoteSessionStateMachineEventArgs> Closed;104 #endregion105 106 #region Constructors107 108 /// <summary>109 /// This constructor instantiates a ServerRemoteSession object and110 /// a ServerRemoteSessionDataStructureHandler object.111 /// </summary>112 /// <param name="senderInfo">113 /// Details about the user creating this session.114 /// </param>115 /// <param name="configurationProviderId">116 /// The resource URI for which this session is being created117 /// </param>118 /// <param name="initializationParameters">119 /// Initialization Parameters xml passed by WSMan API. This data is read from the config120 /// xml.121 /// </param>122 /// <param name="transportManager">123 /// The transport manager this session should use to send/receive data124 /// </param>125 /// <returns></returns>126 internal ServerRemoteSession(PSSenderInfo senderInfo,127 string configurationProviderId,128 string initializationParameters,129 AbstractServerSessionTransportManager transportManager)130 {131 Dbg.Assert(transportManager != null, "transportManager cannot be null.");132 133 // let input,output and error from native commands redirect as we have134 // to send (or receive) them back to client for action.135 NativeCommandProcessor.IsServerSide = true;136 _senderInfo = senderInfo;137 _configProviderId = configurationProviderId;138 _initParameters = initializationParameters;139 _cryptoHelper = (PSRemotingCryptoHelperServer)transportManager.CryptoHelper;140 _cryptoHelper.Session = this;141 142 Context = new ServerRemoteSessionContext();143 SessionDataStructureHandler = new ServerRemoteSessionDSHandlerImpl(this, transportManager);144 BaseSessionDataStructureHandler = SessionDataStructureHandler;145 SessionDataStructureHandler.CreateRunspacePoolReceived += HandleCreateRunspacePool;146 SessionDataStructureHandler.NegotiationReceived += HandleNegotiationReceived;147 SessionDataStructureHandler.SessionClosing += HandleSessionDSHandlerClosing;148 SessionDataStructureHandler.PublicKeyReceived += HandlePublicKeyReceived;149 transportManager.Closing += HandleResourceClosing;150 151 // update the quotas from sessionState..start with default size..and152 // when Custom Session Configuration is loaded (during runspace creation) update this.153 transportManager.ReceivedDataCollection.MaximumReceivedObjectSize =154 BaseTransportManager.MaximumReceivedObjectSize;155 156 // session transport manager can receive unlimited data..however each object is limited157 // by maxRecvdObjectSize. this is to allow clients to use a session for an unlimited time..158 // also the messages that can be sent to a session are limited and very controlled.159 // However a command transport manager can be restricted to receive only a fixed amount of data160 // controlled by maxRecvdDataSizeCommand..This is because commands can accept any number of input161 // objects162 transportManager.ReceivedDataCollection.MaximumReceivedDataSize = null;163 }164 165 #endregion Constructors166 167 #region Creation Factory168 169 /// <summary>170 /// Creates a server remote session for the supplied <paramref name="configurationProviderId"/>171 /// and <paramref name="transportManager"/>.172 /// </summary>173 /// <param name="senderInfo"></param>174 /// <param name="configurationProviderId"></param>175 /// <param name="initializationParameters">176 /// Initialization Parameters xml passed by WSMan API. This data is read from the config177 /// xml.178 /// </param>179 /// <param name="transportManager"></param>180 /// <param name="initialCommand">Optional initial command used for OutOfProc sessions.</param>181 /// <param name="configurationName">Optional configuration endpoint name for OutOfProc sessions.</param>182 /// <param name="configurationFile">Optional configuration file (.pssc) path for OutOfProc sessions.</param>183 /// <param name="initialLocation">Optional configuration initial location of the powershell session.</param>184 /// <returns></returns>185 /// <exception cref="InvalidOperationException">186 /// InitialSessionState provider with <paramref name="configurationProviderId"/> does187 /// not exist on the remote server.188 /// </exception>189 /*190 <InitializationParameters>191 <Param Name="PSVersion" Value="2.0" />192 <Param Name="ApplicationBase" Value="<folder path>" />193 ...194 </InitializationParameters>195 */196 internal static ServerRemoteSession CreateServerRemoteSession(197 PSSenderInfo senderInfo,198 string configurationProviderId,199 string initializationParameters,200 AbstractServerSessionTransportManager transportManager,201 string initialCommand,202 string configurationName,203 string configurationFile,204 string initialLocation)205 {206 Dbg.Assert(207 (senderInfo != null) && (senderInfo.UserInfo != null),208 "senderInfo and userInfo cannot be null.");209 210 s_trace.WriteLine("Finding InitialSessionState provider for id : {0}", configurationProviderId);211 212 if (string.IsNullOrEmpty(configurationProviderId))213 {214 throw PSTraceSource.NewInvalidOperationException("RemotingErrorIdStrings.NonExistentInitialSessionStateProvider", configurationProviderId);215 }216 217 const string shellPrefix = System.Management.Automation.Remoting.Client.WSManNativeApi.ResourceURIPrefix;218 int index = configurationProviderId.IndexOf(shellPrefix, StringComparison.OrdinalIgnoreCase);219 senderInfo.ConfigurationName = (index == 0) ? configurationProviderId.Substring(shellPrefix.Length) : string.Empty;220 ServerRemoteSession result = new ServerRemoteSession(221 senderInfo,222 configurationProviderId,223 initializationParameters,224 transportManager)225 {226 _initScriptForOutOfProcRS = initialCommand,227 _configurationName = configurationName,228 _configurationFile = configurationFile,229 _initialLocation = initialLocation230 };231 232 // start state machine.233 RemoteSessionStateMachineEventArgs startEventArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.CreateSession);234 result.SessionDataStructureHandler.StateMachine.RaiseEvent(startEventArg);235 236 return result;237 }238 239 #endregion240 241 #region Overrides242 243 /// <summary>244 /// This indicates the remote session object is Client, Server or Listener.245 /// </summary>246 internal override RemotingDestination MySelf247 {248 get249 {250 return RemotingDestination.Server;251 }252 }253 254 /// <summary>255 /// This is the data dispatcher for the whole remote connection.256 /// This dispatcher is registered with the server side input queue's InputDataReady event.257 /// When the input queue has received data from client, it calls the InputDataReady listeners.258 ///259 /// This dispatcher distinguishes the negotiation packet as a special case. For all other data,260 /// it dispatches the data through Finite State Machines DoMessageReceived handler by raising the event261 /// MessageReceived. The FSM's DoMessageReceived handler further dispatches to the receiving262 /// components: such as runspace or pipeline which have their own data dispatching methods.263 /// </summary>264 /// <param name="sender">265 /// This parameter is not used by the method, in this implementation.266 /// </param>267 /// <param name="dataEventArg">268 /// This parameter contains the remote data received from client.269 /// </param>270 /// <exception cref="ArgumentNullException">271 /// If the parameter <paramref name="dataEventArg"/> is null.272 /// </exception>273 /// <exception cref="ArgumentException">274 /// If the parameter <paramref name="dataEventArg"/> does not contain remote data.275 /// </exception>276 /// <exception cref="PSRemotingDataStructureException">277 /// If the destination of the data is not for server.278 /// </exception>279 internal void DispatchInputQueueData(object sender, RemoteDataEventArgs dataEventArg)280 {281 if (dataEventArg == null)282 {283 throw PSTraceSource.NewArgumentNullException(nameof(dataEventArg));284 }285 286 RemoteDataObject<PSObject> rcvdData = dataEventArg.ReceivedData;287 288 if (rcvdData == null)289 {290 throw PSTraceSource.NewArgumentException(nameof(dataEventArg));291 }292 293 RemotingDestination destination = rcvdData.Destination;294 295 if ((destination & MySelf) != MySelf)296 {297 // this packet is not target for me.298 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.RemotingDestinationNotForMe, MySelf, destination);299 }300 301 RemotingTargetInterface targetInterface = rcvdData.TargetInterface;302 RemotingDataType dataType = rcvdData.DataType;303 304 RemoteSessionStateMachineEventArgs messageReceivedArg = null;305 306 switch (targetInterface)307 {308 case RemotingTargetInterface.Session:309 {310 switch (dataType)311 {312 // TODO: Directly calling an event handler in StateMachine bypassing the StateMachine's313 // loop of ProcessEvents()..This is needed as connection is already established and the314 // following message does not change state. An ideal solution would be to process315 // non-session messages in this class rather than by StateMachine.316 case RemotingDataType.CreateRunspacePool:317 messageReceivedArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.MessageReceived);318 if (SessionDataStructureHandler.StateMachine.CanByPassRaiseEvent(messageReceivedArg))319 {320 messageReceivedArg.RemoteData = rcvdData;321 SessionDataStructureHandler.StateMachine.DoMessageReceived(this, messageReceivedArg);322 }323 else324 {325 SessionDataStructureHandler.StateMachine.RaiseEvent(messageReceivedArg);326 }327 328 break;329 330 case RemotingDataType.CloseSession:331 SessionDataStructureHandler.RaiseDataReceivedEvent(dataEventArg);332 break;333 334 case RemotingDataType.SessionCapability:335 SessionDataStructureHandler.RaiseDataReceivedEvent(dataEventArg);336 break;337 338 case RemotingDataType.PublicKey:339 SessionDataStructureHandler.RaiseDataReceivedEvent(dataEventArg);340 break;341 342 default:343 Dbg.Assert(false, "Should never reach here");344 break;345 }346 }347 348 break;349 350 // TODO: Directly calling an event handler in StateMachine bypassing the StateMachine's351 // loop of ProcessEvents()..This is needed as connection is already established and the352 // following message does not change state. An ideal solution would be to process353 // non-session messages in this class rather than by StateMachine.354 case RemotingTargetInterface.RunspacePool:355 case RemotingTargetInterface.PowerShell:356 // GETBACK357 messageReceivedArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.MessageReceived);358 if (SessionDataStructureHandler.StateMachine.CanByPassRaiseEvent(messageReceivedArg))359 {360 messageReceivedArg.RemoteData = rcvdData;361 SessionDataStructureHandler.StateMachine.DoMessageReceived(this, messageReceivedArg);362 }363 else364 {365 SessionDataStructureHandler.StateMachine.RaiseEvent(messageReceivedArg);366 }367 368 break;369 }370 }371 372 /// <summary>373 /// Have received a public key from the other side374 /// Import or take other action based on the state.375 /// </summary>376 /// <param name="sender">Sender of this event, unused.</param>377 /// <param name="eventArgs">event arguments which contains the378 /// remote public key</param>379 private void HandlePublicKeyReceived(object sender, RemoteDataEventArgs<string> eventArgs)380 {381 if (SessionDataStructureHandler.StateMachine.State == RemoteSessionState.Established ||382 SessionDataStructureHandler.StateMachine.State == RemoteSessionState.EstablishedAndKeyRequested || // this is only for legacy clients383 SessionDataStructureHandler.StateMachine.State == RemoteSessionState.EstablishedAndKeyExchanged)384 {385 string remotePublicKey = eventArgs.Data;386 387 bool ret = _cryptoHelper.ImportRemotePublicKey(remotePublicKey);388 389 RemoteSessionStateMachineEventArgs args = null;390 if (!ret)391 {392 // importing remote public key failed393 // set state to closed394 args = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeyReceiveFailed);395 396 SessionDataStructureHandler.StateMachine.RaiseEvent(args);397 }398 399 args = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeyReceived);400 SessionDataStructureHandler.StateMachine.RaiseEvent(args);401 }402 }403 404 /// <summary>405 /// Start the key exchange process.406 /// </summary>407 internal override void StartKeyExchange()408 {409 if (SessionDataStructureHandler.StateMachine.State == RemoteSessionState.Established)410 {411 // send using data structure handler412 SessionDataStructureHandler.SendRequestForPublicKey();413 414 RemoteSessionStateMachineEventArgs eventArgs =415 new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeyRequested);416 SessionDataStructureHandler.StateMachine.RaiseEvent(eventArgs);417 }418 }419 420 /// <summary>421 /// Complete the Key exchange process.422 /// </summary>423 internal override void CompleteKeyExchange()424 {425 _cryptoHelper.CompleteKeyExchange();426 }427 428 /// <summary>429 /// Send an encrypted session key to the client.430 /// </summary>431 internal void SendEncryptedSessionKey()432 {433 Dbg.Assert(SessionDataStructureHandler.StateMachine.State == RemoteSessionState.EstablishedAndKeyReceived,434 "Sever must be in EstablishedAndKeyReceived state before you can attempt to send encrypted session key");435 436 string encryptedSessionKey = null;437 bool ret = _cryptoHelper.ExportEncryptedSessionKey(out encryptedSessionKey);438 439 RemoteSessionStateMachineEventArgs args = null;440 if (!ret)441 {442 args =443 new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeySendFailed);444 SessionDataStructureHandler.StateMachine.RaiseEvent(args);445 }446 447 SessionDataStructureHandler.SendEncryptedSessionKey(encryptedSessionKey);448 449 // complete the key exchange process450 CompleteKeyExchange();451 452 args = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.KeySent);453 SessionDataStructureHandler.StateMachine.RaiseEvent(args);454 }455 456 #endregion Overrides457 458 #region Properties459 460 /// <summary>461 /// This property returns the ServerRemoteSessionContext object created inside462 /// this object's constructor.463 /// </summary>464 internal ServerRemoteSessionContext Context { get; }465 466 /// <summary>467 /// This property returns the ServerRemoteSessionDataStructureHandler object created inside468 /// this object's constructor.469 /// </summary>470 internal ServerRemoteSessionDataStructureHandler SessionDataStructureHandler { get; }471 472 #endregion473 474 #region Private/Internal Methods475 476 /// <summary>477 /// Let the session clear its resources.478 /// </summary>479 /// <param name="reasonForClose"></param>480 internal void Close(RemoteSessionStateMachineEventArgs reasonForClose)481 {482 Closed.SafeInvoke(this, reasonForClose);483 if (_runspacePoolDriver != null)484 _runspacePoolDriver.Closed -= HandleResourceClosing;485 }486 487 /// <summary>488 /// ExecutesConnect. expects client capability and connect_runspacepool PSRP489 /// messages in connectData.490 /// If negotiation is successful and max and min runspaces in connect_runspacepool491 /// match the associated runspace pool parameters, it builds up server capability492 /// and runspace_initinfo in connectResponseData.493 /// This is a version of Connect that executes the whole connect algorithm in one single494 /// hop.495 /// This algorithm is being executed synchronously without associating with state machine.496 /// </summary>497 /// <param name="connectData"></param>498 /// <param name="connectResponseData"></param>499 /// The operation is being outside the statemachine because of multiple reasons associated with design simplicity500 /// - Support automatic disconnect and let wsman server stack take care of connection state501 /// - The response data should not travel in transports output stream but as part of connect response502 /// - We want this operation to be synchronous503 internal void ExecuteConnect(byte[] connectData, out byte[] connectResponseData)504 {505 connectResponseData = null;506 Fragmentor fragmentor = new Fragmentor(int.MaxValue, null);507 Fragmentor defragmentor = fragmentor;508 509 int totalDataLen = connectData.Length;510 511 if (totalDataLen < FragmentedRemoteObject.HeaderLength)512 {513 // raise exception514 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);515 }516 517 // TODO: Follow up on comment from Krishna regarding having the serialization/deserialization separate for this518 // operation. This could be integrated as helper functions in fragmentor/serializer components519 long fragmentId = FragmentedRemoteObject.GetFragmentId(connectData, 0);520 bool sFlag = FragmentedRemoteObject.GetIsStartFragment(connectData, 0);521 bool eFlag = FragmentedRemoteObject.GetIsEndFragment(connectData, 0);522 int blobLength = FragmentedRemoteObject.GetBlobLength(connectData, 0);523 524 if (blobLength > totalDataLen - FragmentedRemoteObject.HeaderLength)525 {526 // raise exception527 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);528 }529 530 if (!sFlag || !eFlag)531 {532 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);533 }534 535 // if session is not in expected state536 RemoteSessionState currentState = SessionDataStructureHandler.StateMachine.State;537 if (currentState != RemoteSessionState.Established &&538 currentState != RemoteSessionState.EstablishedAndKeyExchanged)539 {540 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnServerStateValidation);541 }542 543 // process first message544 MemoryStream serializedStream = new MemoryStream();545 serializedStream.Write(connectData, FragmentedRemoteObject.HeaderLength, blobLength);546 547 serializedStream.Seek(0, SeekOrigin.Begin);548 RemoteDataObject<PSObject> capabilityObject = RemoteDataObject<PSObject>.CreateFrom(serializedStream, defragmentor);549 550 if (capabilityObject == null)551 {552 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);553 }554 555 if ((capabilityObject.Destination != RemotingDestination.Server) || (capabilityObject.DataType != RemotingDataType.SessionCapability))556 {557 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);558 }559 560 // process second message561 int secondFragmentLength = totalDataLen - FragmentedRemoteObject.HeaderLength - blobLength;562 if (secondFragmentLength < FragmentedRemoteObject.HeaderLength)563 {564 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);565 }566 567 byte[] secondFragment = new byte[secondFragmentLength];568 Array.Copy(connectData, FragmentedRemoteObject.HeaderLength + blobLength, secondFragment, 0, secondFragmentLength);569 570 fragmentId = FragmentedRemoteObject.GetFragmentId(secondFragment, 0);571 sFlag = FragmentedRemoteObject.GetIsStartFragment(secondFragment, 0);572 eFlag = FragmentedRemoteObject.GetIsEndFragment(secondFragment, 0);573 blobLength = FragmentedRemoteObject.GetBlobLength(secondFragment, 0);574 575 if (blobLength != secondFragmentLength - FragmentedRemoteObject.HeaderLength)576 {577 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);578 }579 580 if (!sFlag || !eFlag)581 {582 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);583 }584 585 // process second message586 serializedStream = new MemoryStream();587 serializedStream.Write(secondFragment, FragmentedRemoteObject.HeaderLength, blobLength);588 589 serializedStream.Seek(0, SeekOrigin.Begin);590 RemoteDataObject<PSObject> connectRunspacePoolObject = RemoteDataObject<PSObject>.CreateFrom(serializedStream, defragmentor);591 592 if (connectRunspacePoolObject == null)593 {594 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnServerStateValidation);595 }596 597 if ((connectRunspacePoolObject.Destination != RemotingDestination.Server) || (connectRunspacePoolObject.DataType != RemotingDataType.ConnectRunspacePool))598 {599 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);600 }601 602 // We have the two objects required for validating the connect operation603 RemoteSessionCapability clientCapability;604 try605 {606 clientCapability = RemotingDecoder.GetSessionCapability(capabilityObject.Data);607 }608 catch (PSRemotingDataStructureException)609 {610 // this will happen if expected properties are not611 // received for session capability612 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);613 }614 615 try616 {617 RunServerNegotiationAlgorithm(clientCapability, true);618 }619 catch (PSRemotingDataStructureException)620 {621 throw;622 }623 624 // validate client connect_runspacepool request625 int clientRequestedMinRunspaces = -1;626 int clientRequestedMaxRunspaces = -1;627 bool clientRequestedRunspaceCount = false;628 if (connectRunspacePoolObject.Data.Properties[RemoteDataNameStrings.MinRunspaces] != null && connectRunspacePoolObject.Data.Properties[RemoteDataNameStrings.MaxRunspaces] != null)629 {630 try631 {632 clientRequestedMinRunspaces = RemotingDecoder.GetMinRunspaces(connectRunspacePoolObject.Data);633 clientRequestedMaxRunspaces = RemotingDecoder.GetMaxRunspaces(connectRunspacePoolObject.Data);634 clientRequestedRunspaceCount = true;635 }636 catch (PSRemotingDataStructureException)637 {638 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);639 }640 }641 642 // these should be positive and max should be greater than min643 if (clientRequestedRunspaceCount &&644 (clientRequestedMinRunspaces == -1 || clientRequestedMaxRunspaces == -1 || clientRequestedMinRunspaces > clientRequestedMaxRunspaces))645 {646 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);647 }648 649 if (_runspacePoolDriver == null)650 {651 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnServerStateValidation);652 }653 654 // currently only one runspace pool per session is allowed. make sure this ID in connect message is the same655 if (connectRunspacePoolObject.RunspacePoolId != _runspacePoolDriver.InstanceId)656 {657 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnInputValidation);658 }659 660 // we currently dont support adjusting runspace count on a connect operation.661 // there is a potential conflict here where in the runspace pool driver is still yet to process a queued662 // setMax or setMinrunspaces request.663 // TODO: resolve this.. probably by letting the runspace pool consume all messages before we execute this.664 if (clientRequestedRunspaceCount665 && (_runspacePoolDriver.RunspacePool.GetMaxRunspaces() != clientRequestedMaxRunspaces)666 && (_runspacePoolDriver.RunspacePool.GetMinRunspaces() != clientRequestedMinRunspaces))667 {668 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnMismatchedRunspacePoolProperties);669 }670 671 // all client messages are validated672 // now build up the server capabilities and connect response messages to be piggybacked on connect response673 RemoteDataObject capability = RemotingEncoder.GenerateServerSessionCapability(Context.ServerCapability, _runspacePoolDriver.InstanceId);674 RemoteDataObject runspacepoolInitData = RemotingEncoder.GenerateRunspacePoolInitData(_runspacePoolDriver.InstanceId,675 _runspacePoolDriver.RunspacePool.GetMaxRunspaces(),676 _runspacePoolDriver.RunspacePool.GetMinRunspaces());677 678 // having this stream operating separately will result in out of sync fragment Ids. but this is still OK679 // as this is executed only when connecting from a new client that does not have any previous fragments context.680 // no problem even if fragment Ids in this response and the sessiontransport stream clash (interfere) and its guaranteed681 // that the fragments in connect response are always complete (enclose a complete object).682 SerializedDataStream stream = new SerializedDataStream(4 * 1024); //Each message with fragment headers cannot cross 4k683 stream.Enter();684 capability.Serialize(stream, fragmentor);685 stream.Exit();686 stream.Enter();687 runspacepoolInitData.Serialize(stream, fragmentor);688 stream.Exit();689 byte[] outbuffer = stream.Read();690 Dbg.Assert(outbuffer != null, "connect response data should be serialized");691 stream.Dispose();692 // we are done693 connectResponseData = outbuffer;694 695 // enqueue a connect event in state machine to let session do any other post-connect operation696 // Do this outside of the synchronous connect operation, as otherwise connect can easily get deadlocked697 ThreadPool.QueueUserWorkItem(new WaitCallback(698 (object state) =>699 {700 RemoteSessionStateMachineEventArgs startEventArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.ConnectSession);701 SessionDataStructureHandler.StateMachine.RaiseEvent(startEventArg);702 }));703 704 _runspacePoolDriver.DataStructureHandler.ProcessConnect();705 return;706 }707 708 // pass on application private data when session is connected from new client709 internal void HandlePostConnect() => _runspacePoolDriver?.SendApplicationPrivateDataToClient();710 711 /// <summary>712 /// </summary>713 /// <param name="sender"></param>714 /// <param name="createRunspaceEventArg"></param>715 /// <exception cref="InvalidOperationException">716 /// 1. InitialSessionState cannot be null.717 /// 2. Non existent InitialSessionState provider for the shellID718 /// </exception>719 private void HandleCreateRunspacePool(object sender, RemoteDataEventArgs createRunspaceEventArg)720 {721 if (createRunspaceEventArg == null)722 {723 throw PSTraceSource.NewArgumentNullException(nameof(createRunspaceEventArg));724 }725 726 RemoteDataObject<PSObject> rcvdData = createRunspaceEventArg.ReceivedData;727 Dbg.Assert(rcvdData != null, "rcvdData must be non-null");728 729 _senderInfo.ApplicationArguments = RemotingDecoder.GetApplicationArguments(rcvdData.Data);730 731 // Get Initial Session State from custom session config suppliers732 // like Exchange.733 ConfigurationDataFromXML configurationData =734 PSSessionConfiguration.LoadEndPointConfiguration(_configProviderId, _initParameters);735 // used by Out-Of-Proc (IPC) runspace.736 configurationData.InitializationScriptForOutOfProcessRunspace = _initScriptForOutOfProcRS;737 // start with data from configuration XML and then override with data738 // from EndPointConfiguration type.739 _maxRecvdObjectSize = configurationData.MaxReceivedObjectSizeMB;740 _maxRecvdDataSizeCommand = configurationData.MaxReceivedCommandSizeMB;741 742 if (string.IsNullOrEmpty(configurationData.ConfigFilePath))743 {744 _sessionConfigProvider = configurationData.CreateEndPointConfigurationInstance();745 }746 else747 {748 System.Security.Principal.WindowsPrincipal windowsPrincipal = new System.Security.Principal.WindowsPrincipal(_senderInfo.UserInfo.WindowsIdentity);749 Func<string, bool> validator = (role) => windowsPrincipal.IsInRole(role);750 _sessionConfigProvider = new DISCPowerShellConfiguration(configurationData.ConfigFilePath, validator);751 }752 753 // exchange of ApplicationArguments and ApplicationPrivateData is be done as early as possible754 // (i.e. to let the _sessionConfigProvider bail out if it can't comply with client's versioning request)755 PSPrimitiveDictionary applicationPrivateData = _sessionConfigProvider.GetApplicationPrivateData(_senderInfo);756 757 InitialSessionState rsSessionStateToUse = null;758 759 if (configurationData.SessionConfigurationData != null)760 {761 // Use the provided WinRM endpoint runspace configuration information.762 try763 {764 rsSessionStateToUse =765 _sessionConfigProvider.GetInitialSessionState(configurationData.SessionConfigurationData, _senderInfo, _configProviderId);766 }767 catch (NotImplementedException)768 {769 rsSessionStateToUse = _sessionConfigProvider.GetInitialSessionState(_senderInfo);770 }771 }772 else if (!string.IsNullOrEmpty(_configurationFile))773 {774 // Use the optional _configurationFile parameter to create the endpoint runspace configuration.775 // This parameter is only used by Out-Of-Proc transports (not WinRM transports).776 var discConfiguration = new Remoting.DISCPowerShellConfiguration(777 configFile: _configurationFile,778 roleVerifier: null, 779 validateFile: true);780 rsSessionStateToUse = discConfiguration.GetInitialSessionState(_senderInfo);781 }782 else783 {784 // Create a runspace configuration based on the provided PSSessionConfiguration provider.785 // This can be either a 'default' configuration, or third party configuration PSSessionConfiguration provider object.786 // So far, only Exchange provides a custom PSSessionConfiguration provider implementation.787 rsSessionStateToUse = _sessionConfigProvider.GetInitialSessionState(_senderInfo);788 }789 790 if (rsSessionStateToUse == null)791 {792 throw PSTraceSource.NewInvalidOperationException(RemotingErrorIdStrings.InitialSessionStateNull, _configProviderId);793 }794 795 rsSessionStateToUse.ThrowOnRunspaceOpenError = true;796 797 // this might throw if the sender info is already present798 rsSessionStateToUse.Variables.Add(799 new SessionStateVariableEntry(RemoteDataNameStrings.SenderInfoPreferenceVariable,800 _senderInfo,801 Remoting.PSRemotingErrorInvariants.FormatResourceString(802 RemotingErrorIdStrings.PSSenderInfoDescription),803 ScopedItemOptions.ReadOnly));804 805 // Get client PS version from PSSenderInfo.806 Version psClientVersion = null;807 if (_senderInfo.ApplicationArguments != null && _senderInfo.ApplicationArguments.ContainsKey("PSversionTable"))808 {809 var value = PSObject.Base(_senderInfo.ApplicationArguments["PSversionTable"]) as PSPrimitiveDictionary;810 if (value != null && value.ContainsKey("PSVersion"))811 {812 psClientVersion = PSObject.Base(value["PSVersion"]) as Version;813 }814 }815 816 if (!string.IsNullOrEmpty(configurationData.EndPointConfigurationTypeName))817 {818 // user specified a type to load for configuration..use the values from this type.819 _maxRecvdObjectSize = _sessionConfigProvider.GetMaximumReceivedObjectSize(_senderInfo);820 _maxRecvdDataSizeCommand = _sessionConfigProvider.GetMaximumReceivedDataSizePerCommand(_senderInfo);821 }822 823 SessionDataStructureHandler.TransportManager.ReceivedDataCollection.MaximumReceivedObjectSize = _maxRecvdObjectSize;824 // MaximumReceivedDataSize is not set for session transport manager...see the constructor825 // for more info.826 827 Guid clientRunspacePoolId = rcvdData.RunspacePoolId;828 int minRunspaces = RemotingDecoder.GetMinRunspaces(rcvdData.Data);829 int maxRunspaces = RemotingDecoder.GetMaxRunspaces(rcvdData.Data);830 PSThreadOptions threadOptions = RemotingDecoder.GetThreadOptions(rcvdData.Data);831 ApartmentState apartmentState = RemotingDecoder.GetApartmentState(rcvdData.Data);832 HostInfo hostInfo = RemotingDecoder.GetHostInfo(rcvdData.Data);833 834 if (_runspacePoolDriver != null)835 {836 throw new PSRemotingDataStructureException(RemotingErrorIdStrings.RunspaceAlreadyExists,837 _runspacePoolDriver.InstanceId);838 }839 840#if !UNIX841 bool isAdministrator = _senderInfo.UserInfo.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator);842#else843 bool isAdministrator = false;844#endif845 846 ServerRunspacePoolDriver tmpDriver = new ServerRunspacePoolDriver(847 clientRunspacePoolId,848 minRunspaces,849 maxRunspaces,850 threadOptions,851 apartmentState,852 hostInfo,853 rsSessionStateToUse,854 applicationPrivateData,855 configurationData,856 this.SessionDataStructureHandler.TransportManager,857 isAdministrator,858 Context.ServerCapability,859 psClientVersion,860 _configurationName,861 _initialLocation);862 863 // attach the necessary event handlers and start the driver.864 Interlocked.Exchange(ref _runspacePoolDriver, tmpDriver);865 _runspacePoolDriver.Closed += HandleResourceClosing;866 _runspacePoolDriver.Start();867 }868 869 /// <summary>870 /// This handler method runs the negotiation algorithm. It decides if the negotiation is successful,871 /// or fails.872 /// </summary>873 /// <param name="sender"></param>874 /// <param name="negotiationEventArg">875 /// This parameter contains the client negotiation capability packet.876 /// </param>877 /// <exception cref="ArgumentNullException">878 /// If the parameter <paramref name="negotiationEventArg"/> is null.879 /// </exception>880 private void HandleNegotiationReceived(object sender, RemoteSessionNegotiationEventArgs negotiationEventArg)881 {882 if (negotiationEventArg == null)883 {884 throw PSTraceSource.NewArgumentNullException(nameof(negotiationEventArg));885 }886 887 try888 {889 Context.ClientCapability = negotiationEventArg.RemoteSessionCapability;890 // This will throw if there is an error running the algorithm891 RunServerNegotiationAlgorithm(negotiationEventArg.RemoteSessionCapability, false);892 893 // Send server's capability to client.894 RemoteSessionStateMachineEventArgs sendingNegotiationArg = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.NegotiationSending);895 SessionDataStructureHandler.StateMachine.RaiseEvent(sendingNegotiationArg);896 897 // if negotiation succeeded change the state to neg. completed.898 RemoteSessionStateMachineEventArgs negotiationCompletedArg =899 new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.NegotiationCompleted);900 SessionDataStructureHandler.StateMachine.RaiseEvent(negotiationCompletedArg);901 }902 catch (PSRemotingDataStructureException dse)903 {904 // Before setting to negotiation failed..send servers capability...that905 // way client can communicate differently if it wants to.906 RemoteSessionStateMachineEventArgs sendingNegotiationArg =907 new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.NegotiationSending);908 SessionDataStructureHandler.StateMachine.RaiseEvent(sendingNegotiationArg);909 910 RemoteSessionStateMachineEventArgs negotiationFailedArg =911 new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.NegotiationFailed,912 dse);913 SessionDataStructureHandler.StateMachine.RaiseEvent(negotiationFailedArg);914 }915 }916 917 /// <summary>918 /// Handle session closing event to close runspace pool drivers this session is hosting.919 /// </summary>920 /// <param name="sender"></param>921 /// <param name="eventArgs"></param>922 private void HandleSessionDSHandlerClosing(object sender, EventArgs eventArgs)923 {924 _runspacePoolDriver?.Close();925 926 // dispose the session configuration object..this will let them927 // clean their resources.928 if (_sessionConfigProvider != null)929 {930 _sessionConfigProvider.Dispose();931 _sessionConfigProvider = null;932 }933 }934 935 /// <summary>936 /// This handles closing of any resource used by this session.937 /// Resources used are RunspacePoolDriver, TransportManager.938 /// </summary>939 /// <param name="sender"></param>940 /// <param name="args"></param>941 private void HandleResourceClosing(object sender, EventArgs args)942 {943 RemoteSessionStateMachineEventArgs closeSessionArgs = new RemoteSessionStateMachineEventArgs(RemoteSessionEvent.Close);944 closeSessionArgs.RemoteData = null;945 SessionDataStructureHandler.StateMachine.RaiseEvent(closeSessionArgs);946 }947 948 /// <summary>949 /// This is the server side remote session capability negotiation algorithm.950 /// </summary>951 /// <param name="clientCapability">952 /// This is the client capability that the server received from client.953 /// </param>954 /// <param name="onConnect">955 /// If the negotiation is on a connect (and not create)956 /// </param>957 /// <returns>958 /// The method returns true if the capability negotiation is successful.959 /// Otherwise, it returns false.960 /// </returns>961 /// <exception cref="PSRemotingDataStructureException">962 /// 1. PowerShell server does not support the PSVersion {1} negotiated by the client.963 /// Make sure the client is compatible with the build {2} of PowerShell.964 /// 2. PowerShell server does not support the SerializationVersion {1} negotiated by the client.965 /// Make sure the client is compatible with the build {2} of PowerShell.966 /// </exception>967 private bool RunServerNegotiationAlgorithm(RemoteSessionCapability clientCapability, bool onConnect)968 {969 Dbg.Assert(clientCapability != null, "Client capability cache must be non-null");970 971 Version clientProtocolVersion = clientCapability.ProtocolVersion;972 Version serverProtocolVersion = Context.ServerCapability.ProtocolVersion;973 974 if (onConnect)975 {976 // PS v7.6 server can support reconstruct/reconnect for all 2.x protocol versions that support reconstruct/reconnect (v2.2+).977 // Major protocol version differences (2.x -> 3.x) are not supported. A reconstruct can only be initiated by a client that understands disconnect (v2.2+).978 if (clientProtocolVersion == RemotingConstants.ProtocolVersion_2_2 ||979 clientProtocolVersion == RemotingConstants.ProtocolVersion_2_3)980 {981 // Report the server as the same version to the client.982 // Client protocol: v2.2, v2.3983 serverProtocolVersion = clientProtocolVersion;984 Context.ServerCapability.ProtocolVersion = serverProtocolVersion;985 }986 else if (!(clientProtocolVersion.Major == serverProtocolVersion.Major &&987 clientProtocolVersion.Minor >= serverProtocolVersion.Minor))988 {989 // Throw for protocol versions 2.x that don't support disconnect/reconnect.990 // Client protocol: < 2.2991 PSRemotingDataStructureException reasonOfFailure =992 new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerConnectFailedOnNegotiation,993 RemoteDataNameStrings.PS_STARTUP_PROTOCOL_VERSION_NAME,994 clientProtocolVersion,995 PSVersionInfo.GitCommitId,996 RemotingConstants.ProtocolVersion);997 throw reasonOfFailure;998 }999 1000 // All other minor versions are supported and the server returns its full capability.1001 // Client protocol: v2.4, v2.5 ...1002 }1003 else1004 {1005 if (clientProtocolVersion == RemotingConstants.ProtocolVersion_2_0 ||1006 clientProtocolVersion == RemotingConstants.ProtocolVersion_2_1 ||1007 clientProtocolVersion == RemotingConstants.ProtocolVersion_2_2 ||1008 clientProtocolVersion == RemotingConstants.ProtocolVersion_2_3)1009 {1010 // We support the those client versions and report the server as the same version to the client.1011 serverProtocolVersion = clientProtocolVersion;1012 Context.ServerCapability.ProtocolVersion = serverProtocolVersion;1013 }1014 else if (!(clientProtocolVersion.Major == serverProtocolVersion.Major &&1015 clientProtocolVersion.Minor >= serverProtocolVersion.Minor))1016 {1017 PSRemotingDataStructureException reasonOfFailure =1018 new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerNegotiationFailed,1019 RemoteDataNameStrings.PS_STARTUP_PROTOCOL_VERSION_NAME,1020 clientProtocolVersion,1021 PSVersionInfo.GitCommitId,1022 RemotingConstants.ProtocolVersion);1023 throw reasonOfFailure;1024 }1025 }1026 1027 // PSVersion Check1028 Version clientPSVersion = clientCapability.PSVersion;1029 Version serverPSVersion = Context.ServerCapability.PSVersion;1030 if (!((clientPSVersion.Major == serverPSVersion.Major) &&1031 (clientPSVersion.Minor >= serverPSVersion.Minor)))1032 {1033 PSRemotingDataStructureException reasonOfFailure =1034 new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerNegotiationFailed,1035 RemoteDataNameStrings.PSVersion,1036 clientPSVersion,1037 PSVersionInfo.GitCommitId,1038 RemotingConstants.ProtocolVersion);1039 throw reasonOfFailure;1040 }1041 1042 // SerializationVersion check1043 Version clientSerVersion = clientCapability.SerializationVersion;1044 Version serverSerVersion = Context.ServerCapability.SerializationVersion;1045 if (!((clientSerVersion.Major == serverSerVersion.Major) &&1046 (clientSerVersion.Minor >= serverSerVersion.Minor)))1047 {1048 PSRemotingDataStructureException reasonOfFailure =1049 new PSRemotingDataStructureException(RemotingErrorIdStrings.ServerNegotiationFailed,1050 RemoteDataNameStrings.SerializationVersion,1051 clientSerVersion,1052 PSVersionInfo.GitCommitId,1053 RemotingConstants.ProtocolVersion);1054 throw reasonOfFailure;1055 }1056 1057 return true;1058 }1059 1060 /// <summary>1061 /// </summary>1062 /// <param name="clientRunspacePoolId"></param>1063 /// <returns></returns>1064 internal ServerRunspacePoolDriver GetRunspacePoolDriver(Guid clientRunspacePoolId)1065 {1066 if (_runspacePoolDriver == null)1067 {1068 return null;1069 }1070 1071 if (_runspacePoolDriver.InstanceId == clientRunspacePoolId)1072 {1073 return _runspacePoolDriver;1074 }1075 1076 return null;1077 }1078 1079 /// <summary>1080 /// Used by Command Session to apply quotas on the command transport manager.1081 /// This method is here because ServerRemoteSession knows about InitialSessionState.1082 /// </summary>1083 /// <param name="cmdTransportManager">1084 /// Command TransportManager to apply the quota on.1085 /// </param>1086 internal void ApplyQuotaOnCommandTransportManager(AbstractServerTransportManager cmdTransportManager)1087 {1088 Dbg.Assert(cmdTransportManager != null, "cmdTransportManager cannot be null");1089 cmdTransportManager.ReceivedDataCollection.MaximumReceivedDataSize = _maxRecvdDataSizeCommand;1090 cmdTransportManager.ReceivedDataCollection.MaximumReceivedObjectSize = _maxRecvdObjectSize;1091 }1092 1093 #endregion1094 }1095}1096 