Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions .github/workflows/windows-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ jobs:
timeout-minutes: 2
run: .\sshd_login_grace_test.ps1 -SshdExe "$env:SSHD_PATH"

- name: Test wolfsshd -D option parsing on Windows
working-directory: ${{ github.workspace }}\wolfssh\apps\wolfsshd\test
shell: pwsh
timeout-minutes: 2
run: .\sshd_dash_d_test.ps1 -SshdExe "$env:SSHD_PATH"

# Build and run the self-contained unit tests with the MSVC AddressSanitizer.
# This is the only job that executes wolfSSH tests under a sanitizer on
# Windows, where the USE_WINDOWS_API console code (e.g. wolfSSH_DoOSC) is
Expand Down Expand Up @@ -220,3 +226,83 @@ jobs:
if ($LASTEXITCODE -ne 0) { throw "$t failed under ASAN (exit $LASTEXITCODE)" }
}

# Build and run the autotools regression tests under MSYS2 MinGW64. MinGW
# defines _WIN32, so wolfssh/settings.h turns on USE_WINDOWS_API and the
# Windows-only coverage in tests/regress.c (TestSftpWindowsOpenFlagMatrix,
# which walks the RecvOpen CREAT/EXCL/TRUNC/APPEND matrix against the
# CreateFile() disposition table) actually compiles and runs. The MSVC
# solution has no regress project and regress.c does not build with cl (it
# uses arpa/inet.h and unistd.h), so without this job that matrix runs in no
# CI at all. wolfsshd is left out of the build: its autotools path is not
# MinGW-clean and the MSVC solution already covers it.
mingw-regress:
name: MSYS2 MinGW64 regression tests
runs-on: windows-latest
timeout-minutes: 40

defaults:
run:
shell: msys2 {0}

steps:
- name: Set up MSYS2 MinGW64
uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: false
install: >-
base-devel
autotools
git
mingw-w64-x86_64-gcc
mingw-w64-x86_64-pkgconf

- name: Checkout wolfssl
uses: actions/checkout@v4
with:
repository: wolfssl/wolfssl
path: wolfssl

- name: Build and install wolfssl
working-directory: wolfssl
run: |
./autogen.sh
# --enable-all pulls in --enable-crl-monitor, which wolfSSL's configure
# rejects on MinGW (it is limited to linux, OS X, and freebsd). Turn it
# back off explicitly; wolfSSH does not use the CRL monitor.
./configure --enable-all --disable-crl-monitor \
--enable-static --disable-shared \
--prefix="$HOME/wolfssl-install"
make -j$(nproc)
make install

- name: Checkout wolfssh
uses: actions/checkout@v4
with:
path: wolfssh

- name: Build and run the regression tests
working-directory: wolfssh
run: |
# Run autoreconf directly instead of ./autogen.sh: for a git checkout
# autogen.sh exports WARNINGS="all,error", turning autotools warnings
# into errors that the MSYS2 automake can trip on.
autoreconf -ivf
# wolfssl is a static archive here, so its Windows socket (ws2_32) and
# certificate store (crypt32) references are only resolved when this
# configure's own AC_CHECK_LIB and later link steps pull them in too.
./configure --enable-sftp \
CPPFLAGS="-I$HOME/wolfssl-install/include" \
LDFLAGS="-L$HOME/wolfssl-install/lib" \
LIBS="-lws2_32 -lcrypt32"
# MinGW's EXEEXT is ".exe", so the check_PROGRAMS targets automake
# generates are tests/regress.test.exe and tests/unit.test.exe, not
# the extension-less names make would use on a POSIX host.
make -j$(nproc) tests/regress.test.exe tests/unit.test.exe
./tests/regress.test.exe
./tests/unit.test.exe

- name: Show config.log on failure
if: failure()
working-directory: wolfssh
run: cat config.log
115 changes: 115 additions & 0 deletions apps/wolfsshd/test/sshd_dash_d_test.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env pwsh
#
# Windows regression test for wolfsshd -D (foreground) option parsing.
#
# On Windows, StartSSHD() rebuilds argv from GetCommandLineW(); a bug in that
# path left -D (foreground) mode parsing the raw wide command line, so -f and
# -p were silently ignored and the daemon fell back to its compiled-in
# defaults. This test starts wolfsshd with -D and a config file at a
# non-default path whose Port line differs from the -p value, then checks that
# the listener comes up on the -p port. That only happens if -D mode parsed
# both -f (to find the config) and -p (to override the config's Port).
#
# No Windows user account or authorized key is required: the check is that the
# daemon binds the requested port, not that a session authenticates.
#
# Usage:
# pwsh sshd_dash_d_test.ps1 -SshdExe <path-to-wolfsshd.exe> [-Port N] [-ConfPort N]
# (SshdExe also accepts the SSHD_PATH environment variable.)

param(
[string]$SshdExe = $env:SSHD_PATH,
[int]$Port = 22335,
[int]$ConfPort = 22336
)

$ErrorActionPreference = "Stop"
$exitCode = 1

