|
21 | 21 | # Added Test-ValidSslFlags to verify the correct bit flag |
22 | 22 | # 11/04/25 3.0 Updated Get-KFCertificates to get specific certificate by thumbprint |
23 | 23 | # 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 |
24 | 25 |
|
25 | 26 | # Set preferences globally at the script level |
26 | 27 | $DebugPreference = "Continue" |
@@ -437,8 +438,9 @@ function New-KFIISSiteBinding { |
437 | 438 | try { |
438 | 439 | # Step 1: Perform verifications and get management info |
439 | 440 | # 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 |
442 | 444 | } |
443 | 445 |
|
444 | 446 | $managementInfo = Get-IISManagementInfo -SiteName $SiteName |
@@ -1989,14 +1991,151 @@ function Parse-DNSubject { |
1989 | 1991 | return $subjectString |
1990 | 1992 | } |
1991 | 1993 |
|
| 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 | +} |
1992 | 2027 | 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 ", ") |
1999 | 2137 | } |
| 2138 | +#### |
2000 | 2139 |
|
2001 | 2140 | # Note: Removed Test-IISBindingConflict function - we now mimic IIS behavior |
2002 | 2141 | # IIS replaces exact matches and allows multiple hostnames (SNI) on same IP:Port |
|
0 commit comments