Skip to content

Commit ceda17c

Browse files
author
Bob Pokorny
committed
Fixed a problem with invalid SSL flags when using different versions of Windows Server and IIS.
1 parent 3668a3d commit ceda17c

File tree

4 files changed

+164
-37
lines changed

4 files changed

+164
-37
lines changed

CHANGELOG.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,26 @@
55
* Improved messaging in the event an Entry Parameter is missing (or does not meet the casing requirements)
66
* Fixed the SNI/SSL flag being returned during inventory, now returns extended SSL flags
77
* Fixed the SNI/SSL flag when binding the certificate to allow for extended SSL flags
8-
* Added SSL Flag validation to make sure the bit flag is correct. These are the current SSL Flags (NOTE: Values greater than 4 are only supported in IIS 10 version 1809 and higher. The default value is 0):
8+
* Added SSL Flag validation to make sure the bit flag is correct. These are the valid bit flags for the version of Windows:
9+
* Windows Server 2012 R2 / Windows 8.1 and earlier (IIS 8.5):
910
* 0 No SNI
1011
* 1 Use SNI
1112
* 2 Use Centralized SSL certificate store.
13+
14+
* Windows Server 2016 (IIS 10.0):
15+
* 0 No SNI
16+
* 1 Use SNI
17+
* 4 Disable HTTP/2.
18+
19+
* Windows Server 2019 (IIS 10.0.17763)
20+
* 0 No SNI
21+
* 1 Use SNI
22+
* 4 Disable HTTP/2.
23+
* 8 Disable OCSP Stapling.
24+
25+
* Windows Server 2022+ (IIS 10.0.20348+)
26+
* 0 No SNI
27+
* 1 Use SNI
1228
* 4 Disable HTTP/2.
1329
* 8 Disable OCSP Stapling.
1430
* 16 Disable QUIC.

IISU/ImplementedStoreTypes/WinIIS/IISBindingInfo.cs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -89,27 +89,6 @@ public static IISBindingInfo ParseAliaseBindingString(string alias)
8989
};
9090
}
9191

92-
public (bool IsValid, string Message) ValidateSslFlags(int flags)
93-
{
94-
const int validBits = 1 | 2 | 4 | 8 | 32 | 64 | 128;
95-
96-
// Unknown bits
97-
if ((flags & ~validBits) != 0)
98-
return (false, $"SslFlags contains unknown bits: {flags}");
99-
100-
bool negotiate = (flags & (int)SslFlags.SslNegotiateCert) != 0;
101-
bool require = (flags & (int)SslFlags.SslRequireCert) != 0;
102-
bool mapCert = (flags & (int)SslFlags.SslMapCert) != 0;
103-
104-
if (negotiate && require)
105-
return (false, "Cannot use both SslNegotiateCert (0x2) and SslRequireCert (0x4).");
106-
107-
if (mapCert && !(negotiate || require))
108-
return (false, "SslMapCert (0x8) requires either SslNegotiateCert (0x2) or SslRequireCert (0x4).");
109-
110-
return (true, $"SslFlags value {flags} is valid.");
111-
}
112-
11392
private string MigrateSNIFlag(string input)
11493
{
11594
if (int.TryParse(input, out int numericValue))

IISU/ImplementedStoreTypes/WinIIS/Management.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,6 @@ public JobResult ProcessJob(ManagementJobConfiguration config)
107107
bindingInfo.SniFlag = "0"; // Set to None if not using HTTPS
108108
}
109109

110-
var (isValid, SslErrorMessage) = bindingInfo.ValidateSslFlags(int.Parse(bindingInfo.SniFlag));
111-
if (!isValid)
112-
{
113-
throw new ArgumentException($"Invalid SSL Flag Combination: {SslErrorMessage}");
114-
}
115-
116-
117110
_psHelper = new(protocol, port, includePortInSPN, _clientMachineName, serverUserName, serverPassword);
118111

119112
_psHelper.Initialize();

IISU/PowerShellScripts/WinCertScripts.ps1

