Compare commits

...

27 Commits

Author SHA1 Message Date
Tess Gauthier
139a1b413d
add pester test for event viewer scenarios (#788)
* add pester test for event viewer scenarios

* revert ci changes

* Update regress/pesterTests/EventLogging.Tests.ps1

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update regress/pesterTests/EventLogging.Tests.ps1

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update EventLogging.Tests.ps1

* add comments

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-21 16:46:25 -04:00
Tess Gauthier
c1a8d54998
Add sshd pester tests (#789)
* add sshd tests

* add test for session child processes

* add sleep

* Update regress/pesterTests/SSHD.Tests.ps1

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* update comments in test

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-07-21 15:47:19 -04:00
Yusi (James) Zhang
a2d4e942df
Fix null pointer reference in sigaction. (#795) 2025-07-21 15:46:35 -04:00
Tess Gauthier
bdacf9868f
Update version.rc (#786) 2025-04-17 16:23:25 -04:00
Tess Gauthier
cb6bfef9c1
Patch version fix (#785)
* add test to verify version match

* bump to p2 in version.h
2025-04-17 16:21:01 -04:00
Tess Gauthier
efa17c848b
cherry-picks from upstream (#783)
* upstream: Don't reply to PING in preauth phase or during KEX

Reported by the Qualys Security Advisory team. ok markus@

OpenBSD-Commit-ID: c656ac4abd1504389d1733d85152044b15830217

* upstream: Fix cases where error codes were not correctly set

Reported by the Qualys Security Advisory team. ok markus@

OpenBSD-Commit-ID: 7bcd4ffe0fa1e27ff98d451fb9c22f5fae6e610d

* fix error code case in Windows ssh-agent code

---------

Co-authored-by: djm@openbsd.org <djm@openbsd.org>
2025-04-08 16:55:50 -04:00
Tess Gauthier
e0ae79ff3c
add flag to pipe creation to reject remote clients (#782) 2025-04-08 16:55:37 -04:00
Tess Gauthier
fdde2326f9
Fix static analysis warning (#779)
* update function declaration and definition to match usage

* change _Outptr_ to _Out_
2025-04-01 14:15:59 -04:00
Tess Gauthier
de4c0c7c59
fix warnings (#780) 2025-03-31 15:15:57 -04:00
LainOTN2
31f8d13ab6
Fix for Y2038 gettimeofday for Win32 builds (#738)
* Fix for Y2038 gettimeofday for Win32 builds

* fixing spaces

* Fixing also the builtin gettimeofday
2025-03-26 14:50:34 -04:00
Tess Gauthier
ae72d833fd
Crank version (#778)
* bump libressl version

* bump openssh version
2025-03-26 11:40:53 -04:00
Tess Gauthier
0096029101
fix non-interactive session regression with sshd-session (#775)
* change sshd to sshd-session for process flag

* add pester test
2025-03-13 15:28:04 -04:00
Tess Gauthier
a96b3fbae4
add publish test results step to CI with pester test fix (#772)
* upload results from setup tests

* Update test results file path in CI

* Update ci.yml

* Update Setup.Tests.ps1

* Add systemDrive variable and update paths

* Update variable names in CI configuration

* Enable task failure on failed tests

* Comment out sshd service stop command to test CI

* uncomment part of pester test
2025-02-24 16:57:30 -05:00
Tess Gauthier
8514f78233
change default keygen key type (#760)
* change default keygen key type

* increase DEFAULT_BITS_ECDSA to 384

* change default key path to ECDSA
2025-02-21 14:26:04 -05:00
Tess Gauthier
41734eb591
Update chroot symlink check (#765)
* start sftp chroot symlink fix

* update symlink logic chroot check

* fix order
2025-02-20 16:26:18 -05:00
Leonard Hecker
8fe096c7b7
Increase stdin chunksize from 16 to 1024 chars (#771) 2025-01-13 10:51:15 -05:00
Tess Gauthier
b36bc85f47
Update ssh folder permissions check in SSHD (#761)
* remove check on progdata/ssh/log folder permissions

* add pester test

* modify permissions check to log event without failing startup

* modify perm check

* update test

* uncomment code

* modify pester test

* address review feedback

* address review feedback

* fix multi-line logging

* cleanup allocations

* address review feedback

* address additional review feedback

* store value in tmp var
2025-01-10 10:47:23 -05:00
Tess Gauthier
7baad0a474
make env vars optional for default allow list path (#757)
* make env vars optional for default allow list path

* add pkcs11 pester test

* use lowercasing within method
2025-01-07 10:02:04 -05:00
Tess Gauthier
86bc0d7df9
remove sntrup761x25519-sha512 from supported kex list (#756) 2025-01-06 14:12:21 -05:00
Tess Gauthier
cdcc8d34d8
Fix tilde expand for Windows paths with backslashes (#768)
* add backslash support for Windows paths

* add pester tests for tilde_expand

* fix typo
2025-01-06 14:11:58 -05:00
Andrew
0c3137f621
Fix hang in syncio_close (#763) 2024-12-18 15:36:43 -05:00
Tess Gauthier
265df19787
Fix include paths (#770)
* fix Include abs path on Windows

* add pester tests for Include directive

* fix tests

* fix typo
2024-12-10 11:57:15 -05:00
Tess Gauthier
348084cc9a
Update sshd_config to latest defaults (#769) 2024-12-09 11:27:41 -05:00
Tess Gauthier
0dd6d2cd21
username logging fix (#762)
* initial pass at including username from sftp

* initialize user to unknown

* update tests

* fix spacing

* fix test take 2
2024-11-21 16:42:02 -05:00
manu0401
27f6cfa7b0
Add an environement variable to control stdio mode (#759)
* Add an environement variable to control stdio mode

stdio descriptors (stdin, stdout and stderr) can be operated in various
modes by win32compat code. The behavior is set very early in
fd_table_initialize() by setting pio->type.

In https://github.com/PowerShell/Win32-OpenSSH/issues/1427 it was
chosen to set pio->type to NONSOCK_SYNC_FD to resolve an I/O hang
problem. Unfortunately this introduce problems for other ssh usage.

sshfs-wiun uses ssh and has at leas 6 open issues for the same
problem introduced by this NONSOCK_SYNC_FD change:
https://github.com/winfsp/sshfs-win/issues?q=is%3Aissue+cb+%3A87

The sshfs-win workaround it to use an older ssh.exe from cygwin, which
is bundled with sshfs-win. This program is unable to use ssh-agent,
which is quite frustrating. And if PATH is not set to use it, sshfs-win
cannot work.

This change introduce an OPENSSH_STDIO_MODE environment variable that
can be set to the following values: unknown, sock, nonsock, nonsock_sync.
It cause pio->type to be set to UNKNOWN_FD, SOCK_FD, NONSOCK_FD, and
NONSOCK_SYNC_FD respecitively. The default behavior when the variable
is not set is unchanged (which means NONSOCK_SYNC_FD).

Setting OPENSSH_STDIO_MODE="nonsock" lets sshfs-win work again with
openssh-portable ssh.exe. ssh-agent can be used, and this is good.

* Leave out  UNKNOWN_FD as the possible rtpes for stdio descriptors

An assert(pio->type != UNKNOWN_FD) in fd_table_set() causes that
case to fail early anyway.
2024-11-19 16:14:43 -05:00
Tess Gauthier
796d297a66
fix open call for ssh-keygen (#764)
* fix open call for ssh-keygen

* fix test

* fix formatting
2024-11-18 16:55:49 -05:00
Mike Gilbert
a915f06c78
Set argv[argc] to NULL when calling main (#755)
* Set argv[argc] to NULL when calling main

ISO C states that argv[argc] shall be a null pointer.

The OpenSSH codebase does not appear to rely on this currently, but
better to be safe in case something changes.

* Check for malloc failure in sshd wmain
2024-10-14 17:43:35 -04:00
45 changed files with 1068 additions and 276 deletions

View File

@ -89,6 +89,8 @@ stages:
pool:
vmImage: windows-latest
displayName: Win32-OpenSSH On Windows
variables:
testFilesDrivePath: '**'
steps:
- powershell: |
$powerShellPath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'powershell'
@ -164,6 +166,17 @@ stages:
Invoke-OpenSSHTests -OpenSSHBinPath "$env:SystemDrive/OpenSSH"
displayName: Run tests
- pwsh: |
Write-Host "##vso[task.setvariable variable=testFilesDrivePath;]$env:SystemDrive"
displayName: Set variable
- task: PublishTestResults@2
inputs:
testResultsFormat: 'NUnit'
testResultsFiles: '$(testFilesDrivePath)/OpenSSHTests/*.xml'
failTaskOnFailedTests: true
condition: always()
- pwsh: |
Import-Module -Name "$(Build.SourcesDirectory)/contrib/win32/openssh/AzDOBuildTools" -Force
#

View File

@ -515,11 +515,19 @@ sshkey_save_public(const struct sshkey *key, const char *path,
if ((fd = open(path, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
return SSH_ERR_SYSTEM_ERROR;
#ifdef WINDOWS
/* Windows POSIX adapter does not support fdopen() on open(file)
but still want file created with same owner as upstream */
close(fd);
if ((f = fopen(path, "w")) == NULL)
return SSH_ERR_SYSTEM_ERROR;
#else /* WINDOWS */
if ((f = fdopen(fd, "w")) == NULL) {
r = SSH_ERR_SYSTEM_ERROR;
close(fd);
goto fail;
}
#endif /* WINDOWS */
if ((r = sshkey_write(key, f)) != 0)
goto fail;
fprintf(f, " %s\n", comment);

View File

@ -72,6 +72,7 @@ function Set-OpenSSHTestEnvironment
$Global:OpenSSHTestInfo.Add("TestAccountPW", $OpenSSHTestAccountsPassword) # common password for all test accounts
$Global:OpenSSHTestInfo.Add("DebugMode", $DebugMode.IsPresent) # run openssh E2E in debug mode
$Global:OpenSSHTestInfo.Add("DelayTime", 3) # delay between stoppig sshd service and trying to access log files
$Global:OpenSSHTestInfo.Add("SshdServiceName", $SSHDTestSvcName) # sshd service name
$Script:EnableAppVerifier = -not ($NoAppVerifier.IsPresent)
if($Script:WindowsInBox = $true)

View File

@ -4,7 +4,7 @@
<OpenSSH-Src-Path>$(SolutionDir)..\..\..\</OpenSSH-Src-Path>
<OpenSSH-Bin-Path>$(SolutionDir)..\..\..\bin\</OpenSSH-Bin-Path>
<OpenSSH-Lib-Path>$(SolutionDir)lib\</OpenSSH-Lib-Path>
<LibreSSLVersion>3.9.2.0</LibreSSLVersion>
<LibreSSLVersion>4.0.0.0</LibreSSLVersion>
<ZLibVersion>1.3.1</ZLibVersion>
<fido2Version>1.15.0</fido2Version>
<!--libcbor version is not used in the build; it is needed for pipeline compliance tasks-->

View File

@ -12,7 +12,6 @@
#ListenAddress ::
#HostKey __PROGRAMDATA__/ssh/ssh_host_rsa_key
#HostKey __PROGRAMDATA__/ssh/ssh_host_dsa_key
#HostKey __PROGRAMDATA__/ssh/ssh_host_ecdsa_key
#HostKey __PROGRAMDATA__/ssh/ssh_host_ed25519_key
@ -61,12 +60,11 @@ AuthorizedKeysFile .ssh/authorized_keys
#PrintMotd yes
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /var/run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none

View File

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 9,8,1,0
PRODUCTVERSION 9,8,1,0
FILEVERSION 9,8,3,0
PRODUCTVERSION 9,8,3,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -67,9 +67,9 @@ BEGIN
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileVersion", "9.8.1.0"
VALUE "FileVersion", "9.8.3.0"
VALUE "ProductName", "OpenSSH for Windows"
VALUE "ProductVersion", "OpenSSH_9.8p1 for Windows"
VALUE "ProductVersion", "OpenSSH_9.8p2 for Windows"
END
END
BLOCK "VarFileInfo"

View File

@ -435,6 +435,13 @@ file_in_chroot_jail(HANDLE handle) {
return 1;
}
return file_in_chroot_jail_helper(final_path);
}
/* returns 1 if true, 0 otherwise */
int
file_in_chroot_jail_helper(wchar_t* final_path) {
/* ensure final path is within chroot */
to_wlower_case(final_path);
if ((wcslen(final_path) < wcslen(chroot_pathw)) ||
memcmp(final_path, chroot_pathw, 2 * wcslen(chroot_pathw)) != 0 ||
@ -442,7 +449,6 @@ file_in_chroot_jail(HANDLE handle) {
debug3("access denied due to attempt to escape chroot jail");
return 0;
}
return 1;
}
@ -1268,6 +1274,7 @@ fileio_symlink(const char *target, const char *linkpath)
DWORD ret = -1;
char target_modified[PATH_MAX] = { 0 };
char *linkpath_resolved = NULL, *target_resolved = NULL;
wchar_t *linkpath_utf16 = NULL, *resolved_target_utf16 = NULL, *resolved_target_chroot = NULL;
if (target == NULL || linkpath == NULL) {
errno = EFAULT;
@ -1301,13 +1308,21 @@ fileio_symlink(const char *target, const char *linkpath)
strcpy_s(target_modified, _countof(target_modified), target_resolved);
}
wchar_t *linkpath_utf16 = resolved_path_utf16(linkpath);
wchar_t *resolved_target_utf16 = utf8_to_utf16(target_modified);
if (resolved_target_utf16 == NULL || linkpath_utf16 == NULL) {
if ((linkpath_utf16 = resolved_path_utf16(linkpath)) == NULL ||
(resolved_target_utf16 = utf8_to_utf16(target_modified)) == NULL) {
errno = ENOMEM;
goto cleanup;
}
/* if chroot, get full path for target, similar to behavior in realpath() in misc.c
note: _wfullpath() is required to resolve paths containing unicode characters */
if (chroot_pathw != NULL &&
(resolved_target_chroot = _wfullpath(NULL, resolved_target_utf16, 0)) != NULL &&
file_in_chroot_jail_helper(resolved_target_chroot) != 1) {
errno = EPERM;
goto cleanup;
}
/* unlike other platforms, we need to know whether the symbolic link target is
* a file or a directory. the only way we can confidently do this is to
* get the attributes of the target. therefore, our symlink() has the
@ -1338,15 +1353,18 @@ fileio_symlink(const char *target, const char *linkpath)
ret = 0;
cleanup:
if (linkpath_resolved)
free(linkpath_resolved);
if (linkpath_utf16)
free(linkpath_utf16);
if (resolved_target_chroot)
free(resolved_target_chroot);
if (resolved_target_utf16)
free(resolved_target_utf16);
if (linkpath_resolved)
free(linkpath_resolved);
if (target_resolved)
free(target_resolved);

View File

@ -808,7 +808,7 @@ done:
OM_uint32
gss_accept_sec_context(_Out_ OM_uint32 * minor_status, _Inout_opt_ gss_ctx_id_t * context_handle,
_In_opt_ gss_cred_id_t acceptor_cred_handle, _In_ gss_buffer_t input_token_buffer, _In_opt_ gss_channel_bindings_t input_chan_bindings,
_Out_opt_ gss_name_t * src_name, _Out_opt_ gss_OID * mech_type, _Outptr_ gss_buffer_t output_token,
_Out_opt_ gss_name_t * src_name, _Out_opt_ gss_OID * mech_type, _Out_ gss_buffer_t output_token,
_Out_ OM_uint32 * ret_flags, _Out_opt_ OM_uint32 * time_rec, _Outptr_opt_ gss_cred_id_t * delegated_cred_handle)
{
OM_uint32 ret = GSS_S_FAILURE;

View File

@ -177,7 +177,7 @@ OM_uint32
gss_accept_sec_context(_Out_ OM_uint32 * minor_status, _Inout_opt_ gss_ctx_id_t * context_handle,
_In_opt_ gss_cred_id_t acceptor_cred_handle, _In_ gss_buffer_t input_token_buffer,
_In_opt_ gss_channel_bindings_t input_chan_bindings, _Out_opt_ gss_name_t * src_name,
_Out_opt_ gss_OID * mech_type, _Outptr_ gss_buffer_t output_token, _Out_ OM_uint32 * ret_flags,
_Out_opt_ gss_OID * mech_type, _Out_ gss_buffer_t output_token, _Out_ OM_uint32 * ret_flags,
_Out_opt_ OM_uint32 * time_rec, _Outptr_opt_ gss_cred_id_t * delegated_cred_handle);
OM_uint32

View File

@ -27,7 +27,7 @@ typedef struct w32_fd_set_ {
#define FD_SETSIZE MAX_FDS
int w32_select(int fds, w32_fd_set * , w32_fd_set * , w32_fd_set * ,
const struct timeval *);
const struct w32_timeval *);
#define select(a,b,c,d,e) w32_select((a), (b), (c), (d), (e))

View File

@ -1,8 +1,16 @@
#pragma once
#include <sys\utime.h>
#define utimbuf _utimbuf
#define utimes w32_utimes
#define timeval w32_timeval
struct timeval
{
long long tv_sec;
long tv_usec;
};
int usleep(unsigned int);
int gettimeofday(struct timeval *, void *);
int nanosleep(const struct timespec *, struct timespec *);

View File

@ -207,7 +207,7 @@ gettimeofday(struct timeval *tv, void *tz)
us = (timehelper.ns - EPOCH_DELTA) / 10;
/* Stuff result into the timeval */
tv->tv_sec = (long)(us / USEC_IN_SEC);
tv->tv_sec = (long long)(us / USEC_IN_SEC);
tv->tv_usec = (long)(us % USEC_IN_SEC);
return 0;
@ -1419,7 +1419,7 @@ is_absolute_path(const char *path)
/* return -1 - in case of failure, 0 - success */
int
create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w)
create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w, BOOL check_permissions)
{
if (GetFileAttributesW(path_w) == INVALID_FILE_ATTRIBUTES) {
PSECURITY_DESCRIPTOR pSD = NULL;
@ -1444,12 +1444,9 @@ create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w)
return -1;
}
}
else {
else if (check_permissions) {
// directory already exists; need to confirm permissions are correct
if (check_secure_folder_permission(path_w, 1) != 0) {
error("Directory already exists but folder permissions are invalid");
return -1;
}
check_secure_folder_permission(path_w, 1);
}
return 0;

View File

@ -67,9 +67,10 @@ void to_lower_case(char *s);
void to_wlower_case(wchar_t *s);
HANDLE get_user_token(const char* user, int impersonation);
int load_user_profile(HANDLE user_token, char* user);
int create_directory_withsddl(wchar_t *path, wchar_t *sddl);
int create_directory_withsddl(wchar_t *path, wchar_t *sddl, BOOL check_permissions);
int is_absolute_path(const char *);
int file_in_chroot_jail(HANDLE);
int file_in_chroot_jail_helper(wchar_t*);
PSID lookup_sid(const wchar_t* name_utf16, PSID psid, DWORD * psid_len);
PSID get_sid(const char*);
int am_system();

View File

@ -341,8 +341,12 @@ sigaction(int signum, const struct sigaction * act, struct sigaction * oldact)
return r;
}
if (act)
oldact->sa_handler = w32_signal(signum, act->sa_handler);
if (act) {
sighandler_t old_handler = w32_signal(signum, act->sa_handler);
if (oldact) {
oldact->sa_handler = old_handler;
}
}
else if (oldact)
oldact->sa_handler = sig_handlers[signum];

View File

@ -121,6 +121,7 @@ agent_listen_loop()
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access
PIPE_TYPE_BYTE | // message type pipe
PIPE_READMODE_BYTE | // message-read mode
PIPE_REJECT_REMOTE_CLIENTS | // no remote client connections allowed
PIPE_WAIT, // blocking mode
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
@ -414,26 +415,41 @@ void
agent_initialize_allow_list() {
/*
* allowed paths for PKCS11 libraries,
* initialize to ProgramFiles and ProgramFiles(x86) by default
* attempt to initialize to ProgramFiles and ProgramFiles(x86) by default
* upstream uses /usr/lib/* and /usr/local/lib/*
*/
size_t prog_files_len = 0, prog_files_x86_len = 0;
char* prog_files = NULL, * prog_files_x86 = NULL;
size_t allowed_len = 0, prog_files_len = 0, prog_files_x86_len = 0;
char* allowed_path = NULL, *prog_files = NULL, *prog_files_x86 = NULL;
_dupenv_s(&prog_files, &prog_files_len, "ProgramFiles");
if (!prog_files)
fatal("couldn't find ProgramFiles environment variable");
convertToForwardslash(prog_files);
_dupenv_s(&prog_files_x86, &prog_files_x86_len, "ProgramFiles(x86)");
if (!prog_files_x86)
fatal("couldn't find ProgramFiles environment variable");
convertToForwardslash(prog_files_x86);
size_t allowed_providers_len = 1 + prog_files_len + 4 + prog_files_x86_len + 3;
allowed_providers = xmalloc(allowed_providers_len);
sprintf_s(allowed_providers, allowed_providers_len, "/%s/*,/%s/*", prog_files, prog_files_x86);
if (!prog_files && !prog_files_x86) {
allowed_providers = xstrdup("");
return;
}
if (prog_files && prog_files_x86) {
allowed_len = prog_files_len + 3 + prog_files_x86_len + 1;
allowed_path = xmalloc(allowed_len);
sprintf_s(allowed_path, allowed_len, "%s\\*,%s", prog_files, prog_files_x86);
free(prog_files);
free(prog_files_x86);
}
else if (prog_files) {
allowed_len = prog_files_len;
allowed_path = prog_files;
}
else if (prog_files_x86) {
allowed_len = prog_files_x86_len;
allowed_path = prog_files_x86;
}
allowed_len += 3; /* for additional characters below */
allowed_providers = xmalloc(allowed_len);
sprintf_s(allowed_providers, allowed_len, "%s\\*", allowed_path);
if (allowed_path) {
free(allowed_path);
}
}

View File

@ -677,9 +677,12 @@ int process_add_smartcard_key(struct sshbuf* request, struct sshbuf* response, s
goto done;
}
if (match_pattern_list(canonical_provider, allowed_providers, 0) != 1) {
to_lower_case(provider);
verbose("provider realpath: \"%.100s\"", provider);
verbose("allowed provider paths: \"%.100s\"", allowed_providers);
if (match_pattern_list(provider, allowed_providers, 1) != 1) {
verbose("refusing PKCS#11 add of \"%.100s\": "
"provider not allowed", canonical_provider);
"provider not allowed", provider);
goto done;
}
@ -992,6 +995,7 @@ process_ext_session_bind(struct sshbuf* request, struct agent_connection* con)
/* record new key/sid */
if (con->nsession_ids >= AGENT_MAX_SESSION_IDS) {
error_f("too many session IDs recorded");
r = -1;
goto out;
}
con->session_ids = xrecallocarray(con->session_ids, con->nsession_ids,

View File

@ -48,8 +48,6 @@
#include "tnnet.h"
#include "misc_internal.h"
#define TERM_IO_BUF_SIZE 2048
extern int in_raw_mode;
BOOL isFirstTime = TRUE;
@ -293,7 +291,10 @@ syncio_close(struct w32_io* pio)
CancelSynchronousIo(pio->read_overlapped.hEvent);
}
WaitForSingleObject(pio->read_overlapped.hEvent, INFINITE);
// give the read thread some time to wind down, but don't block syncio_close
if (WAIT_TIMEOUT == WaitForSingleObject(pio->read_overlapped.hEvent, 1000)) {
debug4("read_overlapped thread timed out");
}
}
/* drain queued APCs */

View File

@ -124,11 +124,84 @@ GetModifierKey(DWORD dwControlKeyState)
return modKey;
}
// ReadConsoleForTermEmul() but for ENABLE_VIRTUAL_TERMINAL_INPUT.
static int
ReadConsoleForTermEmulModern(HANDLE hInput, char *destin, int destinlen)
{
// If the previous input ended on a lead (high) surrogate,
// we stash it here to combine it with the next input.
static wchar_t s_previous_lead;
INPUT_RECORD records[TERM_IO_BUF_SIZE_UTF16];
DWORD records_cap = ARRAYSIZE(records);
DWORD records_len = 0;
wchar_t text[TERM_IO_BUF_SIZE_UTF16];
int text_len = 0;
// If we'll restore the previous lead surrogate, we can only read
// ARRAYSIZE(records)-1 records before the storage overflows.
if (s_previous_lead) {
records_cap--;
}
// As this application heavily relies on APCs, it's important that we call
// DataAvailable(), because it calls WaitForSingleObjectEx with bAlertable=TRUE.
if (!DataAvailable(hInput) ||
!ReadConsoleInputW(hInput, records, records_cap, &records_len) ||
records_len == 0)
return 0;
// Restore the previous lead surrogate if we have one.
if (s_previous_lead) {
text[text_len++] = s_previous_lead;
s_previous_lead = 0;
}
// Accumulate the UTF-16 text.
for (DWORD i = 0; i < records_len; i++) {
switch (records[i].EventType) {
case WINDOW_BUFFER_SIZE_EVENT:
queue_terminal_window_change_event();
break;
case KEY_EVENT: {
const KEY_EVENT_RECORD* k = &records[i].Event.KeyEvent;
if (
// The old Windows console added support for Unicode by encoding the characters in the
// current code page as usual, while stuffing a UCS2 value into a trailing VK_MENU event.
// Modern terminals on Windows stopped doing this and the Windows console may as well at some point.
(k->bKeyDown || k->wVirtualKeyCode == VK_MENU) &&
// Current versions of ConPTY suffer from a bug where pressing modifier keys enqueues
// a KEY_EVENT with UnicodeChar=0 despite ENABLE_VIRTUAL_TERMINAL_INPUT being enabled.
// They can be identified by the fact that their UnicodeChar value is zero,
// but they still have a non-zero wVirtualScanCode.
(k->uChar.UnicodeChar != L'\0' || k->wVirtualScanCode == 0))
text[text_len++] = k->uChar.UnicodeChar;
break;
}
default:
break;
}
}
// Pop any lone lead surrogate from the input for later.
const wchar_t last_char = text[text_len - 1];
if (IS_HIGH_SURROGATE(last_char)) {
s_previous_lead = last_char;
text_len--;
}
// ...and finally convert everything to UTF-8.
// It'll always fit, because we sized TERM_IO_BUF_SIZE to be large enough.
return WideCharToMultiByte(CP_UTF8, 0, text, text_len, destin, destinlen, NULL, NULL);
}
int
ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen)
{
HANDLE hHandle[] = { hInput, NULL };
DWORD nHandle = 1;
if (isConsoleVTSeqAvailable) {
return ReadConsoleForTermEmulModern(hInput, destin, destinlen);
}
DWORD dwInput = 0;
DWORD rc = 0;
unsigned char octets[20];
@ -187,24 +260,8 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen)
break;
}
if (isConsoleVTSeqAvailable) {
if (inputRecord.Event.KeyEvent.uChar.UnicodeChar != L'\0' || inputRecord.Event.KeyEvent.wVirtualScanCode == 0) {
n = WideCharToMultiByte(
CP_UTF8,
0,
&(inputRecord.Event.KeyEvent.uChar.UnicodeChar),
1,
(LPSTR)octets,
20,
NULL,
NULL);
WriteToBuffer((char *)octets, n);
}
} else {
GetVTSeqFromKeyStroke(inputRecord);
}
}
break;
}
}

View File

@ -37,6 +37,9 @@
#include "console.h"
#define TERM_IO_BUF_SIZE_UTF16 1024
#define TERM_IO_BUF_SIZE (3 * TERM_IO_BUF_SIZE_UTF16)
#define UP_ARROW "\x1b[A"
#define DOWN_ARROW "\x1b[B"
#define RIGHT_ARROW "\x1b[C"

View File

@ -33,6 +33,8 @@
#include <Aclapi.h>
#include <lm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "inc\pwd.h"
#include "sshfileperm.h"
@ -40,6 +42,12 @@
#include "misc_internal.h"
#include "config.h"
#define NULL_TERMINATOR_LEN 1
#define COMMA_SPACE_LEN 2
#define BACKSLASH_LEN 1
extern int log_on_stderr;
/*
* The function is to check if current user is secure to access to the file.
* Check the owner of the file is one of these types: Local Administrators groups, system account, current user account
@ -178,18 +186,22 @@ cleanup:
* Check the owner of the file is one of these types: Local Administrators groups or system account
* Check the users have access permission to the file don't violate the following rules:
1. no user other than local administrators group and system account have write permission on the folder
* Returns 0 on success and -1 on failure
* Logs a message if the rules are violated, but does not prevent further execution
*/
int
void
check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
{
PSECURITY_DESCRIPTOR pSD = NULL;
PSID owner_sid = NULL, ti_sid = NULL;
PACL dacl = NULL;
DWORD error_code = ERROR_SUCCESS;
BOOL is_valid_sid = FALSE, is_valid_acl = FALSE;
BOOL is_valid_sid = FALSE, is_valid_acl = FALSE, need_log_msg = FALSE, is_first = TRUE;
wchar_t* bad_user = NULL;
int ret = 0;
size_t log_msg_len = (DNLEN + BACKSLASH_LEN + UNLEN) * 2 + COMMA_SPACE_LEN + NULL_TERMINATOR_LEN;
wchar_t* log_msg = (wchar_t*)malloc(log_msg_len * sizeof(wchar_t));
if (log_msg != NULL) {
log_msg[0] = '\0';
}
/*Get the owner sid of the file.*/
if ((error_code = GetNamedSecurityInfoW(path_utf16, SE_FILE_OBJECT,
@ -197,18 +209,15 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
&owner_sid, NULL, &dacl, NULL, &pSD)) != ERROR_SUCCESS) {
printf("failed to retrieve the owner sid and dacl of file %S with error code: %d", path_utf16, error_code);
errno = EOTHER;
ret = -1;
goto cleanup;
}
if (((is_valid_sid = IsValidSid(owner_sid)) == FALSE) || ((is_valid_acl = IsValidAcl(dacl)) == FALSE)) {
printf("IsValidSid: %d; is_valid_acl: %d", is_valid_sid, is_valid_acl);
ret = -1;
goto cleanup;
}
if (!IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) &&
!IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
printf("Bad owner on %S", path_utf16);
ret = -1;
goto cleanup;
}
/*
@ -224,7 +233,6 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
if (!GetAce(dacl, i, &current_ace)) {
printf("GetAce() failed");
errno = EOTHER;
ret = -1;
goto cleanup;
}
@ -247,15 +255,112 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
continue;
}
else {
ret = -1;
/* collect all SIDs with write permissions */
wchar_t resolved_trustee[UNLEN + NULL_TERMINATOR_LEN] = L"UNKNOWN";
wchar_t resolved_trustee_domain[DNLEN + NULL_TERMINATOR_LEN] = L"UNKNOWN";
DWORD resolved_trustee_len = _countof(resolved_trustee), resolved_trustee_domain_len = _countof(resolved_trustee_domain);
SID_NAME_USE resolved_trustee_type;
need_log_msg = TRUE;
if (log_msg != NULL &&
LookupAccountSidW(NULL, current_trustee_sid, resolved_trustee, &resolved_trustee_len,
resolved_trustee_domain, &resolved_trustee_domain_len, &resolved_trustee_type) != 0) {
if (is_first) {
_snwprintf_s(log_msg, log_msg_len, _TRUNCATE, L"%ls\\%ls", resolved_trustee_domain, resolved_trustee);
is_first = FALSE;
}
else {
size_t currentLength = wcslen(log_msg);
size_t userLength = resolved_trustee_domain_len + BACKSLASH_LEN + resolved_trustee_len + COMMA_SPACE_LEN;
if (wcslen(log_msg) + userLength + NULL_TERMINATOR_LEN > log_msg_len) {
log_msg_len *= 2;
wchar_t* temp_log_msg = (wchar_t*)malloc(log_msg_len * sizeof(wchar_t));
if (temp_log_msg == NULL) {
break;
}
wcscpy_s(temp_log_msg, log_msg_len, log_msg);
if (log_msg)
free(log_msg);
log_msg = temp_log_msg;
}
_snwprintf_s(log_msg + currentLength, log_msg_len - currentLength, _TRUNCATE,
L", %ls\\%ls", resolved_trustee_domain, resolved_trustee);
}
}
}
}
if (need_log_msg) {
log_folder_perms_msg_etw(path_utf16, log_msg);
}
cleanup:
if (bad_user)
if (bad_user) {
LocalFree(bad_user);
if (pSD)
LocalFree(pSD);
if (ti_sid)
free(ti_sid);
return ret;
}
if (log_msg) {
free(log_msg);
}
if (pSD) {
LocalFree(pSD);
}
if (ti_sid) {
free(ti_sid);
}
}
/*
* This function takes in the full path to the ProgramData\ssh folder
* and a string of comma-separated domain\usernames. The function converts
* the well-known built-in Administrators group sid and the Local System
* sid to their corresponding names. With these names, and the input string,
* it logs a message to the Event Viewer. If logging the detailed message fails,
* a generic log message is written to the Event Viewer instead.
*/
void log_folder_perms_msg_etw(const wchar_t* path_utf16, wchar_t* log_msg) {
PSID adminSid = NULL;
WCHAR adminName[UNLEN + NULL_TERMINATOR_LEN];
WCHAR adminDomain[DNLEN + NULL_TERMINATOR_LEN];
DWORD adminNameSize = UNLEN + NULL_TERMINATOR_LEN;
DWORD adminDomainSize = DNLEN + NULL_TERMINATOR_LEN;
DWORD adminSidSize = SECURITY_MAX_SID_SIZE;
PSID systemSid = NULL;
WCHAR systemName[UNLEN + NULL_TERMINATOR_LEN];
WCHAR systemDomain[DNLEN + NULL_TERMINATOR_LEN];
DWORD systemNameSize = UNLEN + NULL_TERMINATOR_LEN;
DWORD systemDomainSize = DNLEN + NULL_TERMINATOR_LEN;
DWORD systemSidSize = SECURITY_MAX_SID_SIZE;
SID_NAME_USE sidType;
BOOL needLog = TRUE;
int temp_log_on_stderr = log_on_stderr;
log_on_stderr = 0;
adminSid = (PSID)malloc(SECURITY_MAX_SID_SIZE);
if (log_msg != NULL && adminSid != NULL &&
CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, adminSid, &adminSidSize) != 0 &&
LookupAccountSidW(NULL, adminSid, adminName, &adminNameSize, adminDomain, &adminDomainSize, &sidType) != 0) {
systemSid = (PSID)malloc(SECURITY_MAX_SID_SIZE);
if (systemSid != NULL &&
CreateWellKnownSid(WinLocalSystemSid, NULL, systemSid, &systemSidSize) != 0 &&
LookupAccountSidW(NULL, systemSid, systemName, &systemNameSize, systemDomain, &systemDomainSize, &sidType) != 0) {
logit("For '%S' folder, write access is granted to the following users: %S. "
"Consider reviewing users to ensure that only %S\\%S, and the %S\\%S group, and its members, have write access.",
path_utf16, log_msg, systemDomain, systemName, adminDomain, adminName);
needLog = FALSE;
}
}
if (needLog) {
/* log generic warning message in unlikely case that lookup for either well-known SID fails or user list is empty */
logit("for '%S' folder, consider downgrading permissions for any users with unnecessary write access.", path_utf16);
}
log_on_stderr = temp_log_on_stderr;
if (adminSid) {
free(adminSid);
}
if (systemSid) {
free(systemSid);
}
}

View File

@ -32,6 +32,7 @@
#include "inc\sys\socket.h"
#include "inc\sys\select.h"
#include "inc\sys\uio.h"
#include "inc\sys\time.h"
#include "inc\sys\types.h"
#include "inc\sys\stat.h"
#include "inc\unistd.h"
@ -88,6 +89,19 @@ fd_table_initialize()
{
struct w32_io *pio;
HANDLE wh;
char *stdio_mode_env;
int stdio_mode = NONSOCK_SYNC_FD;
stdio_mode_env = getenv("OPENSSH_STDIO_MODE");
if (stdio_mode_env != NULL) {
if (strcmp(stdio_mode_env, "sock") == 0)
stdio_mode = SOCK_FD;
else if (strcmp(stdio_mode_env, "nonsock") == 0)
stdio_mode = NONSOCK_FD;
else if (strcmp(stdio_mode_env, "nonsock_sync") == 0)
stdio_mode = NONSOCK_SYNC_FD;
}
/* table entries representing std in, out and error*/
DWORD wh_index[] = { STD_INPUT_HANDLE , STD_OUTPUT_HANDLE , STD_ERROR_HANDLE };
int fd_num = 0;
@ -104,7 +118,7 @@ fd_table_initialize()
return -1;
}
memset(pio, 0, sizeof(struct w32_io));
pio->type = NONSOCK_SYNC_FD;
pio->type = stdio_mode;
pio->handle = wh;
fd_table_set(pio, fd_num);
}
@ -715,12 +729,11 @@ w32_fcntl(int fd, int cmd, ... /* arg */)
int
w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* exceptfds, const struct timeval *timeout)
{
ULONGLONG ticks_start = GetTickCount64(), ticks_spent;
ULONGLONG ticks_start = GetTickCount64(), ticks_spent, timeout_ms = 0, time_rem = 0;
w32_fd_set read_ready_fds, write_ready_fds;
HANDLE events[SELECT_EVENT_LIMIT];
int num_events = 0;
int in_set_fds = 0, out_ready_fds = 0, i;
unsigned int timeout_ms = 0, time_rem = 0;
errno = 0;
/* TODO - the size of these can be reduced based on fds */
@ -843,7 +856,7 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep
else
time_rem = INFINITE;
if (0 != wait_for_any_event(events, num_events, time_rem))
if (0 != wait_for_any_event(events, num_events, (DWORD)time_rem))
return -1;
/* check on fd status */
@ -1071,7 +1084,7 @@ spawn_child_internal(const char* cmd, char *const argv[], HANDLE in, HANDLE out,
si.hStdError = err;
si.dwFlags = STARTF_USESTDHANDLES;
if (strstr(cmd, "sshd.exe")) {
if (strstr(cmd, "sshd-session.exe")) {
flags |= DETACHED_PROCESS;
}

View File

@ -43,13 +43,12 @@ wmain(int argc, wchar_t **wargv) {
char** argv = NULL;
int i, r;
_set_invalid_parameter_handler(invalid_parameter_handler);
if (argc) {
if ((argv = malloc(argc * sizeof(char*))) == NULL)
if ((argv = malloc((argc + 1) * sizeof(char*))) == NULL)
fatal("out of memory");
for (i = 0; i < argc; i++)
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
fatal("out of memory");
}
argv[argc] = NULL;
if (getenv("SSH_AUTH_SOCK") == NULL)
_putenv("SSH_AUTH_SOCK=\\\\.\\pipe\\openssh-ssh-agent");

View File

@ -50,15 +50,13 @@ int sshd_session_main(int argc, wchar_t **wargv) {
int i, r;
_set_invalid_parameter_handler(invalid_parameter_handler);
if (argc) {
if ((argv = malloc(argc * sizeof(char*))) == NULL) {
printf("out of memory");
exit(255);
}
if ((argv = malloc((argc + 1) * sizeof(char*))) == NULL)
fatal("out of memory");
for (i = 0; i < argc; i++)
argv[i] = utf16_to_utf8(wargv[i]);
}
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
fatal("out of memory");
argv[argc] = NULL;
w32posix_initialize();

View File

@ -135,7 +135,7 @@ create_prgdata_ssh_folder()
wchar_t ssh_cfg_dir[PATH_MAX] = { 0, };
wcscpy_s(ssh_cfg_dir, _countof(ssh_cfg_dir), __wprogdata);
wcscat_s(ssh_cfg_dir, _countof(ssh_cfg_dir), L"\\ssh");
if (create_directory_withsddl(ssh_cfg_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)") < 0) {
if (create_directory_withsddl(ssh_cfg_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)", TRUE) < 0) {
printf("failed to create %S", ssh_cfg_dir);
exit(255);
}
@ -144,7 +144,7 @@ create_prgdata_ssh_folder()
wchar_t logs_dir[PATH_MAX] = { 0, };
wcscat_s(logs_dir, _countof(logs_dir), ssh_cfg_dir);
wcscat_s(logs_dir, _countof(logs_dir), L"\\logs");
if (create_directory_withsddl(logs_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)") < 0) {
if (create_directory_withsddl(logs_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)", FALSE) < 0) {
printf("failed to create %S", logs_dir);
exit(255);
}
@ -202,15 +202,14 @@ int sshd_main(int argc, wchar_t **wargv) {
int i, r;
_set_invalid_parameter_handler(invalid_parameter_handler);
if (argc) {
if ((argv = malloc(argc * sizeof(char*))) == NULL) {
printf("out of memory");
exit(255);
}
if ((argv = malloc((argc + 1) * sizeof(char*))) == NULL)
fatal("out of memory");
for (i = 0; i < argc; i++)
argv[i] = utf16_to_utf8(wargv[i]);
}
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
fatal("out of memory");
argv[argc] = NULL;
w32posix_initialize();

4
krl.c
View File

@ -1,4 +1,4 @@
/* $OpenBSD: krl.c,v 1.59 2023/07/17 05:22:30 djm Exp $ */
/* $OpenBSD: krl.c,v 1.60 2025/02/18 08:02:48 djm Exp $ */
/*
* Copyright (c) 2012 Damien Miller <djm@mindrot.org>
*
@ -674,6 +674,7 @@ revoked_certs_generate(struct revoked_certs *rc, struct sshbuf *buf)
break;
case KRL_SECTION_CERT_SERIAL_BITMAP:
if (rs->lo - bitmap_start > INT_MAX) {
r = SSH_ERR_INVALID_FORMAT;
error_f("insane bitmap gap");
goto out;
}
@ -1059,6 +1060,7 @@ ssh_krl_from_blob(struct sshbuf *buf, struct ssh_krl **krlp)
}
if ((krl = ssh_krl_init()) == NULL) {
r = SSH_ERR_ALLOC_FAIL;
error_f("alloc failed");
goto out;
}

4
log.c
View File

@ -54,7 +54,11 @@
#include "match.h"
static LogLevel log_level = SYSLOG_LEVEL_INFO;
#ifdef WINDOWS
int log_on_stderr = 1;
#else
static int log_on_stderr = 1;
#endif /* WINDOWS */
static int log_stderr_fd = STDERR_FILENO;
static int log_facility = LOG_AUTH;
static const char *argv0;

9
misc.c
View File

@ -1261,6 +1261,15 @@ tilde_expand(const char *filename, uid_t uid, char **retp)
path = NULL; /* ~/ */
else
path = copy; /* ~/path */
#ifdef WINDOWS
// also need to account for backward slashes on Windows
} else if (*copy == '\\') {
copy += strspn(copy, "\\");
if (*copy == '\0')
path = NULL; /* ~\ */
else
path = copy; /* ~\path */
#endif /* WINDOWS */
} else {
user = copy;
if ((path = strchr(copy, '/')) != NULL) {

View File

@ -483,14 +483,14 @@ monitor_read_log(struct monitor *pmonitor)
/*log it*/
if (authctxt->authenticated == 0)
sshlogdirect(level, forced, "%s [preauth]", msg);
sshlogdirect(level, forced, "user: %s: %s [preauth]", authctxt->user, msg);
else {
if (strcmp(pname, "sftp-server") == 0) {
log_init(pname, sftp_log_level, sftp_log_facility, sftp_log_stderr);
sshlogdirect(level, forced, "%s", msg);
sshlogdirect(level, forced, "user: %s: %s", authctxt->user, msg);
log_init("sshd", options.log_level, options.log_facility, log_stderr);
} else
sshlogdirect(level, forced, "%s", msg);
sshlogdirect(level, forced, "user: %s: %s", authctxt->user, msg);
}
#else
/*log it*/

View File

@ -24,6 +24,19 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef WINDOWS
// these should be in the same order as upstream, without the ones we don't support
#define KEX_SERVER_KEX \
"curve25519-sha256," \
"curve25519-sha256@libssh.org," \
"ecdh-sha2-nistp256," \
"ecdh-sha2-nistp384," \
"ecdh-sha2-nistp521," \
"diffie-hellman-group-exchange-sha256," \
"diffie-hellman-group16-sha512," \
"diffie-hellman-group18-sha512," \
"diffie-hellman-group14-sha256"
#else
#define KEX_SERVER_KEX \
"sntrup761x25519-sha512@openssh.com," \
"curve25519-sha256," \
@ -35,6 +48,7 @@
"diffie-hellman-group16-sha512," \
"diffie-hellman-group18-sha512," \
"diffie-hellman-group14-sha256"
#endif
#define KEX_CLIENT_KEX KEX_SERVER_KEX

View File

@ -1,4 +1,4 @@
/* $OpenBSD: packet.c,v 1.315 2024/05/31 08:49:35 djm Exp $ */
/* $OpenBSD: packet.c,v 1.318 2025/02/18 08:02:12 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1867,6 +1867,14 @@ ssh_packet_read_poll_seqnr(struct ssh *ssh, u_char *typep, u_int32_t *seqnr_p)
if ((r = sshpkt_get_string_direct(ssh, &d, &len)) != 0)
return r;
DBG(debug("Received SSH2_MSG_PING len %zu", len));
if (!ssh->state->after_authentication) {
DBG(debug("Won't reply to PING in preauth"));
break;
}
if (ssh_packet_is_rekeying(ssh)) {
DBG(debug("Won't reply to PING during KEX"));
break;
}
if ((r = sshpkt_start(ssh, SSH2_MSG_PONG)) != 0 ||
(r = sshpkt_put_string(ssh, d, len)) != 0 ||
(r = sshpkt_send(ssh)) != 0)

View File

@ -0,0 +1,185 @@
If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path}
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
Import-Module OpenSSHUtils -Force
$tC = 1
$tI = 0
$suite = "EventLogging"
Describe "Tests for admin and non-admin event logs" -Tags "CI" {
BeforeAll {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments."
}
$testDir = "$($OpenSSHTestInfo["TestDataPath"])\$suite"
if( -not (Test-path $testDir -PathType Container))
{
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
}
$server = $OpenSSHTestInfo["Target"]
$nonadminusername = $OpenSSHTestInfo['NonAdminUser']
$adminusername = $OpenSSHTestInfo['AdminUser']
$opensshbinpath = $OpenSSHTestInfo['OpenSSHBinPath']
$password = $OpenSSHTestInfo['TestAccountPW']
$port = 47003
$sshdDelay = $OpenSSHTestInfo["DelayTime"]
# Register OpenSSH events in Event Viewer
$etwman = Join-Path $opensshbinpath "openssh-events.man"
if (-not (Test-Path $etwman -PathType Leaf)) {
throw "openssh events manifest is not present in OpenSSH binary path"
}
wevtutil im "$etwman" | Out-Null
}
AfterEach { $tI++ }
AfterAll {
# Unregister etw provider
wevtutil um "$etwman"
}
Context "Tests Logs for SSH connections" {
BeforeAll {
Add-PasswordSetting -Pass $password
$tI=1
}
BeforeEach {
# disable the OpenSSH log channels
wevtutil sl "OpenSSH/Debug" /e:false /q:true | Out-Null
wevtutil sl "OpenSSH/Operational" /e:false /q:true | Out-Null
# clear any existing logs
wevtutil cl "OpenSSH/Debug" | Out-Null
wevtutil cl "OpenSSH/Operational" | Out-Null
# enable the OpenSSH log channels
wevtutil sl "OpenSSH/Debug" /e:true /q:true | Out-Null
wevtutil sl "OpenSSH/Operational" /e:true /q:true | Out-Null
}
AfterAll {
Remove-PasswordSetting
$tC++
}
It "$tC.$tI-Nonadmin SSH Connection" {
$o = ssh -l $nonadminusername test_target echo 1234
$o | Should Be 1234
Start-Sleep $sshdDelay
# query the OpenSSH log channels to make sure events were captured
$eventLogDebug = wevtutil qe "OpenSSH/Debug" /c:5 /f:text
$eventLogDebug | Should Not Be $null
$eventLogOperational = wevtutil qe "OpenSSH/Operational" /c:5 /f:text
$eventLogOperational | Should Not Be $null
}
It "$tC.$tI-Admin SSH Connection" {
$o = ssh -l $adminusername test_target echo 1234
$o | Should Be 1234
Start-Sleep $sshdDelay
# query the OpenSSH log channels to make sure events were captured
$eventLogDebug = wevtutil qe "OpenSSH/Debug" /c:5 /f:text
$eventLogDebug | Should Not Be $null
$eventLogOperational = wevtutil qe "OpenSSH/Operational" /c:5 /f:text
$eventLogOperational | Should Not Be $null
}
}
Context "Tests Logs for SFTP connections" {
BeforeAll {
function Setup-KeyBasedAuth
{
param([string] $Username, [string] $KeyFilePath, [string] $UserProfile)
$userSSHProfilePath = Join-Path $UserProfile .ssh
if (-not (Test-Path $userSSHProfilePath -PathType Container)) {
New-Item $userSSHProfilePath -ItemType directory -Force -ErrorAction Stop | Out-Null
}
$authorizedkeyPath = Join-Path $userSSHProfilePath authorized_keys
if($OpenSSHTestInfo["NoLibreSSL"])
{
ssh-keygen.exe -t ed25519 -f $KeyFilePath -Z -P "" aes128-ctr
}
else
{
ssh-keygen.exe -t ed25519 -f $KeyFilePath -P ""
}
Copy-Item "$KeyFilePath.pub" $authorizedkeyPath -Force -ErrorAction SilentlyContinue
Repair-AuthorizedKeyPermission -Filepath $authorizedkeyPath -confirm:$false
}
$AdminUserProfile = $OpenSSHTestInfo['AdminUserProfile']
$NonAdminUserProfile = $OpenSSHTestInfo['NonAdminUserProfile']
$KeyFileName = $nonadminusername + "_sshtest_EventLog_ed25519"
$NonadminKeyFilePath = Join-Path $testDir $keyFileName
Remove-Item -path "$NonadminKeyFilePath*" -Force -ErrorAction SilentlyContinue
Setup-KeyBasedAuth -Username $nonadminusername -KeyFilePath $NonadminKeyFilePath -UserProfile $NonAdminUserProfile
$KeyFileName = $adminusername + "_sshtest_EventLog_ed25519"
$AdminKeyFilePath = Join-Path $testDir $keyFileName
Remove-Item -path "$AdminKeyFilePath*" -Force -ErrorAction SilentlyContinue
Setup-KeyBasedAuth -Username $adminusername -KeyFilePath $AdminKeyFilePath -UserProfile $AdminUserProfile
#create batch file
$commands =
"ls
exit"
$batchFilePath = Join-Path $testDir "$tC.$tI.commands.txt"
Set-Content $batchFilePath -Encoding UTF8 -value $commands
$tI = 1
}
BeforeEach {
# disable the OpenSSH log channels
wevtutil sl "OpenSSH/Debug" /e:false /q:true | Out-Null
wevtutil sl "OpenSSH/Operational" /e:false /q:true | Out-Null
# clear any existing logs
wevtutil cl "OpenSSH/Debug" | Out-Null
wevtutil cl "OpenSSH/Operational" | Out-Null
# enable the OpenSSH log channels
wevtutil sl "OpenSSH/Debug" /e:true /q:true | Out-Null
wevtutil sl "OpenSSH/Operational" /e:true /q:true | Out-Null
}
AfterAll {
Remove-Item -path "$NonadminKeyFilePath*" -Force -ErrorAction SilentlyContinue
Remove-Item -path "$AdminKeyFilePath*" -Force -ErrorAction SilentlyContinue
$authorized_key = Join-Path '.ssh' authorized_keys
$AdminAuthKeysPath = Join-Path $AdminUserProfile $authorized_key
$NonAdminAuthKeysPath = Join-Path $NonAdminUserProfile $authorized_key
Remove-Item -path "$AdminAuthKeysPath*" -Force -ErrorAction SilentlyContinue
Remove-Item -path "$NonAdminAuthKeysPath*" -Force -ErrorAction SilentlyContinue
$tC++
}
It "$tC.$tI-Nonadmin SFTP Connection" {
sftp -i $NonadminKeyFilePath -b $batchFilePath -o User=$nonadminusername test_target
Start-Sleep $sshdDelay
# query the OpenSSH log channels to make sure events were captured
$eventLogDebug = wevtutil qe "OpenSSH/Debug" /c:5 /f:text
$eventLogDebug | Should Not Be $null
$eventLogOperational = wevtutil qe "OpenSSH/Operational" /c:5 /f:text
$eventLogOperational | Should Not Be $null
}
It "$tC.$tI-Admin SFTP Connection" {
sftp -i $AdminKeyFilePath -b $batchFilePath -o User=$adminusername test_target
Start-Sleep $sshdDelay
# query the OpenSSH log channels to make sure events were captured
$eventLogDebug = wevtutil qe "OpenSSH/Debug" /c:5 /f:text
$eventLogDebug | Should Not Be $null
$eventLogOperational = wevtutil qe "OpenSSH/Operational" /c:5 /f:text
$eventLogOperational | Should Not Be $null
}
}
}

View File

@ -200,9 +200,9 @@ exit"
$sshdlog | Should Contain "Accepted publickey for $nonadminusername"
$sshdlog | Should Contain "KEX done \[preauth\]"
$sshdlog | Should Contain "debug2: subsystem request for sftp by user $nonadminusername"
$sshdlog | Should Contain "debug2: user: $nonadminusername`: subsystem request for sftp by user $nonadminusername"
$sftplog | Should Contain "session opened for local user $nonadminusername"
$sftplog | Should Contain "debug3: request 3: opendir"
$sftplog | Should Contain "debug3: user: $nonadminusername`: request 3: opendir"
$sftplog | Should Contain "session closed for local user $nonadminusername"
}
@ -216,9 +216,9 @@ exit"
$sshdlog | Should Contain "Accepted publickey for $adminusername"
$sshdlog | Should Contain "KEX done \[preauth\]"
$sshdlog | Should Contain "debug2: subsystem request for sftp by user $adminusername"
$sshdlog | Should Contain "debug2: user: $adminusername`: subsystem request for sftp by user $adminusername"
$sftplog | Should Contain "session opened for local user $adminusername"
$sftplog | Should Contain "debug3: request 3: opendir"
$sftplog | Should Contain "debug3: user: $adminusername`: request 3: opendir"
$sftplog | Should Contain "session closed for local user $adminusername"
}
}

View File

@ -17,6 +17,7 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" {
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
}
$pkcs11Pin = "testpin"
$keypassphrase = "testpassword"
$NoLibreSSL = $OpenSSHTestInfo["NoLibreSSL"]
if($NoLibreSSL)
@ -298,6 +299,33 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" {
$allkeys = @(ssh-add -L)
ValidateRegistryACL -count $allkeys.count
}
It "$tC.$tI - ssh-add - pkcs11 library (if available)" {
$pkcs11Path = "C:\\Program Files\\OpenSC Project\\OpenSC\\pkcs11\\opensc-pkcs11.dll"
if (Test-Path $pkcs11Path) {
#set up SSH_ASKPASS
Add-PasswordSetting -Pass $pkcs11Pin
ssh-add -s "$pkcs11Path"
$LASTEXITCODE | Should Be 0
#remove SSH_ASKPASS
Remove-PasswordSetting
#ensure added keys are listed
$allkeys = ssh-add -L
$allKeys -notmatch "The agent has no identities." | Should Be $True
#delete added keys
iex "cmd /c `"ssh-add -D 2> nul `""
#check keys are deleted
$allkeys = ssh-add -L
$allKeys -match "The agent has no identities." | Should Be $True
}
else {
Write-Host "skipping pkcs11 test because provider not found"
}
}
}
Context "$tC ssh-keygen known_hosts operations" {

View File

@ -27,7 +27,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($ssouser, $rights, "ContainerInherit,Objectinherit", "None", "Allow")
$acl.SetAccessRule($accessRule)
Set-Acl -Path $testDir -AclObject $acl
#skip on ps 2 becase non-interactive cmd require a ENTER before it returns on ps2
#skip on ps 2 because non-interactive cmd require a ENTER before it returns on ps2
$skip = $IsWindows -and ($PSVersionTable.PSVersion.Major -le 2)
<#$testData = @(
@ -243,7 +243,40 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$LASTEXITCODE | Should Be 0
$o | Should Be `$env:computername
}
It "$tC.$tI - exiting ssh session exits sshd session child processes" -skip:$skip {
$sshdPidCountBefore = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
ssh test_target "echo '`$env:computername'"
Start-Sleep -Seconds 2
$sshdPidCountAfter = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
$sshdPidCountAfter | Should Be $sshdPidCountBefore
}
}
Context "$tC - configure powershell as default shell with admin user" {
BeforeAll {
$tI=1
$shell_path = (Get-Command powershell.exe -ErrorAction SilentlyContinue).path
if ($shell_path -ne $null) {
ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "-c"
}
$password = $OpenSSHTestInfo['TestAccountPW']
Add-PasswordSetting -Pass $password
}
AfterAll {
$tC++
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
Remove-PasswordSetting
}
It "$tC.$tI - admin session can write to console" -skip:$skip {
$adminusername = $OpenSSHTestInfo['AdminUser']
$o = ssh $adminusername@test_target "Get-ComputerInfo"
$LASTEXITCODE | Should Be 0
$o | Select-String -Pattern "WindowsVersion" | Should Match "WindowsVersion"
}
}
Context "$tC - configure cmd as default shell" {
BeforeAll {
$tI=1
@ -349,6 +382,18 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$logFile | Should Contain "[::1]"
}
It "$tC.$tI - tilde expand for path with forward slash" {
$o = ssh -v -i ~/test/key/path -E $logFile test_target echo 1234
$o | Should Be "1234"
$logFile | Should Not Contain "tilde_expand: No such user"
}
It "$tC.$tI - tilde expand for path with backslash" {
$o = ssh -v -i ~\test\key\path -E $logFile test_target echo 1234
$o | Should Be "1234"
$logFile | Should Not Contain "tilde_expand: No such user"
}
It "$tC.$tI - auto populate known hosts" {
$kh = Join-Path $testDir "$tC.$tI.known_hosts"

View File

@ -0,0 +1,81 @@
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
Describe "E2E scenarios for sshd" -Tags "CI" {
BeforeAll {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments."
}
$server = $OpenSSHTestInfo["Target"]
$port = $OpenSSHTestInfo["Port"]
$user = $OpenSSHTestInfo["PasswdUser"]
}
Context "SSHD scenarios" {
BeforeAll {
# configure logingracetime to 10 seconds and presrerve the original config
$sshdconfig = Join-Path $Global:OpenSSHTestInfo["ServiceConfigDir"] sshd_config
$sshdconfig_temp = Join-Path $Global:OpenSSHTestInfo["ServiceConfigDir"] sshd_config_temp
if (Test-Path $sshdconfig_temp) {
Remove-Item $sshdconfig_temp -Force
}
Copy-Item $sshdconfig $sshdconfig_temp
$content = Get-Content -Path $sshdconfig
$newContent = $content -replace "#LoginGraceTime 2m", "LoginGraceTime 10"
$newContent | Set-Content -Path $sshdconfig
}
BeforeEach {
Restart-Service -Name $OpenSSHTestInfo["SshdServiceName"] -Force
}
AfterAll {
# restore original config
Copy-Item $sshdconfig_temp $sshdconfig -Force
Restart-Service -Name $OpenSSHTestInfo["SshdServiceName"] -Force
Remove-Item $sshdconfig_temp -Force
}
It "sshd child process ends when LoginGraceTime is exceeded" {
# Get a count of any sshd processes before a connection in case there's another service running on the system
# should be at least 1 sshd process for the test service
$sshdPidCountBefore = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
# Start ssh process (do not authenticate)
$sshProc = Start-Process -FilePath ssh -ArgumentList "-l $user test_target" -PassThru
Start-Sleep -Seconds 2
$sshdPidsCountWithConn = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
# Wait for LoginGraceTime to expire
Start-Sleep -Seconds 10
$sshdPidsCountAfter = (Get-Process -Name sshd* | Select-Object -ExpandProperty Id).Count
if ($sshProc -and !$sshProc.HasExited) {
$sshProc | Stop-Process -Force
}
# with a connection, there should be two additional session processes
$sshdPidsCountWithConn | Should Be (2 + $sshdPidCountBefore)
# after LoginGraceTime expires, one of the session processes should exit
$sshdPidsCountAfter | Should Be (1 + $sshdPidCountBefore)
}
It "sshd pre-auth process is spawned under runtime generated virtual account" {
$sshProc = Start-Process -FilePath ssh -ArgumentList "-l $user test_target" -PassThru
Start-Sleep -Seconds 2
$sshdProcessUsers = Get-Process -Name sshd* -IncludeUsername | Select-Object -ExpandProperty UserName
$foundVirtualAccount = $false
foreach ($username in $sshdProcessUsers) {
if ($username -match '^VIRTUAL USERS\\sshd_\d+$') {
$foundVirtualAccount = $true
break
}
}
if ($sshProc -and !$sshProc.HasExited) {
$sshProc | Stop-Process -Force
}
$foundVirtualAccount | Should Be $true
}
}
}

View File

@ -135,6 +135,20 @@ Match User matchuser
}
}
function Set-SSHDConfigLine
{
param([string]$line, [string]$file)
$sshdconfig_ori = Join-Path $Global:OpenSSHTestInfo["ServiceConfigDir"] sshd_config
if (Test-Path $file) {
Remove-Item $file -Force
}
Copy-Item $sshdconfig_ori $file
get-acl $sshdconfig_ori | set-acl $file
$content = Get-Content -Path $file
$newContent = @($line) + $content
Set-Content -Path $file -Value $newContent
}
#skip when the task schedular (*-ScheduledTask) cmdlets does not exist
$ts = (get-command get-ScheduledTask -ErrorAction SilentlyContinue)
$skip = $ts -eq $null
@ -365,4 +379,50 @@ Match User matchuser
Remove-UserFromLocalGroup -UserName $matchuser -GroupName $allowGroup1
}
}
Context "Tests of Other SSHD Config Directives via -T" {
BeforeAll {
$tI=1
$absoluteFilePath = Join-Path $testDir "includeFile"
$relativeFilePath = "includeFile"
$progDataPath = Join-Path $env:ProgramData $relativeFilePath
# adding a line that would not be in a default sshd_config file
$content = "loglevel DEBUG3"
$content | Set-Content $absoluteFilePath
$content | Set-Content $progDataPath
$sshdconfig_custom = Join-Path $Global:OpenSSHTestInfo["ServiceConfigDir"] sshd_config_custom
$binPath = Join-Path $($OpenSSHTestInfo['OpenSSHBinPath']) "sshd.exe"
}
AfterAll {
$tC++
if (Test-Path $absoluteFilePath) {
Remove-Item $absoluteFilePath -force
}
if (Test-Path $progDataPath) {
Remove-Item $progDataPath -force
}
if (Test-Path $sshdconfig_custom) {
Remove-Item $sshdconfig_custom -force
}
}
It "$tC.$tI - Include Directive with absolute path starting with forward slash" {
Set-SSHDConfigLine -line "Include /$absoluteFilePath" -file $sshdconfig_custom
$result = Invoke-Expression "$binPath -T -f '$sshdconfig_custom'"
$result.Contains($content) | Should Be $true
}
It "$tC.$tI - Include Directive with absolute path starting with drive" {
Set-SSHDConfigLine -line "Include $absoluteFilePath" -file $sshdconfig_custom
$result = Invoke-Expression "$binPath -T -f '$sshdconfig_custom'"
$result.Contains($content) | Should Be $true
}
It "$tC.$tI - Include Directive with filename, relative to ProgramData" {
Set-SSHDConfigLine -line "Include $relativeFilePath" -file $sshdconfig_custom
$result = Invoke-Expression "$binPath -T -f '$sshdconfig_custom'"
$result.Contains($content) | Should Be $true
}
}
}

View File

@ -1,4 +1,4 @@
If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path}
If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path}
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
$suite = "Setup"
$tC = 1
@ -165,6 +165,9 @@ Describe "Setup Tests" -Tags "Setup" {
@{
Name = 'sshd.exe'
},
@{
Name = 'sshd-session.exe'
},
@{
Name = 'ssh.exe'
},
@ -253,6 +256,37 @@ Describe "Setup Tests" -Tags "Setup" {
}
}
Context "$tC - Validate OpenSSH version" {
BeforeAll {
$tI = 1
$sshExePath = Join-Path $binPath "ssh.exe"
if (-not (Test-Path -Path $sshExePath)) {
Throw "ssh.exe not found at $sshExePath"
}
}
AfterAll { $tC++ }
AfterEach { $tI++ }
It "$tC.$tI - Validate ssh -V output matches ssh.exe properties" {
$pattern = "\d+\.\dp\d" # i.e. 9.2p2
$sshVersionOutput = & $sshExePath -V 2>&1 | Select-String -Pattern "OpenSSH"
$match = $sshVersionOutput.Line -match $pattern
if (-not $match) {
throw "No matching version pattern found from ssh -V output"
}
$versionNumber = $Matches[0]
$fileVersionInfo = Get-Item $sshExePath | Select-Object -ExpandProperty VersionInfo
$fileVersion = $fileVersionInfo.ProductVersion
$match = $fileVersion -match $pattern
if (-not $match) {
throw "No matching version pattern found from ssh.exe properties"
}
$versionNumber | Should Match $Matches[0]
}
}
Context "$tC - Validate Openssh registry entries" {
BeforeAll {
$tI=1
@ -521,4 +555,55 @@ Describe "Setup Tests" -Tags "Setup" {
$fwportFilter.RemotePort | Should Be 'Any'
}
}
Context "$tC - Validate SSHD service startup" {
BeforeAll {
$tI=1
$sshFolderPath = Join-Path $env:ProgramData "ssh"
$sshACL = $null
if (Test-Path -Path $sshFolderPath) {
$sshACL = Get-Acl $sshFolderPath
}
$logFolderPath = Join-Path $env:ProgramData "ssh" "logs"
$logACL = $null
if (Test-Path -Path $logFolderPath) {
$logACL = Get-Acl $logFolderPath
}
if ((Get-Service sshd).Status -eq 'Running') {
net stop sshd
}
}
AfterAll {
$tC++
if ($logACL -eq $null) {
Remove-Item -Path $logFolderPath -Recurse -Force
}
if ($sshACL -eq $null) {
Remove-Item -Path $sshFolderPath -Recurse -Force
}
}
AfterEach {
$tI++
net stop sshd
if ($sshACL -ne $null) {
Set-Acl -Path $sshFolderPath -AclObject $sshACL
}
if ($logACL -ne $null) {
Set-Acl -Path $logFolderPath -AclObject $logACL
}
}
It "$tC.$tI - SSHD starts up successfully when Authenticated Users have read control over log folder" {
if (-not (Test-Path -Path $logFolderPath)) {
New-Item -Path $logFolderPath -ItemType Directory -Force
}
# Set ACLs on the folder
$acl = Get-Acl $logFolderPath
$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($authenticatedUserSid, "ReadAndExecute", "Allow")
$acl.SetAccessRule($accessRule)
Set-Acl -Path $logFolderPath -AclObject $acl
net start sshd
$LASTEXITCODE | Should Be 0
}
}
}

View File

@ -2319,7 +2319,11 @@ process_server_config_line_depth(ServerOptions *options, char *line,
}
value++;
found = 0;
#ifdef WINDOWS
if (!path_absolute(arg2) && *arg2 != '~') {
#else
if (*arg2 != '/' && *arg2 != '~') {
#endif
xasprintf(&arg, "%s/%s", SSHDIR, arg2);
} else
arg = xstrdup(arg2);

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-agent.c,v 1.306 2024/03/09 05:12:13 djm Exp $ */
/* $OpenBSD: ssh-agent.c,v 1.310 2025/02/18 08:02:48 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -1208,6 +1208,7 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
"restrict-destination-v00@openssh.com") == 0) {
if (*dcsp != NULL) {
error_f("%s already set", ext_name);
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
if ((r = sshbuf_froms(m, &b)) != 0) {
@ -1217,6 +1218,7 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
while (sshbuf_len(b) != 0) {
if (*ndcsp >= AGENT_MAX_DEST_CONSTRAINTS) {
error_f("too many %s constraints", ext_name);
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
*dcsp = xrecallocarray(*dcsp, *ndcsp, *ndcsp + 1,
@ -1234,6 +1236,7 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
}
if (*certs != NULL) {
error_f("%s already set", ext_name);
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
if ((r = sshbuf_get_u8(m, &v)) != 0 ||
@ -1245,6 +1248,7 @@ parse_key_constraint_extension(struct sshbuf *m, char **sk_providerp,
while (sshbuf_len(b) != 0) {
if (*ncerts >= AGENT_MAX_EXT_CERTS) {
error_f("too many %s constraints", ext_name);
r = SSH_ERR_INVALID_FORMAT;
goto out;
}
*certs = xrecallocarray(*certs, *ncerts, *ncerts + 1,
@ -1741,6 +1745,7 @@ process_ext_session_bind(SocketEntry *e)
/* record new key/sid */
if (e->nsession_ids >= AGENT_MAX_SESSION_IDS) {
error_f("too many session IDs recorded");
r = -1;
goto out;
}
e->session_ids = xrecallocarray(e->session_ids, e->nsession_ids,

View File

@ -67,7 +67,11 @@
#include "sk-api.h" /* XXX for SSH_SK_USER_PRESENCE_REQD; remove */
#include "cipher.h"
#ifdef WINDOWS
#define DEFAULT_KEY_TYPE_NAME "ecdsa"
#else
#define DEFAULT_KEY_TYPE_NAME "ed25519"
#endif /* WINDOWS */
/*
* Default number of bits in the RSA, DSA and ECDSA keys. These value can be
@ -82,7 +86,11 @@
*/
#define DEFAULT_BITS 3072
#define DEFAULT_BITS_DSA 1024
#ifdef WINDOWS
#define DEFAULT_BITS_ECDSA 384
#else
#define DEFAULT_BITS_ECDSA 256
#endif /* WINDOWS */
static int quiet = 0;
@ -259,7 +267,11 @@ ask_filename(struct passwd *pw, const char *prompt)
char *name = NULL;
if (key_type_name == NULL)
#ifdef WINDOWS
name = _PATH_SSH_CLIENT_ID_ECDSA;
#else
name = _PATH_SSH_CLIENT_ID_ED25519;
#endif /* WINDOWS */
else {
switch (sshkey_type_from_name(key_type_name)) {
#ifdef WITH_DSA

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-sk-client.c,v 1.12 2022/01/14 03:34:00 djm Exp $ */
/* $OpenBSD: ssh-sk-client.c,v 1.13 2025/02/18 08:02:48 djm Exp $ */
/*
* Copyright (c) 2019 Google LLC
*
@ -570,6 +570,7 @@ sshsk_load_resident(const char *provider_path, const char *device,
}
if ((srk = calloc(1, sizeof(*srk))) == NULL) {
error_f("calloc failed");
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
srk->key = key;
@ -581,6 +582,7 @@ sshsk_load_resident(const char *provider_path, const char *device,
if ((tmp = recallocarray(srks, nsrks, nsrks + 1,
sizeof(*srks))) == NULL) {
error_f("recallocarray keys failed");
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
debug_f("srks[%zu]: %s %s uidlen %zu", nsrks,

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshconnect2.c,v 1.373 2024/05/17 06:38:00 jsg Exp $ */
/* $OpenBSD: sshconnect2.c,v 1.377 2025/02/18 08:02:48 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved.
@ -103,7 +103,7 @@ verify_host_key_callback(struct sshkey *hostkey, struct ssh *ssh)
options.required_rsa_size)) != 0)
fatal_r(r, "Bad server host key");
if (verify_host_key(xxx_host, xxx_hostaddr, hostkey,
xxx_conn_info) == -1)
xxx_conn_info) != 0)
fatal("Host key verification failed.");
return 0;
}
@ -722,6 +722,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
send_pubkey_telemetry("server sent unknown pkalg");
#endif
debug_f("server sent unknown pkalg %s", pkalg);
r = SSH_ERR_INVALID_FORMAT;
goto done;
}
if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
@ -738,6 +739,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
error("input_userauth_pk_ok: type mismatch "
"for decoded key (received %d, expected %d)",
key->type, pktype);
r = SSH_ERR_INVALID_FORMAT;
goto done;
}
@ -757,6 +759,7 @@ input_userauth_pk_ok(int type, u_int32_t seq, struct ssh *ssh)
SSH_FP_DEFAULT);
error_f("server replied with unknown key: %s %s",
sshkey_type(key), fp == NULL ? "<ERROR>" : fp);
r = SSH_ERR_INVALID_FORMAT;
goto done;
}
ident = format_identity(id);

View File

@ -26,5 +26,6 @@
#define _SSH_FILE_PERM_H
int check_secure_file_permission(const char *, struct passwd *, int);
int check_secure_folder_permission(const wchar_t*, int);
void check_secure_folder_permission(const wchar_t*, int);
void log_folder_perms_msg_etw(const wchar_t*, wchar_t*);
#endif /* _SSH_FILE_PERM_H */

View File

@ -1,4 +1,4 @@
/* $OpenBSD: sshsig.c,v 1.35 2024/03/08 22:16:32 djm Exp $ */
/* $OpenBSD: sshsig.c,v 1.38 2025/02/18 08:02:48 djm Exp $ */
/*
* Copyright (c) 2019 Google LLC
*
@ -907,6 +907,7 @@ cert_filter_principals(const char *path, u_long linenum,
}
if ((principals = sshbuf_dup_string(nprincipals)) == NULL) {
error_f("buffer error");
r = SSH_ERR_ALLOC_FAIL;
goto out;
}
/* success */

View File

@ -4,5 +4,5 @@
#define SSH_WINDOWS_BANNER " Win32-OpenSSH-GitHub"
#define SSH_VERSION SSH_WINDOWS_VERSION SSH_WINDOWS_BANNER
#define SSH_PORTABLE "p1"
#define SSH_PORTABLE "p2"
#define SSH_RELEASE SSH_WINDOWS_VERSION SSH_PORTABLE SSH_WINDOWS_BANNER