From 06571637e41367a9089fbbbfe6da50fc2a90883f Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 27 Nov 2018 16:16:00 -0800 Subject: [PATCH] Add SkipCA and SkipCN check requirement to WinRM/OMI HTTPS connection (#8279) --- .../System.Management.Automation.csproj | 1 - .../engine/InitialSessionState.cs | 2 +- .../commands/NewPSSessionOptionCommand.cs | 12 +++ .../remoting/common/remotingexceptions.cs | 1 + .../engine/remoting/fanin/WSManNativeAPI.cs | 3 + .../remoting/fanin/WSManTransportManager.cs | 8 +- .../resources/RemotingErrorIdStrings.resx | 3 + .../engine/Basic/DefaultCommands.Tests.ps1 | 2 +- .../engine/Remoting/PSSession.Tests.ps1 | 79 +++++++++++++++++++ 9 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 test/powershell/engine/Remoting/PSSession.Tests.ps1 diff --git a/src/System.Management.Automation/System.Management.Automation.csproj b/src/System.Management.Automation/System.Management.Automation.csproj index a0056fd4f9..70fbda1bf6 100644 --- a/src/System.Management.Automation/System.Management.Automation.csproj +++ b/src/System.Management.Automation/System.Management.Automation.csproj @@ -138,7 +138,6 @@ - diff --git a/src/System.Management.Automation/engine/InitialSessionState.cs b/src/System.Management.Automation/engine/InitialSessionState.cs index f282968c7a..f1ba2f2914 100644 --- a/src/System.Management.Automation/engine/InitialSessionState.cs +++ b/src/System.Management.Automation/engine/InitialSessionState.cs @@ -5264,7 +5264,6 @@ end {"Get-PSSessionCapability", new SessionStateCmdletEntry("Get-PSSessionCapability", typeof(GetPSSessionCapabilityCommand), helpFile) }, {"Get-PSSessionConfiguration", new SessionStateCmdletEntry("Get-PSSessionConfiguration", typeof(GetPSSessionConfigurationCommand), helpFile) }, {"New-PSSessionConfigurationFile", new SessionStateCmdletEntry("New-PSSessionConfigurationFile", typeof(NewPSSessionConfigurationFileCommand), helpFile) }, - {"New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) }, {"Receive-PSSession", new SessionStateCmdletEntry("Receive-PSSession", typeof(ReceivePSSessionCommand), helpFile) }, {"Register-PSSessionConfiguration", new SessionStateCmdletEntry("Register-PSSessionConfiguration", typeof(RegisterPSSessionConfigurationCommand), helpFile) }, {"Unregister-PSSessionConfiguration", new SessionStateCmdletEntry("Unregister-PSSessionConfiguration", typeof(UnregisterPSSessionConfigurationCommand), helpFile) }, @@ -5294,6 +5293,7 @@ end {"New-ModuleManifest", new SessionStateCmdletEntry("New-ModuleManifest", typeof(NewModuleManifestCommand), helpFile) }, {"New-PSRoleCapabilityFile", new SessionStateCmdletEntry("New-PSRoleCapabilityFile", typeof(NewPSRoleCapabilityFileCommand), helpFile) }, {"New-PSSession", new SessionStateCmdletEntry("New-PSSession", typeof(NewPSSessionCommand), helpFile) }, + {"New-PSSessionOption", new SessionStateCmdletEntry("New-PSSessionOption", typeof(NewPSSessionOptionCommand), helpFile) }, {"New-PSTransportOption", new SessionStateCmdletEntry("New-PSTransportOption", typeof(NewPSTransportOptionCommand), helpFile) }, {"Out-Default", new SessionStateCmdletEntry("Out-Default", typeof(OutDefaultCommand), helpFile) }, {"Out-Host", new SessionStateCmdletEntry("Out-Host", typeof(OutHostCommand), helpFile) }, diff --git a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs index 4252ba0389..340a181eaf 100644 --- a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs +++ b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionOptionCommand.cs @@ -20,6 +20,7 @@ namespace Microsoft.PowerShell.Commands { #region Parameters (specific to PSSessionOption) +#if !UNIX /// /// The MaximumRedirection parameter enables the implicit redirection functionality /// -1 = no limit @@ -178,11 +179,13 @@ namespace Microsoft.PowerShell.Commands set { _idleTimeout = value; } } private int? _idleTimeout; +#endif #endregion Parameters #region Parameters copied from New-WSManSessionOption +#if !UNIX /// /// By default, ProxyAccessType is None, that means Proxy information (ProxyAccessType, /// ProxyAuthenticationMechanism and ProxyCredential)is not passed to WSMan at all. @@ -210,6 +213,7 @@ namespace Microsoft.PowerShell.Commands [ValidateNotNullOrEmpty] [Credential] public PSCredential ProxyCredential { get; set; } +#endif /// /// The following is the definition of the input parameter "SkipCACheck". @@ -241,6 +245,8 @@ namespace Microsoft.PowerShell.Commands } private bool _skipcncheck; +#if !UNIX + /// /// The following is the definition of the input parameter "SkipRevocation". /// Indicates that certificate common name (CN) of the server need not match the @@ -319,6 +325,8 @@ namespace Microsoft.PowerShell.Commands } private bool _includePortInSPN; +#endif + #endregion #region Implementation @@ -330,11 +338,14 @@ namespace Microsoft.PowerShell.Commands { PSSessionOption result = new PSSessionOption(); // Begin: WSMan specific options +#if !UNIX result.ProxyAccessType = this.ProxyAccessType; result.ProxyAuthentication = this.ProxyAuthentication; result.ProxyCredential = this.ProxyCredential; +#endif result.SkipCACheck = this.SkipCACheck; result.SkipCNCheck = this.SkipCNCheck; +#if !UNIX result.SkipRevocationCheck = this.SkipRevocationCheck; if (_operationtimeout.HasValue) { @@ -385,6 +396,7 @@ namespace Microsoft.PowerShell.Commands { result.ApplicationArguments = this.ApplicationArguments; } +#endif this.WriteObject(result); } diff --git a/src/System.Management.Automation/engine/remoting/common/remotingexceptions.cs b/src/System.Management.Automation/engine/remoting/common/remotingexceptions.cs index 1c7af32710..8eb3b1c016 100644 --- a/src/System.Management.Automation/engine/remoting/common/remotingexceptions.cs +++ b/src/System.Management.Automation/engine/remoting/common/remotingexceptions.cs @@ -127,6 +127,7 @@ namespace System.Management.Automation.Remoting CannotSetStdOutHandle = 822, CannotSetStdErrHandle = 823, InvalidConfigurationName = 824, + ConnectSkipCheckFailed = 825, // Error codes added to support new WSMan Fan-In Model API CreateSessionFailed = 851, CreateExFailed = 853, diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs index 6f32cf8a0e..7d981fc225 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManNativeAPI.cs @@ -1924,8 +1924,11 @@ namespace System.Management.Automation.Remoting.Client 0, result1.data.binaryData.bufferLength); } + +#if !UNIX Dbg.Assert(result1.data.type == (uint)WSManDataType.WSMAN_DATA_TYPE_BINARY, "ReceiveDataResult can receive only binary data"); +#endif WSManReceiveDataResult result = new WSManReceiveDataResult(); result.data = dataRecvd; diff --git a/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs b/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs index dea3d3af50..1ed9072e90 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/WSManTransportManager.cs @@ -1528,11 +1528,17 @@ namespace System.Management.Automation.Remoting.Client } #if UNIX - // explicitly disallow Basic auth over HTTP on Unix. + // Explicitly disallow Basic auth over HTTP on Unix. if (connectionInfo.AuthenticationMechanism == AuthenticationMechanism.Basic && !isSSLSpecified && connectionUri.Scheme != Uri.UriSchemeHttps) { throw new PSRemotingTransportException(PSRemotingErrorId.ConnectFailed, RemotingErrorIdStrings.BasicAuthOverHttpNotSupported); } + + // Allow HTTPS on Unix only if SkipCACheck and SkipCNCheck are selected, because OMI client does not support validating server certificates. + if (isSSLSpecified && (!connectionInfo.SkipCACheck || !connectionInfo.SkipCNCheck)) + { + throw new PSRemotingTransportException(PSRemotingErrorId.ConnectSkipCheckFailed, RemotingErrorIdStrings.UnixOnlyHttpsWithoutSkipCACheckNotSupported); + } #endif if (connectionInfo.NoEncryption) diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index 37f976684f..ef1c0554f9 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -1670,4 +1670,7 @@ All WinRM sessions connected to PowerShell session configurations, such as Micro Host system does not have the correct version of Hyper-V schema. + + HTTPS on Unix does not currently support CA or CN checks. Use the PSSessionOption -SkipCACheck and -SkipCNCheck if you are certain you trust the server you are connecting to and the network in between. + diff --git a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 index bc988c984e..5fd62c5f89 100644 --- a/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 +++ b/test/powershell/engine/Basic/DefaultCommands.Tests.ps1 @@ -340,7 +340,7 @@ Describe "Verify approved aliases list" -Tags "CI" { "Cmdlet", "New-PSRoleCapabilityFile", , $( $CoreWindows -or $CoreUnix) "Cmdlet", "New-PSSession", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "New-PSSessionConfigurationFile", , $($FullCLR -or $CoreWindows ) -"Cmdlet", "New-PSSessionOption", , $($FullCLR -or $CoreWindows ) +"Cmdlet", "New-PSSessionOption", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "New-PSTransportOption", , $($FullCLR -or $CoreWindows -or $CoreUnix) "Cmdlet", "New-Service", , $($FullCLR -or $CoreWindows ) "Cmdlet", "New-TemporaryFile", , $( $CoreWindows -or $CoreUnix) diff --git a/test/powershell/engine/Remoting/PSSession.Tests.ps1 b/test/powershell/engine/Remoting/PSSession.Tests.ps1 new file mode 100644 index 0000000000..8221e405e9 --- /dev/null +++ b/test/powershell/engine/Remoting/PSSession.Tests.ps1 @@ -0,0 +1,79 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +# +# PSSession tests for non-Windows platforms +# + +Describe "New-PSSessionOption parameters for non-Windows platforms" -Tag "CI" { + + BeforeAll { + $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() + + if ($IsWindows) { + $PSDefaultParameterValues['it:skip'] = $true + } + } + + AfterAll { + $global:PSDefaultParameterValues = $originalDefaultParameterValues + } + + It "Verifies New-PSSessionOption parameters" { + + $cmdInfo = Get-Command New-PSSessionOption + + $commonParameterCount = [System.Management.Automation.Internal.CommonParameters].GetProperties().Length + $cmdInfo.Parameters.Count | Should -Be ($commonParameterCount + 2) -Because "Only -SkipCACheck and -SkipCNCheck switch parameters are available" + + { $null = $cmdInfo.ResolveParameter("SkipCACheck") } | Should -Not -Throw -Because "SkipCACheck parameter should be available" + { $null = $cmdInfo.ResolveParameter("SkipCNCheck") } | Should -Not -Throw -Because "SkipCNCheck parameter should be available" + } +} + +Describe "SkipCACheck and SkipCNCheck PSSession options are required for New-PSSession on non-Windows platforms" -Tag "CI" { + + BeforeAll { + $originalDefaultParameterValues = $PSDefaultParameterValues.Clone() + + if ($IsWindows) { + $PSDefaultParameterValues['it:skip'] = $true + } + else { + $userName = "User_$(Get-Random -Maximum 99999)" + $userPassword = "Password_$(Get-Random -Maximum 99999)" + $cred = [pscredential]::new($userName, (ConvertTo-SecureString -String $userPassword -AsPlainText -Force)) + $soSkipCA = New-PSSessionOption -SkipCACheck + $soSkipCN = New-PSSessionOption -SkipCNCheck + } + } + + AfterAll { + $global:PSDefaultParameterValues = $originalDefaultParameterValues + } + + $testCases = @( + @{ + Name = 'Verifies expected error when session options is missing' + ScriptBlock = { New-PSSession -cn localhost -Credential $cred -Authentication Basic -UseSSL } + ExpectedErrorCode = 825 + }, + @{ + Name = 'Verifies expected error when SkipCACheck option is missing' + ScriptBlock = { New-PSSession -cn localhost -Credential $cred -Authentication Basic -UseSSl -SessionOption $soSkipCN } + ExpectedErrorCode = 825 + }, + @{ + Name = 'Verifies expected error when SkipCNCheck option is missing' + ScriptBlock = { New-PSSession -cn localhost -Credential $cred -Authentication Basic -UseSSl -SessionOption $soSkipCA } + ExpectedErrorCode = 825 + } + ) + + It "" -TestCases $testCases { + param ($scriptBlock, $expectedErrorCode) + + $er = { & $scriptBlock } | Should -Throw -ErrorId 'System.Management.Automation.Remoting.PSRemotingDataStructureException,Microsoft.PowerShell.Commands.NewPSSessionCommand' -PassThru + $er.Exception.ErrorCode | Should -Be $expectedErrorCode + } +}