Compare commits

..

20 Commits

Author SHA1 Message Date
Tess Gauthier
fdde2326f9
Fix static analysis warning ()
* 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 () 2025-03-31 15:15:57 -04:00
LainOTN2
31f8d13ab6
Fix for Y2038 gettimeofday for Win32 builds ()
* 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 ()
* 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 ()
* 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 ()
* 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 ()
* 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 ()
* 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 () 2025-01-13 10:51:15 -05:00
Tess Gauthier
b36bc85f47
Update ssh folder permissions check in SSHD ()
* 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 ()
* 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 () 2025-01-06 14:12:21 -05:00
Tess Gauthier
cdcc8d34d8
Fix tilde expand for Windows paths with backslashes ()
* 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 () 2024-12-18 15:36:43 -05:00
Tess Gauthier
265df19787
Fix include paths ()
* 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 () 2024-12-09 11:27:41 -05:00
Tess Gauthier
0dd6d2cd21
username logging fix ()
* 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 ()
* 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 ()
* 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 ()
* 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
34 changed files with 656 additions and 199 deletions

@ -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
#

@ -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);

@ -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-->

@ -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

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 9,8,1,0
PRODUCTVERSION 9,8,1,0
FILEVERSION 9,8,2,0
PRODUCTVERSION 9,8,2,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.2.0"
VALUE "ProductName", "OpenSSH for Windows"
VALUE "ProductVersion", "OpenSSH_9.8p1 for Windows"
VALUE "ProductVersion", "OpenSSH_9.8p2 for Windows"
END
END
BLOCK "VarFileInfo"

@ -435,14 +435,20 @@ 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 ||
final_path[wcslen(chroot_pathw)] != '\\') {
memcmp(final_path, chroot_pathw, 2 * wcslen(chroot_pathw)) != 0 ||
final_path[wcslen(chroot_pathw)] != '\\') {
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);

@ -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;

@ -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

@ -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))

@ -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 *);

@ -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;

@ -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();

@ -414,26 +414,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;
}
free(prog_files);
free(prog_files_x86);
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);
}
}

@ -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;
}

@ -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 */

@ -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,23 +260,7 @@ 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);
}
GetVTSeqFromKeyStroke(inputRecord);
}
break;
}

@ -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"

@ -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)
}
if (log_msg) {
free(log_msg);
}
if (pSD) {
LocalFree(pSD);
if (ti_sid)
}
if (ti_sid) {
free(ti_sid);
return ret;
}
}
/*
* 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);
}
}

@ -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;
}

@ -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");
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");

@ -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]);
}
for (i = 0; i < argc; i++)
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
fatal("out of memory");
argv[argc] = NULL;
w32posix_initialize();

@ -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]);
}
for (i = 0; i < argc; i++)
if ((argv[i] = utf16_to_utf8(wargv[i])) == NULL)
fatal("out of memory");
argv[argc] = NULL;
w32posix_initialize();

4
log.c

@ -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

@ -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) {

@ -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*/

@ -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

@ -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"
}
}

@ -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" {

