Skip to content

Commit f195d73

Browse files
Enforce LoginGraceTime in wolfsshd on Windows and make the grace flag per-connection
1 parent e2bfa19 commit f195d73

5 files changed

Lines changed: 262 additions & 38 deletions

File tree

.github/workflows/windows-check.yml

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,13 +43,12 @@ jobs:
4343
working-directory: ${{env.GITHUB_WORKSPACE}}wolfssl
4444
run: nuget restore ${{env.WOLFSSL_SOLUTION_FILE_PATH}}
4545

46-
- name: updated user_settings.h for sshd and x509
46+
- name: Enable wolfSSH options (sshd, sftp, x509) in user_settings.h
4747
working-directory: ${{env.GITHUB_WORKSPACE}}
48-
run: cp ${{env.USER_SETTINGS_H_NEW}} ${{env.USER_SETTINGS_H}}
49-
50-
- name: replace wolfSSL user_settings.h with wolfSSH user_settings.h
51-
working-directory: ${{env.GITHUB_WORKSPACE}}
52-
run: get-content ${{env.USER_SETTINGS_H_NEW}} | %{$_ -replace "if 0","if 1"}
48+
shell: bash
49+
run: |
50+
sed -i 's/#if 0/#if 1/g' ${{env.USER_SETTINGS_H_NEW}}
51+
cp ${{env.USER_SETTINGS_H_NEW}} ${{env.USER_SETTINGS_H}}
5352
5453
- name: Build wolfssl library
5554
working-directory: ${{env.GITHUB_WORKSPACE}}wolfssl
@@ -65,3 +64,27 @@ jobs:
6564
# See https://docs.microsoft.com/visualstudio/msbuild/msbuild-command-line-reference
6665
run: msbuild /m /p:PlatformToolset=v142 /p:Platform=${{env.BUILD_PLATFORM}} /p:WindowsTargetPlatformVersion=${{env.TARGET_PLATFORM}} /p:Configuration=${{env.WOLFSSH_BUILD_CONFIGURATION}} ${{env.SOLUTION_FILE_PATH}}
6766

67+
- name: Locate wolfsshd.exe and stage wolfssl.dll
68+
working-directory: ${{ github.workspace }}\wolfssh
69+
shell: pwsh
70+
run: |
71+
$sshdExe = Get-ChildItem -Path "${{ github.workspace }}\wolfssh" -Recurse -Filter "wolfsshd.exe" -ErrorAction SilentlyContinue |
72+
Where-Object { $_.FullName -like "*Release*" } | Select-Object -First 1
73+
if (-not $sshdExe) {
74+
Write-Host "ERROR: wolfsshd.exe not found"
75+
exit 1
76+
}
77+
Add-Content -Path $env:GITHUB_ENV -Value "SSHD_PATH=$($sshdExe.FullName)"
78+
79+
$sshdDir = Split-Path -Parent $sshdExe.FullName
80+
$wolfsslDll = Get-ChildItem -Path "${{ github.workspace }}\wolfssl" -Recurse -Filter "wolfssl.dll" -ErrorAction SilentlyContinue | Select-Object -First 1
81+
if ($wolfsslDll -and -not (Test-Path (Join-Path $sshdDir "wolfssl.dll"))) {
82+
Copy-Item -Path $wolfsslDll.FullName -Destination (Join-Path $sshdDir "wolfssl.dll") -Force
83+
}
84+
85+
- name: Test LoginGraceTime enforcement on Windows
86+
working-directory: ${{ github.workspace }}\wolfssh\apps\wolfsshd\test
87+
shell: pwsh
88+
timeout-minutes: 2
89+
run: pwsh -File .\sshd_login_grace_test.ps1 -SshdExe "$env:SSHD_PATH"
90+

apps/wolfsshd/test/run_all_sshd_tests.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ else
132132

133133
#Github actions needs resolved for these test cases
134134
#run_test "error_return.sh"
135-
#run_test "sshd_login_grace_test.sh"
136135

137136
# add additional tests here, check on var USING_LOCAL_HOST if can make sshd
138137
# server start/restart with changes
@@ -147,9 +146,10 @@ else
147146
run_test "sshd_forcedcmd_test.sh"
148147
run_test "sshd_window_full_test.sh"
149148
run_test "sshd_empty_password_test.sh"
149+
run_test "sshd_login_grace_test.sh"
150150
else
151151
printf "Skipping tests that need to setup local SSHD\n"
152-
SKIPPED=$((SKIPPED+3))
152+
SKIPPED=$((SKIPPED+4))
153153
fi
154154

