Skip to content

Commit 5779c1f

Browse files
authored
Add TLS protocol management commands (#58)
* Add TLS protocol management commands * Fix default values to allow docs to be generated * Fix capture inner exception messages * Refactor protocol handling to use [System.Security.Authentication.SslProtocols] enum values across functions and tests * Enhance TLS protocol functions to support enabled/disabled checks for client and server connections * Update error ID for failure handling in Disable-TlsProtocol function * Update Test-TlsNegotiation function to use localized message for protocol attempts * Fix logic in Test-TlsProtocol to handle null protocol states correctly * Refactor Get-TlsProtocolRegistryPath tests to move mocks into BeforeAll blocks for better organization * Add parameter validation tests for TLS protocol commands * Add parameter validation tests for TLS protocol commands * Clarify comment in Test-TlsProtocol function regarding default protocol states for -Disabled parameter * Enhance ShouldProcess messages in Enable-TlsProtocol and Disable-TlsProtocol functions; improve error handling in Test-TlsNegotiation function * Remove unnecessary blank line in Get-TlsProtocolRegistryPath.Tests.ps1 * Refactor test descriptions for clarity and consistency across TLS protocol tests * Remove redundant parameter set checks from TLS protocol tests * Fix logic in Test-TlsProtocol function for protocol state checks * Refactor variable names in Enable-TlsProtocol function for consistency and clarity * Improve timeout handling in Test-TlsNegotiation function with proper resource disposal * Refactor parameter declaration in cert validation callback for improved clarity * Add INPUTS section to Get-TlsProtocolRegistryPath and change variable types to UInt32 in Get-TlsProtocol * Change expected type assertions for Enabled and DisabledByDefault properties to UInt32 in Get-TlsProtocol tests * Add test case to validate default behavior of Test-TlsNegotiation when Protocol parameter is not provided * Add test case for handling failure in New-Item during Disable-TlsProtocol * Add test case for handling failure in New-Item during Enable-TlsProtocol * Add test case for handling omitted Protocol parameter in Get-TlsProtocol * Update error message for missing DscResource.Test module dependency in test scripts * Refactor tests to suppress output from Disable-TlsProtocol and Enable-TlsProtocol commands * Remove empty placeholder file from module source * Update Test-TlsNegotiation examples and change default port to 443 * Add regex pattern to cSpell ignore localized string IDs * Implement Set-TlsProtocolRegistryValue function for managing TLS/SSL protocol registry settings * Refactor variable names in Get-TlsProtocol, Test-TlsNegotiation, and Test-TlsProtocol functions for improved readability * Refactor test assertions for Protocol parameter to use Should -BeTrue for clarity * Refactor error message handling in Set-TlsProtocolRegistryValue function for improved clarity * Add SuppressMessage attributes and refactor tests to use Set-TlsProtocolRegistryValue for protocol management * Add Force parameter to Set-TlsProtocolRegistryValue for bypassing confirmation prompts * Fix default TCP port in Test-TlsNegotiation documentation from 1433 to 443 * Refactor Get-TlsProtocol tests to use Should -HaveType for type assertions * Refactor parameter assertions in tests to use Should -BeFalse for clarity * Add support for Tls13 in Get-TlsProtocol for older .NET versions * Refactor Get-TlsProtocolRegistryPath to use string formatting for registry path construction * Fix registry path escaping in Test-TlsProtocol tests * Refactor ConvertTo-TlsProtocolRegistryKeyName tests to use a data-driven approach for known protocol mappings * Update tests to use strict mode and fix registry path escaping * Remove Cmdlet parameter from examples in Set-TlsProtocolRegistryValue documentation * Refactor Test-TlsProtocol function for improved readability and consistency in control flow * Add -Scope It to Should-Invoke assertions in tests for consistency * Update Get-TlsProtocol tests to use Should -BeOfType for type assertions * Refactor Test-TlsNegotiation tests to improve protocol handling and assertions * Fix registry path escape characters in Get-TlsProtocolRegistryPath and related tests * Update Get-TlsProtocol to include Tls13 in default protocols and adjust handling for older .NET versions * Refactor test scripts to remove script scope for protocol variables * Refactor test scripts to eliminate script scope for commandInfo variable
1 parent 29db5e1 commit 5779c1f

33 files changed

Lines changed: 2731 additions & 11 deletions

.vscode/settings.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
"cSpell.ignorePaths": [
4545
".git"
4646
],
47+
"cSpell.ignoreRegExpList": [
48+
"/\\b[A-Za-z]{2,}\\d{4}\\b/g"
49+
],
4750
"[markdown]": {
4851
"files.trimTrailingWhitespace": false,
4952
"files.encoding": "utf8"

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
55

66
## [Unreleased]
77

8+
### Added
9+
10+
- Public commands
11+
- Assert-TlsProtocol
12+
- Disable-TlsProtocol
13+
- Enable-TlsProtocol
14+
- Get-TlsProtocol
15+
- Test-TlsNegotiation
16+
- Test-TlsProtocol
17+
818
### Changed
919

1020
- SChannelDsc

RequiredModules.psd1

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,4 @@
2929
# Prerequisite modules for documentation.
3030
'DscResource.DocGenerator' = 'latest'
3131
PlatyPS = 'latest'
32-
3332
}

Resolve-Dependency.psd1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
set to $false then PowerShellGet will be used to resolve dependencies.
6868
#>
6969
UsePSResourceGet = $true
70-
PSResourceGetVersion = '1.0.1'
70+
#PSResourceGetVersion = '1.0.1'
7171

7272
# PowerShellGet compatibility module only works when using PSResourceGet or ModuleFast.
7373
UsePowerShellGetCompatibilityModule = $true

build.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ BuildWorkflow:
4747
CopyPaths:
4848
- en-US
4949
- DSCResources
50+
Prefix: prefix.ps1
5051
Encoding: UTF8
5152
VersionedOutputDirectory: true
5253
BuiltModuleSubdirectory: builtModule
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<#
2+
.SYNOPSIS
3+
Converts a protocol identifier to the SCHANNEL registry key name.
4+
5+
.DESCRIPTION
6+
Maps protocol values from the `[System.Security.Authentication.SslProtocols]`
7+
enum to the actual SCHANNEL registry key names (e.g. 'TLS 1.2').
8+
9+
.PARAMETER Protocol
10+
The protocol value from the `[System.Security.Authentication.SslProtocols]`
11+
enum, e.g. `Tls12`, `Ssl3`, `Tls`.
12+
13+
.OUTPUTS
14+
System.String
15+
16+
.EXAMPLE
17+
ConvertTo-TlsProtocolRegistryKeyName -Protocol Tls12
18+
19+
Returns the string 'TLS 1.2'.
20+
#>
21+
function ConvertTo-TlsProtocolRegistryKeyName
22+
{
23+
[CmdletBinding()]
24+
[OutputType([System.String])]
25+
param
26+
(
27+
[Parameter(Mandatory = $true)]
28+
[System.Security.Authentication.SslProtocols]
29+
$Protocol
30+
)
31+
32+
$protocolRegistryKeyName = switch ($Protocol)
33+
{
34+
([System.Security.Authentication.SslProtocols]::Ssl2)
35+
{
36+
'SSL 2.0'
37+
}
38+
39+
([System.Security.Authentication.SslProtocols]::Ssl3)
40+
{
41+
'SSL 3.0'
42+
}
43+
44+
([System.Security.Authentication.SslProtocols]::Tls)
45+
{
46+
'TLS 1.0'
47+
}
48+
49+
([System.Security.Authentication.SslProtocols]::Tls11)
50+
{
51+
'TLS 1.1'
52+
}
53+
54+
([System.Security.Authentication.SslProtocols]::Tls12)
55+
{
56+
'TLS 1.2'
57+
}
58+
59+
([System.Security.Authentication.SslProtocols]::Tls13)
60+
{
61+
'TLS 1.3'
62+
}
63+
64+
default
65+
{
66+
$errorMessage = $script:localizedData.ConvertTo_TlsProtocolRegistryKeyName_UnknownProtocol -f $Protocol
67+
$exception = New-Exception -Message $errorMessage
68+
$errorRecord = New-ErrorRecord -Exception $exception -ErrorId 'CTTPRKN0001' -ErrorCategory ([System.Management.Automation.ErrorCategory]::InvalidArgument) -TargetObject $Protocol
69+
$PSCmdlet.ThrowTerminatingError($errorRecord)
70+
}
71+
}
72+
73+
return $protocolRegistryKeyName
74+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<#
2+
.SYNOPSIS
3+
Returns the SCHANNEL registry path for a given protocol and target.
4+
5+
.DESCRIPTION
6+
Builds the registry path under
7+
HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols
8+
for the provided friendly protocol name and selects the `Server` or
9+
`Client` subkey depending on the `-Client` switch.
10+
11+
.PARAMETER Protocol
12+
The protocol identifier, e.g. 'Tls12'.
13+
14+
.PARAMETER Client
15+
When specified, return the path for the `Client` subkey, otherwise
16+
return the `Server` subkey path.
17+
18+
.INPUTS
19+
None.
20+
21+
.OUTPUTS
22+
System.String
23+
24+
.EXAMPLE
25+
Get-TlsProtocolRegistryPath -Protocol Tls12
26+
27+
Returns the string:
28+
'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server'.
29+
30+
.EXAMPLE
31+
Get-TlsProtocolRegistryPath -Protocol Tls13 -Client
32+
33+
Returns the string:
34+
'HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.3\Client'.
35+
#>
36+
function Get-TlsProtocolRegistryPath
37+
{
38+
[CmdletBinding()]
39+
[OutputType([System.String])]
40+
param
41+
(
42+
[Parameter(Mandatory = $true)]
43+
[ValidateNotNullOrEmpty()]
44+
[System.String]
45+
$Protocol,
46+
47+
[Parameter()]
48+
[System.Management.Automation.SwitchParameter]
49+
$Client
50+
)
51+
52+
$protocolKeyName = ConvertTo-TlsProtocolRegistryKeyName -Protocol $Protocol
53+
54+
$target = Get-TlsProtocolTargetRegistryName -Client:$Client
55+
56+
return ('HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\{0}\{1}' -f $protocolKeyName, $target)
57+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<#
2+
.SYNOPSIS
3+
Returns the SCHANNEL protocol target name for registry keys.
4+
5+
.DESCRIPTION
6+
Returns either 'Server' or 'Client' depending on the provided
7+
`-Client` switch. This centralizes the logic used by public commands
8+
for choosing the registry subkey name.
9+
10+
.PARAMETER Client
11+
When specified, return 'Client', otherwise return 'Server'.
12+
13+
.OUTPUTS
14+
System.String
15+
16+
.EXAMPLE
17+
Get-TlsProtocolTargetRegistryName
18+
19+
Returns the string 'Server'.
20+
21+
.EXAMPLE
22+
Get-TlsProtocolTargetRegistryName -Client
23+
24+
Returns the string 'Client'.
25+
#>
26+
function Get-TlsProtocolTargetRegistryName
27+
{
28+
[CmdletBinding()]
29+
[OutputType([System.String])]
30+
param
31+
(
32+
[Parameter()]
33+
[System.Management.Automation.SwitchParameter]
34+
$Client
35+
)
36+
37+
if ($Client.IsPresent)
38+
{
39+
return 'Client'
40+
}
41+
42+
return 'Server'
43+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<#
2+
.SYNOPSIS
3+
Sets TLS/SSL protocol registry values for enabling or disabling protocols.
4+
5+
.DESCRIPTION
6+
Internal helper function that writes SCHANNEL protocol registry values
7+
under HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols.
8+
This function creates the registry key if it does not exist and sets
9+
the Enabled DWORD value. Optionally sets the DisabledByDefault value.
10+
11+
The function handles ShouldProcess confirmation using the caller's
12+
PSCmdlet object.
13+
14+
.PARAMETER Protocol
15+
One or more protocol names to set registry values for. Accepts values
16+
from the `[System.Security.Authentication.SslProtocols]` enum such as
17+
`Ssl2`, `Ssl3`, `Tls`, `Tls11`, `Tls12`, `Tls13`.
18+
19+
.PARAMETER Enable
20+
Enables the protocol by setting Enabled to 1 and DisabledByDefault to 0.
21+
22+
.PARAMETER Disable
23+
Disables the protocol by setting Enabled to 0 and DisabledByDefault to 1.
24+
25+
.PARAMETER Client
26+
When specified, operate on the protocol `Client` registry key instead of
27+
the default `Server` key.
28+
29+
.PARAMETER SetDisabledByDefault
30+
When specified, also set the DisabledByDefault registry value.
31+
32+
.PARAMETER Force
33+
When specified, bypasses confirmation prompts and suppresses
34+
ShouldProcess confirmations.
35+
36+
.PARAMETER Cmdlet
37+
The PSCmdlet object from the calling command, used to perform
38+
ShouldProcess confirmation.
39+
40+
.INPUTS
41+
None.
42+
43+
.OUTPUTS
44+
None.
45+
46+
.EXAMPLE
47+
Set-TlsProtocolRegistryValue -Protocol Tls12 -Enable -SetDisabledByDefault
48+
49+
Enables TLS 1.2 for server-side connections by setting the Enabled
50+
registry value to 1 and DisabledByDefault to 0.
51+
52+
.EXAMPLE
53+
Set-TlsProtocolRegistryValue -Protocol Tls12, Tls13 -Enable
54+
55+
Enables TLS 1.2 and TLS 1.3 for server-side connections.
56+
57+
.EXAMPLE
58+
Set-TlsProtocolRegistryValue -Protocol Ssl3 -Disable -Client
59+
60+
Disables SSL 3.0 for client-side connections by setting the Enabled
61+
registry value to 0.
62+
#>
63+
function Set-TlsProtocolRegistryValue
64+
{
65+
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Enable')]
66+
[OutputType()]
67+
param
68+
(
69+
[Parameter(Mandatory = $true)]
70+
[System.Security.Authentication.SslProtocols[]]
71+
$Protocol,
72+
73+
[Parameter(Mandatory = $true, ParameterSetName = 'Enable')]
74+
[System.Management.Automation.SwitchParameter]
75+
$Enable,
76+
77+
[Parameter(Mandatory = $true, ParameterSetName = 'Disable')]
78+
[System.Management.Automation.SwitchParameter]
79+
$Disable,
80+
81+
[Parameter()]
82+
[System.Management.Automation.SwitchParameter]
83+
$Client,
84+
85+
[Parameter()]
86+
[System.Management.Automation.SwitchParameter]
87+
$SetDisabledByDefault,
88+
89+
[Parameter()]
90+
[System.Management.Automation.SwitchParameter]
91+
$Force
92+
)
93+
94+
# Need to do this check with Get-Variable instead of $Confirm due to strict mode.
95+
if ($Force.IsPresent -and -not (Get-Variable -Name 'Confirm' -ValueOnly -ErrorAction SilentlyContinue))
96+
{
97+
$ConfirmPreference = 'None'
98+
}
99+
100+
foreach ($currentProtocol in $Protocol)
101+
{
102+
$protocolKeyName = ConvertTo-TlsProtocolRegistryKeyName -Protocol $currentProtocol
103+
$target = Get-TlsProtocolTargetRegistryName -Client:$Client
104+
105+
if ($Enable.IsPresent)
106+
{
107+
$enabledValue = 1
108+
$disabledByDefaultValue = 0
109+
$descriptionMessage = $script:localizedData.Set_TlsProtocolRegistryValue_Enable_ShouldProcessDescription -f $protocolKeyName, $target
110+
$confirmationMessage = $script:localizedData.Set_TlsProtocolRegistryValue_Enable_ShouldProcessConfirmation -f $protocolKeyName
111+
$captionMessage = $script:localizedData.Set_TlsProtocolRegistryValue_Enable_ShouldProcessCaption
112+
$errorMessage = $script:localizedData.Set_TlsProtocolRegistryValue_FailedToEnable
113+
$errorId = 'STPRV0001'
114+
}
115+
else
116+
{
117+
$enabledValue = 0
118+
$disabledByDefaultValue = 1
119+
$descriptionMessage = $script:localizedData.Set_TlsProtocolRegistryValue_Disable_ShouldProcessDescription -f $protocolKeyName, $target
120+
$confirmationMessage = $script:localizedData.Set_TlsProtocolRegistryValue_Disable_ShouldProcessConfirmation -f $protocolKeyName
121+
$captionMessage = $script:localizedData.Set_TlsProtocolRegistryValue_Disable_ShouldProcessCaption
122+
$errorMessage = $script:localizedData.Set_TlsProtocolRegistryValue_FailedToDisable
123+
$errorId = 'STPRV0002'
124+
}
125+
126+
if ($PSCmdlet.ShouldProcess($descriptionMessage, $confirmationMessage, $captionMessage))
127+
{
128+
$regPath = Get-TlsProtocolRegistryPath -Protocol $currentProtocol -Client:$Client
129+
130+
try
131+
{
132+
$null = New-Item -Path $regPath -Force -ErrorAction 'Stop'
133+
$null = New-ItemProperty -Path $regPath -Name 'Enabled' -Value $enabledValue -PropertyType DWord -Force -ErrorAction 'Stop'
134+
135+
if ($SetDisabledByDefault.IsPresent)
136+
{
137+
$null = New-ItemProperty -Path $regPath -Name 'DisabledByDefault' -Value $disabledByDefaultValue -PropertyType DWord -Force -ErrorAction 'Stop'
138+
}
139+
}
140+
catch
141+
{
142+
$errorMessage = $errorMessage -f $currentProtocol
143+
144+
$exception = New-Exception -Message $errorMessage -ErrorRecord $_
145+
$errorRecord = New-ErrorRecord -Exception $exception -ErrorId $errorId -ErrorCategory 'InvalidOperation' -TargetObject $currentProtocol
146+
$PSCmdlet.ThrowTerminatingError($errorRecord)
147+
}
148+
}
149+
}
150+
}

0 commit comments

Comments
 (0)