TlsCertificateValidation.psm1
# Copyright WebMD Health Services # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License #Requires -Version 5.1 Set-StrictMode -Version 'Latest' # Functions should use $moduleRoot as the relative root from which to find # things. A published module has its function appended to this file, while a # module in development has its functions in the Functions directory. $moduleRoot = $PSScriptRoot $assemblyPath = Join-Path -Path $script:moduleRoot -ChildPath 'bin\TlsCertificateValidation.dll' -Resolve $assembly = [AppDomain]::CurrentDomain.GetAssemblies() | Where-Object 'Location' -EQ $assemblyPath $assemblyTypes = $assembly.GetTypes() $script:serverCertCallbackShim = $assemblyTypes | Where-Object 'Name' -EQ 'ServerCertificateCallbackShim' if (-not $script:serverCertCallbackShim) { $msg = "Failed to find [TlsCertificateValidation.ServerCertificateCallbackShim] type from assembly " + """${assemblyPath}""." Write-Error -Message $msg } $functionsPath = Join-Path -Path $moduleRoot -ChildPath 'Functions\*.ps1' if( (Test-Path -Path $functionsPath) ) { foreach( $functionPath in (Get-Item $functionsPath) ) { . $functionPath.FullName } } function Clear-TlsCertificateValidator { <# .SYNOPSIS Removes the current server certificate validation callback function. .DESCRIPTION The `Clear-TlsCertificateValidator` function removes the current server certificate validaton function. .NET allows you to set a custom callback function that it will call when validating TLS certificates. This function sets that callback function to `$null` (i.e. sets the `[Net.ServicePointManager]::ServerCertificateValidationCallback` property to `$null`). .EXAMPLE Clear-TlsCertificateValidator Demonstrates how to remove the current custom server certificate validator. #> [CmdletBinding()] param( ) Set-StrictMode -Version 'Latest' [Net.ServicePointManager]::ServerCertificateValidationCallback = $null } function Disable-TlsCertificateValidation { <# .SYNOPSIS Turns off TLS server certificate validation in the current PowerShell session. .DESCRIPTION The `Disable-TlsCertificateValidation` function disables TLS server certificate validation for the current PowerShell session by setting the `[Net.ServicePointManager]::ServerCertificateValidationCallback` to a function that accepts all certificates. All https web requests made after calling `Disable-TlsCertificateValidation` will accept all invalid TLS certificates. To re-enable server certificate validation, use the `Enable-TlsCertificateValidation` function. .EXAMPLE Disable-TlsCertificateValidation Demonstrates how to turn off TLS server certificate validation. #> [CmdletBinding()] param( [switch] $Force ) Set-StrictMode -Version 'Latest' Set-TlsCertificateValidator -ScriptBlock { return $true } } function Enable-TlsCertificateValidation { <# .SYNOPSIS Turns on TLS server certificate validation in the current PowerShell session. .DESCRIPTION The `Enable-TlsCertificateValidation` function enables TLS server certificate validation for the current PowerShell session by clearing the `[Net.ServicePointManager]::ServerCertificateValidationCallback`. All https web requests made after calling `Enable-TlsCertificateValidation` will reject all invalid TLS certificates. To disable server certificate validation, use the `Disable-TlsCertificateValidation` function. .EXAMPLE Enable-TlsCertificateValidation Demonstrates how to turn on TLS server certificate validation. #> [CmdletBinding()] param( [switch] $Force ) Set-StrictMode -Version 'Latest' Clear-TlsCertificateValidator } function Set-TlsCertificateValidator { <# .SYNOPSIS Sets the .NET custom server certificate validation callback function to use a PowerShell script block. .DESCRIPTION The `Set-TlsCertificateValidator` allows you use a PowerShell script block to add custom validation when .NET is valiating a server's TLS certificate. Pass the script block to the `ScriptBlock` parameter. Your script block *must* return `$true` if the certificate is valid and should be trusted, or `$false` or nothing if the certificate is invalid and should not be trusted. The script block is passed four *optional* parameters, in this order: * `[Object] $Sender`: contains state information for the validation. * `[Security.Cryptography.X509Certificates.X509Certificate2] $Certificate`: the certificate to validate. * `[Security.Cryptography.X509Certificates.X509Chain] $Chain`: the certificate chain of the certificate. * `[Net.Security.SslPolicyErrors] $PolicyErrors`: a flags enum for the certificate's policy errors (if any). Your validator will continue to be used in the current PowerShell session until you call the `Clear-TlsCertificateValidator` function to remove it. .EXAMPLE Set-TlsCertificateValidator { $true } Demonstrates how to use this function to trust all TLS certificates. #> [CmdletBinding()] param( [Parameter(Mandatory, Position=0)] [scriptblock] $ScriptBlock ) Set-StrictMode -Version 'Latest' $script:serverCertCallbackShim::RegisterScriptBlockValidator($ScriptBlock) } function Test-SkipCertificateCheck { <# .SYNOPSIS Tests if PowerShell supports the `SkipCertificateCheck` parameter on `Invoke-WebRequest` and `Invoke-RestMethod`. .DESCRIPTION The `Test-SkipCertificateCheck` function tests if the `Invoke-WebRequest` and `Invoke-RestMethod` have the `SkipCertificateCheck` switch. The purpose of this function is for use in cross-platform or cross-edition scripts when they need to ignore invalid TLS certificates. If this function returns `$true`, code should use the `SkipCertificateCheck` switch on `Invoke-WebRequest` and `Invoke-RestMethod`. If `$false`, scripts should use the `Disable-TlsCertificateValidation` and `Enable-TlsCertificateValidation` functions. This logic is shown in the following sample code: $iwrArgs = @{} if( (Test-SkipCertificateCheck) ) { $iwrArgs['SkipCertificateCheck'] = $true } else { Disable-TlsCertificateValidation } try { Invoke-WebRequest -Uri 'https://expired.badssl.com/' @iwrArgs } finally { if( -not (Test-SkipCertificateCheck) ) { Enable-TlsCertificateValidation } } .EXAMPLE Test-SkipCertificateCheck #> [CmdletBinding()] param( ) Set-StrictMode -Version 'Latest' return $null -ne (Get-Command -Name 'Invoke-WebRequest' -ParameterName 'SkipCertificateCheck' -ErrorAction Ignore) } function Use-CallerPreference { <# .SYNOPSIS Sets the PowerShell preference variables in a module's function based on the callers preferences. .DESCRIPTION Script module functions do not automatically inherit their caller's variables, including preferences set by common parameters. This means if you call a script with switches like `-Verbose` or `-WhatIf`, those that parameter don't get passed into any function that belongs to a module. When used in a module function, `Use-CallerPreference` will grab the value of these common parameters used by the function's caller: * ErrorAction * Debug * Confirm * InformationAction * Verbose * WarningAction * WhatIf This function should be used in a module's function to grab the caller's preference variables so the caller doesn't have to explicitly pass common parameters to the module function. This function is adapted from the [`Get-CallerPreference` function written by David Wyatt](https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d). There is currently a [bug in PowerShell](https://connect.microsoft.com/PowerShell/Feedback/Details/763621) that causes an error when `ErrorAction` is implicitly set to `Ignore`. If you use this function, you'll need to add explicit `-ErrorAction $ErrorActionPreference` to every `Write-Error` call. Please vote up this issue so it can get fixed. .LINK about_Preference_Variables .LINK about_CommonParameters .LINK https://gallery.technet.microsoft.com/scriptcenter/Inherit-Preference-82343b9d .LINK http://powershell.org/wp/2014/01/13/getting-your-script-module-functions-to-inherit-preference-variables-from-the-caller/ .EXAMPLE Use-CallerPreference -Cmdlet $PSCmdlet -SessionState $ExecutionContext.SessionState Demonstrates how to set the caller's common parameter preference variables in a module function. #> [CmdletBinding()] param ( [Parameter(Mandatory)] #[Management.Automation.PSScriptCmdlet] # The module function's `$PSCmdlet` object. Requires the function be decorated with the `[CmdletBinding()]` # attribute. $Cmdlet, [Parameter(Mandatory)] # The module function's `$ExecutionContext.SessionState` object. Requires the function be decorated with the # `[CmdletBinding()]` attribute. # # Used to set variables in its callers' scope, even if that caller is in a different script module. [Management.Automation.SessionState]$SessionState ) Set-StrictMode -Version 'Latest' # List of preference variables taken from the about_Preference_Variables and their common parameter name (taken # from about_CommonParameters). $commonPreferences = @{ 'ErrorActionPreference' = 'ErrorAction'; 'DebugPreference' = 'Debug'; 'ConfirmPreference' = 'Confirm'; 'InformationPreference' = 'InformationAction'; 'VerbosePreference' = 'Verbose'; 'WarningPreference' = 'WarningAction'; 'WhatIfPreference' = 'WhatIf'; } foreach( $prefName in $commonPreferences.Keys ) { $parameterName = $commonPreferences[$prefName] # Don't do anything if the parameter was passed in. if( $Cmdlet.MyInvocation.BoundParameters.ContainsKey($parameterName) ) { continue } $variable = $Cmdlet.SessionState.PSVariable.Get($prefName) # Don't do anything if caller didn't use a common parameter. if( -not $variable ) { continue } if( $SessionState -eq $ExecutionContext.SessionState ) { Set-Variable -Scope 1 -Name $variable.Name -Value $variable.Value -Force -Confirm:$false -WhatIf:$false } else { $SessionState.PSVariable.Set($variable.Name, $variable.Value) } } } |