155155
# these tests run with X509 sshd-config loaded
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
#!/usr/bin/env pwsh
2+
#
3+
# Windows regression test for wolfsshd LoginGraceTime enforcement.
4+
#
5+
# Opens a raw TCP connection that never authenticates and verifies that
6+
# wolfsshd drops it at the login grace deadline. No Windows user account or
7+
# authorized key is required, because the connection is closed before
8+
# authentication ever completes - this exercises the pre-auth grace timer only.
9+
#
10+
# Usage:
11+
# pwsh sshd_login_grace_test.ps1 -SshdExe <path-to-wolfsshd.exe> [-Port N] [-Grace N]
12+
# (SshdExe also accepts the SSHD_PATH environment variable.)
13+
14+
param(
15+
[string]$SshdExe = $env:SSHD_PATH,
16+
[int]$Port = 22224,
17+
[int]$Grace = 5
18+
)
19+
20+
$ErrorActionPreference = "Stop"
21+
$exitCode = 1
22+
23+
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
24+
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..\..\..")).Path
25+
$keyPath = (Resolve-Path (Join-Path $repoRoot "keys\server-key.pem")).Path
26+
$logFile = Join-Path $scriptDir "grace_log.txt"
27+
$confFile = Join-Path $scriptDir "sshd_config_test_login_grace"
28+
$authFile = Join-Path $scriptDir "authorized_keys_test"
29+
30+
if (-not $SshdExe -or -not (Test-Path $SshdExe)) {
31+
Write-Host "ERROR: wolfsshd.exe not found (pass -SshdExe or set SSHD_PATH)"
32+
exit 1
33+
}
34+
35+
@"
36+
Port $Port
37+
Protocol 2
38+
LoginGraceTime $Grace
39+
PermitRootLogin yes
40+
PasswordAuthentication yes
41+
PermitEmptyPasswords no
42+
UseDNS no
43+
HostKey $keyPath
44+
AuthorizedKeysFile $authFile
45+
"@ | Out-File -FilePath $confFile -Encoding ASCII
46+
47+
"" | Out-File -FilePath $authFile -Encoding ASCII
48+
if (Test-Path $logFile) { Remove-Item $logFile -Force }
49+
50+
# Run wolfsshd in the foreground (-D) with debug logging to a file (-E). On
51+
# Windows, -D selects the non-service foreground path, which lets us read the
52+
# log just like the Unix test does.
53+
$sshd = Start-Process -FilePath $SshdExe `
54+
-ArgumentList "-D", "-d", "-E", "`"$logFile`"", "-f", "`"$confFile`"", "-p", "$Port" `
55+
-NoNewWindow -PassThru
56+
57+
try {
58+
# Wait for the listener to accept connections.
59+
$up = $false
60+
for ($i = 0; $i -lt 20; $i++) {
61+
try {
62+
$probe = New-Object System.Net.Sockets.TcpClient
63+
$probe.Connect("127.0.0.1", $Port)
64+
$probe.Close()
65+
$up = $true
66+
break
67+
}
68+
catch {
69+
Start-Sleep -Milliseconds 500
70+
}
71+
}
72+
if (-not $up) {
73+
# throw rather than exit so the finally block still stops the daemon
74+
throw "wolfsshd did not start listening on port $Port"
75+
}
76+
77+
# Open a raw TCP connection and hold it open without authenticating.
78+
$stall = New-Object System.Net.Sockets.TcpClient
79+
$stall.Connect("127.0.0.1", $Port)
80+
81+
# Wait past the grace deadline (plus margin) for the server to drop it.
82+
Start-Sleep -Seconds ($Grace + 3)
83+
$stall.Close()
84+
85+
# Stop wolfsshd before reading the log. On Windows the running daemon keeps
86+
# the log file open, so it cannot be read concurrently; the grace-period
87+
# line is already flushed to disk by the logging callback.
88+
if ($sshd -and -not $sshd.HasExited) {
89+
Stop-Process -Id $sshd.Id -Force -ErrorAction SilentlyContinue
90+
$sshd.WaitForExit(5000) | Out-Null
91+
}
92+
93+
if (Select-String -Path $logFile -Pattern "Failed login within grace period" -Quiet) {
94+
Write-Host "PASS: unauthenticated connection dropped at login grace deadline"
95+
$exitCode = 0
96+
}
97+
else {
98+
Write-Host "FAIL: grace period not enforced"
99+
if (Test-Path $logFile) { Get-Content $logFile -Tail 40 }
100+
}
101+
}
102+
catch {
103+
Write-Host "ERROR: $_"
104+
$exitCode = 1
105+
}
106+
finally {
107+
if ($sshd -and -not $sshd.HasExited) {
108+
Stop-Process -Id $sshd.Id -Force -ErrorAction SilentlyContinue
109+
}
110+
}
111+
112+
exit $exitCode

apps/wolfsshd/test/sshd_login_grace_test.sh

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,30 @@ if [ "$RESULT" != 0 ]; then
4747
exit 1
4848
fi
4949

50-
# attempt clearing out stdin from previous echo/grep
51-
read -t 1 -n 1000 discard
50+
popd
5251

53-
# test grace login timeout by stalling on password prompt
54-
timeout --foreground 7 "$TEST_CLIENT" -u "$USER" -h "$TEST_HOST" -p "$TEST_PORT" -t
52+
# Test the grace-time timeout deterministically. Open a raw TCP connection and
53+
# hold it open without ever sending authentication. This needs no TTY or stdin
54+
# interaction, so it is reliable under CI (the previous version relied on the
55+
# client stalling at an interactive password prompt, which fails without a tty).
56+
GRACE=5
57+
(
58+
exec 3<>"/dev/tcp/$TEST_HOST/$TEST_PORT" || exit 1
59+
sleep $((GRACE + 4))
60+
exec 3>&-
61+
) &
62+
STALL_PID=$!
5563

56-
popd
57-
cat ./log.txt | grep "Failed login within grace period"
64+
# wait past the grace deadline (plus margin) for the server to drop the
65+
# unauthenticated connection
66+
sleep $((GRACE + 3))
67+
68+
grep "Failed login within grace period" ./log.txt
5869
RESULT=$?
70+
71+
kill "$STALL_PID" 2>/dev/null
72+
wait "$STALL_PID" 2>/dev/null
73+
5974
if [ "$RESULT" != 0 ]; then
6075
echo "FAIL: Grace period not hit"
6176
cat ./log.txt

0 commit comments

Comments
 (0)