@ -5,9 +5,9 @@ Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
$tC = 1
$tI = 0
$suite = "sshclient"
Describe "E2E scenarios for ssh client" -Tags "CI" {
BeforeAll {
BeforeAll {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments."
@ -27,12 +27,12 @@ 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 = @(
@{
Title = 'Simple logon no option';
Title = 'Simple logon no option';
LogonStr = "$($server.localAdminUserName)@$($server.MachineName)"
Options = ""
},
@ -42,7 +42,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
Options = "-C -l $($server.localAdminUserName)"
}
)
$testData1 = @(
@{
Title = "logon using -i -q option"
@ -84,7 +84,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
[string] $default_shell_path,
[string] $default_shell_cmd_option_val = $null
)
if (!(Test-Path $dfltShellRegPath)) {
New-Item -Path $dfltShellRegPath -Force | Out-Null
}
@ -99,12 +99,12 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$stderrFile=Join-Path $testDir "$tC.$tI.stderr.txt"
$stdoutFile=Join-Path $testDir "$tC.$tI.stdout.txt"
$logFile = Join-Path $testDir "$tC.$tI.log.txt"
}
}
AfterEach {$tI++;}
Context "$tC - Basic Scenarios" {
BeforeAll {$tI=1}
AfterAll{$tC++}
@ -117,13 +117,13 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
iex "cmd /c `"ssh -? 2> $stderrFile`""
$stderrFile | Should Contain "usage: ssh"
}
It "$tC.$tI - remote echo command" {
iex "$sshDefaultCmd echo 1234" | Should Be "1234"
}
}
Context "$tC - exit code (exit-status.sh)" {
BeforeAll {$tI=1}
AfterAll{$tC++}
@ -132,12 +132,12 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
foreach ($i in (0,1,4,5,44)) {
ssh -p $port $ssouser@$server exit $i
$LASTEXITCODE | Should Be $i
}
}
}
}
Context "$tC - Redirection Scenarios" {
BeforeAll {$tI=1}
AfterAll{$tC++}
@ -192,7 +192,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
}
}
Context "$tC - configure powershell default shell Scenarios" {
BeforeAll {
$tI=1
@ -205,13 +205,13 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$tC++
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
}
}
It "$tC.$tI - basic powershell" -skip:$skip {
$o = ssh test_target Write-Output 1234
$o | Should Be "1234"
}
It "$tC.$tI - basic in powershell cmdlet" -skip:$skip {
$o = ssh test_target "cd `$env:ProgramFiles;pwd"
$LASTEXITCODE | Should Be 0
@ -240,26 +240,52 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
It "$tC.$tI - single quotes in powershell cmdlet" -skip:$skip {
# actual command line ssh target echo '$env:computername'
$o = ssh test_target "echo '`$env:computername'"
$LASTEXITCODE | Should Be 0
$LASTEXITCODE | Should Be 0
$o | Should Be `$env:computername
}
}
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
$shell_path = (Get-Command cmd.exe -ErrorAction SilentlyContinue).path
if($shell_path -ne $null) {
ConfigureDefaultShell -default_shell_path $shell_path -default_shell_cmd_option_val "/c"
}
}
}
AfterAll{
AfterAll {
$tC++
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellRegKeyName -ErrorAction SilentlyContinue
Remove-ItemProperty -Path $dfltShellRegPath -Name $dfltShellCmdOptionRegKeyName -ErrorAction SilentlyContinue
}
It "$tC.$tI - default shell as cmd" -skip:$skip {
It "$tC.$tI - default shell as cmd" -skip:$skip {
$o = ssh test_target where cmd
$o | Should Contain "cmd"
$o | Should Contain "cmd"
}
It "$tC.$tI - cmd as default shell and double quotes in cmdline" {
# actual command line ssh target echo "hello"
@ -269,7 +295,7 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
It "$tC.$tI - single quotes in powershell cmdlet" -skip:$skip {
# actual command line ssh target echo '$env:computername'
$o = ssh test_target "echo 'hello'"
$LASTEXITCODE | Should Be 0
$LASTEXITCODE | Should Be 0
$o | Should Be "'hello'"
}
}
@ -290,15 +316,15 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
$o | Should Be "`"hello`""
}
}
Context "$tC - cmdline parameters" {
Context "$tC - cmdline parameters" {
BeforeAll {$tI=1}
AfterAll{$tC++}
It "$tC.$tI - verbose to file (-v -E)" {
$o = ssh -v -E $logFile test_target echo 1234
$o | Should Be "1234"
#TODO - checks below are very inefficient (time taking).
#TODO - checks below are very inefficient (time taking).
$logFile | Should Contain "OpenSSH_"
$logFile | Should Contain "Exit Status 0"
}
@ -333,11 +359,11 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
" Port $port" | Add-Content $goodConfigFile
" User $ssouser" | Add-Content $goodConfigFile
$o = ssh -F $goodConfigFile myhost echo 1234
$o | Should Be "1234"
$o | Should Be "1234"
}
It "$tC.$tI - IP options - (-4) (-6)" {
# TODO - this test assumes target is localhost.
# TODO - this test assumes target is localhost.
# make it work independent of target
#-4
$o = ssh -4 -v -E $logFile test_target echo 1234
@ -346,11 +372,23 @@ Describe "E2E scenarios for ssh client" -Tags "CI" {
#-4
$o = ssh -6 -v -E $logFile test_target echo 1234
$o | Should Be "1234"
$logFile | Should Contain "[::1]"
$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"
$nul | Set-Content $kh
# doing via cmd to intercept and drain stderr output

@ -134,6 +134,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)
@ -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
}
}
}

