Skip to content

Commit 19f18cd

Browse files
test with echoserver
1 parent c6cfe7c commit 19f18cd

2 files changed

Lines changed: 231 additions & 29 deletions

File tree

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

Lines changed: 97 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,64 @@ 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+
# Start echoserver detached (via cmd start /B) so it survives after this step ends;
1202+
# otherwise the runner may kill the process when the step completes.
1203+
$echoserverPath = $env:ECHOSERVER_PATH
1204+
if (-not $echoserverPath -or -not (Test-Path $echoserverPath)) {
1205+
Write-Host "ERROR: echoserver.exe not found (ECHOSERVER_PATH not set or missing)"
1206+
Get-ChildItem -Path "${{ github.workspace }}" -Recurse -Filter "echoserver.exe" -ErrorAction SilentlyContinue | Select-Object FullName
1207+
exit 1
1208+
}
1209+
$exeDir = Split-Path -Parent $echoserverPath
1210+
$exeName = Split-Path -Leaf $echoserverPath
1211+
$store = "My"
1212+
$subject = "wolfSSH-Test-Server"
1213+
$location = "LOCAL_MACHINE"
1214+
$port = ${{env.TEST_PORT}}
1215+
$spec = "${store}:${subject}:${location}"
1216+
Write-Host "=== Starting echoserver with cert store (detached, debug logs) ==="
1217+
Write-Host "Command: $echoserverPath -W `"$spec`" -p $port"
1218+
# cmd /c start /B runs the exe in background; working dir set so it finds wolfssl.dll
1219+
Start-Process -FilePath "cmd.exe" `
1220+
-ArgumentList "/c", "start", "/B", $exeName, "-W", $spec, "-p", $port `
1221+
-WorkingDirectory $exeDir -NoNewWindow -Wait:$false
1222+
Start-Sleep -Seconds 2
1223+
$proc = Get-Process -Name "echoserver" -ErrorAction SilentlyContinue | Select-Object -First 1
1224+
if ($proc) {
1225+
Add-Content -Path $env:GITHUB_ENV -Value "ECHOSERVER_PID=$($proc.Id)"
1226+
Write-Host "echoserver started with PID $($proc.Id)"
1227+
} else {
1228+
Write-Host "WARNING: echoserver process not found by name after start"
1229+
}
1230+
# Wait for port to be listening (max 15 seconds)
1231+
$timeout = 15
1232+
$elapsed = 0
1233+
while ($elapsed -lt $timeout) {
1234+
Start-Sleep -Seconds 1
1235+
$elapsed++
1236+
try {
1237+
$conn = New-Object System.Net.Sockets.TcpClient("127.0.0.1", $port)
1238+
if ($conn.Connected) { $conn.Close(); break }
1239+
} catch {}
1240+
$stillRunning = Get-Process -Name "echoserver" -ErrorAction SilentlyContinue
1241+
if ($stillRunning) { continue }
1242+
Write-Host "ERROR: echoserver exited before port was ready"
1243+
exit 1
1244+
}
1245+
if ($elapsed -ge $timeout) {
1246+
Write-Host "WARNING: Port $port not listening after ${timeout}s (echoserver may still be starting)"
1247+
} else {
1248+
Write-Host "echoserver is listening on port $port"
1249+
}
1250+
11711251
- name: Start wolfSSHd as Windows service
1252+
if: matrix.server_key_source != 'store'
11721253
working-directory: ${{ github.workspace }}\wolfssh
11731254
shell: pwsh
11741255
run: |
@@ -1849,6 +1930,14 @@ jobs:
18491930
working-directory: ${{ github.workspace }}\wolfssh
18501931
shell: pwsh
18511932
run: |
1933+
# Stop echoserver if we started it (cert store matrix)
1934+
$echoserverPid = $env:ECHOSERVER_PID
1935+
if (-not [string]::IsNullOrEmpty($echoserverPid)) {
1936+
Write-Host "Stopping echoserver (PID $echoserverPid)"
1937+
Stop-Process -Id $echoserverPid -Force -ErrorAction SilentlyContinue
1938+
Start-Sleep -Seconds 1
1939+
}
1940+
18521941
# Stop and remove wolfSSHd service
18531942
$serviceName = $env:SSHD_SERVICE_NAME
18541943
if ([string]::IsNullOrEmpty($serviceName)) {

examples/echoserver/echoserver.c

Lines changed: 134 additions & 21 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) {
@@ -2871,7 +2962,29 @@ int wolfSSH_Echoserver(int argc, char** argv)
28712962
#endif
28722963

28732964
#if !defined(WOLFSSL_NUCLEUS) && !defined(INTEGRITY) && !defined(__INTEGRITY)
2874-
ChangeToWolfSshRoot();
2965+
{
2966+
int useStore = 0;
2967+
#if defined(USE_WINDOWS_API) && defined(WOLFSSH_CERTS)
2968+
/* When using the Windows certificate store for host keys, the
2969+
* echoserver does not need file-based keys, so skip the root
2970+
* directory search that looks for ./keys/server-key-rsa.pem. */
2971+
if (getenv("WOLFSSH_CERT_STORE") != NULL) {
2972+
useStore = 1;
2973+
}
2974+
else {
2975+
int i;
2976+
for (i = 1; i < argc; i++) {
2977+
if (WSTRCMP(argv[i], "-W") == 0) {
2978+
useStore = 1;
2979+
break;
2980+
}
2981+
}
2982+
}
2983+
#endif
2984+
if (!useStore) {
2985+
ChangeToWolfSshRoot();
2986+
}
2987+
}
28752988
#endif
28762989
#ifndef NO_WOLFSSH_SERVER
28772990
echoserver_test(&args);

0 commit comments

Comments
 (0)