Lines changed: 147 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
# Added Test-ValidSslFlags to verify the correct bit flag
2222
# 11/04/25 3.0 Updated Get-KFCertificates to get specific certificate by thumbprint
2323
# 01/14/26 3.0 Released version 3.0.0 with updated function documentation and error handling
24+
# 01/20/26 3.0 Fixed a problem for invalid SSL flags depending on the version of IIS and Windows Server is being used
2425

2526
# Set preferences globally at the script level
2627
$DebugPreference = "Continue"
@@ -437,8 +438,9 @@ function New-KFIISSiteBinding {
437438
try {
438439
# Step 1: Perform verifications and get management info
439440
# Check SslFlags
440-
if (-not (Test-ValidSslFlags -SslFlags $SslFlags)) {
441-
return New-ResultObject -Status Error 400 -Step "Validation" -ErrorMessage "Invalid SSL Flag bit configuration ($SslFlags)"
441+
$sslValidationResult = Test-ValidSslFlags -Flags $SslFlags
442+
if (-not $sslValidationResult.IsValid) {
443+
return New-ResultObject -Status Error 400 -Step "SSL Validation" -ErrorMessage $sslValidationResult.Message
442444
}
443445

444446
$managementInfo = Get-IISManagementInfo -SiteName $SiteName
@@ -1989,14 +1991,151 @@ function Parse-DNSubject {
19891991
return $subjectString
19901992
}
19911993

1994+
#### Functions to test SSL flags
1995+
function Get-ValidSslFlagsForSystem {
1996+
<#
1997+
.SYNOPSIS
1998+
Gets the valid SSL flag bits for the current Windows Server version
1999+
#>
2000+
[CmdletBinding()]
2001+
param()
2002+
2003+
$build = [System.Environment]::OSVersion.Version.Build
2004+
2005+
# Return array of valid flag values based on Windows Server version
2006+
if ($build -ge 20348) {
2007+
# Windows Server 2022+ (IIS 10.0.20348+)
2008+
Write-Verbose "Detected Windows Server 2022 or later (Build: $build)"
2009+
return @(1, 4, 8, 16, 32, 64) # Include unknowns for testing
2010+
}
2011+
elseif ($build -ge 17763) {
2012+
# Windows Server 2019 (IIS 10.0.17763)
2013+
Write-Verbose "Detected Windows Server 2019 (Build: $build)"
2014+
return @(1, 4, 8)
2015+
}
2016+
elseif ($build -ge 14393) {
2017+
# Windows Server 2016 (IIS 10.0)
2018+
Write-Verbose "Detected Windows Server 2016 (Build: $build)"
2019+
return @(1, 4)
2020+
}
2021+
else {
2022+
# Windows Server 2012 R2 and earlier (IIS 8.5)
2023+
Write-Verbose "Detected Windows Server 2012 R2 or earlier (Build: $build)"
2024+
return @(1, 2)
2025+
}
2026+
}
19922027
function Test-ValidSslFlags {
1993-
param([int]$SslFlags)
1994-
1995-
$validBits = 1,2,4,8,32,64,128
1996-
$invalidBits = $SslFlags -bxor ($SslFlags -band ($validBits | Measure-Object -Sum).Sum)
1997-
1998-
return ($invalidBits -eq 0)
2028+
[CmdletBinding()]
2029+
param(
2030+
[Parameter(Mandatory = $true)]
2031+
[int]$Flags,
2032+
2033+
[Parameter(Mandatory = $false)]
2034+
[switch]$ThrowOnError
2035+
)
2036+
2037+
$build = [System.Environment]::OSVersion.Version.Build
2038+
$validBits = Get-ValidSslFlagsForSystem
2039+
2040+
# Calculate valid bitmask
2041+
$validMask = 0
2042+
foreach ($bit in $validBits) {
2043+
$validMask = $validMask -bor $bit
2044+
}
2045+
2046+
# Check for unknown/unsupported bits
2047+
$unknownBits = $Flags -band (-bnot $validMask)
2048+
if ($unknownBits -ne 0) {
2049+
$errorMsg = "SslFlags value $Flags (0x$($Flags.ToString('X'))) contains unsupported bits " +
2050+
"for this Windows Server version (Build: $build): $unknownBits (0x$($unknownBits.ToString('X'))). " +
2051+
"Supported flags: $($validBits -join ', ')"
2052+
2053+
if ($ThrowOnError) {
2054+
throw $errorMsg
2055+
}
2056+
else {
2057+
return [PSCustomObject]@{
2058+
IsValid = $false
2059+
ErrorCode = 400
2060+
Message = $errorMsg
2061+
WindowsBuild = $build
2062+
ValidFlags = $validBits
2063+
InvalidBits = $unknownBits
2064+
}
2065+
}
2066+
}
2067+
2068+
# Check for known invalid combinations
2069+
$hasSni = ($Flags -band 1) -ne 0
2070+
$hasCentralCert = ($Flags -band 2) -ne 0
2071+
2072+
if ($hasCentralCert -and -not $hasSni) {
2073+
$errorMsg = "SslFlags value $Flags (0x$($Flags.ToString('X'))) is invalid: " +
2074+
"CentralCertStore (0x2) requires SNI (0x1) to be enabled."
2075+
2076+
if ($ThrowOnError) {
2077+
throw $errorMsg
2078+
}
2079+
else {
2080+
return [PSCustomObject]@{
2081+
IsValid = $false
2082+
ErrorCode = 400
2083+
Message = $errorMsg
2084+
WindowsBuild = $build
2085+
ValidFlags = $validBits
2086+
InvalidBits = 0
2087+
}
2088+
}
2089+
}
2090+
2091+
# Validation passed
2092+
$successMsg = "SslFlags value $Flags (0x$($Flags.ToString('X'))) is valid for this system (Build: $build)."
2093+
2094+
if ($ThrowOnError) {
2095+
Write-Verbose $successMsg
2096+
return $true
2097+
}
2098+
else {
2099+
return [PSCustomObject]@{
2100+
IsValid = $true
2101+
ErrorCode = 0
2102+
Message = $successMsg
2103+
WindowsBuild = $build
2104+
ValidFlags = $validBits
2105+
InvalidBits = 0
2106+
}
2107+
}
2108+
}
2109+
function Get-SslFlagDescription {
2110+
<#
2111+
.SYNOPSIS
2112+
Returns a human-readable description of SSL flags
2113+
2114+
.PARAMETER Flags
2115+
The SSL flags value to describe
2116+
#>
2117+
[CmdletBinding()]
2118+
param(
2119+
[Parameter(Mandatory = $true)]
2120+
[int]$Flags
2121+
)
2122+
2123+
$descriptions = @()
2124+
2125+
if (($Flags -band 1) -ne 0) { $descriptions += "SNI (Server Name Indication)" }
2126+
if (($Flags -band 2) -ne 0) { $descriptions += "CentralCertStore (Centralized Certificate Store)" }
2127+
if (($Flags -band 4) -ne 0) { $descriptions += "DisableHTTP2 (Disable HTTP/2)" }
2128+
if (($Flags -band 16) -ne 0) { $descriptions += "DisableQUIC (Disable QUIC/HTTP3)" }
2129+
if (($Flags -band 32) -ne 0) { $descriptions += "DisableTLS13 (Disable TLS 1.3 over TCP)" }
2130+
if (($Flags -band 64) -ne 0) { $descriptions += "DisableLegacyTLS (Disable Legacy TLS)" }
2131+
2132+
if ($descriptions.Count -eq 0) {
2133+
return "None (0x0)"
2134+
}
2135+
2136+
return ($descriptions -join ", ")
19992137
}
2138+
####
20002139

20012140
# Note: Removed Test-IISBindingConflict function - we now mimic IIS behavior
20022141
# IIS replaces exact matches and allows multiple hostnames (SNI) on same IP:Port

0 commit comments

Comments
 (0)