$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..\..\..")).Path
$keyPath = (Resolve-Path (Join-Path $repoRoot "keys\server-key.pem")).Path
$confFile = Join-Path $scriptDir "sshd_config_test_dash_d"
$authFile = Join-Path $scriptDir "authorized_keys_test_dash_d"

if (-not $SshdExe -or -not (Test-Path $SshdExe)) {
Write-Host "ERROR: wolfsshd.exe not found (pass -SshdExe or set SSHD_PATH)"
exit 1
}

if ($Port -eq $ConfPort) {
Write-Host "ERROR: -Port and -ConfPort must differ so the test can tell them apart"
exit 1
}

# The config's Port is deliberately not the port we probe. If -p is parsed it
# wins (wolfsshd only reads the config Port when none was given on the command
# line), so a listener on $Port proves the -p override took effect.
@"
Port $ConfPort
Protocol 2
PermitRootLogin yes
PasswordAuthentication yes
UseDNS no
HostKey $keyPath
AuthorizedKeysFile $authFile
"@ | Out-File -FilePath $confFile -Encoding ASCII

"" | Out-File -FilePath $authFile -Encoding ASCII

# -D selects the non-service (foreground) path on Windows.
$sshd = Start-Process -FilePath $SshdExe `
-ArgumentList "-D", "-f", "`"$confFile`"", "-p", "$Port" `
-NoNewWindow -PassThru

try {
$up = $false
for ($i = 0; $i -lt 20; $i++) {
if ($sshd.HasExited) {
throw "wolfsshd exited early (code $($sshd.ExitCode)); -D option parsing likely failed"
}
try {
$probe = New-Object System.Net.Sockets.TcpClient
$probe.Connect("127.0.0.1", $Port)
$probe.Close()
$up = $true
break
}
catch {
Start-Sleep -Milliseconds 500
}
}
if (-not $up) {
throw "wolfsshd did not listen on the -p port $Port; -D did not honor -f/-p"
}

# The config Port must not have been used: nothing should answer there.
$confBound = $false
try {
$probe = New-Object System.Net.Sockets.TcpClient
$probe.Connect("127.0.0.1", $ConfPort)
$probe.Close()
$confBound = $true
}
catch {
# expected: no listener on the config Port
}
if ($confBound) {
throw "wolfsshd listened on the config Port $ConfPort; -p override was not applied"
}

Write-Host "PASS: -D mode parsed -f and -p (listening on $Port, not $ConfPort)"
$exitCode = 0
}
catch {
Write-Host "FAIL: $_"
$exitCode = 1
}
finally {
if ($sshd -and -not $sshd.HasExited) {
Stop-Process -Id $sshd.Id -Force -ErrorAction SilentlyContinue
}
Remove-Item -Path $confFile, $authFile -Force -ErrorAction SilentlyContinue
}

exit $exitCode
26 changes: 13 additions & 13 deletions src/internal.c
Original file line number Diff line number Diff line change
Expand Up @@ -22616,6 +22616,9 @@ int wolfSSH_CleanPath(WOLFSSH* ssh, char* in, int inSz)
byte found;
char *path;
void *heap = NULL;
#if defined(WOLFSSL_NUCLEUS) || defined(USE_WINDOWS_API)
int j;
#endif

if (in == NULL || inSz <= 0) {
return WS_BAD_ARGUMENT;
Expand Down Expand Up @@ -22715,19 +22718,16 @@ int wolfSSH_CleanPath(WOLFSSH* ssh, char* in, int inSz)
}

/* clean up any multiple drive listed i.e. A:/A: */
{
int i,j;
sz = (long)WSTRLEN(path);
for (i = 0, j = 0; i < sz; i++) {
if (path[i] == ':') {
if (j == 0) j = i;
else {
/* @TODO only checking once */
WMEMMOVE(path, path + i - WS_DRIVE_SIZE,
sz - i + WS_DRIVE_SIZE);
path[sz - i + WS_DRIVE_SIZE] = '\0';
break;
}
sz = (long)WSTRLEN(path);
for (i = 0, j = 0; i < sz; i++) {
if (path[i] == ':') {
if (j == 0) j = i;
else {
/* @TODO only checking once */
WMEMMOVE(path, path + i - WS_DRIVE_SIZE,
sz - i + WS_DRIVE_SIZE);
path[sz - i + WS_DRIVE_SIZE] = '\0';
break;
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion tests/regress.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#ifndef _WIN32
#include <arpa/inet.h>
#endif
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
Expand Down
4 changes: 2 additions & 2 deletions wolfssh/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,7 @@ static INLINE int mygetopt(int argc, char** argv, const char* optstring)
}


#ifdef USE_WINDOWS_API
#if defined(USE_WINDOWS_API) && defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable:4996)
/* For Windows builds, disable compiler warnings for:
Expand Down Expand Up @@ -563,7 +563,7 @@ static INLINE void build_addr(SOCKADDR_IN_T* addr, const char* peer,
}
#endif /* WOLFSSH_NUCLEUS */

#ifdef USE_WINDOWS_API
#if defined(USE_WINDOWS_API) && defined(_MSC_VER)
#pragma warning(pop)
#endif

Expand Down
Loading