Skip to content

Commit 034b160

Browse files
test with echoserver
1 parent c6cfe7c commit 034b160

2 files changed

Lines changed: 195 additions & 28 deletions

File tree

.github/workflows/windows-cert-store-test.yml

Lines changed: 84 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -714,6 +714,17 @@ jobs:
714714
Write-Host "WARNING: win-cert-store-test.exe not found (standalone cert store test will be skipped)"
715715
}
716716
717+
# Find echoserver.exe (used for cert store server test instead of wolfsshd for better debug logs)
718+
$echoserverExe = Get-ChildItem -Path $searchRoot -Recurse -Filter "echoserver.exe" -ErrorAction SilentlyContinue |
719+
Where-Object { $_.FullName -like "*Release*" -or $_.FullName -like "*Debug*" } |
720+
Select-Object -First 1
721+
if ($echoserverExe) {
722+
Write-Host "Found echoserver.exe at: $($echoserverExe.FullName)"
723+
Add-Content -Path $env:GITHUB_ENV -Value "ECHOSERVER_PATH=$($echoserverExe.FullName)"
724+
} else {
725+
Write-Host "WARNING: echoserver.exe not found (cert store server test will need it)"
726+
}
727+
717728
- name: Copy wolfSSL DLL to executable directory (if dynamic build)
718729
working-directory: ${{ github.workspace }}
719730
shell: pwsh
@@ -978,17 +989,30 @@ jobs:
978989
}
979990
Write-Host "Standalone cert store test passed."
980991
981-
- name: Grant service (LocalSystem) read access to config and keys
992+
- name: Grant service (LocalSystem) access to config, keys, and executable
982993
working-directory: ${{ github.workspace }}\wolfssh
983994
shell: pwsh
984995
run: |
985-
# wolfsshd runs as LocalSystem; it must be able to read config and key files.
986-
# Grant NT AUTHORITY\SYSTEM read access so the service can load config and host key.
987-
$configPathFull = (Resolve-Path "sshd_config_test" -ErrorAction Stop).Path
988-
$keysDir = (Resolve-Path "keys" -ErrorAction Stop).Path
989-
Write-Host "Granting SYSTEM read access to config and keys for LocalSystem service"
990-
icacls $configPathFull /grant "NT AUTHORITY\SYSTEM:R" /q
991-
icacls $keysDir /grant "NT AUTHORITY\SYSTEM:(OI)(CI)R" /q
996+
# wolfsshd runs as LocalSystem; it must be able to read config, key files, and run the exe.
997+
# Grant NT AUTHORITY\SYSTEM read+execute on the entire wolfssh tree so the service can:
998+
# - run wolfsshd.exe (and load wolfssl.dll if dynamic)
999+
# - read sshd_config_test and all files under keys/
1000+
# /T = apply to existing files and subdirs; (OI)(CI) = inherit to new objects
1001+
$wolfsshRoot = (Get-Location).Path
1002+
Write-Host "Granting SYSTEM (RX) on entire wolfssh tree: $wolfsshRoot"
1003+
icacls $wolfsshRoot /grant "NT AUTHORITY\SYSTEM:(OI)(CI)RX" /T /q
1004+
if ($LASTEXITCODE -ne 0) {
1005+
Write-Host "WARNING: icacls on wolfssh root failed, trying config and keys only"
1006+
$configPathFull = (Resolve-Path "sshd_config_test" -ErrorAction Stop).Path
1007+
$keysDir = (Resolve-Path "keys" -ErrorAction Stop).Path
1008+
icacls $configPathFull /grant "NT AUTHORITY\SYSTEM:R" /q
1009+
icacls $keysDir /grant "NT AUTHORITY\SYSTEM:(OI)(CI)R" /T /q
1010+
$sshdPath = $env:SSHD_PATH
1011+
if ($sshdPath -and (Test-Path $sshdPath)) {
1012+
$sshdDir = (Resolve-Path (Split-Path -Parent $sshdPath)).Path
1013+
icacls $sshdDir /grant "NT AUTHORITY\SYSTEM:(OI)(CI)RX" /T /q
1014+
}
1015+
}
9921016
Write-Host "Done."
9931017
9941018
- name: Test cert store access as LocalSystem
@@ -1168,7 +1192,51 @@ jobs:
11681192
Remove-Item $scriptPath -ErrorAction SilentlyContinue
11691193
Remove-Item $outputFile -ErrorAction SilentlyContinue
11701194
1195+
- name: Start echoserver with cert store (cert store matrix – more debug logs)
1196+
if: matrix.server_key_source == 'store'
1197+
working-directory: ${{ github.workspace }}\wolfssh
1198+
shell: pwsh
1199+
run: |
1200+
# For cert store we use echoserver instead of wolfsshd for now (better debug logs).
1201+
$echoserverPath = $env:ECHOSERVER_PATH
1202+
if (-not $echoserverPath -or -not (Test-Path $echoserverPath)) {
1203+
Write-Host "ERROR: echoserver.exe not found (ECHOSERVER_PATH not set or missing)"
1204+
Get-ChildItem -Path "${{ github.workspace }}" -Recurse -Filter "echoserver.exe" -ErrorAction SilentlyContinue | Select-Object FullName
1205+
exit 1
1206+
}
1207+
$store = "My"
1208+
$subject = "wolfSSH-Test-Server"
1209+
$location = "LOCAL_MACHINE"
1210+
$port = ${{env.TEST_PORT}}
1211+
Write-Host "=== Starting echoserver with cert store (debug logs) ==="
1212+
Write-Host "Command: $echoserverPath -W `"${store}:${subject}:${location}`" -p $port"
1213+
$proc = Start-Process -FilePath $echoserverPath `
1214+
-ArgumentList "-W", "${store}:${subject}:${location}", "-p", $port `
1215+
-PassThru -NoNewWindow
1216+
Add-Content -Path $env:GITHUB_ENV -Value "ECHOSERVER_PID=$($proc.Id)"
1217+
Write-Host "echoserver started with PID $($proc.Id)"
1218+
# Wait for port to be listening (max 15 seconds)
1219+
$timeout = 15
1220+
$elapsed = 0
1221+
while ($elapsed -lt $timeout) {
1222+
Start-Sleep -Seconds 1
1223+
$elapsed++
1224+
try {
1225+
$conn = New-Object System.Net.Sockets.TcpClient("127.0.0.1", $port)
1226+
if ($conn.Connected) { $conn.Close(); break }
1227+
} catch {}
1228+
if (-not $proc.HasExited) { continue }
1229+
Write-Host "ERROR: echoserver exited early (exit code: $($proc.ExitCode))"
1230+
exit 1
1231+
}
1232+
if ($elapsed -ge $timeout) {
1233+
Write-Host "WARNING: Port $port not listening after ${timeout}s (echoserver may still be starting)"
1234+
} else {
1235+
Write-Host "echoserver is listening on port $port"
1236+
}
1237+
11711238
- name: Start wolfSSHd as Windows service
1239+
if: matrix.server_key_source != 'store'
11721240
working-directory: ${{ github.workspace }}\wolfssh
11731241
shell: pwsh
11741242
run: |
@@ -1849,6 +1917,14 @@ jobs:
18491917
working-directory: ${{ github.workspace }}\wolfssh
18501918
shell: pwsh
18511919
run: |
1920+
# Stop echoserver if we started it (cert store matrix)
1921+
$echoserverPid = $env:ECHOSERVER_PID
1922+
if (-not [string]::IsNullOrEmpty($echoserverPid)) {
1923+
Write-Host "Stopping echoserver (PID $echoserverPid)"
1924+
Stop-Process -Id $echoserverPid -Force -ErrorAction SilentlyContinue
1925+
Start-Sleep -Seconds 1
1926+
}
1927+
18521928
# Stop and remove wolfSSHd service
18531929
$serviceName = $env:SSHD_SERVICE_NAME
18541930
if ([string]::IsNullOrEmpty($serviceName)) {

examples/echoserver/echoserver.c

Lines changed: 111 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@
114114
#define SOCKET_EWOULDBLOCK WSAEWOULDBLOCK
115115
#endif
116116

117+
#if defined(USE_WINDOWS_API) && defined(WOLFSSH_CERTS)
118+
#include <windows.h>
119+
#include <wincrypt.h>
120+
#ifndef CERT_SYSTEM_STORE_CURRENT_USER
121+
#define CERT_SYSTEM_STORE_CURRENT_USER 0x00010000
122+
#endif
123+
#ifndef CERT_SYSTEM_STORE_LOCAL_MACHINE
124+
#define CERT_SYSTEM_STORE_LOCAL_MACHINE 0x00020000
125+
#endif
126+
#endif
117127

118128
#ifndef NO_WOLFSSH_SERVER
119129

@@ -2314,6 +2324,9 @@ static void ShowUsage(void)
23142324
" add password to accept from peer\n");
23152325
#ifdef WOLFSSH_CERTS
23162326
printf(" -a <file> load in a root CA certificate file\n");
2327+
#endif
2328+
#if defined(USE_WINDOWS_API) && defined(WOLFSSH_CERTS)
2329+
printf(" -W <spec> Windows cert store: \"store:subject:flags\" (e.g. My:CN=Server:CURRENT_USER)\n");
23172330
#endif
23182331
printf(" -k set the list of key algos to use\n");
23192332
printf(" -b <num> test user auth would block\n");
@@ -2372,13 +2385,19 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
23722385
#ifdef WOLFSSH_CERTS
23732386
char* caCert = NULL;
23742387
#endif
2388+
#if defined(USE_WINDOWS_API) && defined(WOLFSSH_CERTS)
2389+
const char* certStoreSpec = NULL;
2390+
#endif
23752391

23762392
int argc = serverArgs->argc;
23772393
char** argv = serverArgs->argv;
23782394
serverArgs->return_code = EXIT_SUCCESS;
23792395

2396+
#if defined(USE_WINDOWS_API) && defined(WOLFSSH_CERTS)
2397+
certStoreSpec = getenv("WOLFSSH_CERT_STORE");
2398+
#endif
23802399
if (argc > 0) {
2381-
const char* optlist = "?1a:d:efEp:R:Ni:j:I:J:K:P:k:b:";
2400+
const char* optlist = "?1a:d:efEp:R:Ni:j:I:J:K:P:k:b:W:";
23822401
myoptind = 0;
23832402
while ((ch = mygetopt(argc, argv, optlist)) != -1) {
23842403
switch (ch) {
@@ -2466,6 +2485,12 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
24662485
userAuthWouldBlock = atoi(myoptarg);
24672486
break;
24682487

2488+
#if defined(USE_WINDOWS_API) && defined(WOLFSSH_CERTS)
2489+
case 'W':
2490+
certStoreSpec = myoptarg;
2491+
break;
2492+
#endif
2493+
24692494
default:
24702495
ShowUsage();
24712496
serverArgs->return_code = MY_EX_USAGE;
@@ -2580,28 +2605,94 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args)
25802605
#endif
25812606
bufSz = EXAMPLE_KEYLOAD_BUFFER_SZ;
25822607

2583-
bufSz = load_key(peerEcc, keyLoadBuf, bufSz);
2584-
if (bufSz == 0) {
2585-
ES_ERROR("Couldn't load first key file.\n");
2586-
}
2587-
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, keyLoadBuf, bufSz,
2588-
WOLFSSH_FORMAT_ASN1) < 0) {
2589-
ES_ERROR("Couldn't use first key buffer.\n");
2590-
}
2608+
#if defined(USE_WINDOWS_API) && defined(WOLFSSH_CERTS)
2609+
if (certStoreSpec != NULL) {
2610+
/* Load host key from Windows certificate store: store:subject:flags */
2611+
char* specCopy = NULL;
2612+
char* storeName = NULL;
2613+
char* subjectName = NULL;
2614+
char* flagsStr = NULL;
2615+
wchar_t* wStoreName = NULL;
2616+
wchar_t* wSubjectName = NULL;
2617+
DWORD dwFlags = CERT_SYSTEM_STORE_CURRENT_USER;
2618+
int ret;
2619+
size_t specLen = WSTRLEN(certStoreSpec) + 1;
25912620

2592-
#if !defined(WOLFSSH_NO_RSA) && !defined(WOLFSSH_NO_ECC)
2593-
peerEcc = !peerEcc;
2594-
bufSz = EXAMPLE_KEYLOAD_BUFFER_SZ;
2621+
specCopy = (char*)WMALLOC(specLen, NULL, 0);
2622+
if (specCopy == NULL) {
2623+
ES_ERROR("Memory allocation failed for cert store spec\n");
2624+
}
2625+
WSTRNCPY(specCopy, certStoreSpec, specLen);
2626+
2627+
storeName = specCopy;
2628+
subjectName = WSTRCHR(storeName, ':');
2629+
if (subjectName != NULL) {
2630+
*subjectName++ = '\0';
2631+
flagsStr = WSTRCHR(subjectName, ':');
2632+
if (flagsStr != NULL) {
2633+
*flagsStr++ = '\0';
2634+
if (WSTRCMP(flagsStr, "CURRENT_USER") == 0) {
2635+
dwFlags = CERT_SYSTEM_STORE_CURRENT_USER;
2636+
} else if (WSTRCMP(flagsStr, "LOCAL_MACHINE") == 0) {
2637+
dwFlags = CERT_SYSTEM_STORE_LOCAL_MACHINE;
2638+
} else {
2639+
dwFlags = (DWORD)atoi(flagsStr);
2640+
}
2641+
}
2642+
}
2643+
if (storeName == NULL || subjectName == NULL) {
2644+
WFREE(specCopy, NULL, 0);
2645+
ES_ERROR("Invalid cert store spec. Use: store:subject:flags\n");
2646+
}
25952647

2596-
bufSz = load_key(peerEcc, keyLoadBuf, bufSz);
2597-
if (bufSz == 0) {
2598-
ES_ERROR("Couldn't load second key file.\n");
2599-
}
2600-
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, keyLoadBuf, bufSz,
2601-
WOLFSSH_FORMAT_ASN1) < 0) {
2602-
ES_ERROR("Couldn't use second key buffer.\n");
2648+
{
2649+
int wStoreNameLen = MultiByteToWideChar(CP_UTF8, 0, storeName, -1, NULL, 0);
2650+
int wSubjectNameLen = MultiByteToWideChar(CP_UTF8, 0, subjectName, -1, NULL, 0);
2651+
wStoreName = (wchar_t*)WMALLOC(wStoreNameLen * sizeof(wchar_t), NULL, 0);
2652+
wSubjectName = (wchar_t*)WMALLOC(wSubjectNameLen * sizeof(wchar_t), NULL, 0);
2653+
if (wStoreName == NULL || wSubjectName == NULL) {
2654+
if (wStoreName != NULL) WFREE(wStoreName, NULL, 0);
2655+
if (wSubjectName != NULL) WFREE(wSubjectName, NULL, 0);
2656+
WFREE(specCopy, NULL, 0);
2657+
ES_ERROR("Memory allocation failed for cert store wide strings\n");
2658+
}
2659+
MultiByteToWideChar(CP_UTF8, 0, storeName, -1, wStoreName, wStoreNameLen);
2660+
MultiByteToWideChar(CP_UTF8, 0, subjectName, -1, wSubjectName, wSubjectNameLen);
2661+
}
2662+
2663+
ret = wolfSSH_CTX_UsePrivateKey_fromStore(ctx, wStoreName, dwFlags, wSubjectName);
2664+
WFREE(specCopy, NULL, 0);
2665+
WFREE(wStoreName, NULL, 0);
2666+
WFREE(wSubjectName, NULL, 0);
2667+
if (ret != WS_SUCCESS) {
2668+
ES_ERROR("Couldn't load host key from certificate store.\n");
2669+
}
2670+
} else
2671+
#endif
2672+
{
2673+
bufSz = load_key(peerEcc, keyLoadBuf, bufSz);
2674+
if (bufSz == 0) {
2675+
ES_ERROR("Couldn't load first key file.\n");
2676+
}
2677+
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, keyLoadBuf, bufSz,
2678+
WOLFSSH_FORMAT_ASN1) < 0) {
2679+
ES_ERROR("Couldn't use first key buffer.\n");
2680+
}
2681+
2682+
#if !defined(WOLFSSH_NO_RSA) && !defined(WOLFSSH_NO_ECC)
2683+
peerEcc = !peerEcc;
2684+
bufSz = EXAMPLE_KEYLOAD_BUFFER_SZ;
2685+
2686+
bufSz = load_key(peerEcc, keyLoadBuf, bufSz);
2687+
if (bufSz == 0) {
2688+
ES_ERROR("Couldn't load second key file.\n");
2689+
}
2690+
if (wolfSSH_CTX_UsePrivateKey_buffer(ctx, keyLoadBuf, bufSz,
2691+
WOLFSSH_FORMAT_ASN1) < 0) {
2692+
ES_ERROR("Couldn't use second key buffer.\n");
2693+
}
2694+
#endif
26032695
}
2604-
#endif
26052696

26062697
#ifndef NO_FILESYSTEM
26072698
if (userPubKey) {

0 commit comments

Comments
 (0)