@ -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
@ -8,12 +8,12 @@ Describe "Setup Tests" -Tags "Setup" {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Set-OpenSSHTestEnvironment to set test environments."
}
}
$windowsInBox = $OpenSSHTestInfo["WindowsInBox"]
$binPath = $OpenSSHTestInfo["OpenSSHBinPath"]
$dataPath = Join-path $env:ProgramData ssh
$dataPath = Join-path $env:ProgramData ssh
$systemSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::LocalSystemSid)
$adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid)
$usersSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::BuiltinUsersSid)
@ -34,24 +34,24 @@ Describe "Setup Tests" -Tags "Setup" {
([System.UInt32] [System.Security.AccessControl.FileSystemRights]::Synchronize.value__)
$RegReadKeyPerm = ([System.UInt32] [System.Security.AccessControl.RegistryRights]::ReadKey.value__)
$RegFullControlPerm = [System.UInt32] [System.Security.AccessControl.RegistryRights]::FullControl.value__
$RegFullControlPerm = [System.UInt32] [System.Security.AccessControl.RegistryRights]::FullControl.value__
#only validate owner and ACEs of the registry
function ValidateRegistryACL {
param([string]$RegPath, $Ownersid = $adminsSid, $IdAcls)
Test-Path -Path $RegPath | Should Be $true
Test-Path -Path $RegPath | Should Be $true
$myACL = Get-ACL $RegPath
$OwnerSid = Get-UserSid -User $myACL.Owner
$OwnerSid.Equals($Ownersid) | Should Be $true
$myACL.Access | Should Not Be $null
$CAPABILITY_SID = "S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681"
$CAPABILITY_SID = "S-1-15-3-1024-1065365936-1281604716-3511738428-1654721687-432734479-3232135806-4053264122-3456934681"
$nonPropagate = $myACL.Access | ? {($_.PropagationFlags -eq ([System.Security.AccessControl.PropagationFlags]::None)) -and ($_.IdentityReference -ine $CAPABILITY_SID)}
foreach ($a in $nonPropagate) {
$findItem = $IdAcls | ? {
($a.IdentityReference -eq (Get-UserAccount -UserSid ($_.Identity))) -and `
($a.IsInherited -eq $_.IsInherited) -and `
($a.AccessControlType -eq ([System.Security.AccessControl.AccessControlType]::Allow)) -and `
($a.AccessControlType -eq ([System.Security.AccessControl.AccessControlType]::Allow)) -and `
(([System.Int32]$a.RegistryRights.value__) -eq ($_.RegistryRights))
}
$findItem | Should Not Be $null
@ -60,11 +60,11 @@ Describe "Setup Tests" -Tags "Setup" {
foreach ($expected in $IdAcls) {
$findItem = $nonPropagate | ? {
((Get-UserAccount -UserSid ($expected.Identity)) -eq $_.IdentityReference) -and `
($expected.IsInherited -eq $_.IsInherited) -and `
($expected.IsInherited -eq $_.IsInherited) -and `
($expected.RegistryRights -eq ([System.Int32]$_.RegistryRights.value__))
}
$findItem | Should Not Be $null
}
}
}
#only validate owner and ACEs of the file
@ -86,8 +86,8 @@ Describe "Setup Tests" -Tags "Setup" {
$myACL = Get-ACL $FilePath
$currentOwnerSid = Get-UserSid -User $myACL.Owner
if(-not $windowsInBox) {return}
$currentOwnerSid.Equals($OwnerSid) | Should Be $true
if(-not $windowsInBox) {return}
$currentOwnerSid.Equals($OwnerSid) | Should Be $true
$myACL.Access | Should Not Be $null
if($IsDirectory)
{
@ -111,7 +111,7 @@ Describe "Setup Tests" -Tags "Setup" {
if($id -eq $null)
{
$idRefShortValue = ($a.IdentityReference.Value).split('\')[-1]
$id = Get-UserSID -User $idRefShortValue
$id = Get-UserSID -User $idRefShortValue
}
$identities -contains $id | Should be $true
@ -127,12 +127,12 @@ Describe "Setup Tests" -Tags "Setup" {
else
{
([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSReadAndExecutePerm
}
}
break;
}
{@($usersSid, $allApplicationPackagesSid, $allRestrictedApplicationPackagesSid, $authenticatedUserSid) -contains $_}
{
([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSReadAndExecutePerm
{
([System.UInt32]$a.FileSystemRights.value__) | Should Be $FSReadAndExecutePerm
break;
}
$trustedInstallerSid
@ -141,7 +141,7 @@ Describe "Setup Tests" -Tags "Setup" {
break;
}
}
$a.AccessControlType | Should Be ([System.Security.AccessControl.AccessControlType]::Allow)
if($IsDirectory)
{
@ -154,8 +154,8 @@ Describe "Setup Tests" -Tags "Setup" {
}
$a.PropagationFlags | Should Be ([System.Security.AccessControl.PropagationFlags]::None)
}
}
}
}
}
Context "$tC - Validate Openssh binary files" {
@ -230,7 +230,7 @@ Describe "Setup Tests" -Tags "Setup" {
}
)
}
AfterAll{$tC++}
AfterAll{$tC++}
AfterEach { $tI++ }
It "$tC.$tI - Validate Openssh binary files--<Name>" -TestCases:$binaries{
@ -238,7 +238,7 @@ Describe "Setup Tests" -Tags "Setup" {
ValidateFileSystem -FilePath (join-path $binPath $Name)
}
It "$tC.$tI - Validate Openssh script files--<Name>" -TestCases:$dataFile {
param([string]$Name, [boolean]$IsDirectory = $false)
param([string]$Name, [boolean]$IsDirectory = $false)
if(-not $WindowsInbox) { ValidateFileSystem -FilePath (join-path $binPath $Name) }
}
@ -248,17 +248,17 @@ Describe "Setup Tests" -Tags "Setup" {
{
Start-Service sshd
}
ValidateFileSystem -FilePath (join-path $dataPath $Name) -IsDirectory $IsDirectory -OwnerSid $adminsSid -IsDataFile
}
}
}
Context "$tC - Validate Openssh registry entries" {
BeforeAll {
$tI=1
$servicePath = "HKLM:\SYSTEM\ControlSet001\Services"
$opensshRegPath = "HKLM:\SOFTWARE\OpenSSH"
$opensshACLs = @(
@{
Identity=$systemSid
@ -271,7 +271,7 @@ Describe "Setup Tests" -Tags "Setup" {
IsInherited = $false
RegistryRights = $RegFullControlPerm
PropagationFlags = "None"
},
},
@{
Identity=$authenticatedUserSid
IsInherited = $false
@ -294,9 +294,9 @@ Describe "Setup Tests" -Tags "Setup" {
PropagationFlags = "None"
}
)
}
}
AfterAll{$tC++}
AfterEach { $tI++ }
AfterEach { $tI++ }
It "$tC.$tI - Validate Registry key ssh-agent\Description" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Description"
@ -317,10 +317,10 @@ Describe "Setup Tests" -Tags "Setup" {
It "$tC.$tI - Validate Registry key ssh-agent\ObjectName" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "ObjectName"
$p | Should Be "LocalSystem"
}
}
It "$tC.$tI - Validate Registry key ssh-agent\Start" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Start"
$p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Start"
if($windowsInBox) {
$p | Should Be 4
}
@ -332,12 +332,12 @@ Describe "Setup Tests" -Tags "Setup" {
It "$tC.$tI - Validate Registry key ssh-agent\Type" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent") -Name "Type"
$p | Should Be 16
}
}
It "$tC.$tI - Validate Registry key to ssh-agent\Security\Security" {
It "$tC.$tI - Validate Registry key to ssh-agent\Security\Security" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "ssh-agent\Security") -Name Security
$p.Gettype() | Should Be byte[]
}
}
It "$tC.$tI - Validate Registry key sshd\Description" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Description"
@ -356,17 +356,17 @@ Describe "Setup Tests" -Tags "Setup" {
}
It "$tC.$tI - Validate Registry key sshd\ObjectName" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "ObjectName"
$p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "ObjectName"
$p | Should Be "LocalSystem"
}
}
It "$tC.$tI - Validate Registry key sshd\Start" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Start"
$p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Start"
$p | Should Be 3
}
It "$tC.$tI - Validate Registry key sshd\Type" {
$p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Type"
$p = Get-ItemPropertyValue (Join-Path $servicePath "sshd") -Name "Type"
$p | Should Be 16
}
@ -383,18 +383,18 @@ Describe "Setup Tests" -Tags "Setup" {
{
Start-Service ssh-agent
ValidateRegistryACL -RegPath $agentPath -IdAcls $opensshAgentACLs
}
}
}
}
Context "$tC - Validate service settings" {
BeforeAll {
BeforeAll {
$tI=1
}
}
AfterAll{$tC++}
AfterEach { $tI++ }
It "$tC.$tI - Validate properties of ssh-agent service" {
It "$tC.$tI - Validate properties of ssh-agent service" {
$sshdSvc = Get-service ssh-agent
if($windowsInBox) {
$sshdSvc.StartType | Should Be ([System.ServiceProcess.ServiceStartMode]::Disabled)
@ -411,7 +411,7 @@ Describe "Setup Tests" -Tags "Setup" {
($sshdSvc.RequiredServices).Count | Should Be 0
}
It "$tC.$tI - Validate properties of sshd service" {
It "$tC.$tI - Validate properties of sshd service" {
$sshdSvc = Get-service sshd
$sshdSvc.StartType | Should Be ([System.ServiceProcess.ServiceStartMode]::Manual)
$sshdSvc.ServiceType | Should Be ([System.ServiceProcess.ServiceType]::Win32OwnProcess)
@ -422,7 +422,7 @@ Describe "Setup Tests" -Tags "Setup" {
($sshdSvc.ServicesDependedOn).Count | Should Be 0
($sshdSvc.RequiredServices).Count | Should Be 0
}
It "$tC.$tI - Validate RequiredPrivileges of ssh-agent" {
$expected = @("SeAssignPrimaryTokenPrivilege", "SeTcbPrivilege", "SeBackupPrivilege", "SeRestorePrivilege", "SeImpersonatePrivilege")
$a = sc.exe qprivs ssh-agent 256
@ -449,7 +449,7 @@ Describe "Setup Tests" -Tags "Setup" {
}
}
It "$tC.$tI - Validate security access to ssh-agent service" {
It "$tC.$tI - Validate security access to ssh-agent service" {
$a = @(sc.exe sdshow ssh-agent)
$b = $a[-1] -split "[D|S]:"
@ -460,19 +460,19 @@ Describe "Setup Tests" -Tags "Setup" {
$actual_dacl_aces = $dacl_aces | ? { -not [string]::IsNullOrWhiteSpace($_) }
$expected_dacl_aces | % {
$actual_dacl_aces -contains $_ | Should be $true
$actual_dacl_aces -contains $_ | Should be $true
}
$actual_dacl_aces | % {
$expected_dacl_aces -contains $_ | Should be $true
}
<# ignore sacl for now
if($c.Count -gt 1) {
$c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
if($c.Count -gt 1) {
$c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
}#>
}
It "$tC.$tI - Validate security access to sshd service" {
It "$tC.$tI - Validate security access to sshd service" {
$a = @(sc.exe sdshow sshd)
$b = $a[-1] -split "[D|S]:"
@ -490,8 +490,8 @@ Describe "Setup Tests" -Tags "Setup" {
}
<# ignore sacl for now
if($c.Count -gt 1) {
$c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
if($c.Count -gt 1) {
$c[1] | Should Be "(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)"
}#>
}
}
@ -501,12 +501,12 @@ Describe "Setup Tests" -Tags "Setup" {
$firwallRuleName = "OpenSSH-Server-In-TCP"
$tI=1
}
AfterAll{$tC++}
AfterEach { $tI++ }
It "$tC.$tI - Validate Firewall settings" -skip:(!$windowsInBox) {
$rule = Get-NetFirewallRule -Name $firwallRuleName
$rule = Get-NetFirewallRule -Name $firwallRuleName
$rule.Group | Should BeLike "OpenSSH*"
$rule.Description | Should BeLike "*OpenSSH*"
$rule.DisplayName | Should BeLike "OpenSSH*"
@ -519,6 +519,57 @@ Describe "Setup Tests" -Tags "Setup" {
$fwportFilter.Protocol | Should Be 'TCP'
$fwportFilter.LocalPort | Should Be 22
$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
}
}
}

@ -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);

@ -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

@ -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 */