diff --git a/appveyor.yml b/appveyor.yml index 8c456ec..cccd93f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.0.24.0.{build} +version: 1.0.0.0.{build} image: Visual Studio 2015 branches: diff --git a/auth-passwd.c b/auth-passwd.c index f9dfd27..2c04017 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -41,9 +41,6 @@ #include #include -#ifdef WINDOWS -#include -#endif #include #include #include @@ -59,6 +56,11 @@ #include "auth-options.h" #include "authfd.h" +#ifdef WINDOWS +#include "logonuser.h" +#include "monitor_wrap.h" +#endif + extern Buffer loginmsg; extern ServerOptions options; @@ -228,10 +230,53 @@ sys_auth_passwd(Authctxt *authctxt, const char *password) } #elif defined(WINDOWS) +HANDLE password_auth_token = NULL; +HANDLE process_custom_lsa_auth(char*, const char*, char*); + +void +sys_auth_passwd_lsa(Authctxt *authctxt, const char *password) +{ + char *lsa_auth_pkg = NULL; + wchar_t *lsa_auth_pkg_w = NULL; + int domain_len = 0, lsa_auth_pkg_len = 0; + HKEY reg_key = 0; + REGSAM mask = STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_WOW64_64KEY; + + if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\OpenSSH", 0, mask, ®_key) == ERROR_SUCCESS) && + (RegQueryValueExW(reg_key, L"LSAAuthenticationPackage", 0, NULL, NULL, &lsa_auth_pkg_len) == ERROR_SUCCESS)) { + lsa_auth_pkg_w = (wchar_t *) malloc(lsa_auth_pkg_len); // lsa_auth_pkg_len includes the null terminating character. + if (!lsa_auth_pkg_w) + fatal("%s: out of memory", __func__); + + memset(lsa_auth_pkg_w, 0, lsa_auth_pkg_len); + if (RegQueryValueExW(reg_key, L"LSAAuthenticationPackage", 0, NULL, (LPBYTE)lsa_auth_pkg_w, &lsa_auth_pkg_len) == ERROR_SUCCESS) { + lsa_auth_pkg = utf16_to_utf8(lsa_auth_pkg_w); + if (!lsa_auth_pkg) + fatal("utf16_to_utf8 failed to convert lsa_auth_pkg_w:%ls", lsa_auth_pkg_w); + + debug("Authenticating using LSA Auth Package:%ls", lsa_auth_pkg_w); + password_auth_token = process_custom_lsa_auth(authctxt->pw->pw_name, password, lsa_auth_pkg); + } + } + +done: + if (lsa_auth_pkg_w) + free(lsa_auth_pkg_w); + + if (lsa_auth_pkg) + free(lsa_auth_pkg); + + if (reg_key) + RegCloseKey(reg_key); +} + /* -* Authenticate on Windows - Call LogonUser and retrieve user token +* Authenticate on Windows +* - Call LogonUser and retrieve user token +* - If LogonUser fails, then try the LSA (Local Security Authority) authentication. */ -int sys_auth_passwd(Authctxt *authctxt, const char *password) +int +sys_auth_passwd(Authctxt *authctxt, const char *password) { wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *pwd_utf16 = NULL, *tmp; HANDLE token = NULL; @@ -249,25 +294,33 @@ int sys_auth_passwd(Authctxt *authctxt, const char *password) } if (LogonUserExExWHelper(user_utf16, udom_utf16, pwd_utf16, LOGON32_LOGON_NETWORK_CLEARTEXT, - LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL) == FALSE) { - if (GetLastError() == ERROR_PASSWORD_MUST_CHANGE) - /* - * TODO - need to add support to force password change - * by sending back SSH_MSG_USERAUTH_PASSWD_CHANGEREQ - */ + LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL) == TRUE) + password_auth_token = token; + else { + if (GetLastError() == ERROR_PASSWORD_MUST_CHANGE) + /* + * TODO - need to add support to force password change + * by sending back SSH_MSG_USERAUTH_PASSWD_CHANGEREQ + */ error("password for user %s has expired", authctxt->pw->pw_name); - else - debug("failed to logon user: %ls domain: %ls error:%d", user_utf16, udom_utf16, GetLastError()); - goto done; - } + else { + debug("Windows authentication failed for user: %ls domain: %ls error:%d", user_utf16, udom_utf16, GetLastError()); - authctxt->auth_token = (void*)(INT_PTR)token; - r = 1; + /* If LSA authentication package is configured then it will return the auth_token */ + sys_auth_passwd_lsa(authctxt, password); + } + } + done: + if (password_auth_token) + r = 1; + if (user_utf16) free(user_utf16); + if (pwd_utf16) SecureZeroMemory(pwd_utf16, sizeof(wchar_t) * wcslen(pwd_utf16)); + return r; } #endif /* WINDOWS */ diff --git a/auth.h b/auth.h index 9aedbc1..29835ae 100644 --- a/auth.h +++ b/auth.h @@ -93,9 +93,6 @@ struct Authctxt { /* Information exposed to session */ struct sshbuf *session_info; /* Auth info for environment */ -#ifdef WINDOWS - void *auth_token; -#endif }; /* diff --git a/auth2-pubkey.c b/auth2-pubkey.c index d8b98f4..3745235 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -199,13 +199,8 @@ userauth_pubkey(struct ssh *ssh) authenticated = 0; if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && -#ifdef WINDOWS - (authctxt->auth_token = mm_auth_pubkey(authctxt->pw->pw_name, - key, sig, slen, b)) != NULL) { -#else PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), ssh->compat)) == 0) { -#endif authenticated = 1; } sshbuf_free(b); diff --git a/authfd.c b/authfd.c index c025a29..a460fa3 100644 --- a/authfd.c +++ b/authfd.c @@ -120,12 +120,7 @@ ssh_get_authentication_socket(int *fdp) } /* Communicate with agent: send request and read reply */ -#ifdef WINDOWS -/* for Windows we need to access this function from other places to talk to agent*/ -int -#else /* !WINDOWS */ static int -#endif /* !WINDOWS */ ssh_request_reply(int sock, struct sshbuf *request, struct sshbuf *reply) { int r; diff --git a/contrib/win32/openssh/FixHostFilePermissions.ps1 b/contrib/win32/openssh/FixHostFilePermissions.ps1 index fbff664..c990a0d 100644 --- a/contrib/win32/openssh/FixHostFilePermissions.ps1 +++ b/contrib/win32/openssh/FixHostFilePermissions.ps1 @@ -5,14 +5,14 @@ If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $ Import-Module $PSScriptRoot\OpenSSHUtils -Force #check sshd config file -$sshdConfigPath = join-path $PSScriptRoot "sshd_config" +$sshdConfigPath = join-path $env:ProgramData\ssh "sshd_config" if(Test-Path $sshdConfigPath -PathType Leaf) { Repair-SshdConfigPermission -FilePath $sshdConfigPath @psBoundParameters } else { - Write-host "$FilePath does not exist" -ForegroundColor Yellow + Write-host "$sshdConfigPath does not exist" -ForegroundColor Yellow } #check host keys @@ -36,7 +36,7 @@ If you choose not to register the keys with ssh-agent, please grant sshd read ac Write-Host " " }#> -Get-ChildItem $PSScriptRoot\ssh_host_*_key -ErrorAction SilentlyContinue | % { +Get-ChildItem $env:ProgramData\ssh\ssh_host_*_key -ErrorAction SilentlyContinue | % { Repair-SshdHostKeyPermission -FilePath $_.FullName @psBoundParameters } diff --git a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 b/contrib/win32/openssh/OpenSSHBuildHelper.psm1 index 8564b90..029ef62 100644 --- a/contrib/win32/openssh/OpenSSHBuildHelper.psm1 +++ b/contrib/win32/openssh/OpenSSHBuildHelper.psm1 @@ -344,8 +344,8 @@ function Start-OpenSSHPackage $buildDir = Join-Path $repositoryRoot ("bin\" + $folderName + "\" + $Configuration) $payload = "sshd.exe", "ssh.exe", "ssh-agent.exe", "ssh-add.exe", "sftp.exe" $payload += "sftp-server.exe", "scp.exe", "ssh-shellhost.exe", "ssh-keygen.exe", "ssh-keyscan.exe" - $payload += "sshd_config", "install-sshd.ps1", "uninstall-sshd.ps1" - $payload +="FixHostFilePermissions.ps1", "FixUserFilePermissions.ps1", "OpenSSHUtils.psm1", "OpenSSHUtils.psd1", "ssh-add-hostkey.ps1" + $payload += "sshd_config_default", "install-sshd.ps1", "uninstall-sshd.ps1" + $payload +="FixHostFilePermissions.ps1", "FixUserFilePermissions.ps1", "OpenSSHUtils.psm1", "OpenSSHUtils.psd1" $packageName = "OpenSSH-Win64" if ($NativeHostArch -ieq 'x86') { diff --git a/contrib/win32/openssh/OpenSSHTestHelper.psm1 b/contrib/win32/openssh/OpenSSHTestHelper.psm1 index dcf74d1..1d33594 100644 --- a/contrib/win32/openssh/OpenSSHTestHelper.psm1 +++ b/contrib/win32/openssh/OpenSSHTestHelper.psm1 @@ -13,6 +13,7 @@ $PubKeyUser = "sshtest_pubkeyuser" $PasswdUser = "sshtest_passwduser" $OpenSSHTestAccountsPassword = "P@ssw0rd_1" $OpenSSHTestAccounts = $Script:SSOUser, $Script:PubKeyUser, $Script:PasswdUser +$OpenSSHConfigPath = Join-Path $env:ProgramData "ssh" $Script:TestDataPath = "$env:SystemDrive\OpenSSHTests" $Script:E2ETestResultsFile = Join-Path $TestDataPath $E2ETestResultsFileName @@ -106,7 +107,7 @@ function Set-OpenSSHTestEnvironment } } else - { + { if (-not (Test-Path (Join-Path $OpenSSHBinPath ssh.exe) -PathType Leaf)) { Throw "Cannot find OpenSSH binaries under $OpenSSHBinPath. Please specify -OpenSSHBinPath to the OpenSSH installed location" @@ -162,25 +163,19 @@ WARNING: Following changes will be made to OpenSSH configuration } #Backup existing OpenSSH configuration - $backupConfigPath = Join-Path $script:OpenSSHBinPath sshd_config.ori + $backupConfigPath = Join-Path $OpenSSHConfigPath sshd_config.ori if (-not (Test-Path $backupConfigPath -PathType Leaf)) { - Copy-Item (Join-Path $script:OpenSSHBinPath sshd_config) $backupConfigPath -Force + Copy-Item (Join-Path $OpenSSHConfigPath sshd_config) $backupConfigPath -Force } - $targetsshdConfig = Join-Path $script:OpenSSHBinPath sshd_config + $targetsshdConfig = Join-Path $OpenSSHConfigPath sshd_config # copy new sshd_config - if($Script:WindowsInBox -and (Test-Path $targetsshdConfig)) - { - $currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME)) - Add-PermissionToFileACL -FilePath $targetsshdConfig -User $currentUser -Perm "Read,Write" - } - Copy-Item (Join-Path $Script:E2ETestDirectory sshd_config) $targetsshdConfig -Force Start-Service ssh-agent #copy sshtest keys - Copy-Item "$($Script:E2ETestDirectory)\sshtest*hostkey*" $script:OpenSSHBinPath -Force - Get-ChildItem "$($script:OpenSSHBinPath)\sshtest*hostkey*"| % { + Copy-Item "$($Script:E2ETestDirectory)\sshtest*hostkey*" $OpenSSHConfigPath -Force + Get-ChildItem "$($OpenSSHConfigPath)\sshtest*hostkey*"| % { #workaround for the cariggage new line added by git before copy them $filePath = "$($_.FullName)" $con = (Get-Content $filePath | Out-String).Replace("`r`n","`n") @@ -188,16 +183,11 @@ WARNING: Following changes will be made to OpenSSH configuration if (-not ($_.Name.EndsWith(".pub"))) { Repair-SshdHostKeyPermission -FilePath $_.FullName -confirm:$false - if($psversiontable.BuildVersion.Major -gt 6) - { - #register private key with agent - ssh-add-hostkey.ps1 $_.FullName - } } } - #copy ca pubkey to SSHD bin path - Copy-Item "$($Script:E2ETestDirectory)\sshtest_ca_userkeys.pub" $script:OpenSSHBinPath -Force + #copy ca pubkey to ssh config path + Copy-Item "$($Script:E2ETestDirectory)\sshtest_ca_userkeys.pub" $OpenSSHConfigPath -Force #copy ca private key to test dir $ca_priv_key = (Join-Path $Global:OpenSSHTestInfo["TestDataPath"] sshtest_ca_userkeys) @@ -462,11 +452,6 @@ function Clear-OpenSSHTestEnvironment Throw "Cannot find OpenSSH binaries under $script:OpenSSHBinPath. " } - #unregister test host keys from agent - Get-ChildItem "$sshBinPath\sshtest*hostkey*.pub"| % { - ssh-add-hostkey.ps1 -Delete_key $_.FullName - } - if($Global:OpenSSHTestInfo["EnableAppVerifier"] -and (Test-path $env:windir\System32\appverif.exe)) { # clear all applications in application verifier @@ -479,14 +464,14 @@ function Clear-OpenSSHTestEnvironment Remove-ItemProperty "HKLM:Software\Microsoft\Windows NT\CurrentVersion\AeDebug" -Name Auto -ErrorAction SilentlyContinue -Force | Out-Null } - Remove-Item "$sshBinPath\sshtest*hostkey*" -Force -ErrorAction SilentlyContinue - Remove-Item "$sshBinPath\sshtest*ca_userkeys*" -Force -ErrorAction SilentlyContinue + Remove-Item "$OpenSSHConfigPath\sshtest*hostkey*" -Force -ErrorAction SilentlyContinue + Remove-Item "$OpenSSHConfigPath\sshtest*ca_userkeys*" -Force -ErrorAction SilentlyContinue #Restore sshd_config - $backupConfigPath = Join-Path $sshBinPath sshd_config.ori + $backupConfigPath = Join-Path $OpenSSHConfigPath sshd_config.ori if (Test-Path $backupConfigPath -PathType Leaf) { - Copy-Item $backupConfigPath (Join-Path $sshBinPath sshd_config) -Force -ErrorAction SilentlyContinue - Remove-Item (Join-Path $sshBinPath sshd_config.ori) -Force -ErrorAction SilentlyContinue + Copy-Item $backupConfigPath (Join-Path $OpenSSHConfigPath sshd_config) -Force -ErrorAction SilentlyContinue + Remove-Item (Join-Path $OpenSSHConfigPath sshd_config.ori) -Force -ErrorAction SilentlyContinue Restart-Service sshd } diff --git a/contrib/win32/openssh/OpenSSHUtils.psm1 b/contrib/win32/openssh/OpenSSHUtils.psm1 index 0a7ae5f..02a6fa5 100644 --- a/contrib/win32/openssh/OpenSSHUtils.psm1 +++ b/contrib/win32/openssh/OpenSSHUtils.psm1 @@ -41,8 +41,6 @@ $adminsSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnown # get the everyone $everyoneSid = Get-UserSID -WellKnownSidType ([System.Security.Principal.WellKnownSidType]::WorldSid) -$sshdSid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-80-3847866527-469524349-687026318-516638107-1125189541") - $currentUserSid = Get-UserSID -User "$($env:USERDOMAIN)\$($env:USERNAME)" #Taken from P/Invoke.NET with minor adjustments. @@ -112,7 +110,7 @@ function Repair-SshdConfigPermission [ValidateNotNullOrEmpty()] [string]$FilePath) - Repair-FilePermission -Owners $systemSid,$adminsSid -FullAccessNeeded $systemSid -ReadAccessNeeded $sshdSid @psBoundParameters + Repair-FilePermission -Owners $systemSid,$adminsSid -FullAccessNeeded $systemSid @psBoundParameters } <# @@ -134,10 +132,10 @@ function Repair-SshdHostKeyPermission $PSBoundParameters["FilePath"] = $PSBoundParameters["FilePath"].Replace(".pub", "") } - Repair-FilePermission -Owners $systemSid,$adminsSid -ReadAccessNeeded $sshdSid @psBoundParameters + Repair-FilePermission -Owners $systemSid,$adminsSid @psBoundParameters $PSBoundParameters["FilePath"] += ".pub" - Repair-FilePermission -Owners $systemSid,$adminsSid -ReadAccessOK $everyoneSid -ReadAccessNeeded $sshdSid @psBoundParameters + Repair-FilePermission -Owners $systemSid,$adminsSid -ReadAccessOK $everyoneSid @psBoundParameters } <# @@ -175,7 +173,7 @@ function Repair-AuthorizedKeyPermission if($profileItem) { $userSid = $profileItem.PSChildName - Repair-FilePermission -Owners $userSid,$adminsSid,$systemSid -AnyAccessOK $userSid -FullAccessNeeded $systemSid -ReadAccessNeeded $sshdSid @psBoundParameters + Repair-FilePermission -Owners $userSid,$adminsSid,$systemSid -AnyAccessOK $userSid -FullAccessNeeded $systemSid @psBoundParameters } else diff --git a/contrib/win32/openssh/config.h.vs b/contrib/win32/openssh/config.h.vs index 3e55284..a5e8184 100644 --- a/contrib/win32/openssh/config.h.vs +++ b/contrib/win32/openssh/config.h.vs @@ -102,7 +102,7 @@ /* Define if your platform needs to skip post auth file descriptor passing */ -#define DISABLE_FD_PASSING 1 +/* #undef DISABLE_FD_PASSING */ /* Define if you don't want to use lastlog */ /* #undef DISABLE_LASTLOG */ @@ -1691,7 +1691,9 @@ #define HAVE_MBLEN 1 -#define SSHDIR "." +#define _PATH_PRIVSEP_CHROOT_DIR "." +#define SSHDIR "__PROGRAMDATA__\\ssh" #define _PATH_SFTP_SERVER "sftp-server.exe" #define _PATH_SSH_PROGRAM "ssh.exe" #define _PATH_LS "dir" +#define FORK_NOT_SUPPORTED 1 \ No newline at end of file diff --git a/contrib/win32/openssh/config.vcxproj b/contrib/win32/openssh/config.vcxproj index 40226c4..f946442 100644 --- a/contrib/win32/openssh/config.vcxproj +++ b/contrib/win32/openssh/config.vcxproj @@ -196,8 +196,7 @@ copy /Y "$(SolutionDir)uninstall-ssh*ps1" "$(OutDir)" copy /Y "$(SolutionDir)OpenSSHUtils.ps*1" "$(OutDir)" copy /Y "$(SolutionDir)Fix*FilePermissions.ps1" "$(OutDir)" -copy /Y "$(SolutionDir)ssh-add-hostkey.ps1" "$(OutDir)" -If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir)") +copy /Y "$(SolutionDir)sshd_config" "$(OutDir)sshd_config_default" Copy install-sshd.ps1, uninstall-sshd.ps1, OpenSSHUtils.psm1, OpenSSHUtils.psd1, FixHostFilePermissions.ps1, FixUserFilePermissions.ps1, ssh-add-hostkey.ps1, and sshd_config (if not already present) to build directory @@ -229,8 +228,7 @@ If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir copy /Y "$(SolutionDir)uninstall-ssh*ps1" "$(OutDir)" copy /Y "$(SolutionDir)OpenSSHUtils.ps*1" "$(OutDir)" copy /Y "$(SolutionDir)Fix*FilePermissions.ps1" "$(OutDir)" -copy /Y "$(SolutionDir)ssh-add-hostkey.ps1" "$(OutDir)" -If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir)") +copy /Y "$(SolutionDir)sshd_config" "$(OutDir)sshd_config_default" Copy install-sshd.ps1, uninstall-sshd.ps1, OpenSSHUtils.psm1, OpenSSHUtils.psd1, FixHostFilePermissions.ps1, FixUserFilePermissions.ps1, ssh-add-hostkey.ps1, and sshd_config (if not already present) to build directory @@ -262,8 +260,7 @@ If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir copy /Y "$(SolutionDir)uninstall-ssh*ps1" "$(OutDir)" copy /Y "$(SolutionDir)OpenSSHUtils.ps*1" "$(OutDir)" copy /Y "$(SolutionDir)Fix*FilePermissions.ps1" "$(OutDir)" -copy /Y "$(SolutionDir)ssh-add-hostkey.ps1" "$(OutDir)" -If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir)") +copy /Y "$(SolutionDir)sshd_config" "$(OutDir)sshd_config_default" Copy install-sshd.ps1, uninstall-sshd.ps1, OpenSSHUtils.psm1, OpenSSHUtils.psd1, FixHostFilePermissions.ps1, FixUserFilePermissions.ps1, ssh-add-hostkey.ps1, and sshd_config (if not already present) to build directory @@ -295,8 +292,7 @@ If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir copy /Y "$(SolutionDir)uninstall-ssh*ps1" "$(OutDir)" copy /Y "$(SolutionDir)OpenSSHUtils.ps*1" "$(OutDir)" copy /Y "$(SolutionDir)Fix*FilePermissions.ps1" "$(OutDir)" -copy /Y "$(SolutionDir)ssh-add-hostkey.ps1" "$(OutDir)" -If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir)") +copy /Y "$(SolutionDir)sshd_config" "$(OutDir)sshd_config_default" Copy install-sshd.ps1, uninstall-sshd.ps1, OpenSSHUtils.psm1, OpenSSHUtils.psd1, FixHostFilePermissions.ps1, FixUserFilePermissions.ps1, ssh-add-hostkey.ps1, and sshd_config (if not already present) to build directory @@ -332,8 +328,7 @@ If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir copy /Y "$(SolutionDir)uninstall-ssh*ps1" "$(OutDir)" copy /Y "$(SolutionDir)OpenSSHUtils.ps*1" "$(OutDir)" copy /Y "$(SolutionDir)Fix*FilePermissions.ps1" "$(OutDir)" -copy /Y "$(SolutionDir)ssh-add-hostkey.ps1" "$(OutDir)" -If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir)") +copy /Y "$(SolutionDir)sshd_config" "$(OutDir)sshd_config_default" Copy install-sshd.ps1, uninstall-sshd.ps1, OpenSSHUtils.psm1, OpenSSHUtils.psd1, FixHostFilePermissions.ps1, FixUserFilePermissions.ps1, ssh-add-hostkey.ps1, and sshd_config (if not already present) to build directory @@ -369,8 +364,7 @@ If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir copy /Y "$(SolutionDir)uninstall-ssh*ps1" "$(OutDir)" copy /Y "$(SolutionDir)OpenSSHUtils.ps*1" "$(OutDir)" copy /Y "$(SolutionDir)Fix*FilePermissions.ps1" "$(OutDir)" -copy /Y "$(SolutionDir)ssh-add-hostkey.ps1" "$(OutDir)" -If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir)") +copy /Y "$(SolutionDir)sshd_config" "$(OutDir)sshd_config_default" Copy install-sshd.ps1, uninstall-sshd.ps1, OpenSSHUtils.psm1, OpenSSHUtils.psd1, FixHostFilePermissions.ps1, FixUserFilePermissions.ps1, ssh-add-hostkey.ps1, and sshd_config (if not already present) to build directory @@ -406,8 +400,7 @@ If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir copy /Y "$(SolutionDir)uninstall-ssh*ps1" "$(OutDir)" copy /Y "$(SolutionDir)OpenSSHUtils.ps*1" "$(OutDir)" copy /Y "$(SolutionDir)Fix*FilePermissions.ps1" "$(OutDir)" -copy /Y "$(SolutionDir)ssh-add-hostkey.ps1" "$(OutDir)" -If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir)") +copy /Y "$(SolutionDir)sshd_config" "$(OutDir)sshd_config_default" Copy install-sshd.ps1, uninstall-sshd.ps1, OpenSSHUtils.psm1, OpenSSHUtils.psd1, FixHostFilePermissions.ps1, FixUserFilePermissions.ps1, ssh-add-hostkey.ps1, and sshd_config (if not already present) to build directory @@ -443,8 +436,7 @@ If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir copy /Y "$(SolutionDir)uninstall-ssh*ps1" "$(OutDir)" copy /Y "$(SolutionDir)OpenSSHUtils.ps*1" "$(OutDir)" copy /Y "$(SolutionDir)Fix*FilePermissions.ps1" "$(OutDir)" -copy /Y "$(SolutionDir)ssh-add-hostkey.ps1" "$(OutDir)" -If NOT exist "$(OutDir)\sshd_config" (copy "$(SolutionDir)sshd_config" "$(OutDir)") +copy /Y "$(SolutionDir)sshd_config" "$(OutDir)sshd_config_default" Copy install-sshd.ps1, uninstall-sshd.ps1, OpenSSHUtils.psm1, OpenSSHUtils.psd1, FixHostFilePermissions.ps1, FixUserFilePermissions.ps1, ssh-add-hostkey.ps1, and sshd_config (if not already present) to build directory diff --git a/contrib/win32/openssh/install-sshd.ps1 b/contrib/win32/openssh/install-sshd.ps1 index e781a77..dc7a16b 100644 --- a/contrib/win32/openssh/install-sshd.ps1 +++ b/contrib/win32/openssh/install-sshd.ps1 @@ -8,245 +8,8 @@ $scriptdir = Split-Path $scriptpath $sshdpath = Join-Path $scriptdir "sshd.exe" $sshagentpath = Join-Path $scriptdir "ssh-agent.exe" -$logsdir = Join-Path $scriptdir "logs" - -$sshdAccount = "NT SERVICE\SSHD" -$sshdSid = "S-1-5-80-3847866527-469524349-687026318-516638107-1125189541" - -#Idea borrowed from https://gallery.technet.microsoft.com/scriptcenter/Grant-Revoke-Query-user-26e259b0 -$definition = @' -using System; - -namespace MyLsaWrapper -{ - using System.Runtime.InteropServices; - using System.Security; - using System.ComponentModel; - using System.Security.Principal; - - using LSA_HANDLE = IntPtr; - - [StructLayout(LayoutKind.Sequential)] - struct LSA_OBJECT_ATTRIBUTES - { - internal int Length; - internal IntPtr RootDirectory; - internal IntPtr ObjectName; - internal int Attributes; - internal IntPtr SecurityDescriptor; - internal IntPtr SecurityQualityOfService; - } - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - struct LSA_UNICODE_STRING - { - internal ushort Length; - internal ushort MaximumLength; - [MarshalAs(UnmanagedType.LPWStr)] - internal string Buffer; - } - sealed class Win32Sec - { - [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint LsaOpenPolicy( - LSA_UNICODE_STRING[] SystemName, - ref LSA_OBJECT_ATTRIBUTES ObjectAttributes, - int AccessMask, - out IntPtr PolicyHandle - ); - - [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint LsaAddAccountRights( - LSA_HANDLE PolicyHandle, - IntPtr pSID, - LSA_UNICODE_STRING[] UserRights, - int CountOfRights - ); - - [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint LsaRemoveAccountRights( - LSA_HANDLE PolicyHandle, - IntPtr pSID, - bool AllRights, - LSA_UNICODE_STRING[] UserRights, - int CountOfRights - ); - - [DllImport("advapi32")] - internal static extern int LsaNtStatusToWinError(int NTSTATUS); - - [DllImport("advapi32")] - internal static extern int LsaClose(IntPtr PolicyHandle); - } - - internal sealed class Sid : IDisposable - { - public IntPtr pSid = IntPtr.Zero; - public System.Security.Principal.SecurityIdentifier sid = null; - - public Sid(string account) - { - try { sid = new SecurityIdentifier(account); } - catch { sid = (SecurityIdentifier)(new NTAccount(account)).Translate(typeof(SecurityIdentifier)); } - Byte[] buffer = new Byte[sid.BinaryLength]; - sid.GetBinaryForm(buffer, 0); - - pSid = Marshal.AllocHGlobal(sid.BinaryLength); - Marshal.Copy(buffer, 0, pSid, sid.BinaryLength); - } - - public void Dispose() - { - if (pSid != IntPtr.Zero) - { - Marshal.FreeHGlobal(pSid); - pSid = IntPtr.Zero; - } - GC.SuppressFinalize(this); - } - ~Sid() { Dispose(); } - } - - public sealed class LsaWrapper : IDisposable - { - enum Access : int - { - POLICY_READ = 0x20006, - POLICY_ALL_ACCESS = 0x00F0FFF, - POLICY_EXECUTE = 0X20801, - POLICY_WRITE = 0X207F8 - } - const uint STATUS_ACCESS_DENIED = 0xc0000022; - const uint STATUS_INSUFFICIENT_RESOURCES = 0xc000009a; - const uint STATUS_NO_MEMORY = 0xc0000017; - const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034; - const uint STATUS_NO_MORE_ENTRIES = 0x8000001a; - - IntPtr lsaHandle; - - public LsaWrapper() : this(null) { } // local system if systemName is null - public LsaWrapper(string systemName) - { - LSA_OBJECT_ATTRIBUTES lsaAttr; - lsaAttr.RootDirectory = IntPtr.Zero; - lsaAttr.ObjectName = IntPtr.Zero; - lsaAttr.Attributes = 0; - lsaAttr.SecurityDescriptor = IntPtr.Zero; - lsaAttr.SecurityQualityOfService = IntPtr.Zero; - lsaAttr.Length = Marshal.SizeOf(typeof(LSA_OBJECT_ATTRIBUTES)); - lsaHandle = IntPtr.Zero; - LSA_UNICODE_STRING[] system = null; - if (systemName != null) - { - system = new LSA_UNICODE_STRING[1]; - system[0] = InitLsaString(systemName); - } - - uint ret = Win32Sec.LsaOpenPolicy(system, ref lsaAttr, (int)Access.POLICY_ALL_ACCESS, out lsaHandle); - if (ret == 0) return; - if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); - if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); - throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); - } - - public void AddPrivilege(string account, string privilege) - { - uint ret = 0; - using (Sid sid = new Sid(account)) - { - LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; - privileges[0] = InitLsaString(privilege); - ret = Win32Sec.LsaAddAccountRights(lsaHandle, sid.pSid, privileges, 1); - } - if (ret == 0) return; - if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); - if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); - throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); - } - - public void RemovePrivilege(string account, string privilege) - { - uint ret = 0; - using (Sid sid = new Sid(account)) - { - LSA_UNICODE_STRING[] privileges = new LSA_UNICODE_STRING[1]; - privileges[0] = InitLsaString(privilege); - ret = Win32Sec.LsaRemoveAccountRights(lsaHandle, sid.pSid, false, privileges, 1); - } - if (ret == 0) return; - if (ret == STATUS_ACCESS_DENIED) throw new UnauthorizedAccessException(); - if ((ret == STATUS_INSUFFICIENT_RESOURCES) || (ret == STATUS_NO_MEMORY)) throw new OutOfMemoryException(); - throw new Win32Exception(Win32Sec.LsaNtStatusToWinError((int)ret)); - } - - public void Dispose() - { - if (lsaHandle != IntPtr.Zero) - { - Win32Sec.LsaClose(lsaHandle); - lsaHandle = IntPtr.Zero; - } - GC.SuppressFinalize(this); - } - ~LsaWrapper() { Dispose(); } - - // helper functions: - static LSA_UNICODE_STRING InitLsaString(string s) - { - // Unicode strings max. 32KB - if (s.Length > 0x7ffe) throw new ArgumentException("String too long"); - LSA_UNICODE_STRING lus = new LSA_UNICODE_STRING(); - lus.Buffer = s; - lus.Length = (ushort)(s.Length * sizeof(char)); - lus.MaximumLength = (ushort)(lus.Length + sizeof(char)); - return lus; - } - } - public class LsaWrapperCaller - { - public static void AddPrivilege(string account, string privilege) - { - using (LsaWrapper lsaWrapper = new LsaWrapper()) - { - lsaWrapper.AddPrivilege(account, privilege); - } - } - public static void RemovePrivilege(string account, string privilege) - { - using (LsaWrapper lsaWrapper = new LsaWrapper()) - { - lsaWrapper.RemovePrivilege(account, privilege); - } - } - } -} -'@ - -$references = @("System.Security.Principal.Windows", "Microsoft.Win32.Primitives") -try { - $null = [MyLsaWrapper.LsaWrapperCaller] -} -catch { - try { - $types = Add-Type $definition -ref $references -WarningAction SilentlyContinue -ErrorAction SilentlyContinue - } - catch { - $types = Add-Type $definition -WarningAction SilentlyContinue -ErrorAction SilentlyContinue - } -} - -function Add-Privilege -{ - param( - [ValidateNotNullOrEmpty()] - [string] $Account, - - [ValidateSet("SeAssignPrimaryTokenPrivilege", "SeServiceLogonRight")] - [string] $Privilege - ) - - [MyLsaWrapper.LsaWrapperCaller]::AddPrivilege($Account, $Privilege) -} - +$sshdir = Join-Path $env:ProgramData "\ssh" +$logsdir = Join-Path $sshdir "logs" if (-not (Test-Path $sshdpath)) { throw "sshd.exe is not present in script path" @@ -267,12 +30,21 @@ if (Get-Service ssh-agent -ErrorAction SilentlyContinue) New-Service -Name ssh-agent -BinaryPathName `"$sshagentpath`" -Description "SSH Agent" -StartupType Manual | Out-Null cmd.exe /c 'sc.exe sdset ssh-agent D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RP;;;AU)' -New-Service -Name sshd -BinaryPathName `"$sshdpath`" -Description "SSH Daemon" -StartupType Manual -DependsOn ssh-agent | Out-Null -sc.exe config sshd obj= $sshdAccount -sc.exe privs sshd SeAssignPrimaryTokenPrivilege +New-Service -Name sshd -BinaryPathName `"$sshdpath`" -Description "SSH Daemon" -StartupType Manual | Out-Null -Add-Privilege -Account $sshdSid -Privilege SeAssignPrimaryTokenPrivilege -Add-Privilege -Account $sshdSid -Privilege SeServiceLogonRight +#create the ssh config folder and set its permissions +if(-not (test-path $sshdir -PathType Container)) +{ + $null = New-Item $sshdir -ItemType Directory -Force -ErrorAction Stop +} +$acl = Get-Acl -Path $sshdir +# following SDDL implies +# - owner - built in Administrators +# - disabled inheritance +# - Full access to System +# - Full access to built in Administrators +$acl.SetSecurityDescriptorSddlForm("O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)") +Set-Acl -Path $sshdir -AclObject $acl # create logs folder and set its permissions if(-not (test-path $logsdir -PathType Container)) @@ -288,15 +60,12 @@ $acl = Get-Acl -Path $logsdir $acl.SetSecurityDescriptorSddlForm("O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)") Set-Acl -Path $logsdir -AclObject $acl -$agentlog = Join-Path $logsdir "ssh-agent.log" -if(-not (test-path $agentlog)){ $null | Set-Content $agentlog } -Set-Acl -Path $agentlog -AclObject $acl - -$sshdlog = Join-Path $logsdir "sshd.log" -if(-not (test-path $sshdlog)){ $null | Set-Content $sshdlog } -$rights = [System.Security.AccessControl.FileSystemRights]"Read, Write" -$accessRule = New-Object System.Security.AccessControl.FileSystemAccessRule($sshdAccount, $rights, "None", "None", "Allow") -$acl.SetAccessRule($accessRule) -Set-Acl -Path $sshdlog -AclObject $acl +#copy sshd_config_default to $sshdir\sshd_config +$sshdconfigpath = Join-Path $sshdir "sshd_config" +$sshddefaultconfigpath = Join-Path $scriptdir "sshd_config_default" +if(-not (test-path $sshdconfigpath -PathType Leaf)) +{ + $null = Copy-Item $sshddefaultconfigpath -Destination $sshdconfigpath -ErrorAction Stop +} Write-Host -ForegroundColor Green "sshd and ssh-agent services successfully installed" diff --git a/contrib/win32/openssh/paths.targets b/contrib/win32/openssh/paths.targets index 5804954..94f5221 100644 --- a/contrib/win32/openssh/paths.targets +++ b/contrib/win32/openssh/paths.targets @@ -12,7 +12,7 @@ true libcrypto.lib; 8.1 - bcrypt.lib;Userenv.lib;Crypt32.lib;Ws2_32.lib;Secur32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Netapi32.lib + bcrypt.lib;Userenv.lib;Crypt32.lib;Ws2_32.lib;Secur32.lib;Shlwapi.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Netapi32.lib;Rpcrt4.lib false \ No newline at end of file diff --git a/contrib/win32/openssh/ssh-agent.vcxproj b/contrib/win32/openssh/ssh-agent.vcxproj index 2406d5a..cee3be8 100644 --- a/contrib/win32/openssh/ssh-agent.vcxproj +++ b/contrib/win32/openssh/ssh-agent.vcxproj @@ -382,25 +382,15 @@ - - - - - - - - - - diff --git a/contrib/win32/openssh/sshd.vcxproj b/contrib/win32/openssh/sshd.vcxproj index 570025a..5b724ec 100644 --- a/contrib/win32/openssh/sshd.vcxproj +++ b/contrib/win32/openssh/sshd.vcxproj @@ -446,8 +446,9 @@ - + + diff --git a/contrib/win32/openssh/sshd.vcxproj.filters b/contrib/win32/openssh/sshd.vcxproj.filters index 14bfc82..285a435 100644 --- a/contrib/win32/openssh/sshd.vcxproj.filters +++ b/contrib/win32/openssh/sshd.vcxproj.filters @@ -150,10 +150,13 @@ Source Files - + Source Files - + + Source Files + + Source Files diff --git a/contrib/win32/openssh/sshd_config b/contrib/win32/openssh/sshd_config index 7a25ba8..ccf5113 100644 --- a/contrib/win32/openssh/sshd_config +++ b/contrib/win32/openssh/sshd_config @@ -1,10 +1,6 @@ -# $OpenBSD: sshd_config,v 1.84 2011/05/23 03:30:07 djm Exp $ - # This is the sshd server system-wide configuration file. See # sshd_config(5) for more information. -# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin - # The strategy used for options in the default sshd_config shipped with # OpenSSH is to specify options with their default value where # possible, but leave them commented. Uncommented options override the @@ -15,46 +11,38 @@ #ListenAddress 0.0.0.0 #ListenAddress :: -# The default requires explicit activation of protocol 1 -#Protocol 2 - -# HostKey for protocol version 1 -#HostKey /etc/ssh/ssh_host_key -# HostKeys for protocol version 2 #HostKey /etc/ssh/ssh_host_rsa_key #HostKey /etc/ssh/ssh_host_dsa_key #HostKey /etc/ssh/ssh_host_ecdsa_key +#HostKey /etc/ssh/ssh_host_ed25519_key -# Lifetime and size of ephemeral version 1 server key -#KeyRegenerationInterval 1h -#ServerKeyBits 1024 +# Ciphers and keying +#RekeyLimit default none # Logging -# obsoletes QuietMode and FascistLogging #SyslogFacility AUTH #LogLevel INFO # Authentication: #LoginGraceTime 2m -#PermitRootLogin yes +#PermitRootLogin prohibit-password #StrictModes yes #MaxAuthTries 6 #MaxSessions 10 -#RSAAuthentication yes #PubkeyAuthentication yes # The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 # but this is overridden so installations will only check .ssh/authorized_keys AuthorizedKeysFile .ssh/authorized_keys -# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts -#RhostsRSAAuthentication no -# similar for protocol version 2 +#AuthorizedPrincipalsFile none + +# For this to work you will also need host keys in %windir%/programdata/openssh/config/ssh_known_hosts #HostbasedAuthentication no # Change to yes if you don't trust ~/.ssh/known_hosts for -# RhostsRSAAuthentication and HostbasedAuthentication +# HostbasedAuthentication #IgnoreUserKnownHosts no # Don't read the user's ~/.rhosts and ~/.shosts files #IgnoreRhosts yes @@ -63,50 +51,23 @@ AuthorizedKeysFile .ssh/authorized_keys #PasswordAuthentication yes #PermitEmptyPasswords no -# Change to no to disable s/key passwords -#ChallengeResponseAuthentication yes - -# Kerberos options -#KerberosAuthentication no -#KerberosOrLocalPasswd yes -#KerberosTicketCleanup yes -#KerberosGetAFSToken no - -# GSSAPI options -#GSSAPIAuthentication no -#GSSAPICleanupCredentials yes - -# Set this to 'yes' to enable PAM authentication, account processing, -# and session processing. If this is enabled, PAM authentication will -# be allowed through the ChallengeResponseAuthentication and -# PasswordAuthentication. Depending on your PAM configuration, -# PAM authentication via ChallengeResponseAuthentication may bypass -# the setting of "PermitRootLogin without-password". -# If you just want the PAM account and session checks to run without -# PAM authentication, then enable this but set PasswordAuthentication -# and ChallengeResponseAuthentication to 'no'. -#UsePAM no - #AllowAgentForwarding yes #AllowTcpForwarding yes #GatewayPorts no -#X11Forwarding no -#X11DisplayOffset 10 -#X11UseLocalhost yes +#PermitTTY yes #PrintMotd yes #PrintLastLog yes #TCPKeepAlive yes #UseLogin no -#UsePrivilegeSeparation yes #PermitUserEnvironment no -#Compression delayed #ClientAliveInterval 0 #ClientAliveCountMax 3 -#UseDNS yes +#UseDNS no #PidFile /var/run/sshd.pid -#MaxStartups 10 +#MaxStartups 10:30:100 #PermitTunnel no #ChrootDirectory none +#VersionAddendum none # no default banner path #Banner none @@ -116,8 +77,6 @@ Subsystem sftp sftp-server.exe # Example of overriding settings on a per-user basis #Match User anoncvs -# X11Forwarding no # AllowTcpForwarding no +# PermitTTY no # ForceCommand cvs server -# PubkeyAcceptedKeyTypes ssh-ed25519* -hostkeyagent \\.\pipe\openssh-ssh-agent \ No newline at end of file diff --git a/contrib/win32/openssh/version.rc b/contrib/win32/openssh/version.rc index da25b84..4fb3216 100644 Binary files a/contrib/win32/openssh/version.rc and b/contrib/win32/openssh/version.rc differ diff --git a/contrib/win32/openssh/win32iocompat.vcxproj b/contrib/win32/openssh/win32iocompat.vcxproj index a1497f7..b8d2794 100644 --- a/contrib/win32/openssh/win32iocompat.vcxproj +++ b/contrib/win32/openssh/win32iocompat.vcxproj @@ -292,6 +292,7 @@ + @@ -337,6 +338,7 @@ + diff --git a/contrib/win32/openssh/win32iocompat.vcxproj.filters b/contrib/win32/openssh/win32iocompat.vcxproj.filters index 3bd9917..e01bdd0 100644 --- a/contrib/win32/openssh/win32iocompat.vcxproj.filters +++ b/contrib/win32/openssh/win32iocompat.vcxproj.filters @@ -19,6 +19,7 @@ + @@ -138,6 +139,9 @@ + + inc + diff --git a/contrib/win32/win32compat/console.c b/contrib/win32/win32compat/console.c index 49d2957..a21609e 100644 --- a/contrib/win32/win32compat/console.c +++ b/contrib/win32/win32compat/console.c @@ -118,7 +118,8 @@ ConEnterRawMode() dwAttributes |= (DWORD)ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; char *envValue = NULL; - _dupenv_s(&envValue, NULL, "SSH_TERM_CONHOST_PARSER"); + size_t len = 0; + _dupenv_s(&envValue, &len, "SSH_TERM_CONHOST_PARSER"); if (NULL != envValue) { isConHostParserEnabled = atoi(envValue); diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index 2cb0855..9ebbf17 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -174,11 +174,12 @@ cleanup: static int pipe_counter = 0; /* - * pipe() implementation. Creates an inbound named pipe, uses CreateFile to connect + * pipe() (unidirectional) and socketpair() (duplex) + * implementation. Creates an inbound named pipe, uses CreateFile to connect * to it. These handles are associated with read end and write end of the pipe */ int -fileio_pipe(struct w32_io* pio[2]) +fileio_pipe(struct w32_io* pio[2], int duplex) { HANDLE read_handle = INVALID_HANDLE_VALUE, write_handle = INVALID_HANDLE_VALUE; struct w32_io *pio_read = NULL, *pio_write = NULL; @@ -205,7 +206,7 @@ fileio_pipe(struct w32_io* pio[2]) /* create named pipe */ write_handle = CreateNamedPipeA(pipe_name, - PIPE_ACCESS_OUTBOUND | FILE_FLAG_OVERLAPPED, + (duplex ? PIPE_ACCESS_DUPLEX : PIPE_ACCESS_OUTBOUND ) | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, 4096, @@ -220,7 +221,7 @@ fileio_pipe(struct w32_io* pio[2]) /* connect to named pipe */ read_handle = CreateFileA(pipe_name, - GENERIC_READ, + duplex ? GENERIC_READ | GENERIC_WRITE : GENERIC_READ, 0, &sec_attributes, OPEN_EXISTING, @@ -923,14 +924,12 @@ fileio_close(struct w32_io* pio) /* let queued APCs (if any) drain */ SleepEx(0, TRUE); CloseHandle(WINHANDLE(pio)); - /* free up non stdio */ - if (!IS_STDIO(pio)) { - if (pio->read_details.buf) - free(pio->read_details.buf); - if (pio->write_details.buf) - free(pio->write_details.buf); - free(pio); - } + if (pio->read_details.buf) + free(pio->read_details.buf); + if (pio->write_details.buf) + free(pio->write_details.buf); + free(pio); + return 0; } diff --git a/contrib/win32/win32compat/inc/fcntl.h b/contrib/win32/win32compat/inc/fcntl.h index 6b3730c..ce71cea 100644 --- a/contrib/win32/win32compat/inc/fcntl.h +++ b/contrib/win32/win32compat/inc/fcntl.h @@ -20,7 +20,6 @@ int w32_fcntl(int fd, int cmd, ... /* arg */); int w32_open(const char *pathname, int flags, ... /* arg */); void* w32_fd_to_handle(int fd); -int w32_allocate_fd_for_handle(HANDLE, BOOL); #define O_RDONLY _O_RDONLY #define O_WRONLY _O_WRONLY diff --git a/contrib/win32/win32compat/inc/pwd.h b/contrib/win32/win32compat/inc/pwd.h index 298d58f..7728ccf 100644 --- a/contrib/win32/win32compat/inc/pwd.h +++ b/contrib/win32/win32compat/inc/pwd.h @@ -40,6 +40,7 @@ struct passwd *w32_getpwuid(uid_t uid); struct passwd *w32_getpwnam(const char *username); struct passwd* w32_getpwtoken(HANDLE); struct passwd *getpwent(void); +void endpwent(void); #define getpwuid w32_getpwuid #define getpwnam w32_getpwnam diff --git a/contrib/win32/win32compat/inc/spawn.h b/contrib/win32/win32compat/inc/spawn.h new file mode 100644 index 0000000..dabbb5b --- /dev/null +++ b/contrib/win32/win32compat/inc/spawn.h @@ -0,0 +1,77 @@ +/* +* Author: Manoj Ampalam +* +* Declarations of POSIX spawn family of functions +*/ +#pragma once +#include "sys\types.h" + +#define POSIX_SPAWN_RESETIDS 0x1 +#define POSIX_SPAWN_SETPGROUP 0x2 +#define POSIX_SPAWN_SETSIGDEF 0x4 +#define POSIX_SPAWN_SETSIGMASK 0x8 +#define POSIX_SPAWN_SETSCHEDPARAM 0x10 +#define POSIX_SPAWN_SETSCHEDULER 0x20 + +#define MAX_INHERITED_FDS 10 +typedef struct +{ + /* stdio to be redirected*/ + int stdio_redirect[3]; + /* number of additinal fds to be duplicated/inherited*/ + int num_aux_fds; + /* additional fds to be duplicated/inherited */ + struct { + int parent_fd[MAX_INHERITED_FDS]; + int child_fd[MAX_INHERITED_FDS]; + }aux_fds_info; +}posix_spawn_file_actions_t; + +typedef struct +{ + int flags; +}posix_spawnattr_t; + +int +posix_spawn(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); + +int +__posix_spawn_asuser(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], char* user); + +int +posix_spawnp(pid_t *pidp, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]); + +int +posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions); + +int +posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions); + +int +posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, int fildes); + +int +posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, int fildes, int newfildes); + +int +posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *file_actions, int fildes, const char *path, int oflag, mode_t mode); + +int +posix_spawnattr_init(posix_spawnattr_t *attr); + +int +posix_spawnattr_destroy(posix_spawnattr_t *attr); + +int +posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags); + +int +posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags); + +int posix_spawnattr_getpgroup(const posix_spawnattr_t * attr, pid_t * pgroup); + +int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup); + + + + diff --git a/contrib/win32/win32compat/inc/stdio.h b/contrib/win32/win32compat/inc/stdio.h index 269ece8..1702b35 100644 --- a/contrib/win32/win32compat/inc/stdio.h +++ b/contrib/win32/win32compat/inc/stdio.h @@ -20,3 +20,5 @@ FILE* w32_fdopen(int fd, const char *mode); int w32_rename(const char *old_name, const char *new_name); #define rename w32_rename + +int is_absolute_path(char *); \ No newline at end of file diff --git a/contrib/win32/win32compat/inc/unistd.h b/contrib/win32/win32compat/inc/unistd.h index 25d55c3..d371889 100644 --- a/contrib/win32/win32compat/inc/unistd.h +++ b/contrib/win32/win32compat/inc/unistd.h @@ -7,6 +7,7 @@ #include #include "sys\types.h" #include "fcntl.h" +#include "spawn.h" #define STDIN_FILENO 0 #define STDOUT_FILENO 1 @@ -79,7 +80,8 @@ int daemon(int nochdir, int noclose); char *crypt(const char *key, const char *salt); int link(const char *oldpath, const char *newpath); int readlink(const char *path, char *link, int linklen); -int spawn_child(char*, char**, int, int, int, unsigned long); + +int chroot(const char *path); /* * readpassphrase.h definitions diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index b19966a..9ce5e87 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -241,26 +241,29 @@ dlsym(HMODULE handle, const char *symbol) * only r, w, a are supported for now */ FILE * -w32_fopen_utf8(const char *path, const char *mode) +w32_fopen_utf8(const char *input_path, const char *mode) { wchar_t wpath[PATH_MAX], wmode[5]; FILE* f; char utf8_bom[] = { 0xEF,0xBB,0xBF }; char first3_bytes[3]; int status = 1; - errno_t r = 0; + errno_t r = 0; + char *path = NULL; if (mode[1] != '\0') { errno = ENOTSUP; return NULL; } - if(NULL == path) { + if(NULL == input_path) { errno = EINVAL; debug3("fopen - ERROR:%d", errno); return NULL; } + path = resolved_path(input_path); + /* if opening null device, point to Windows equivalent */ if (0 == strncmp(path, NULL_DEVICE, strlen(NULL_DEVICE)+1)) { if ((r = wcsncpy_s(wpath, PATH_MAX, L"NUL", 3)) != 0) { @@ -518,7 +521,7 @@ int w32_chmod(const char *pathname, mode_t mode) { int ret; - wchar_t *resolvedPathName_utf16 = utf8_to_utf16(sanitized_path(pathname)); + wchar_t *resolvedPathName_utf16 = utf8_to_utf16(resolved_path(pathname)); if (resolvedPathName_utf16 == NULL) { errno = ENOMEM; return -1; @@ -646,7 +649,7 @@ w32_utimes(const char *filename, struct timeval *tvp) { int ret; FILETIME acttime, modtime; - wchar_t *resolvedPathName_utf16 = utf8_to_utf16(sanitized_path(filename)); + wchar_t *resolvedPathName_utf16 = utf8_to_utf16(resolved_path(filename)); if (resolvedPathName_utf16 == NULL) { errno = ENOMEM; return -1; @@ -680,8 +683,14 @@ link(const char *oldpath, const char *newpath) int w32_rename(const char *old_name, const char *new_name) { - wchar_t *resolvedOldPathName_utf16 = utf8_to_utf16(sanitized_path(old_name)); - wchar_t *resolvedNewPathName_utf16 = utf8_to_utf16(sanitized_path(new_name)); + char old_name_resolved[PATH_MAX] = {0, }; + char new_name_resolved[PATH_MAX] = {0, }; + + strcpy_s(old_name_resolved, _countof(old_name_resolved), resolved_path(old_name)); + strcpy_s(new_name_resolved, _countof(new_name_resolved), resolved_path(new_name)); + + wchar_t *resolvedOldPathName_utf16 = utf8_to_utf16(old_name_resolved); + wchar_t *resolvedNewPathName_utf16 = utf8_to_utf16(new_name_resolved); if (NULL == resolvedOldPathName_utf16 || NULL == resolvedNewPathName_utf16) { errno = ENOMEM; @@ -694,17 +703,17 @@ w32_rename(const char *old_name, const char *new_name) * 2) if the new_name is directory and it is empty then delete it so that _wrename will succeed. */ struct _stat64 st; - if (fileio_stat(sanitized_path(new_name), &st) != -1) { + if (fileio_stat(resolved_path(new_name_resolved), &st) != -1) { if (((st.st_mode & _S_IFMT) == _S_IFREG)) - w32_unlink(new_name); + w32_unlink(new_name_resolved); else { - DIR *dirp = opendir(new_name); + DIR *dirp = opendir(new_name_resolved); if (NULL != dirp) { struct dirent *dp = readdir(dirp); closedir(dirp); if (dp == NULL) - w32_rmdir(new_name); + w32_rmdir(new_name_resolved); } } } @@ -719,7 +728,7 @@ w32_rename(const char *old_name, const char *new_name) int w32_unlink(const char *path) { - wchar_t *resolvedPathName_utf16 = utf8_to_utf16(sanitized_path(path)); + wchar_t *resolvedPathName_utf16 = utf8_to_utf16(resolved_path(path)); if (NULL == resolvedPathName_utf16) { errno = ENOMEM; return -1; @@ -734,7 +743,7 @@ w32_unlink(const char *path) int w32_rmdir(const char *path) { - wchar_t *resolvedPathName_utf16 = utf8_to_utf16(sanitized_path(path)); + wchar_t *resolvedPathName_utf16 = utf8_to_utf16(resolved_path(path)); if (NULL == resolvedPathName_utf16) { errno = ENOMEM; return -1; @@ -794,7 +803,7 @@ int w32_mkdir(const char *path_utf8, unsigned short mode) { int curmask; - wchar_t *path_utf16 = utf8_to_utf16(sanitized_path(path_utf8)); + wchar_t *path_utf16 = utf8_to_utf16(resolved_path(path_utf8)); if (path_utf16 == NULL) { errno = ENOMEM; return -1; @@ -816,16 +825,16 @@ w32_mkdir(const char *path_utf8, unsigned short mode) } int -w32_stat(const char *path, struct w32_stat *buf) +w32_stat(const char *input_path, struct w32_stat *buf) { - return fileio_stat(sanitized_path(path), (struct _stat64*)buf); + return fileio_stat(resolved_path(input_path), (struct _stat64*)buf); } /* if file is symbolic link, copy its link into "link" */ int readlink(const char *path, char *link, int linklen) { - if(strcpy_s(link, linklen, sanitized_path(path))) + if(strcpy_s(link, linklen, resolved_path(path))) return -1; return 0; } @@ -909,31 +918,44 @@ realpath(const char *path, char resolved[PATH_MAX]) return resolved; } +/* This function is not thread safe. +* TODO - It uses static memory. Is this a good design? +*/ char* -sanitized_path(const char *path) +resolved_path(const char *input_path) { - if(!path) return NULL; - + static char resolved_path[PATH_MAX] = {0,}; static char newPath[PATH_MAX] = { '\0', }; errno_t r = 0; - if (path[0] == '/' && path[1]) { - if (path[2] == ':') { - if (path[3] == '\0') { /* make "/x:" as "x:\\" */ - if((r = strncpy_s(newPath, sizeof(newPath), path + 1, strlen(path) - 1)) != 0 ) { - debug3("memcpy_s failed with error: %d.", r); - return NULL; - } - newPath[2] = '\\'; - newPath[3] = '\0'; + if (!input_path) return NULL; - return newPath; + /* If filename contains __PROGRAMDATA__ then expand it to %programData% and return the resolved path */ + if ((strlen(input_path) >= strlen(PROGRAM_DATA)) && (memcmp(input_path, PROGRAM_DATA, strlen(PROGRAM_DATA)) == 0)) { + resolved_path[0] = '\0'; + strcat_s(resolved_path, _countof(resolved_path), get_program_data_path()); + strcat_s(resolved_path, _countof(resolved_path), &input_path[strlen(PROGRAM_DATA)]); + + return resolved_path; /* return here as its doesn't start with "/" */ + } + + strcpy_s(resolved_path, _countof(resolved_path), input_path); + if (resolved_path[0] == '/' && resolved_path[1]) { + if (resolved_path[2] == ':') { + if (resolved_path[3] == '\0') { + /* make "/x:" as "x:\\" */ + resolved_path[0] = resolved_path[1]; + resolved_path[1] = resolved_path[2]; + resolved_path[2] = '\\'; + resolved_path[3] = '\0'; + + return resolved_path; } else - return (char *)(path + 1); /* skip the first "/" */ + return (char *)(resolved_path + 1); /* skip the first "/" */ } } - return (char *)path; + return (char *)resolved_path; } int @@ -944,7 +966,7 @@ statvfs(const char *path, struct statvfs *buf) DWORD freeClusters; DWORD totalClusters; - wchar_t* path_utf16 = utf8_to_utf16(sanitized_path(path)); + wchar_t* path_utf16 = utf8_to_utf16(resolved_path(path)); if (path_utf16 && (GetDiskFreeSpaceW(path_utf16, §orsPerCluster, &bytesPerSector, &freeClusters, &totalClusters) == TRUE)) { debug5("path : [%s]", path); @@ -1413,3 +1435,34 @@ cleanup: LocalFree(pSD); return ret; } + +char* +get_program_data_path() +{ + if (ssh_cfg_dir_path) return ssh_cfg_dir_path; + + wchar_t ssh_cfg_dir_path_w[PATH_MAX] = {0, }; + int return_val = ExpandEnvironmentStringsW(L"%programData%", ssh_cfg_dir_path_w, PATH_MAX); + if (return_val > PATH_MAX) + fatal("%s, buffer too small to expand:%s", __func__, "%programData%"); + else if (!return_val) + fatal("%s, failed to expand:%s error:%s", __func__, "%programData%", GetLastError()); + + ssh_cfg_dir_path = utf16_to_utf8(ssh_cfg_dir_path_w); + if(!ssh_cfg_dir_path) + fatal("%s utf16_to_utf8 failed", __func__); + + return ssh_cfg_dir_path; +} + +/* Windows absolute paths - \abc, /abc, c:\abc, c:/abc, __PROGRAMDATA__\openssh\sshd_config */ +int +is_absolute_path(char *path) +{ + int retVal = 0; + if (*path == '/' || *path == '\\' || (*path != '\0' && path[1] == ':') || + ((strlen(path) >= strlen(PROGRAM_DATA)) && (memcmp(path, PROGRAM_DATA, strlen(PROGRAM_DATA)) == 0))) + retVal = 1; + + return retVal; +} diff --git a/contrib/win32/win32compat/misc_internal.h b/contrib/win32/win32compat/misc_internal.h index 628a0b7..b7d39c8 100644 --- a/contrib/win32/win32compat/misc_internal.h +++ b/contrib/win32/win32compat/misc_internal.h @@ -2,9 +2,6 @@ #include #define PATH_MAX MAX_PATH -#define SSH_ASYNC_STDIN "SSH_ASYNC_STDIN" -#define SSH_ASYNC_STDOUT "SSH_ASYNC_STDOUT" -#define SSH_ASYNC_STDERR "SSH_ASYNC_STDERR" #define GOTO_CLEANUP_IF(_cond_,_err_) do { \ if ((_cond_)) { \ @@ -18,25 +15,28 @@ #define IS_INVALID_HANDLE(h) ( ((NULL == h) || (INVALID_HANDLE_VALUE == h)) ? 1 : 0 ) #define IS_VALID_HANDLE(h) (!IS_INVALID_HANDLE(h)) +#define PROGRAM_DATA "__PROGRAMDATA__" + +#define errno_from_Win32LastError() errno_from_Win32Error(GetLastError()) + +static char *machine_domain_name; +static char *ssh_cfg_dir_path = NULL; /* removes first '/' for Windows paths that are unix styled. Ex: /c:/ab.cd */ -char * sanitized_path(const char *); - +char * resolved_path(const char *); void w32posix_initialize(); void w32posix_done(); - char* w32_programdir(); - void convertToBackslash(char *str); void convertToBackslashW(wchar_t *str); void convertToForwardslash(char *str); - -#define errno_from_Win32LastError() errno_from_Win32Error(GetLastError()) int errno_from_Win32Error(int); void unix_time_to_file_time(ULONG, LPFILETIME); void file_time_to_unix_time(const LPFILETIME, time_t *); int file_attr_to_st_mode(wchar_t * path, DWORD attributes); void invalid_parameter_handler(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t); -static char *machine_domain_name; void to_lower_case(char *s); -int get_machine_domain_name(wchar_t *domain, int size); \ No newline at end of file +int get_machine_domain_name(wchar_t *domain, int size); +char* get_program_data_path(); +HANDLE get_user_token(char* user); +int load_user_profile(HANDLE user_token, char* user); diff --git a/contrib/win32/win32compat/no-ops.c b/contrib/win32/win32compat/no-ops.c index 3976e06..7ecfb2d 100644 --- a/contrib/win32/win32compat/no-ops.c +++ b/contrib/win32/win32compat/no-ops.c @@ -93,7 +93,7 @@ innetgr(const char *netgroup, const char *host, const char *user, const char *do int chroot(const char *path) { - return -1; + return 0; } int diff --git a/contrib/win32/win32compat/pwd.c b/contrib/win32/win32compat/pwd.c index 93af222..f4e3f04 100644 --- a/contrib/win32/win32compat/pwd.c +++ b/contrib/win32/win32compat/pwd.c @@ -358,3 +358,9 @@ setegid(gid_t gid) { return 0; } + +void +endpwent(void) +{ + return; +} diff --git a/contrib/win32/win32compat/shell-host.c b/contrib/win32/win32compat/shell-host.c index cfc04a4..31f1619 100644 --- a/contrib/win32/win32compat/shell-host.c +++ b/contrib/win32/win32compat/shell-host.c @@ -690,7 +690,7 @@ SendCharacter(HANDLE hInput, WORD attributes, wchar_t character) StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, ";%u", Color); - StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, ";%c", 'm'); + StringCbPrintfExA(Next, SizeLeft, &Next, &SizeLeft, 0, "%c", 'm'); if (bUseAnsiEmulation && attributes != pattributes) WriteFile(hInput, formatted_output, (DWORD)(Next - formatted_output), &wr, NULL); @@ -1204,7 +1204,7 @@ get_default_shell_path() wchar_t *tmp = malloc(PATH_MAX + 1); if (!tmp) { - printf_s("get_default_shell_path(), Unable to allocate memory"); + printf_s("%s: out of memory", __func__); exit(255); } @@ -1257,6 +1257,9 @@ get_default_shell_path() if (tmp) free(tmp); + + if (reg_key) + RegCloseKey(reg_key); return default_shell_path; } @@ -1586,7 +1589,7 @@ start_withno_pty(wchar_t *command) } /* for backspace, we need to send space and another backspace for visual erase */ - if (buf[i] == '\b') { + if (buf[i] == '\b' || buf[i] == '\x7f') { if (in_cmd_len > 0) { GOTO_CLEANUP_ON_FALSE(WriteFile(pipe_out, "\b \b", 3, &wr, NULL)); in_cmd_len--; diff --git a/contrib/win32/win32compat/signal.c b/contrib/win32/win32compat/signal.c index dbccc83..3260c92 100644 --- a/contrib/win32/win32compat/signal.c +++ b/contrib/win32/win32compat/signal.c @@ -174,14 +174,17 @@ w32_raise(int sig) return 0; } - /* if set to ignore, nothing to do */ - if (sig_handlers[sig] == W32_SIG_IGN) + /* if set to ignore, handle SIGCHLD since zombies need to be automatically reaped */ + if (sig_handlers[sig] == W32_SIG_IGN) { + if (sig == W32_SIGCHLD) + sw_cleanup_child_zombies(); return 0; + } /* execute any default handlers */ switch (sig) { case W32_SIGCHLD: - sw_cleanup_child_zombies(); + /* do nothing for SIGCHLD */; break; default: /* exit process */ exit(0); @@ -222,7 +225,8 @@ sw_process_pending_signals() /* sftp client is not expecting it */ if (exp[i] != W32_SIGALRM) sig_int = TRUE; - } + } else if (exp[i] == W32_SIGCHLD) /*if SIGCHLD is SIG_IGN, reap zombies*/ + sw_cleanup_child_zombies(); sigdelset(&pending_tmp, exp[i]); } diff --git a/contrib/win32/win32compat/socketio.c b/contrib/win32/win32compat/socketio.c index d0078dc..f99d591 100644 --- a/contrib/win32/win32compat/socketio.c +++ b/contrib/win32/win32compat/socketio.c @@ -168,8 +168,8 @@ CALLBACK WSARecvCompletionRoutine(IN DWORD dwError, /* initiates async receive operation*/ /* TODO - always return 0, or make this a void func. any error should be put in context*/ -int -socketio_WSARecv(struct w32_io* pio, BOOL* completed) +static int +socketio_WSARecv(struct w32_io* pio, BOOL* completed, int len) { int ret = 0; WSABUF wsabuf; @@ -194,6 +194,9 @@ socketio_WSARecv(struct w32_io* pio, BOOL* completed) } else wsabuf.buf = pio->read_details.buf; + if (len) + wsabuf.len = len; + ret = WSARecv(pio->sock, &wsabuf, 1, NULL, &recv_flags, &pio->read_overlapped, &WSARecvCompletionRoutine); if (ret == 0) { pio->read_details.pending = TRUE; @@ -420,7 +423,7 @@ socketio_recv(struct w32_io* pio, void *buf, size_t len, int flags) } } - if (0 != socketio_WSARecv(pio, &completed)) + if (0 != socketio_WSARecv(pio, &completed, (int)len)) return -1; if (completed) { @@ -701,6 +704,7 @@ socketio_accept(struct w32_io* pio, struct sockaddr* addr, int* addrlen) if (pio->read_details.error) { errno = errno_from_WSAError(pio->read_details.error); debug3("accept - ERROR: async io completed with error: %d, io:%p", pio->read_details.error, pio); + pio->read_details.error = 0; goto on_error; } @@ -952,7 +956,7 @@ socketio_on_select(struct w32_io* pio, BOOL rd) } } else if(sock_state == SOCK_READY) { /* connected socket - WSARecv if needed */ - if ((!pio->read_details.pending) && (!socketio_is_io_available(pio, rd)) && (socketio_WSARecv(pio, NULL) != 0)) + if ((!pio->read_details.pending) && (!socketio_is_io_available(pio, rd)) && (socketio_WSARecv(pio, NULL, 0) != 0)) { /* set error, recv() will pick it */ pio->read_details.error = errno; diff --git a/contrib/win32/win32compat/spawn-ext.c b/contrib/win32/win32compat/spawn-ext.c new file mode 100644 index 0000000..41a41c1 --- /dev/null +++ b/contrib/win32/win32compat/spawn-ext.c @@ -0,0 +1,27 @@ +#include +#include "misc_internal.h" +#include "inc\unistd.h" +#include "debug.h" + +int posix_spawn_internal(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], HANDLE user_token); + +int +__posix_spawn_asuser(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], char* user) +{ + extern HANDLE password_auth_token; + int r = -1; + /* use token generated from password auth if already present */ + HANDLE user_token = password_auth_token; + + if (!user_token && (user_token = get_user_token(user)) == NULL) { + error("unable to get security token for user %s", user); + errno = EOTHER; + return -1; + } + if (strcmp(user, "sshd")) + load_user_profile(user_token, user); + + r = posix_spawn_internal(pidp, path, file_actions, attrp, argv, envp, user_token); + CloseHandle(user_token); + return r; +} \ No newline at end of file diff --git a/contrib/win32/win32compat/spawn.c b/contrib/win32/win32compat/spawn.c new file mode 100644 index 0000000..f00628a --- /dev/null +++ b/contrib/win32/win32compat/spawn.c @@ -0,0 +1,104 @@ +/* +* Author: Manoj Ampalam +* +* Implementation of POSIX spawn family of functions +*/ +#include +#include "inc\spawn.h" +#include "inc\unistd.h" + +int +posix_spawnp(pid_t *pidp, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) +{ + errno = ENOTSUP; + return -1; +} + +int +posix_spawn_file_actions_init(posix_spawn_file_actions_t *file_actions) +{ + memset(file_actions, 0, sizeof(posix_spawn_file_actions_t)); + file_actions->stdio_redirect[0] = 0; + file_actions->stdio_redirect[1] = 1; + file_actions->stdio_redirect[2] = 2; + return 0; +} + +int +posix_spawn_file_actions_destroy(posix_spawn_file_actions_t *file_actions) +{ + return 0; +} + +int +posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *file_actions, int fildes) +{ + errno = ENOTSUP; + return -1; +} + +int +posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *file_actions, int fildes, int newfildes) +{ + if (newfildes <= STDERR_FILENO) { + file_actions->stdio_redirect[newfildes] = fildes; + return 0; + } + + if (file_actions->num_aux_fds == MAX_INHERITED_FDS) { + errno = ENOMEM; + return -1; + } + + file_actions->aux_fds_info.parent_fd[file_actions->num_aux_fds] = fildes; + file_actions->aux_fds_info.child_fd[file_actions->num_aux_fds] = newfildes; + file_actions->num_aux_fds++; + return 0; +} + +int +posix_spawn_file_actions_addopen(posix_spawn_file_actions_t *file_actions, int fildes, const char *path, int oflag, mode_t mode) +{ + errno = ENOTSUP; + return -1; +} + +int +posix_spawnattr_init(posix_spawnattr_t *attr) +{ + memset(attr, 0, sizeof(posix_spawnattr_t)); + return 0; +} + +int +posix_spawnattr_destroy(posix_spawnattr_t *attr) +{ + memset(attr, 0, sizeof(posix_spawnattr_t)); + return 0; +} + +int +posix_spawnattr_getflags(const posix_spawnattr_t *attr, short *flags) +{ + errno = ENOTSUP; + return -1; +} + +int +posix_spawnattr_setflags(posix_spawnattr_t *attr, short flags) +{ + if (flags != POSIX_SPAWN_SETPGROUP) { + errno = ENOTSUP; + return -1; + } + attr->flags = flags; + return 0; +} + +int posix_spawnattr_getpgroup(const posix_spawnattr_t * attr, pid_t * pgroup) { + return 0; +} + +int posix_spawnattr_setpgroup(posix_spawnattr_t *attr, pid_t pgroup) { + return 0; +} diff --git a/contrib/win32/win32compat/ssh-agent/agent-main.c b/contrib/win32/win32compat/ssh-agent/agent-main.c index 08013d6..7b4522c 100644 --- a/contrib/win32/win32compat/ssh-agent/agent-main.c +++ b/contrib/win32/win32compat/ssh-agent/agent-main.c @@ -113,14 +113,15 @@ fix_cwd() _wchdir(path); } +/* TODO - get rid of this dependency */ +void log_init(char*, int, int, int); + int wmain(int argc, wchar_t **argv) { _set_invalid_parameter_handler(invalid_parameter_handler); w32posix_initialize(); fix_cwd(); - /* this exits() on failure*/ - load_config(); if (!StartServiceCtrlDispatcherW(dispatch_table)) { if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { /* @@ -146,7 +147,7 @@ wmain(int argc, wchar_t **argv) char* h = 0; h += _wtoi(*(argv + 1)); if (h != 0) { - log_init("ssh-agent", config_log_level(), 1, 0); + log_init("ssh-agent", 3, 1, 0); agent_process_connection(h); return 0; } @@ -184,7 +185,7 @@ scm_start_service(DWORD num, LPWSTR* args) service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 300); ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); - log_init("ssh-agent", config_log_level(), 1, 0); + log_init("ssh-agent", 3, 1, 0); agent_start(FALSE); return 0; } diff --git a/contrib/win32/win32compat/ssh-agent/agent-request.h b/contrib/win32/win32compat/ssh-agent/agent-request.h index 4bc92a7..9cec7de 100644 --- a/contrib/win32/win32compat/ssh-agent/agent-request.h +++ b/contrib/win32/win32compat/ssh-agent/agent-request.h @@ -16,6 +16,5 @@ int process_request_identities(struct sshbuf*, struct sshbuf*, struct agent_conn int process_sign_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); int process_remove_key(struct sshbuf*, struct sshbuf*, struct agent_connection*); int process_remove_all(struct sshbuf*, struct sshbuf*, struct agent_connection*); -int process_privagent_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); /* auth */ diff --git a/contrib/win32/win32compat/ssh-agent/agent.c b/contrib/win32/win32compat/ssh-agent/agent.c index a184010..cc18320 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.c +++ b/contrib/win32/win32compat/ssh-agent/agent.c @@ -168,10 +168,6 @@ agent_cleanup_connection(struct agent_connection* con) { debug("connection %p clean up", con); CloseHandle(con->pipe_handle); - if (con->profile_handle) - UnloadUserProfile(con->profile_token, con->profile_handle); - if (con->profile_token) - CloseHandle(con->profile_token); if (con->client_impersonation_token) CloseHandle(con->client_impersonation_token); if (con->client_process_handle) @@ -222,7 +218,7 @@ agent_start(BOOL dbg_mode) } static char* -con_type_to_string(struct agent_connection* con) +con_type_to_string(struct agent_connection* con) { switch (con->client_type) { case UNKNOWN: @@ -258,16 +254,16 @@ get_con_client_info(struct agent_connection* con) BOOL isMember = FALSE; if (GetNamedPipeClientProcessId(con->pipe_handle, &client_pid) == FALSE || - (client_process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, FALSE, client_pid)) == NULL || - OpenProcessToken(client_process_handle, TOKEN_QUERY | TOKEN_DUPLICATE, &client_primary_token) == FALSE || - DuplicateToken(client_primary_token, SecurityImpersonation, &client_impersonation_token) == FALSE) { + (client_process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, FALSE, client_pid)) == NULL || + OpenProcessToken(client_process_handle, TOKEN_QUERY | TOKEN_DUPLICATE, &client_primary_token) == FALSE || + DuplicateToken(client_primary_token, SecurityImpersonation, &client_impersonation_token) == FALSE) { error("cannot retrieve client impersonation token"); goto done; } if (GetTokenInformation(client_primary_token, TokenUser, NULL, 0, &info_len) == TRUE || - (info = (TOKEN_USER*)malloc(info_len)) == NULL || - GetTokenInformation(client_primary_token, TokenUser, info, info_len, &info_len) == FALSE) + (info = (TOKEN_USER*)malloc(info_len)) == NULL || + GetTokenInformation(client_primary_token, TokenUser, info, info_len, &info_len) == FALSE) goto done; /* check if its localsystem */ @@ -322,7 +318,7 @@ get_con_client_info(struct agent_connection* con) sid_size = SECURITY_MAX_SID_SIZE; if (CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, sid, &sid_size) == FALSE) goto done; - if (CheckTokenMembership(client_impersonation_token, sid, &isMember) == FALSE) + if (CheckTokenMembership(client_impersonation_token, sid, &isMember) == FALSE) goto done; if (isMember) { con->client_type = ADMIN_USER; @@ -355,7 +351,7 @@ done: CloseHandle(client_process_handle); if (client_impersonation_token) CloseHandle(client_impersonation_token); - } + } return r; } diff --git a/contrib/win32/win32compat/ssh-agent/agent.h b/contrib/win32/win32compat/ssh-agent/agent.h index f732908..e5fe79c 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.h +++ b/contrib/win32/win32compat/ssh-agent/agent.h @@ -1,7 +1,7 @@ #include #include -#define __attribute__(A) -#include "log.h" +#include "Debug.h" + #define MAX_MESSAGE_SIZE 256 * 1024 #define SSH_ROOT L"SOFTWARE\\OpenSSH" @@ -37,9 +37,6 @@ struct agent_connection { SYSTEM, /* client is running as System */ SERVICE, /* client is running as LS or NS */ } client_type; - /* user profile related members */ - HANDLE profile_token; - HANDLE profile_handle; }; void agent_connection_on_io(struct agent_connection*, DWORD, OVERLAPPED*); @@ -50,6 +47,3 @@ void agent_start(BOOL); void agent_process_connection(HANDLE); void agent_shutdown(); void agent_cleanup_connection(struct agent_connection*); - -int load_config(); -int config_log_level(); \ No newline at end of file diff --git a/contrib/win32/win32compat/ssh-agent/connection.c b/contrib/win32/win32compat/ssh-agent/connection.c index 2ef51a1..edda01e 100644 --- a/contrib/win32/win32compat/ssh-agent/connection.c +++ b/contrib/win32/win32compat/ssh-agent/connection.c @@ -30,7 +30,6 @@ */ #include "agent.h" #include "agent-request.h" -#include "..\priv-agent.h" #pragma warning(push, 3) @@ -117,26 +116,6 @@ agent_connection_disconnect(struct agent_connection* con) DisconnectNamedPipe(con->pipe_handle); } -static char* -con_type_to_string(struct agent_connection* con) { - switch (con->client_type) { - case UNKNOWN: - return "unknown"; - case NONADMIN_USER: - return "restricted user"; - case ADMIN_USER: - return "administrator"; - case SSHD_SERVICE: - return "sshd service"; - case SYSTEM: - return "system"; - case SERVICE: - return "service"; - default: - return "unexpected"; - } -} - static int process_request(struct agent_connection* con) { @@ -170,9 +149,6 @@ process_request(struct agent_connection* con) case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: r = process_remove_all(request, response, con); break; - case SSH_PRIV_AGENT_MSG_ID: - r = process_privagent_request(request, response, con); - break; default: debug("unknown agent request %d", type); r = -1; diff --git a/contrib/win32/win32compat/termio.c b/contrib/win32/win32compat/termio.c index 372d5b0..35b8a17 100644 --- a/contrib/win32/win32compat/termio.c +++ b/contrib/win32/win32compat/termio.c @@ -105,16 +105,18 @@ ReadThread(_In_ LPVOID lpParameter) goto done; } - char *p = NULL; - if (p = strstr(pio->read_details.buf, "\r\n")) - *p++ = '\n'; - else if (p = strstr(pio->read_details.buf, "\r")) - *p++ = '\n'; + if (pio->sync_read_status.transferred) { + char *p = NULL; + if (p = strstr(pio->read_details.buf, "\r\n")) + *p++ = '\n'; + else if (p = strstr(pio->read_details.buf, "\r")) + *p++ = '\n'; - if (p) { - *p = '\0'; - pio->read_details.buf_size = (DWORD)strlen(pio->read_details.buf); - pio->sync_read_status.transferred = pio->read_details.buf_size; + if (p) { + *p = '\0'; + pio->read_details.buf_size = (DWORD)strlen(pio->read_details.buf); + pio->sync_read_status.transferred = pio->read_details.buf_size; + } } } } else { @@ -265,13 +267,10 @@ syncio_close(struct w32_io* pio) /* drain queued APCs */ SleepEx(0, TRUE); CloseHandle(WINHANDLE(pio)); - /* free up if non stdio */ - if (!IS_STDIO(pio)) { - if (pio->read_details.buf) - free(pio->read_details.buf); - if (pio->write_details.buf) - free(pio->write_details.buf); - free(pio); - } + if (pio->read_details.buf) + free(pio->read_details.buf); + if (pio->write_details.buf) + free(pio->write_details.buf); + free(pio); return 0; } diff --git a/contrib/win32/win32compat/tncon.c b/contrib/win32/win32compat/tncon.c index 3deed8d..897f051 100644 --- a/contrib/win32/win32compat/tncon.c +++ b/contrib/win32/win32compat/tncon.c @@ -131,6 +131,7 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) DWORD nHandle = 1; DWORD dwInput = 0; DWORD dwControlKeyState = 0; + DWORD dwAltGrFlags = LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED; DWORD rc = 0; unsigned char octets[20]; char aChar = 0; @@ -161,6 +162,11 @@ ReadConsoleForTermEmul(HANDLE hInput, char *destin, int destinlen) bShift = (InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED); dwControlKeyState = InputRecord.Event.KeyEvent.dwControlKeyState & ~(CAPSLOCK_ON | ENHANCED_KEY | NUMLOCK_ON | SCROLLLOCK_ON); + + /* ignore the AltGr flags*/ + if ((dwControlKeyState & dwAltGrFlags) == dwAltGrFlags) + dwControlKeyState = dwControlKeyState & ~dwAltGrFlags; + modKey = GetModifierKey(dwControlKeyState); if (InputRecord.Event.KeyEvent.bKeyDown) { int n = WideCharToMultiByte( diff --git a/contrib/win32/win32compat/w32-sshfileperm.c b/contrib/win32/win32compat/w32-sshfileperm.c index 9382092..652063e 100644 --- a/contrib/win32/win32compat/w32-sshfileperm.c +++ b/contrib/win32/win32compat/w32-sshfileperm.c @@ -34,8 +34,8 @@ #include "inc\pwd.h" #include "sshfileperm.h" #include "debug.h" - -#define SSHD_ACCOUNT L"NT Service\\sshd" +#include "misc_internal.h" +#include "config.h" /* * The function is to check if current user is secure to access to the file. @@ -46,10 +46,10 @@ * Returns 0 on success and -1 on failure */ int -check_secure_file_permission(const char *name, struct passwd * pw) +check_secure_file_permission(const char *input_path, struct passwd * pw) { PSECURITY_DESCRIPTOR pSD = NULL; - wchar_t * name_utf16 = NULL; + wchar_t * path_utf16 = NULL; PSID owner_sid = NULL, user_sid = NULL; PACL dacl = NULL; DWORD error_code = ERROR_SUCCESS; @@ -57,6 +57,7 @@ check_secure_file_permission(const char *name, struct passwd * pw) struct passwd * pwd = pw; char *bad_user = NULL; int ret = 0; + char *path = NULL; if (pwd == NULL) if ((pwd = getpwuid(0)) == NULL) @@ -68,17 +69,19 @@ check_secure_file_permission(const char *name, struct passwd * pw) ret = -1; goto cleanup; } - if ((name_utf16 = utf8_to_utf16(name)) == NULL) { + + path = resolved_path(input_path); + if ((path_utf16 = utf8_to_utf16(path)) == NULL) { ret = -1; errno = ENOMEM; goto cleanup; } /*Get the owner sid of the file.*/ - if ((error_code = GetNamedSecurityInfoW(name_utf16, SE_FILE_OBJECT, + if ((error_code = GetNamedSecurityInfoW(path_utf16, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, &owner_sid, NULL, &dacl, NULL, &pSD)) != ERROR_SUCCESS) { - debug3("failed to retrieve the owner sid and dacl of file %s with error code: %d", name, error_code); + debug3("failed to retrieve the owner sid and dacl of file %s with error code: %d", path, error_code); errno = EOTHER; ret = -1; goto cleanup; @@ -91,7 +94,7 @@ check_secure_file_permission(const char *name, struct passwd * pw) if (!IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) && !IsWellKnownSid(owner_sid, WinLocalSystemSid) && !EqualSid(owner_sid, user_sid)) { - debug3("Bad owner on %s", name); + debug3("Bad owner on %s", path); ret = -1; goto cleanup; } @@ -127,21 +130,13 @@ check_secure_file_permission(const char *name, struct passwd * pw) IsWellKnownSid(current_trustee_sid, WinLocalSystemSid) || EqualSid(current_trustee_sid, user_sid)) { continue; - } - else if(is_sshd_account(current_trustee_sid)){ - if ((current_access_mask & ~FILE_GENERIC_READ) != 0){ - debug3("Bad permission. %s can only read access to %s", SSHD_ACCOUNT, name); - ret = -1; - break; - } - } - else { + } else { ret = -1; if (ConvertSidToStringSid(current_trustee_sid, &bad_user) == FALSE) { debug3("ConvertSidToSidString failed with %d. ", GetLastError()); break; } - debug3("Bad permissions. Try removing permissions for user: %s on file %s.", bad_user, name); + debug3("Bad permissions. Try removing permissions for user: %s on file %s.", bad_user, path); break; } } @@ -152,31 +147,8 @@ cleanup: LocalFree(pSD); if (user_sid) LocalFree(user_sid); - if(name_utf16) - free(name_utf16); + if(path_utf16) + free(path_utf16); return ret; } -/*TODO: optimize to get sshd sid first and then call EqualSid*/ -static BOOL -is_sshd_account(PSID user_sid) { - wchar_t user_name[UNCLEN] = { 0 }, full_name[UNCLEN + DNLEN + 2] = { 0 }; - DWORD name_length = UNCLEN, domain_name_length = 0, full_name_len = UNCLEN + DNLEN + 2; - SID_NAME_USE sid_type = SidTypeInvalid; - BOOL ret = FALSE; - errno_t r = 0; - - if (LookupAccountSidLocalW(user_sid, user_name, &name_length, full_name, &full_name_len, &sid_type) == FALSE) - { - debug3("LookupAccountSidLocalW() failed with error: %d. ", GetLastError()); - errno = ENOENT; - return FALSE; - } - domain_name_length = wcsnlen(full_name, _countof(full_name)); - full_name[domain_name_length] = L'\\'; - if ((r = wmemcpy_s(full_name + domain_name_length + 1, _countof(full_name) - domain_name_length -1, user_name, wcsnlen_s(user_name, UNCLEN) + 1)) != 0) { - debug3("wmemcpy_s failed with error: %d.", r); - return FALSE; - } - return (wcsicmp(full_name, SSHD_ACCOUNT) == 0); -} diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c index 91394dc..93527db 100644 --- a/contrib/win32/win32compat/w32fd.c +++ b/contrib/win32/win32compat/w32fd.c @@ -60,64 +60,54 @@ struct w32fd_table { /* mapping table*/ static struct w32fd_table fd_table; -/* static table entries representing std in, out and error*/ -static struct w32_io w32_io_stdin, w32_io_stdout, w32_io_stderr; - /* main thread handle*/ HANDLE main_thread; void fd_table_set(struct w32_io* pio, int index); +void fd_decode_state(char*); +#define POSIX_STATE_ENV "c28fc6f98a2c44abbbd89d6a3037d0d9_POSIX_STATE" + /* initializes mapping table*/ static int fd_table_initialize() { + char *posix_state; + struct w32_io *pio; + HANDLE wh; + /* table entries representing std in, out and error*/ + DWORD wh_index[] = { STD_INPUT_HANDLE , STD_OUTPUT_HANDLE , STD_ERROR_HANDLE }; + int fd_num = 0; + memset(&fd_table, 0, sizeof(fd_table)); - memset(&w32_io_stdin, 0, sizeof(w32_io_stdin)); - w32_io_stdin.std_handle = STD_INPUT_HANDLE; - w32_io_stdin.type = NONSOCK_SYNC_FD; - char *envValue = NULL; - _dupenv_s(&envValue, NULL, SSH_ASYNC_STDIN); - if (NULL != envValue) { - if(strcmp(envValue, "1") == 0) - w32_io_stdin.type = NONSOCK_FD; - - free(envValue); + /* prepare std io fds */ + for (fd_num = STDIN_FILENO; fd_num <= STDERR_FILENO; fd_num++) { + wh = GetStdHandle(wh_index[fd_num]); + if (wh != NULL && wh != INVALID_HANDLE_VALUE) { + pio = malloc(sizeof(struct w32_io)); + if (!pio) { + errno = ENOMEM; + return -1; + } + memset(pio, 0, sizeof(struct w32_io)); + pio->type = NONSOCK_SYNC_FD; + pio->handle = wh; + fd_table_set(pio, fd_num); + } } - _putenv_s(SSH_ASYNC_STDIN, ""); - fd_table_set(&w32_io_stdin, STDIN_FILENO); - memset(&w32_io_stdout, 0, sizeof(w32_io_stdout)); - w32_io_stdout.std_handle = STD_OUTPUT_HANDLE; - w32_io_stdout.type = NONSOCK_SYNC_FD; - - envValue = NULL; - _dupenv_s(&envValue, NULL, SSH_ASYNC_STDOUT); - if (NULL != envValue) { - if(strcmp(envValue, "1") == 0) - w32_io_stdout.type = NONSOCK_FD; + _dupenv_s(&posix_state, NULL, POSIX_STATE_ENV); + /*TODO - validate parent process - to accomodate these scenarios - + * A posix parent process launches a regular process that inturn launches a posix child process + * In this case the posix child process may misinterpret POSIX_STATE_ENV set by grand parent + */ - free(envValue); + if (NULL != posix_state) { + fd_decode_state(posix_state); + free(posix_state); + _putenv_s(POSIX_STATE_ENV, ""); } - - _putenv_s(SSH_ASYNC_STDOUT, ""); - fd_table_set(&w32_io_stdout, STDOUT_FILENO); - memset(&w32_io_stderr, 0, sizeof(w32_io_stderr)); - w32_io_stderr.std_handle = STD_ERROR_HANDLE; - w32_io_stderr.type = NONSOCK_SYNC_FD; - - envValue = NULL; - _dupenv_s(&envValue, NULL, SSH_ASYNC_STDERR); - if (NULL != envValue) { - if(strcmp(envValue, "1") == 0) - w32_io_stderr.type = NONSOCK_FD; - - free(envValue); - } - - _putenv_s(SSH_ASYNC_STDERR, ""); - fd_table_set(&w32_io_stderr, STDERR_FILENO); return 0; } @@ -371,9 +361,34 @@ w32_shutdown(int fd, int how) int w32_socketpair(int domain, int type, int protocol, int sv[2]) { - errno = ENOTSUP; - debug3("socketpair - ERROR not supported"); - return -1; + int p0, p1; + struct w32_io* pio[2]; + + errno = 0; + p0 = fd_table_get_min_index(); + if (p0 == -1) + return -1; + + /*temporarily set occupied bit*/ + FD_SET(p0, &fd_table.occupied); + p1 = fd_table_get_min_index(); + FD_CLR(p0, &fd_table.occupied); + if (p1 == -1) + return -1; + + if (-1 == fileio_pipe(pio, 1)) + return -1; + + pio[0]->type = NONSOCK_FD; + pio[1]->type = NONSOCK_FD; + fd_table_set(pio[0], p0); + fd_table_set(pio[1], p1); + sv[0] = p0; + sv[1] = p1; + debug4("socketpair - r-h:%d,io:%p,fd:%d w-h:%d,io:%p,fd:%d", + pio[0]->handle, pio[0], p0, pio[1]->handle, pio[1], p1); + + return 0; } @@ -395,7 +410,7 @@ w32_pipe(int *pfds) if (write_index == -1) return -1; - if (-1 == fileio_pipe(pio)) + if (-1 == fileio_pipe(pio, 0)) return -1; pio[0]->type = NONSOCK_FD; @@ -427,7 +442,7 @@ w32_open(const char *pathname, int flags, ... /* arg */) va_end(valist); } - pio = fileio_open(sanitized_path(pathname), flags, mode); + pio = fileio_open(resolved_path(pathname), flags, mode); if (pio == NULL) return -1; @@ -799,95 +814,91 @@ w32_select(int fds, w32_fd_set* readfds, w32_fd_set* writefds, w32_fd_set* excep return out_ready_fds; } -int -w32_dup(int oldfd) +static HANDLE +dup_handle(int fd) { - int min_index; - struct w32_io* pio; - HANDLE src, target; - CHECK_FD(oldfd); - if (oldfd > STDERR_FILENO) { - errno = EOPNOTSUPP; - debug3("dup - ERROR: supports only std io, fd:%d", oldfd); - return -1; + HANDLE h = fd_table.w32_ios[fd]->handle; + int is_sock = fd_table.w32_ios[fd]->type == SOCK_FD; + + if (is_sock) { + SOCKET dup_sock; + SOCKET sock = (SOCKET)h; + WSAPROTOCOL_INFOW info; + if (WSADuplicateSocketW(sock, GetCurrentProcessId(), &info) != 0) { + errno = EOTHER; + error("WSADuplicateSocket failed, WSALastError: %d", WSAGetLastError()); + return NULL; + } + dup_sock = WSASocketW(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO, &info, 0, 0); + if (dup_sock == INVALID_SOCKET) { + errno = EOTHER; + error("WSASocketW failed, WSALastError: %d", WSAGetLastError()); + return NULL; + } + return (HANDLE)dup_sock; } - - if ((min_index = fd_table_get_min_index()) == -1) - return -1; - - src = GetStdHandle(fd_table.w32_ios[oldfd]->std_handle); - if (src == INVALID_HANDLE_VALUE) { - errno = EINVAL; - debug3("dup - ERROR: unable to get underlying handle for std fd:%d", oldfd); - return -1; + else { + HANDLE dup_handle; + if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(), &dup_handle, 0, TRUE, DUPLICATE_SAME_ACCESS)) { + errno = EOTHER; + error("dup - ERROR: DuplicatedHandle() :%d", GetLastError()); + } + return dup_handle; } - - if (!DuplicateHandle(GetCurrentProcess(), src, GetCurrentProcess(), &target, 0, TRUE, DUPLICATE_SAME_ACCESS)) { - errno = EOTHER; - debug3("dup - ERROR: DuplicatedHandle() :%d", GetLastError()); - return -1; - } - - pio = (struct w32_io*) malloc(sizeof(struct w32_io)); - if (pio == NULL) { - CloseHandle(target); - errno = ENOMEM; - debug3("dup - ERROR: %d", errno); - return -1; - } - - memset(pio, 0, sizeof(struct w32_io)); - pio->handle = target; - pio->type = fd_table.w32_ios[oldfd]->type; - fd_table_set(pio, min_index); - return min_index; } int w32_dup2(int oldfd, int newfd) { - CHECK_FD(oldfd); - errno = EOPNOTSUPP; - debug3("dup2 - ERROR: not implemented yet"); - return -1; -} - -HANDLE -w32_fd_to_handle(int fd) -{ - HANDLE h = fd_table.w32_ios[fd]->handle; - if (fd <= STDERR_FILENO) - h = GetStdHandle(fd_table.w32_ios[fd]->std_handle); - return h; -} - -int -w32_allocate_fd_for_handle(HANDLE h, BOOL is_sock) -{ - int min_index = fd_table_get_min_index(); struct w32_io* pio; + CHECK_FD(oldfd); - if (min_index == -1) { - return -1; - } + if (fd_table.w32_ios[newfd]) + w32_close(newfd); pio = malloc(sizeof(struct w32_io)); if (pio == NULL) { errno = ENOMEM; return -1; } + memset(pio, 0, sizeof(struct w32_io)); + if ((pio->handle = dup_handle(oldfd)) == 0) { + free(pio); + return -1; + } - pio->type = is_sock ? SOCK_FD : NONSOCK_FD; - pio->handle = h; + pio->type = fd_table.w32_ios[oldfd]->type; + if (pio->type == SOCK_FD) + pio->internal.state = SOCK_READY; + + fd_table_set(pio, newfd); + return 0; +} + +int +w32_dup(int oldfd) +{ + int min_index, r; + CHECK_FD(oldfd); + + if ((min_index = fd_table_get_min_index()) == -1) + return -1; + + if ((r = w32_dup2(oldfd, min_index)) != 0) + return r; - /* TODO - get socket state and confirm that its connected */ - pio->internal.state = SOCK_READY; - fd_table_set(pio, min_index); return min_index; } + +HANDLE +w32_fd_to_handle(int fd) +{ + return fd_table.w32_ios[fd]->handle; +} + int w32_ftruncate(int fd, off_t length) { @@ -910,6 +921,11 @@ w32_fsync(int fd) return FlushFileBuffers(w32_fd_to_handle(fd)); } +int fork() +{ + error("fork is not supported"); + return -1; +} /* * spawn a child process @@ -920,14 +936,18 @@ w32_fsync(int fd) * cmd will be internally decoarated with a set of '"' * to account for any spaces within the commandline * this decoration is done only when additional arguments are passed in argv +* +* spawned child will run as as_user if its not NULL */ -int -spawn_child(char* cmd, char** argv, int in, int out, int err, unsigned long flags) + +static int +spawn_child_internal(char* cmd, char *const argv[], HANDLE in, HANDLE out, HANDLE err, unsigned long flags, HANDLE* as_user) { PROCESS_INFORMATION pi; STARTUPINFOW si; BOOL b; - char *cmdline, *t, **t1; + char *cmdline, *t; + char * const *t1; DWORD cmdline_len = 0; wchar_t * cmdline_utf16 = NULL; int add_module_path = 0, ret = -1; @@ -998,22 +1018,17 @@ spawn_child(char* cmd, char** argv, int in, int out, int err, unsigned long flag memset(&si, 0, sizeof(STARTUPINFOW)); si.cb = sizeof(STARTUPINFOW); - si.hStdInput = w32_fd_to_handle(in); - si.hStdOutput = w32_fd_to_handle(out); - si.hStdError = w32_fd_to_handle(err); + si.hStdInput = in; + si.hStdOutput = out; + si.hStdError = err; si.dwFlags = STARTF_USESTDHANDLES; debug3("spawning %ls", cmdline_utf16); - if (fd_table.w32_ios[in]->type != NONSOCK_SYNC_FD) - _putenv_s(SSH_ASYNC_STDIN, "1"); - if (fd_table.w32_ios[out]->type != NONSOCK_SYNC_FD) - _putenv_s(SSH_ASYNC_STDOUT, "1"); - if (fd_table.w32_ios[err]->type != NONSOCK_SYNC_FD) - _putenv_s(SSH_ASYNC_STDERR, "1"); - b = CreateProcessW(NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi); - _putenv_s(SSH_ASYNC_STDIN, ""); - _putenv_s(SSH_ASYNC_STDOUT, ""); - _putenv_s(SSH_ASYNC_STDERR, ""); + + if (as_user) + b = CreateProcessAsUserW(as_user, NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi); + else + b = CreateProcessW(NULL, cmdline_utf16, NULL, NULL, TRUE, flags, NULL, NULL, &si, &pi); if (b) { if (register_child(pi.hProcess, pi.dwProcessId) == -1) { @@ -1037,3 +1052,192 @@ cleanup: return ret; } + +#include "inc\spawn.h" + +/* structures defining binary layout of fd info to be transmitted between parent and child processes*/ +struct std_fd_state { + int num_inherited; + char in_type; + char out_type; + char err_type; + char padding; +}; + +struct inh_fd_state { + int handle; + short index; + char type; + char padding; +}; + + +/* encodes the fd info into a base64 encoded binary blob */ +static char* +fd_encode_state(const posix_spawn_file_actions_t *file_actions, HANDLE aux_h[]) +{ + char *buf, *encoded; + struct std_fd_state *std_fd_state; + struct inh_fd_state *c; + DWORD len_req; + BOOL b; + int i; + int fd_in = file_actions->stdio_redirect[STDIN_FILENO]; + int fd_out = file_actions->stdio_redirect[STDOUT_FILENO]; + int fd_err = file_actions->stdio_redirect[STDERR_FILENO]; + int num_aux_fds = file_actions->num_aux_fds; + const int *parent_aux_fds = file_actions->aux_fds_info.parent_fd; + const int *child_aux_fds = file_actions->aux_fds_info.child_fd; + + buf = malloc(8 * (1 + num_aux_fds)); + if (!buf) { + errno = ENOMEM; + return NULL; + } + + std_fd_state = (struct std_fd_state *)buf; + std_fd_state->num_inherited = num_aux_fds; + std_fd_state->in_type = fd_table.w32_ios[fd_in]->type; + std_fd_state->out_type = fd_table.w32_ios[fd_out]->type; + std_fd_state->err_type = fd_table.w32_ios[fd_err]->type; + + c = (struct inh_fd_state*)(buf + 8); + for (i = 0; i < num_aux_fds; i++) { + c->handle = (int)(intptr_t)aux_h[i]; + c->index = child_aux_fds[i]; + c->type = fd_table.w32_ios[parent_aux_fds[i]]->type; + c++; + } + + b = CryptBinaryToStringA(buf, 8 * (1 + num_aux_fds), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &len_req); + encoded = malloc(len_req); + if (!encoded) { + free(buf); + errno = ENOMEM; + return NULL; + } + b = CryptBinaryToStringA(buf, 8 * (1 + num_aux_fds), CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, encoded, &len_req); + + free(buf); + return encoded; +} + +/* decodes fd info from an encoded binary blob */ +static void +fd_decode_state(char* enc_buf) +{ + char* buf; + DWORD req, skipped, out_flags; + struct std_fd_state *std_fd_state; + struct inh_fd_state *c; + int num_inherited = 0; + + CryptStringToBinary(enc_buf, 0, CRYPT_STRING_BASE64 | CRYPT_STRING_STRICT, NULL, &req, &skipped, &out_flags); + buf = malloc(req); + if (!buf) + fatal("out of memory"); + + CryptStringToBinary(enc_buf, 0, CRYPT_STRING_BASE64 | CRYPT_STRING_STRICT, buf, &req, &skipped, &out_flags); + + std_fd_state = (struct std_fd_state *)buf; + fd_table.w32_ios[0]->type = std_fd_state->in_type; + if (fd_table.w32_ios[0]->type == SOCK_FD) + fd_table.w32_ios[0]->internal.state = SOCK_READY; + fd_table.w32_ios[1]->type = std_fd_state->out_type; + if (fd_table.w32_ios[1]->type == SOCK_FD) + fd_table.w32_ios[1]->internal.state = SOCK_READY; + fd_table.w32_ios[2]->type = std_fd_state->err_type; + if (fd_table.w32_ios[2]->type == SOCK_FD) + fd_table.w32_ios[2]->internal.state = SOCK_READY; + num_inherited = std_fd_state->num_inherited; + + c = (struct inh_fd_state*)(buf + 8); + while (num_inherited--) { + struct w32_io* pio = malloc(sizeof(struct w32_io)); + if (!pio) + fatal("out of memory"); + ZeroMemory(pio, sizeof(struct w32_io)); + pio->handle = (void*)(INT_PTR)c->handle; + pio->type = c->type; + if (pio->type == SOCK_FD) + pio->internal.state = SOCK_READY; + fd_table_set(pio, c->index); + c++; + } + + free(buf); + return; +} + +int +posix_spawn_internal(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], HANDLE user_token) +{ + int i, ret = -1; + int sc_flags = 0; + char* fd_info = NULL; + HANDLE aux_handles[MAX_INHERITED_FDS]; + HANDLE stdio_handles[STDERR_FILENO + 1]; + if (file_actions == NULL || envp) { + errno = ENOTSUP; + return -1; + } + + if (attrp && attrp->flags == POSIX_SPAWN_SETPGROUP) + sc_flags = CREATE_NEW_PROCESS_GROUP; + + /* prepare handles */ + memset(stdio_handles, 0, sizeof(stdio_handles)); + memset(aux_handles, 0, sizeof(aux_handles)); + stdio_handles[STDIN_FILENO] = dup_handle(file_actions->stdio_redirect[STDIN_FILENO]); + stdio_handles[STDOUT_FILENO] = dup_handle(file_actions->stdio_redirect[STDOUT_FILENO]); + stdio_handles[STDERR_FILENO] = dup_handle(file_actions->stdio_redirect[STDERR_FILENO]); + if (!stdio_handles[STDIN_FILENO] || !stdio_handles[STDOUT_FILENO] || !stdio_handles[STDERR_FILENO]) + goto cleanup; + + for (i = 0; i < file_actions->num_aux_fds; i++) { + aux_handles[i] = dup_handle(file_actions->aux_fds_info.parent_fd[i]); + if (aux_handles[i] == NULL) + goto cleanup; + } + + /* set fd info */ + if ((fd_info = fd_encode_state(file_actions, aux_handles)) == NULL) + goto cleanup; + + if (_putenv_s(POSIX_STATE_ENV, fd_info) != 0) + goto cleanup; + i = spawn_child_internal(argv[0], argv + 1, stdio_handles[STDIN_FILENO], stdio_handles[STDOUT_FILENO], stdio_handles[STDERR_FILENO], sc_flags, user_token); + if (i == -1) + goto cleanup; + if (pidp) + *pidp = i; + ret = 0; +cleanup: + _putenv_s(POSIX_STATE_ENV, ""); + for (i = 0; i <= STDERR_FILENO; i++) { + if (stdio_handles[i] != NULL) { + if (fd_table.w32_ios[file_actions->stdio_redirect[i]]->type == SOCK_FD) + closesocket((SOCKET)stdio_handles[i]); + else + CloseHandle(stdio_handles[i]); + } + } + for (i = 0; i < file_actions->num_aux_fds; i++) { + if (aux_handles[i] != NULL) { + if (fd_table.w32_ios[file_actions->aux_fds_info.parent_fd[i]]->type == SOCK_FD) + closesocket((SOCKET)aux_handles[i]); + else + CloseHandle(aux_handles[i]); + } + } + if (fd_info) + free(fd_info); + + return ret; +} + +int +posix_spawn(pid_t *pidp, const char *path, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[]) +{ + return posix_spawn_internal(pidp, path, file_actions, attrp, argv, envp, NULL); +} diff --git a/contrib/win32/win32compat/w32fd.h b/contrib/win32/win32compat/w32fd.h index 1768a34..4413046 100644 --- a/contrib/win32/win32compat/w32fd.h +++ b/contrib/win32/win32compat/w32fd.h @@ -99,7 +99,6 @@ struct w32_io { union { SOCKET sock; HANDLE handle; - DWORD std_handle; /* ex. STD_INPUT_HANDLE */ }; /*internal state used by synchronous io - terminal handles and external @@ -122,8 +121,7 @@ struct w32_io { }internal; }; -#define IS_STDIO(pio) ((pio)->table_index <= 2) -#define WINHANDLE(pio) (IS_STDIO(pio)? GetStdHandle((pio)->std_handle):(pio)->handle) +#define WINHANDLE(pio) ((pio)->handle) #define FILETYPE(pio) (GetFileType(WINHANDLE(pio))) extern HANDLE main_thread; @@ -155,7 +153,7 @@ int socketio_close(struct w32_io* pio); BOOL fileio_is_io_available(struct w32_io* pio, BOOL rd); void fileio_on_select(struct w32_io* pio, BOOL rd); int fileio_close(struct w32_io* pio); -int fileio_pipe(struct w32_io* pio[2]); +int fileio_pipe(struct w32_io* pio[2], int); struct w32_io* fileio_afunix_socket(); int fileio_connect(struct w32_io*, char*); struct w32_io* fileio_open(const char *pathname, int flags, mode_t mode); diff --git a/contrib/win32/win32compat/w32log.c b/contrib/win32/win32compat/w32log.c index ae5fd40..1d21c63 100644 --- a/contrib/win32/win32compat/w32log.c +++ b/contrib/win32/win32compat/w32log.c @@ -35,46 +35,48 @@ #include "inc\syslog.h" #include "misc_internal.h" +#include "inc\utf.h" #define MSGBUFSIZ 1024 static int logfd = -1; /* - * open a log file using the name of executable under logs folder - * Ex. if called from c:\windows\system32\openssh\sshd.exe - * logfile - c:\windows\system32\openssh\logs\sshd.log + * log file location will be - "%programData%\\openssh\\logs\\.log" */ void openlog(char *ident, unsigned int option, int facility) -{ - wchar_t *logs_dir = L"\\logs\\"; +{ if (logfd != -1 || ident == NULL) return; - wchar_t path[PATH_MAX] = { 0 }, log_file[PATH_MAX + 12] = { 0 }; - errno_t r = 0; - if (GetModuleFileNameW(NULL, path, PATH_MAX) == 0) + wchar_t *logs_dir = L"\\logs\\"; + wchar_t module_path[PATH_MAX] = { 0 }, log_file[PATH_MAX + 12] = { 0 }; + + if (GetModuleFileNameW(NULL, module_path, PATH_MAX) == 0) return; - path[PATH_MAX - 1] = L'\0'; - - if (wcsnlen(path, MAX_PATH) > MAX_PATH - wcslen(logs_dir)) + if (wcsnlen(module_path, MAX_PATH) > MAX_PATH - wcslen(logs_dir)) return; /* split path root and module */ { - wchar_t* tail = path + wcsnlen(path, MAX_PATH); - while (tail > path && *tail != L'\\' && *tail != L'/') + wchar_t* tail = module_path + wcsnlen(module_path, MAX_PATH); + while (tail > module_path && *tail != L'\\' && *tail != L'/') tail--; + + char ssh_cfg_path[PATH_MAX] = {0 ,}; + strcat_s(ssh_cfg_path, _countof(ssh_cfg_path), get_program_data_path()); /* "%programData%" */ + strcat_s(ssh_cfg_path, _countof(ssh_cfg_path), "\\ssh"); /* "%programData%\\ssh" */ - if (((r = wcsncat_s(log_file, PATH_MAX + 12, path, tail - path)) != 0 ) || - (r = wcsncat_s(log_file, PATH_MAX + 12, logs_dir, 6) != 0 )|| - (r = wcsncat_s(log_file, PATH_MAX + 12, tail + 1, wcslen(tail + 1) - 3) != 0 ) || - (r = wcsncat_s(log_file, PATH_MAX + 12, L"log", 3) != 0 )) + wchar_t* ssh_root_path_w = utf8_to_utf16(ssh_cfg_path); /* "%programData%\\ssh" */ + + if ((wcsncat_s(log_file, PATH_MAX + 12, ssh_root_path_w, wcslen(ssh_root_path_w)) != 0) || + (wcsncat_s(log_file, PATH_MAX + 12, logs_dir, 6) != 0) || + (wcsncat_s(log_file, PATH_MAX + 12, tail + 1, wcslen(tail + 1) - 3) != 0 ) || + (wcsncat_s(log_file, PATH_MAX + 12, L"log", 3) != 0)) return; } - errno_t err = _wsopen_s(&logfd, log_file, O_WRONLY | O_CREAT | O_APPEND, SH_DENYNO, S_IREAD | S_IWRITE); if (logfd != -1) @@ -98,8 +100,8 @@ syslog(int priority, const char *format, const char *formatBuffer) return; GetLocalTime(&st); - r = _snprintf_s(msgbufTimestamp, sizeof(msgbufTimestamp), _TRUNCATE, "%d %02d:%02d:%02d:%03d %s\n", - GetCurrentProcessId(), st.wHour, st.wMinute, st.wSecond, + r = _snprintf_s(msgbufTimestamp, sizeof(msgbufTimestamp), _TRUNCATE, "%d %04d-%02d-%02d %02d:%02d:%02d.%03d %s\n", + GetCurrentProcessId(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, formatBuffer); if (r == -1) { _write(logfd, "_snprintf_s failed.", 30); @@ -107,4 +109,4 @@ syslog(int priority, const char *format, const char *formatBuffer) } msgbufTimestamp[strnlen(msgbufTimestamp, MSGBUFSIZ)] = '\0'; _write(logfd, msgbufTimestamp, (unsigned int)strnlen(msgbufTimestamp, MSGBUFSIZ)); -} \ No newline at end of file +} diff --git a/contrib/win32/win32compat/win32_dirent.c b/contrib/win32/win32compat/win32_dirent.c index dfa3ffd..f2a5796 100644 --- a/contrib/win32/win32compat/win32_dirent.c +++ b/contrib/win32/win32compat/win32_dirent.c @@ -108,7 +108,7 @@ opendir(const char *name) if (name && strcmp(name, "/") == 0) return openrootdir(name); - if ((wname = utf8_to_utf16(sanitized_path(name))) == NULL) { + if ((wname = utf8_to_utf16(resolved_path(name))) == NULL) { errno = ENOMEM; return NULL; } diff --git a/contrib/win32/win32compat/win32_usertoken_utils.c b/contrib/win32/win32compat/win32_usertoken_utils.c new file mode 100644 index 0000000..7cb6fd2 --- /dev/null +++ b/contrib/win32/win32compat/win32_usertoken_utils.c @@ -0,0 +1,381 @@ +/* +* Author: Manoj Ampalam +* Utitilites to generate user tokens +* +* Copyright (c) 2015 Microsoft Corp. +* All rights reserved +* +* Microsoft openssh win32 port +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#define UMDF_USING_NTSTATUS +#include +#include +#include +#include +#include +#include "inc\utf.h" +#include "logonuser.h" +#include +#include +#include "misc_internal.h" +#include "Debug.h" + +#pragma warning(push, 3) + +static void +InitLsaString(LSA_STRING *lsa_string, const char *str) +{ + if (!str) + memset(lsa_string, 0, sizeof(LSA_STRING)); + else { + lsa_string->Buffer = (char *)str; + lsa_string->Length = (USHORT)strlen(str); + lsa_string->MaximumLength = lsa_string->Length + 1; + } +} + +static void +EnablePrivilege(const char *privName, int enabled) +{ + TOKEN_PRIVILEGES tp; + HANDLE hProcToken = NULL; + LUID luid; + + int exitCode = 1; + + if (LookupPrivilegeValueA(NULL, privName, &luid) == FALSE || + OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcToken) == FALSE) + goto done; + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = enabled ? SE_PRIVILEGE_ENABLED : 0; + + AdjustTokenPrivileges(hProcToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); + +done: + if (hProcToken) + CloseHandle(hProcToken); + + return; +} + + +static HANDLE +LoadProfile(HANDLE user_token, wchar_t* user, wchar_t* domain) { + PROFILEINFOW profileInfo; + HANDLE ret = NULL; + + profileInfo.dwFlags = PI_NOUI; + profileInfo.lpProfilePath = NULL; + profileInfo.lpUserName = user; + profileInfo.lpDefaultPath = NULL; + profileInfo.lpServerName = domain; + profileInfo.lpPolicyPath = NULL; + profileInfo.hProfile = NULL; + profileInfo.dwSize = sizeof(profileInfo); + EnablePrivilege("SeBackupPrivilege", 1); + EnablePrivilege("SeRestorePrivilege", 1); + if (LoadUserProfileW(user_token, &profileInfo) == FALSE) { + debug("Loading user (%ls,%ls) profile failed ERROR: %d", user, domain, GetLastError()); + goto done; + } + else + ret = profileInfo.hProfile; +done: + EnablePrivilege("SeBackupPrivilege", 0); + EnablePrivilege("SeRestorePrivilege", 0); + return ret; +} + +#define MAX_USER_LEN 64 +/* https://technet.microsoft.com/en-us/library/active-directory-maximum-limits-scalability(v=ws.10).aspx */ +#define MAX_FQDN_LEN 64 +#define MAX_PW_LEN 64 + +static HANDLE +generate_user_token(wchar_t* user_cpn) { + HANDLE lsa_handle = 0, token = 0; + LSA_OPERATIONAL_MODE mode; + ULONG auth_package_id; + NTSTATUS ret, subStatus; + void * logon_info = NULL; + size_t logon_info_size; + LSA_STRING logon_process_name, auth_package_name, originName; + TOKEN_SOURCE sourceContext; + PKERB_INTERACTIVE_PROFILE pProfile = NULL; + LUID logonId; + QUOTA_LIMITS quotas; + DWORD cbProfile; + BOOL domain_user; + + domain_user = wcschr(user_cpn, L'@')? TRUE : FALSE; + + InitLsaString(&logon_process_name, "sshd"); + if (domain_user) + InitLsaString(&auth_package_name, MICROSOFT_KERBEROS_NAME_A); + else + InitLsaString(&auth_package_name, MSV1_0_PACKAGE_NAME); + + InitLsaString(&originName, "sshd"); + if (ret = LsaRegisterLogonProcess(&logon_process_name, &lsa_handle, &mode) != STATUS_SUCCESS) + goto done; + + if (ret = LsaLookupAuthenticationPackage(lsa_handle, &auth_package_name, &auth_package_id) != STATUS_SUCCESS) + goto done; + + if (domain_user) { + KERB_S4U_LOGON *s4u_logon; + logon_info_size = sizeof(KERB_S4U_LOGON); + logon_info_size += (wcslen(user_cpn) * 2 + 2); + logon_info = malloc(logon_info_size); + if (logon_info == NULL) + goto done; + s4u_logon = (KERB_S4U_LOGON*)logon_info; + s4u_logon->MessageType = KerbS4ULogon; + s4u_logon->Flags = 0; + s4u_logon->ClientUpn.Length = (USHORT)wcslen(user_cpn) * 2; + s4u_logon->ClientUpn.MaximumLength = s4u_logon->ClientUpn.Length; + s4u_logon->ClientUpn.Buffer = (WCHAR*)(s4u_logon + 1); + if (memcpy_s(s4u_logon->ClientUpn.Buffer, s4u_logon->ClientUpn.Length + 2, user_cpn, s4u_logon->ClientUpn.Length + 2)) + goto done; + s4u_logon->ClientRealm.Length = 0; + s4u_logon->ClientRealm.MaximumLength = 0; + s4u_logon->ClientRealm.Buffer = 0; + } else { + MSV1_0_S4U_LOGON *s4u_logon; + logon_info_size = sizeof(MSV1_0_S4U_LOGON); + /* additional buffer size = size of user_cpn + size of "." and their null terminators */ + logon_info_size += (wcslen(user_cpn) * 2 + 2) + 4; + logon_info = malloc(logon_info_size); + if (logon_info == NULL) + goto done; + s4u_logon = (MSV1_0_S4U_LOGON*)logon_info; + s4u_logon->MessageType = MsV1_0S4ULogon; + s4u_logon->Flags = 0; + s4u_logon->UserPrincipalName.Length = (USHORT)wcslen(user_cpn) * 2; + s4u_logon->UserPrincipalName.MaximumLength = s4u_logon->UserPrincipalName.Length; + s4u_logon->UserPrincipalName.Buffer = (WCHAR*)(s4u_logon + 1); + if(memcpy_s(s4u_logon->UserPrincipalName.Buffer, s4u_logon->UserPrincipalName.Length + 2, user_cpn, s4u_logon->UserPrincipalName.Length + 2)) + goto done; + s4u_logon->DomainName.Length = 2; + s4u_logon->DomainName.MaximumLength = 2; + s4u_logon->DomainName.Buffer = ((WCHAR*)s4u_logon->UserPrincipalName.Buffer) + wcslen(user_cpn) + 1; + if(memcpy_s(s4u_logon->DomainName.Buffer, 4, L".", 4)) + goto done; + } + + if(memcpy_s(sourceContext.SourceName, TOKEN_SOURCE_LENGTH, "sshd", sizeof(sourceContext.SourceName))) + goto done; + + if (AllocateLocallyUniqueId(&sourceContext.SourceIdentifier) != TRUE) + goto done; + + if (ret = LsaLogonUser(lsa_handle, + &originName, + Network, + auth_package_id, + logon_info, + (ULONG)logon_info_size, + NULL, + &sourceContext, + (PVOID*)&pProfile, + &cbProfile, + &logonId, + &token, + "as, + &subStatus) != STATUS_SUCCESS) { + debug("LsaLogonUser failed NTSTATUS: %d", ret); + goto done; + } + debug3("LsaLogonUser succeeded"); +done: + if (lsa_handle) + LsaDeregisterLogonProcess(lsa_handle); + if (logon_info) + free(logon_info); + if (pProfile) + LsaFreeReturnBuffer(pProfile); + + return token; +} + +HANDLE +process_custom_lsa_auth(char* user, const char* pwd, char* lsa_pkg) +{ + wchar_t *userw = NULL, *pwdw = NULL, *domw = NULL, *tmp, *providerw = NULL; + HANDLE token = NULL, lsa_handle = NULL; + LSA_OPERATIONAL_MODE mode; + ULONG auth_package_id, logon_info_size = 0; + NTSTATUS ret, subStatus; + wchar_t *logon_info = NULL; + LSA_STRING logon_process_name, lsa_auth_package_name, originName; + TOKEN_SOURCE sourceContext; + PVOID pProfile = NULL; + LUID logonId; + QUOTA_LIMITS quotas; + DWORD cbProfile; + int retVal = -1; + + debug("LSA auth request, user:%s lsa_pkg:%s ", user, lsa_pkg); + + if ((userw = utf8_to_utf16(user)) == NULL || + (pwdw = utf8_to_utf16(pwd)) == NULL) { + debug("out of memory"); + goto done; + } + + /* split user and domain */ + if ((tmp = wcschr(userw, L'@')) != NULL) { + domw = tmp + 1; + *tmp = L'\0'; + } + + /* call into LSA provider , get and duplicate token */ + InitLsaString(&logon_process_name, "sshd"); + InitLsaString(&lsa_auth_package_name, lsa_pkg); + InitLsaString(&originName, "sshd"); + + if ((ret = LsaRegisterLogonProcess(&logon_process_name, &lsa_handle, &mode)) != STATUS_SUCCESS) { + error("LsaRegisterLogonProcess failed, error:%x", ret); + goto done; + } + + if ((ret = LsaLookupAuthenticationPackage(lsa_handle, &lsa_auth_package_name, &auth_package_id)) != STATUS_SUCCESS) { + error("LsaLookupAuthenticationPackage failed, lsa auth pkg:%ls error:%x", lsa_pkg, ret); + goto done; + } + + logon_info_size = (ULONG)((wcslen(userw) + wcslen(pwdw) + wcslen(domw) + 3) * sizeof(wchar_t)); + logon_info = (wchar_t *)malloc(logon_info_size); + if (NULL == logon_info) + fatal("%s:out of memory", __func__); + + wcscpy_s(logon_info, logon_info_size, userw); + wcscat_s(logon_info, logon_info_size, L";"); + wcscat_s(logon_info, logon_info_size, pwdw); + wcscat_s(logon_info, logon_info_size, L";"); + wcscat_s(logon_info, logon_info_size, domw); + + memcpy(sourceContext.SourceName, "sshd", sizeof(sourceContext.SourceName)); + + if (!AllocateLocallyUniqueId(&sourceContext.SourceIdentifier)) { + error("AllocateLocallyUniqueId failed, error:%d", GetLastError()); + goto done; + } + + if ((ret = LsaLogonUser(lsa_handle, + &originName, + Network, + auth_package_id, + logon_info, + logon_info_size, + NULL, + &sourceContext, + &pProfile, + &cbProfile, + &logonId, + &token, + "as, + &subStatus)) != STATUS_SUCCESS) { + if(ret == STATUS_ACCOUNT_RESTRICTION) + error("LsaLogonUser failed, error:%x subStatus:%ld", ret, subStatus); + else + error("LsaLogonUser failed error:%x", ret); + + goto done; + } + + retVal = 0; +done: + /* delete allocated memory*/ + if (lsa_handle) + LsaDeregisterLogonProcess(lsa_handle); + if (logon_info) + free(logon_info); + if (pProfile) + LsaFreeReturnBuffer(pProfile); + if (userw) + free(userw); + if (pwdw) + free(pwdw); + + return token; +} + +HANDLE +get_user_token(char* user) { + HANDLE token = NULL; + wchar_t *user_utf16 = NULL; + + if ((user_utf16 = utf8_to_utf16(user)) == NULL) { + debug("out of memory"); + goto done; + } + + if ((token = generate_user_token(user_utf16)) == 0) { + error("unable to generate token for user %ls", user_utf16); + /* work around for https://github.com/PowerShell/Win32-OpenSSH/issues/727 by doing a fake login */ + LogonUserExExWHelper(L"FakeUser", L"FakeDomain", L"FakePasswd", + LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL); + if ((token = generate_user_token(user_utf16)) == 0) { + error("unable to generate token on 2nd attempt for user %ls", user_utf16); + goto done; + } + } + +done: + if (user_utf16) + free(user_utf16); + + return token; +} + +int load_user_profile(HANDLE user_token, char* user) { + int r = 0; + HANDLE profile_handle = NULL; + wchar_t *user_utf16 = NULL, *dom_utf16 = NULL, *tmp; + + if ((user_utf16 = utf8_to_utf16(user)) == NULL) { + debug("out of memory"); + goto done; + } + + /* split user and domain */ + if ((tmp = wcschr(user_utf16, L'@')) != NULL) { + dom_utf16 = tmp + 1; + *tmp = L'\0'; + } + + if ((profile_handle = LoadProfile(user_token, user_utf16, dom_utf16)) == NULL) + goto done; + +done: + if (user_utf16) + free(user_utf16); + return r; +} + +#pragma warning(pop) \ No newline at end of file diff --git a/contrib/win32/win32compat/wmain_sshd.c b/contrib/win32/win32compat/wmain_sshd.c index 89e5fdf..c98ec5a 100644 --- a/contrib/win32/win32compat/wmain_sshd.c +++ b/contrib/win32/win32compat/wmain_sshd.c @@ -34,12 +34,12 @@ #define __STDC__ 1 #include #include +#include #include "inc\utf.h" #include "misc_internal.h" int main(int, char **); extern HANDLE main_thread; -extern int is_child; int scm_start_service(DWORD, LPWSTR*); @@ -97,6 +97,60 @@ static VOID WINAPI service_handler(DWORD dwControl) ReportSvcStatus(service_status.dwCurrentState, NO_ERROR, 0); } +#define SSH_HOSTKEY_GEN_CMDLINE L"ssh-keygen -A" +static void +prereq_setup() +{ + TOKEN_USER* info = NULL; + DWORD info_len = 0, dwError = 0; + HANDLE proc_token = NULL; + UUID uuid; + RPC_CWSTR rpc_str; + USER_INFO_1 ui; + NET_API_STATUS nStatus; + STARTUPINFOW si; + PROCESS_INFORMATION pi; + wchar_t cmdline[MAX_PATH]; + + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &proc_token) == FALSE || + GetTokenInformation(proc_token, TokenUser, NULL, 0, &info_len) == TRUE || + (info = (TOKEN_USER*)malloc(info_len)) == NULL || + GetTokenInformation(proc_token, TokenUser, info, info_len, &info_len) == FALSE) + goto cleanup; + + if (IsWellKnownSid(info->User.Sid, WinLocalSystemSid)) { + /* create sshd account if it does not exist */ + UuidCreate(&uuid); + UuidToStringW(&uuid, (RPC_WSTR*)&rpc_str); + ui.usri1_name = L"sshd"; + ui.usri1_password = (LPWSTR)rpc_str; + ui.usri1_priv = USER_PRIV_USER; + ui.usri1_home_dir = NULL; + ui.usri1_comment = NULL; + ui.usri1_flags = UF_SCRIPT; + ui.usri1_script_path = NULL; + + NetUserAdd(NULL, 1, (LPBYTE)&ui, &dwError); + RpcStringFreeW((RPC_WSTR*)&rpc_str); + + /* create host keys if they dont already exist */ + ZeroMemory(&si, sizeof(si)); + si.cb = sizeof(STARTUPINFOW); + ZeroMemory(&pi, sizeof(pi)); + memcpy(cmdline, SSH_HOSTKEY_GEN_CMDLINE, wcslen(SSH_HOSTKEY_GEN_CMDLINE) * 2 + 2); + if (CreateProcessW(NULL, cmdline, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) { + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hThread); + CloseHandle(pi.hProcess); + } + } +cleanup: + if (proc_token) + CloseHandle(proc_token); + if (info) + free(info); +} + int sshd_main(int argc, wchar_t **wargv) { char** argv = NULL; int i, r; @@ -110,20 +164,26 @@ int sshd_main(int argc, wchar_t **wargv) { } w32posix_initialize(); - if (getenv("SSHD_REMSOC")) - is_child = 1; - - /* change current directory to sshd.exe root */ - wchar_t* path_utf16 = utf8_to_utf16(w32_programdir()); - _wchdir(path_utf16); - free(path_utf16); r = main(argc, argv); w32posix_done(); return r; } +int argc_original = 0; +wchar_t **wargv_original = NULL; + int wmain(int argc, wchar_t **wargv) { + wchar_t* path_utf16; + argc_original = argc; + wargv_original = wargv; + + /* change current directory to sshd.exe root */ + if ( (path_utf16 = utf8_to_utf16(w32_programdir())) == NULL) + return -1; + _wchdir(path_utf16); + free(path_utf16); + if (!StartServiceCtrlDispatcherW(dispatch_table)) { if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) return sshd_main(argc, wargv); /* sshd running NOT as service*/ @@ -139,8 +199,9 @@ int scm_start_service(DWORD num, LPWSTR* args) { ZeroMemory(&service_status, sizeof(service_status)); service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 300); + prereq_setup(); ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); - return sshd_main(num, args); + return sshd_main(argc_original, wargv_original); } diff --git a/misc.c b/misc.c index 93011e4..acf8d71 100644 --- a/misc.c +++ b/misc.c @@ -1126,10 +1126,6 @@ tun_open(int tun, int mode, char **ifname) void sanitise_stdfd(void) { -#ifdef WINDOWS - /* nothing to do for Windows*/ - return; -#else /* !WINDOWS */ int nullfd, dupfd; if ((nullfd = dupfd = open(_PATH_DEVNULL, O_RDWR)) == -1) { @@ -1148,7 +1144,6 @@ sanitise_stdfd(void) } if (nullfd > STDERR_FILENO) close(nullfd); -#endif /* !WINDOWS */ } char * diff --git a/monitor.c b/monitor.c index 9547a16..7657eb9 100644 --- a/monitor.c +++ b/monitor.c @@ -1585,6 +1585,43 @@ mm_answer_audit_command(int socket, Buffer *m) } #endif /* SSH_AUDIT_EVENTS */ +void +monitor_send_keystate(struct monitor *pmonitor) { + struct sshbuf *m; + int r; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if ((r = sshbuf_put_stringb(m, child_state)) != 0) + fatal("%s: buffer error: %s", __func__, ssh_err(r)); + + if (ssh_msg_send(pmonitor->m_sendfd, 0, m) == -1) + fatal("%s: ssh_msg_send failed", __func__); + + sshbuf_free(m); +} + +void +monitor_recv_keystate(struct monitor*pmonitor) { + Buffer m; + char *cp; + u_int len; + + buffer_init(&m); + + if (ssh_msg_recv(pmonitor->m_recvfd, &m) == -1) + fatal("%s: ssh_msg_recv failed", __func__); + if (buffer_get_char(&m) != 0) + fatal("%s: recv_keystate version mismatch", __func__); + + cp = buffer_get_string(&m, &len); + child_state = sshbuf_new(); + buffer_append(child_state, cp, len); + free(cp); + buffer_free(&m); +} + + void monitor_clear_keystate(struct monitor *pmonitor) { diff --git a/monitor_wrap.h b/monitor_wrap.h index 72ba1cd..9e032d2 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h @@ -98,9 +98,4 @@ int mm_bsdauth_respond(void *, u_int, char **); int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **); int mm_skey_respond(void *, u_int, char **); -/* Windows specific */ -void* mm_auth_pubkey(const char*, const struct sshkey *, const u_char *, size_t, - struct sshbuf*); -int mm_load_profile(const char*, u_int ); - #endif /* _MM_WRAP_H_ */ diff --git a/readpass.c b/readpass.c index de62434..f5c99dc 100644 --- a/readpass.c +++ b/readpass.c @@ -66,14 +66,33 @@ ssh_askpass(char *askpass, const char *msg) return NULL; } osigchld = signal(SIGCHLD, SIG_DFL); -#ifdef WINDOWS - /* spawd child for Windows */ fcntl(p[0], F_SETFD, FD_CLOEXEC); - pid = spawn_child(askpass, NULL, p[1], p[1], STDERR_FILENO, 0); - if (pid < 0) { -#else /* !WINDOWS */ - if ((pid = fork()) < 0) { -#endif /* !WINDOWS */ + fcntl(p[1], F_SETFD, FD_CLOEXEC); +#ifdef FORK_NOT_SUPPORTED + { + posix_spawn_file_actions_t actions; + pid = -1; + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, p[1], STDOUT_FILENO) != 0 ) { + error("posix_spawn initialization failed"); + signal(SIGCHLD, osigchld); + return NULL; + } else { + char* spawn_argv[2]; + spawn_argv[0] = askpass; + spawn_argv[1] = NULL; + if (posix_spawn(&pid, spawn_argv[0], &actions, NULL, spawn_argv, NULL) != 0) { + posix_spawn_file_actions_destroy(&actions); + error("ssh_askpass: posix_spawn: %s", strerror(errno)); + signal(SIGCHLD, osigchld); + return NULL; + } + posix_spawn_file_actions_destroy(&actions); + } + + } +#else + if ((pid = fork()) < 0) { error("ssh_askpass: fork: %s", strerror(errno)); signal(SIGCHLD, osigchld); return NULL; @@ -86,6 +105,7 @@ ssh_askpass(char *askpass, const char *msg) execlp(askpass, askpass, msg, (char *)NULL); fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno)); } +#endif close(p[1]); len = 0; diff --git a/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 b/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 index 4106939..885955f 100644 --- a/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 +++ b/regress/pesterTests/Authorized_keys_fileperm.Tests.ps1 @@ -32,6 +32,27 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { #suppress the firewall blocking dialogue on win7 netsh advfirewall firewall add rule name="sshd" program="$($OpenSSHTestInfo['OpenSSHBinPath'])\sshd.exe" protocol=any action=allow dir=in } + + $Taskfolder = "\OpenSSHTestTasks\" + $Taskname = "StartTestDaemon" + + function Start-SSHD-TestDaemon + { + param([string] $Arguments) + $opensshbinpath = $OpenSSHTestInfo['OpenSSHBinPath'] + + $ac = New-ScheduledTaskAction -Execute (join-path $opensshbinpath "sshd") -WorkingDirectory $opensshbinpath -Argument $Arguments + $task = Register-ScheduledTask -TaskName $Taskname -User system -Action $ac -TaskPath $Taskfolder -Force + Start-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname + } + + function Stop-SSHD-TestDaemon + { + Stop-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname + #stop-scheduledTask does not wait for worker process to end. Kill it if still running. Logic below assume sshd service is running + $svcpid = ((tasklist /svc | select-string -Pattern ".+sshd").ToString() -split "\s+")[1] + (gps sshd).id | foreach { if ((-not($_ -eq $svcpid))) {Stop-Process $_ -Force} } + } } AfterEach { $tI++ } @@ -91,12 +112,11 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Repair-FilePermission -Filepath $authorizedkeyPath -Owners $objUserSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" $o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 + Stop-SSHD-TestDaemon $o | Should Be "1234" - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by local system)" { @@ -104,12 +124,12 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Repair-FilePermission -Filepath $authorizedkeyPath -Owner $systemSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" + $o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 + Stop-SSHD-TestDaemon $o | Should Be "1234" - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by admins group and pwd does not have explict ACE)" { @@ -117,12 +137,11 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Repair-FilePermission -Filepath $authorizedkeyPath -Owner $adminsSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" $o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 + Stop-SSHD-TestDaemon $o | Should Be "1234" - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-positive(authorized_keys is owned by admins group and pwd have explict ACE)" { @@ -130,12 +149,11 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Repair-FilePermission -Filepath $authorizedkeyPath -Owner $adminsSid -FullAccessNeeded $adminsSid,$systemSid,$objUserSid -confirm:$false #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" $o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 + Stop-SSHD-TestDaemon $o | Should Be "1234" - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-authorized_keys-negative(authorized_keys is owned by other admin user)" { @@ -143,14 +161,11 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Repair-FilePermission -Filepath $authorizedkeyPath -Owner $currentUserSid -FullAccessNeeded $adminsSid,$systemSid -confirm:$false #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 - - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue + Stop-SSHD-TestDaemon + $logPath | Should Contain "Authentication refused." } It "$tC.$tI-authorized_keys-negative(other account can access private key file)" { @@ -162,14 +177,11 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Set-FilePermission -FilePath $authorizedkeyPath -User $objPwdUserSid -Perm "Read" #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 - - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue + Stop-SSHD-TestDaemon + $logPath | Should Contain "Authentication refused." } It "$tC.$tI-authorized_keys-negative(authorized_keys is owned by other non-admin user)" { @@ -178,30 +190,11 @@ Describe "Tests for authorized_keys file permission" -Tags "CI" { Repair-FilePermission -Filepath $authorizedkeyPath -Owner $objPwdUserSid -FullAccessNeeded $adminsSid,$systemSid,$objPwdUser -confirm:$false #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -p $port -o `"AuthorizedKeysFile .testssh/authorized_keys`" -E $logPath" ssh -p $port -E $FilePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 - - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue - } - It "$tC.$tI-authorized_keys-negative(the running process does not have read access to the authorized_keys)" -skip:$skip { - #setup to have ssouser as owner and grant it full control - Repair-FilePermission -Filepath $authorizedkeyPath -Owner $objUserSid -FullAccessNeeded $systemSid,$objUserSid -confirm:$false - Set-FilePermission -Filepath $authorizedkeyPath -UserSid $adminsSid -Action Delete - - #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow - ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $ssouser@$server echo 1234 - $LASTEXITCODE | Should Not Be 0 - - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 - - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue + Stop-SSHD-TestDaemon + $logPath | Should Contain "Authentication refused." } } } diff --git a/regress/pesterTests/KeyUtils.Tests.ps1 b/regress/pesterTests/KeyUtils.Tests.ps1 index c9ed634..5b41f8c 100644 --- a/regress/pesterTests/KeyUtils.Tests.ps1 +++ b/regress/pesterTests/KeyUtils.Tests.ps1 @@ -171,32 +171,17 @@ Describe "E2E scenarios for ssh key management" -Tags "CI" { # Executing ssh-agent will start agent service # This is to support typical Unix scenarios where # running ssh-agent will setup the agent for current session - It "$tC.$tI - ssh-agent starts agent service and sshd depends on ssh-agent" { + It "$tC.$tI - ssh-agent starts agent service" { if ((Get-Service ssh-agent).Status -eq "Running") { Stop-Service ssh-agent -Force } (Get-Service ssh-agent).Status | Should Be "Stopped" - (Get-Service sshd).Status | Should Be "Stopped" ssh-agent WaitForStatus -ServiceName ssh-agent -Status "Running" (Get-Service ssh-agent).Status | Should Be "Running" - - Stop-Service ssh-agent -Force - - WaitForStatus -ServiceName ssh-agent -Status "Stopped" - - (Get-Service ssh-agent).Status | Should Be "Stopped" - (Get-Service sshd).Status | Should Be "Stopped" - - # this should automatically start both the services - Start-Service sshd - - WaitForStatus -ServiceName sshd -Status "Running" - (Get-Service ssh-agent).Status | Should Be "Running" - (Get-Service sshd).Status | Should Be "Running" } It "$tC.$tI - ssh-add - add and remove all key types" { diff --git a/regress/pesterTests/SSHDConfig.tests.ps1 b/regress/pesterTests/SSHDConfig.tests.ps1 index eb1d510..6b1c583 100644 --- a/regress/pesterTests/SSHDConfig.tests.ps1 +++ b/regress/pesterTests/SSHDConfig.tests.ps1 @@ -27,6 +27,26 @@ Describe "Tests of sshd_config" -Tags "CI" { $ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Machine $PrincipalContext = new-object -TypeName System.DirectoryServices.AccountManagement.PrincipalContext -ArgumentList @($ContextType, $ContextName) $IdentityType = [System.DirectoryServices.AccountManagement.IdentityType]::SamAccountName + $Taskfolder = "\OpenSSHTestTasks\" + $Taskname = "StartTestDaemon" + + function Start-SSHD-TestDaemon + { + param([string] $Arguments) + $opensshbinpath = $OpenSSHTestInfo['OpenSSHBinPath'] + + $ac = New-ScheduledTaskAction -Execute (join-path $opensshbinpath "sshd") -WorkingDirectory $opensshbinpath -Argument $Arguments + $task = Register-ScheduledTask -TaskName $Taskname -User system -Action $ac -TaskPath $Taskfolder -Force + Start-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname + } + + function Stop-SSHD-TestDaemon + { + Stop-ScheduledTask -TaskPath $Taskfolder -TaskName $Taskname + #stop-scheduledTask does not wait for worker process to end. Kill it if still running. Logic below assume sshd service is running + $svcpid = ((tasklist /svc | select-string -Pattern ".+sshd").ToString() -split "\s+")[1] + (gps sshd).id | foreach { if ((-not($_ -eq $svcpid))) {Stop-Process $_ -Force} } + } function Add-LocalUser { @@ -164,7 +184,6 @@ Describe "Tests of sshd_config" -Tags "CI" { $denyGroup3 = "denygroup3" $sshdConfigPath = Join-Path $PSScriptRoot testdata\SSHD_Config $testknownhosts = Join-path $PSScriptRoot testdata\test_known_hosts - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue #add wrong password so ssh does not prompt password if failed with authorized keys Add-PasswordSetting -Pass $password $tI=1 @@ -173,7 +192,6 @@ Describe "Tests of sshd_config" -Tags "CI" { BeforeEach { $filePath = Join-Path $testDir "$tC.$tI.$fileName" $logPath = Join-Path $testDir "$tC.$tI.$logName" - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } AfterAll { @@ -183,162 +201,146 @@ Describe "Tests of sshd_config" -Tags "CI" { It "$tC.$tI-User with full name in the list of AllowUsers" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $allowUser1 -Password $password -GroupName $allowGroup1 $o = ssh -p $port $allowUser1@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 + Stop-SSHD-TestDaemon $o | Should Be "1234" Remove-UserFromLocalGroup -UserName $allowUser1 -GroupName $allowGroup1 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-User with * wildcard" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $allowUser2 -Password $password -GroupName $allowGroup1 $o = ssh -p $port $allowUser2@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 + Stop-SSHD-TestDaemon $o | Should Be "1234" Remove-UserFromLocalGroup -UserName $allowUser2 -GroupName $allowGroup1 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-User with ? wildcard" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $allowUser3 -Password $password -GroupName $allowGroup1 $o = ssh -p $port $allowUser3@$server -o "UserKnownHostsFile $testknownhosts" echo 1234 + Stop-SSHD-TestDaemon $o | Should Be "1234" Remove-UserFromLocalGroup -UserName $allowUser3 -GroupName $allowGroup1 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-User with full name in the list of AllowUsers but not in any AllowGroups" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-LocalUser -UserName $allowUser4 -Password $password ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $allowUser4@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 - - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue + Stop-SSHD-TestDaemon + $logPath | Should Contain "not allowed because not in any group" + } It "$tC.$tI-User with full name in the list of DenyUsers" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $denyUser1 -Password $password -GroupName $allowGroup1 ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $denyUser1@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 + Stop-SSHD-TestDaemon + $logPath | Should Contain "not allowed because listed in DenyUsers" Remove-UserFromLocalGroup -UserName $denyUser1 -GroupName $allowGroup1 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-User with * wildcard in the list of DenyUsers" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $denyUser2 -Password $password -GroupName $allowGroup1 ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $denyUser2@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 + Stop-SSHD-TestDaemon + $logPath | Should Contain "not allowed because listed in DenyUsers" Remove-UserFromLocalGroup -UserName $denyUser2 -GroupName $allowGroup1 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-User with ? wildcard in the list of DenyUsers" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $denyUser3 -Password $password -GroupName $allowGroup1 ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $denyUser3@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 + Stop-SSHD-TestDaemon + $logPath | Should Contain "not allowed because not listed in AllowUsers" Remove-UserFromLocalGroup -UserName $denyUser3 -GroupName $allowGroup1 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-User is listed in the list of AllowUsers but also in a full name DenyGroups and AllowGroups" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $localuser1 -Password $password -GroupName $allowGroup1 Add-UserToLocalGroup -UserName $localuser1 -Password $password -GroupName $denyGroup1 ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $localuser1@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 + Stop-SSHD-TestDaemon + $logPath | Should Contain "not allowed because a group is listed in DenyGroups" Remove-UserFromLocalGroup -UserName $localuser1 -GroupName $allowGroup1 Remove-UserFromLocalGroup -UserName $localuser1 -GroupName $denyGroup1 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-User is listed in the list of AllowUsers but also in a wildcard * DenyGroups" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $localuser2 -Password $password -GroupName $denyGroup2 ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $localuser2@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 + Stop-SSHD-TestDaemon + $logPath | Should Contain "not allowed because a group is listed in DenyGroups" Remove-UserFromLocalGroup -UserName $localuser2 -GroupName $denyGroup2 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } It "$tC.$tI-User is listed in the list of AllowUsers but also in a wildcard ? DenyGroups" { #Run - Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-f $sshdConfigPath", "-E $logPath") -NoNewWindow + Start-SSHD-TestDaemon -Arguments "-d -f $sshdConfigPath -E $logPath" Add-UserToLocalGroup -UserName $localuser3 -Password $password -GroupName $denyGroup3 ssh -p $port -E $filePath -o "UserKnownHostsFile $testknownhosts" $localuser3@$server echo 1234 $LASTEXITCODE | Should Not Be 0 - $matches = Get-Content $filePath | Select-String -pattern "Permission denied" - $matches.Count | Should BeGreaterThan 2 + Stop-SSHD-TestDaemon + $logPath | Should Contain "not allowed because a group is listed in DenyGroups" Remove-UserFromLocalGroup -UserName $localuser3 -GroupName $denyGroup3 - #Cleanup - Get-Process -Name sshd -ErrorAction SilentlyContinue | Where-Object {$_.SessionID -ne 0} | Stop-process -force -ErrorAction SilentlyContinue } +#> } } diff --git a/regress/pesterTests/SSHD_Config b/regress/pesterTests/SSHD_Config index 10eab4a..2eb15cb 100644 --- a/regress/pesterTests/SSHD_Config +++ b/regress/pesterTests/SSHD_Config @@ -21,10 +21,10 @@ Port 47002 # HostKey for protocol version 1 #HostKey /etc/ssh/ssh_host_key # HostKeys for protocol version 2 -HostKey sshtest_hostkey_rsa -HostKey sshtest_hostkey_dsa -HostKey sshtest_hostkey_ecdsa -HostKey sshtest_hostkey_ed25519 +HostKey __PROGRAMDATA__\ssh\sshtest_hostkey_rsa +HostKey __PROGRAMDATA__\ssh\sshtest_hostkey_dsa +HostKey __PROGRAMDATA__\ssh\sshtest_hostkey_ecdsa +HostKey __PROGRAMDATA__\ssh\sshtest_hostkey_ed25519 # Lifetime and size of ephemeral version 1 server key #KeyRegenerationInterval 1h @@ -126,5 +126,4 @@ PubkeyAcceptedKeyTypes ssh-ed25519* #AllowUsers allowuser1 allowu*r2 allow?se?3 allowuser4 localuser1 localu*r2 loc?lu?er3 localadmin #DenyGroups denygroup1 denygr*p2 deny?rou?3 #AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm* -hostkeyagent \\.\pipe\openssh-ssh-agent -TrustedUserCAKeys sshtest_ca_userkeys.pub +TrustedUserCAKeys __PROGRAMDATA__\ssh\sshtest_ca_userkeys.pub diff --git a/regress/pesterTests/testdata/SSHD_Config b/regress/pesterTests/testdata/SSHD_Config index d947d9f..b50697a 100644 --- a/regress/pesterTests/testdata/SSHD_Config +++ b/regress/pesterTests/testdata/SSHD_Config @@ -11,10 +11,10 @@ Port 47003 # HostKey for protocol version 1 #HostKey /etc/ssh/ssh_host_key # HostKeys for protocol version 2 -HostKey sshtest_hostkey_rsa -HostKey sshtest_hostkey_dsa -HostKey sshtest_hostkey_ecdsa -HostKey sshtest_hostkey_ed25519 +HostKey __PROGRAMDATA__\ssh\sshtest_hostkey_rsa +HostKey __PROGRAMDATA__\ssh\sshtest_hostkey_dsa +HostKey __PROGRAMDATA__\ssh\sshtest_hostkey_ecdsa +HostKey __PROGRAMDATA__\ssh\sshtest_hostkey_ed25519 # Lifetime and size of ephemeral version 1 server key #KeyRegenerationInterval 1h @@ -116,4 +116,3 @@ DenyUsers denyuser1 deny*2 denyuse?3, AllowUsers allowuser1 allowu*r2 allow?se?3 allowuser4 localuser1 localu*r2 loc?lu?er3 localadmin DenyGroups denygroup1 denygr*p2 deny?rou?3 AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm* -hostkeyagent \\.\pipe\openssh-ssh-agent diff --git a/regress/unittests/win32compat/file_tests.c b/regress/unittests/win32compat/file_tests.c index a9750ca..9597af7 100644 --- a/regress/unittests/win32compat/file_tests.c +++ b/regress/unittests/win32compat/file_tests.c @@ -460,9 +460,6 @@ file_miscellaneous_tests() h = w32_fd_to_handle(STDERR_FILENO); ASSERT_HANDLE(h); - retValue = w32_allocate_fd_for_handle(h, FALSE); - ASSERT_HANDLE(h); - f = open(tmp_filename, O_RDWR | O_CREAT | O_TRUNC, 0666); ASSERT_INT_NE(f, -1); wchar_t *t = utf8_to_utf16(tmp_filename); diff --git a/regress/unittests/win32compat/miscellaneous_tests.c b/regress/unittests/win32compat/miscellaneous_tests.c index fafbe03..27b61af 100644 --- a/regress/unittests/win32compat/miscellaneous_tests.c +++ b/regress/unittests/win32compat/miscellaneous_tests.c @@ -67,9 +67,9 @@ test_sanitizedpath() char *win32prgdir = w32_programdir(); ASSERT_PTR_NE(win32prgdir, NULL); - ASSERT_PTR_EQ(sanitized_path(NULL), NULL); + ASSERT_PTR_EQ(resolved_path(NULL), NULL); - char *ret = sanitized_path(win32prgdir); + char *ret = resolved_path(win32prgdir); retValue = strcmp(win32prgdir, ret); ASSERT_INT_EQ(retValue, 0); @@ -79,14 +79,14 @@ test_sanitizedpath() strncpy(tmp_path+1, win32prgdir, win32prgdir_len); tmp_path[win32prgdir_len+1] = '\0'; - ret = sanitized_path(tmp_path); + ret = resolved_path(tmp_path); retValue = strcmp(win32prgdir, ret); ASSERT_INT_EQ(retValue, 0); char *s1 = malloc(4), *s2 = malloc(4); s1[0] = '/', s1[1] = win32prgdir[0], s1[2] = ':', s1[3] = '\0'; s2[0] = win32prgdir[0], s2[1] = ':', s2[2] = '\\', s2[3] = '\0'; - ret = sanitized_path(s1); + ret = resolved_path(s1); retValue = strcmp(ret, s2); ASSERT_INT_EQ(retValue, 0); diff --git a/scp.c b/scp.c index 8a9d1cc..337277f 100644 --- a/scp.c +++ b/scp.c @@ -281,6 +281,10 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) fatal("pipe: %s", strerror(errno)); if (pipe(pout) < 0) fatal("pipe: %s", strerror(errno)); + fcntl(pout[0], F_SETFD, FD_CLOEXEC); + fcntl(pout[1], F_SETFD, FD_CLOEXEC); + fcntl(pin[0], F_SETFD, FD_CLOEXEC); + fcntl(pin[1], F_SETFD, FD_CLOEXEC); /* Free the reserved descriptors. */ close(reserved[0]); @@ -291,9 +295,12 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) signal(SIGTTOU, suspchild); /* Fork a child to execute the command on the remote host using ssh. */ -#ifdef WINDOWS - /* generate command line and spawn_child */ +#ifdef FORK_NOT_SUPPORTED replacearg(&args, 0, "%s", ssh_program); + if (port != -1) { + addargs(&args, "-p"); + addargs(&args, "%d", port); + } if (remuser != NULL) { addargs(&args, "-l"); addargs(&args, "%s", remuser); @@ -302,14 +309,22 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) addargs(&args, "%s", host); addargs(&args, "%s", cmd); - fcntl(pout[0], F_SETFD, FD_CLOEXEC); - fcntl(pin[1], F_SETFD, FD_CLOEXEC); + { + posix_spawn_file_actions_t actions; + do_cmd_pid = -1; - do_cmd_pid = spawn_child(args.list[0], args.list + 1, pin[0], pout[1], STDERR_FILENO, 0); + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, pin[0], STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, pout[1], STDOUT_FILENO) != 0 ) + fatal("posix_spawn initialization failed"); + else if (posix_spawn(&do_cmd_pid, args.list[0], &actions, NULL, args.list, NULL) != 0) + fatal("posix_spawn: %s", strerror(errno)); + + posix_spawn_file_actions_destroy(&actions); + } -#else /* !WINDOWS */ +#else do_cmd_pid = fork(); -#endif /* !WINDOWS */ if (do_cmd_pid == 0) { /* Child. */ close(pin[1]); @@ -338,6 +353,7 @@ do_cmd(char *host, char *remuser, int port, char *cmd, int *fdin, int *fdout) } else if (do_cmd_pid == -1) { fatal("fork: %s", strerror(errno)); } +#endif /* Parent. Close the other side, and return the local side. */ close(pin[0]); *fdout = pin[1]; @@ -370,9 +386,13 @@ do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout) port = sshport; /* Fork a child to execute the command on the remote host using ssh. */ -#ifdef WINDOWS +#ifdef FORK_NOT_SUPPORTED /* generate command line and spawn_child */ replacearg(&args, 0, "%s", ssh_program); + if (port != -1) { + addargs(&args, "-p"); + addargs(&args, "%d", port); + } if (remuser != NULL) { addargs(&args, "-l"); addargs(&args, "%s", remuser); @@ -381,11 +401,21 @@ do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout) addargs(&args, "%s", host); addargs(&args, "%s", cmd); - pid = spawn_child(args.list[0], args.list + 1, fdin, fdout, STDERR_FILENO, 0); - -#else /* !WINDOWS */ + { + posix_spawn_file_actions_t actions; + pid = -1; + + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, fdin, STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, fdout, STDOUT_FILENO) != 0 ) + fatal("posix_spawn initialization failed"); + else if (posix_spawn(&pid, args.list[0], &actions, NULL, args.list, NULL) != 0) + fatal("posix_spawn: %s", strerror(errno)); + + posix_spawn_file_actions_destroy(&actions); + } +#else pid = fork(); -#endif /* !WINDOWS */ if (pid == 0) { dup2(fdin, 0); dup2(fdout, 1); @@ -409,6 +439,7 @@ do_cmd2(char *host, char *remuser, int port, char *cmd, int fdin, int fdout) } else if (pid == -1) { fatal("fork: %s", strerror(errno)); } +#endif while (waitpid(pid, &status, 0) == -1) if (errno != EINTR) fatal("do_cmd2: waitpid: %s", strerror(errno)); diff --git a/servconf.c b/servconf.c index d958fbf..241e696 100644 --- a/servconf.c +++ b/servconf.c @@ -660,10 +660,8 @@ derelativise_path(const char *path) if (strcasecmp(path, "none") == 0) return xstrdup("none"); expanded = tilde_expand_filename(path, getuid()); -#ifdef WINDOWS - /* Windows absolute paths - \abc, /abc, c:\abc, c:/abc*/ - if (*expanded == '/' || *expanded == '\\' || - (*expanded != '\0' && expanded[1] == ':')) +#ifdef WINDOWS + if (is_absolute_path(expanded)) #else /* !WINDOWS */ if (*expanded == '/') #endif /* !WINDOWS */ diff --git a/session.c b/session.c index 6d3b605..7bcd36a 100644 --- a/session.c +++ b/session.c @@ -518,9 +518,6 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) { *cmd = '\0'; } - /* load user profile */ - mm_load_profile(s->pw->pw_name, ((INT_PTR)s->authctxt->auth_token) & 0xffffffff); - /* start the process */ { memset(&si, 0, sizeof(STARTUPINFO)); @@ -536,28 +533,13 @@ int do_exec_windows(struct ssh *ssh, Session *s, const char *command, int pty) { si.hStdError = (HANDLE)w32_fd_to_handle(pipeerr[1]); si.lpDesktop = NULL; - hToken = s->authctxt->auth_token; - debug("Executing command: %s", exec_command); UTF8_TO_UTF16_FATAL(exec_command_w, exec_command); - _putenv_s("SSH_ASYNC_STDIN", "1"); - _putenv_s("SSH_ASYNC_STDOUT", "1"); - _putenv_s("SSH_ASYNC_STDERR", "1"); - /* in debug mode launch using sshd.exe user context */ - if (debug_flag) - create_process_ret_val = CreateProcessW(NULL, exec_command_w, NULL, NULL, TRUE, - DETACHED_PROCESS, NULL, pw_dir_w, - &si, &pi); - else /* launch as client user context */ - create_process_ret_val = CreateProcessAsUserW(hToken, NULL, exec_command_w, NULL, NULL, TRUE, - DETACHED_PROCESS , NULL, pw_dir_w, - &si, &pi); - - _putenv_s("SSH_ASYNC_STDIN", ""); - _putenv_s("SSH_ASYNC_STDOUT", ""); - _putenv_s("SSH_ASYNC_STDERR", ""); + create_process_ret_val = CreateProcessW(NULL, exec_command_w, NULL, NULL, TRUE, + DETACHED_PROCESS, NULL, pw_dir_w, + &si, &pi); if (!create_process_ret_val) fatal("ERROR. Cannot create process (%u).\n", GetLastError()); @@ -2162,7 +2144,11 @@ session_pty_req(struct ssh *ssh, Session *s) /* Allocate a pty and open it. */ debug("Allocating pty."); +#ifdef WINDOWS + if (!(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, +#else if (!PRIVSEP(pty_allocate(&s->ptyfd, &s->ttyfd, s->tty, +#endif sizeof(s->tty)))) { free(s->term); s->term = NULL; diff --git a/sftp.c b/sftp.c index d9592d6..22a18a2 100644 --- a/sftp.c +++ b/sftp.c @@ -2309,6 +2309,10 @@ connect_to_server(char *path, char **args, int *in, int *out) *out = pout[1]; c_in = pout[0]; c_out = pin[1]; + fcntl(pout[0], F_SETFD, FD_CLOEXEC); + fcntl(pout[1], F_SETFD, FD_CLOEXEC); + fcntl(pin[0], F_SETFD, FD_CLOEXEC); + fcntl(pin[1], F_SETFD, FD_CLOEXEC); #else /* USE_PIPES */ int inout[2]; @@ -2316,19 +2320,27 @@ connect_to_server(char *path, char **args, int *in, int *out) fatal("socketpair: %s", strerror(errno)); *in = *out = inout[0]; c_in = c_out = inout[1]; + fcntl(inout[0], F_SETFD, FD_CLOEXEC); + fcntl(inout[1], F_SETFD, FD_CLOEXEC); #endif /* USE_PIPES */ -#ifdef WINDOWS - /* fork replacement on Windows */ - /* disable inheritance on local pipe ends*/ - fcntl(pout[1], F_SETFD, FD_CLOEXEC); - fcntl(pin[0], F_SETFD, FD_CLOEXEC); - sshpid = spawn_child(path, args + 1, c_in, c_out, STDERR_FILENO, 0); - if (sshpid == -1) -#else /* !WINDOWS */ +#ifdef FORK_NOT_SUPPORTED + { + posix_spawn_file_actions_t actions; + sshpid = -1; + + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, c_in, STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, c_out, STDOUT_FILENO) != 0 ) + fatal("posix_spawn initialization failed"); + else if (posix_spawn(&sshpid, path, &actions, NULL, args, NULL) != 0) + fatal("posix_spawn: %s", strerror(errno)); + + posix_spawn_file_actions_destroy(&actions); + } +#else if ((sshpid = fork()) == -1) -#endif /* !WINDOWS */ fatal("fork: %s", strerror(errno)); else if (sshpid == 0) { if ((dup2(c_in, STDIN_FILENO) == -1) || @@ -2354,7 +2366,7 @@ connect_to_server(char *path, char **args, int *in, int *out) fprintf(stderr, "exec: %s: %s\n", path, strerror(errno)); _exit(1); } - +#endif signal(SIGTERM, killchild); signal(SIGINT, killchild); signal(SIGHUP, killchild); diff --git a/sshd.c b/sshd.c index a913dc9..c7061f5 100644 --- a/sshd.c +++ b/sshd.c @@ -128,6 +128,12 @@ #define REEXEC_CONFIG_PASS_FD (STDERR_FILENO + 3) #define REEXEC_MIN_FREE_FD (STDERR_FILENO + 4) +/* Privilege separation related spawn fds */ +#define PRIVSEP_MONITOR_FD (STDERR_FILENO + 1) +#define PRIVSEP_LOG_FD (STDERR_FILENO + 2) +#define PRIVSEP_UNAUTH_MIN_FREE_FD (PRIVSEP_LOG_FD + 1) +#define PRIVSEP_AUTH_MIN_FREE_FD (PRIVSEP_MONITOR_FD + 1) + extern char *__progname; /* Server configuration options. */ @@ -167,12 +173,7 @@ int saved_argc; /* re-exec */ int rexeced_flag = 0; -#ifdef WINDOWS -/* rexec is not applicable in Windows */ -int rexec_flag = 0; -#else /* !WINDOWS */ int rexec_flag = 1; -#endif /* !WINDOWS */ int rexec_argc = 0; char **rexec_argv; @@ -195,6 +196,10 @@ char *server_version_string = NULL; int auth_sock = -1; int have_agent = 0; +int privsep_unauth_child = 0; +int privsep_auth_child = 0; +int tmp_sock = 0; + /* * Any really sensitive data in the application is contained in this * structure. The idea is that this structure could be locked into memory so @@ -229,20 +234,10 @@ int *startup_pipes = NULL; int startup_pipe; /* in child */ /* variables used for privilege separation */ -#ifdef WINDOWS -/* Windows does not use Unix privilege separation model */ -int use_prevsep = 0; -#else int use_privsep = -1; -#endif struct monitor *pmonitor = NULL; int privsep_is_preauth = 1; -#ifdef WINDOWS -/* Windows does not use Unix privilege separation model */ -static int privsep_chroot = 0; -#else static int privsep_chroot = 1; -#endif /* global authentication context */ Authctxt *the_authctxt = NULL; @@ -256,9 +251,6 @@ Buffer loginmsg; /* Unprivileged user */ struct passwd *privsep_pw = NULL; -/* is child process - used by Windows implementation*/ -int is_child = 0; - /* Prototypes for various functions defined later in this file. */ void destroy_sensitive_data(void); void demote_sensitive_data(void); @@ -542,29 +534,6 @@ reseed_prngs(void) explicit_bzero(rnd, sizeof(rnd)); } -#ifdef WINDOWS -/* - * No-OP defs for preauth routines for Windows - * these should go away once the privilege separation - * related code is refactored to be invoked only when applicable - */ -static void -privsep_preauth_child(void) { - return; -} - -static int -privsep_preauth(Authctxt *authctxt) { - return 0; -} - -static void -privsep_postauth(Authctxt *authctxt) { - return; -} - -#else /* !WINDOWS */ -/* Unix privilege separation routines */ static void privsep_preauth_child(void) { @@ -603,6 +572,162 @@ privsep_preauth_child(void) } } +void +send_rexec_state(int, struct sshbuf *); +static void send_config_state(int fd, struct sshbuf *conf) +{ + send_rexec_state(fd, conf); +} + +void +recv_rexec_state(int, Buffer *); +static void recv_config_state(int fd, Buffer *conf) +{ + recv_rexec_state(fd, conf); +} + + +static void +send_idexch_state(int fd) +{ + struct sshbuf *m; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + if (sshbuf_put_cstring(m, client_version_string) != 0 || + sshbuf_put_cstring(m, server_version_string) != 0) + fatal("%s: buffer error", __func__); + + if (ssh_msg_send(fd, 0, m) == -1) + fatal("%s: ssh_msg_send failed", __func__); + + sshbuf_free(m); +} + +static void +recv_idexch_state(int fd) +{ + Buffer m; + char *cp; + size_t tmp; + + buffer_init(&m); + + if (ssh_msg_recv(fd, &m) == -1) + fatal("%s: ssh_msg_recv failed", __func__); + + if (buffer_get_char(&m) != 0) + fatal("%s: recv_idexch_state version mismatch", __func__); + + if (sshbuf_get_cstring(&m, &client_version_string, &tmp) != 0 || + sshbuf_get_cstring(&m, &server_version_string, &tmp) != 0 ) + fatal("%s: unable to retrieve idexch state", __func__); + + buffer_free(&m); +} + +static void +send_hostkeys_state(int fd) +{ + struct sshbuf *m; + int i; + u_char *blob = NULL; + size_t blen = 0; + + if ((m = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new failed", __func__); + + sshbuf_put_u32(m, options.num_host_key_files); + for (i = 0; i < options.num_host_key_files; i++) { + if (blob) { + free(blob); + blob = NULL; + } + if (sensitive_data.host_pubkeys[i]) { + sshkey_to_blob(sensitive_data.host_pubkeys[i], &blob, &blen); + sshbuf_put_string(m, blob, blen); + } + else + sshbuf_put_string(m, NULL, 0); + } + + for (i = 0; i < options.num_host_key_files; i++) { + if (blob) { + free(blob); + blob = NULL; + } + if (sensitive_data.host_certificates[i]) { + sshkey_to_blob(sensitive_data.host_certificates[i], &blob, &blen); + sshbuf_put_string(m, blob, blen); + } + else + sshbuf_put_string(m, NULL, 0); + } + + if (ssh_msg_send(fd, 0, m) == -1) + fatal("%s: ssh_msg_send failed", __func__); + + if (blob) + free(blob); + sshbuf_free(m); +} + +static void +recv_hostkeys_state(int fd) +{ + Buffer b, *m = &b; + char *cp; + struct sshkey *key = NULL; + const char *blob; + int blen; + + buffer_init(m); + + if (ssh_msg_recv(fd, m) == -1) + fatal("%s: ssh_msg_recv failed", __func__); + + if (buffer_get_char(m) != 0) + fatal("%s: recv_hostkeys_state version mismatch", __func__); + + int num = buffer_get_int(m); + sensitive_data.host_keys = xcalloc(num, sizeof(struct sshkey *)); + sensitive_data.host_pubkeys = xcalloc(num, sizeof(struct sshkey *)); + sensitive_data.host_certificates = xcalloc(num, sizeof(struct sshkey *)); + for (int i = 0; i < num; i++) { + blob = buffer_get_string_ptr(m, &blen); + sensitive_data.host_pubkeys[i] = NULL; + sensitive_data.host_keys[i] = NULL; + + if (blen) { + sshkey_from_blob(blob, blen, &key); + sensitive_data.host_pubkeys[i] = key; + } + } + for (int i = 0; i < num; i++) { + blob = buffer_get_string_ptr(m, &blen); + sensitive_data.host_certificates[i] = NULL; + if (blen) { + sshkey_from_blob(blob, blen, &key); + sensitive_data.host_certificates[i] = key; + } + } + + buffer_free(m); +} + +static char** +privsep_child_cmdline(int authenticated) +{ + char** argv = rexec_argv ? rexec_argv : saved_argv; + int argc = rexec_argv ? rexec_argc : saved_argc - 1; + if (authenticated) + argv[argc] = "-z"; + else + argv[argc] = "-y"; + + return argv; +} + static int privsep_preauth(Authctxt *authctxt) { @@ -615,6 +740,60 @@ privsep_preauth(Authctxt *authctxt) /* Store a pointer to the kex for later rekeying */ pmonitor->m_pkex = &active_state->kex; +#ifdef FORK_NOT_SUPPORTED + if (privsep_auth_child) { + authctxt->pw = w32_getpwuid(1); + authctxt->valid = 1; + return 1; + } + else if (privsep_unauth_child) { + close(pmonitor->m_sendfd); + close(pmonitor->m_log_recvfd); + close(pmonitor->m_recvfd); + close(pmonitor->m_log_sendfd); + + pmonitor->m_recvfd = PRIVSEP_MONITOR_FD; + pmonitor->m_log_sendfd = PRIVSEP_LOG_FD; + + /* Arrange for logging to be sent to the monitor */ + set_log_handler(mm_log_handler, pmonitor); + + privsep_preauth_child(); + setproctitle("%s", "[net]"); + return 0; + } + else { /* parent */ + posix_spawn_file_actions_t actions; + + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, tmp_sock, STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, tmp_sock, STDOUT_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, pmonitor->m_recvfd, PRIVSEP_MONITOR_FD) != 0 || + posix_spawn_file_actions_adddup2(&actions, pmonitor->m_log_sendfd, PRIVSEP_LOG_FD) != 0 ) + error("posix_spawn initialization failed"); + else { + char** argv = privsep_child_cmdline(0); + if (__posix_spawn_asuser(&pid, argv[0], &actions, NULL, argv, NULL, SSH_PRIVSEP_USER) != 0) + error("posix_spawn failed"); + posix_spawn_file_actions_destroy(&actions); + } + close(pmonitor->m_recvfd); + close(pmonitor->m_log_sendfd); + send_config_state(pmonitor->m_sendfd, &cfg); + send_hostkeys_state(pmonitor->m_sendfd); + send_idexch_state(pmonitor->m_sendfd); + monitor_child_preauth(authctxt, pmonitor); + while (waitpid(pid, &status, 0) < 0) { + if (errno == EINTR) + continue; + pmonitor->m_pid = -1; + fatal("%s: waitpid: %s", __func__, strerror(errno)); + } + privsep_is_preauth = 0; + pmonitor->m_pid = -1; + return 1; + } +#else if (use_privsep == PRIVSEP_ON) box = ssh_sandbox_init(pmonitor); pid = fork(); @@ -670,6 +849,7 @@ privsep_preauth(Authctxt *authctxt) return 0; } +#endif } static void @@ -688,6 +868,44 @@ privsep_postauth(Authctxt *authctxt) /* New socket pair */ monitor_reinit(pmonitor); +#ifdef FORK_NOT_SUPPORTED + if (!privsep_auth_child) { /* parent */ + posix_spawn_file_actions_t actions; + + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, tmp_sock, STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, tmp_sock, STDOUT_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, pmonitor->m_recvfd, PRIVSEP_MONITOR_FD) != 0) + error("posix_spawn initialization failed"); + else { + char** argv = privsep_child_cmdline(1); + if (__posix_spawn_asuser(&pmonitor->m_pid, argv[0], &actions, NULL, argv, NULL, authctxt->pw->pw_name) != 0) + error("posix_spawn failed"); + posix_spawn_file_actions_destroy(&actions); + } + + send_config_state(pmonitor->m_sendfd, &cfg); + send_hostkeys_state(pmonitor->m_sendfd); + send_idexch_state(pmonitor->m_sendfd); + monitor_send_keystate(pmonitor); + monitor_clear_keystate(pmonitor); + monitor_child_postauth(pmonitor); + /* NEVERREACHED */ + exit(0); + } + /* child */ + close(pmonitor->m_sendfd); + close(pmonitor->m_recvfd); + + pmonitor->m_recvfd = PRIVSEP_MONITOR_FD; + + monitor_recv_keystate(pmonitor); + monitor_apply_keystate(pmonitor); + packet_set_authenticated(); +skip: + return; + +#else pmonitor->m_pid = fork(); if (pmonitor->m_pid == -1) fatal("fork of unprivileged child failed"); @@ -723,10 +941,9 @@ privsep_postauth(Authctxt *authctxt) * this information is not part of the key state. */ packet_set_authenticated(); +#endif } -#endif /* !WINDOWS */ - static char * list_hostkey_types(void) { @@ -1272,6 +1489,11 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) close(startup_p[1]); continue; } + fcntl(startup_p[0], F_SETFD, FD_CLOEXEC); + fcntl(startup_p[1], F_SETFD, FD_CLOEXEC); + fcntl(config_s[0], F_SETFD, FD_CLOEXEC); + fcntl(config_s[1], F_SETFD, FD_CLOEXEC); + for (j = 0; j < options.max_startups; j++) if (startup_pipes[j] == -1) { @@ -1308,52 +1530,34 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) break; } +#ifdef FORK_NOT_SUPPORTED + { + posix_spawn_file_actions_t actions; + posix_spawnattr_t attributes; + if (posix_spawn_file_actions_init(&actions) != 0 || + posix_spawn_file_actions_adddup2(&actions, *newsock, STDIN_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, *newsock, STDOUT_FILENO) != 0 || + posix_spawn_file_actions_adddup2(&actions, startup_p[1], REEXEC_STARTUP_PIPE_FD) != 0 || + posix_spawn_file_actions_adddup2(&actions, config_s[1], REEXEC_CONFIG_PASS_FD) != 0 || + posix_spawnattr_init(&attributes) != 0 || + posix_spawnattr_setflags(&attributes, POSIX_SPAWN_SETPGROUP) != 0 || + posix_spawnattr_setpgroup(&attributes, 0) != 0) + error("posix_spawn initialization failed"); + else { + if (posix_spawn(&pid, rexec_argv[0], &actions, &attributes, rexec_argv, NULL) != 0) + error("posix_spawn failed"); + posix_spawn_file_actions_destroy(&actions); + posix_spawnattr_destroy(&attributes); + } + + } +#else /* * Normal production daemon. Fork, and have * the child process the connection. The * parent continues listening. */ platform_pre_fork(); -#ifdef WINDOWS - /* - * fork() repleacement for Windows - - * - Put accepted socket in a env varaibale - * - disable inheritance on listening socket and startup fds - * - Spawn child sshd.exe - */ - { - char* path_utf8 = utf16_to_utf8(GetCommandLineW()); - /* large enough to hold pointer value in hex */ - char fd_handle[30]; - - if (path_utf8 == NULL) - fatal("Failed to alloc memory"); - - if (snprintf(fd_handle, sizeof(fd_handle), "%p", - w32_fd_to_handle(*newsock)) == -1 - || SetEnvironmentVariable("SSHD_REMSOC", fd_handle) == FALSE - || snprintf(fd_handle, sizeof(fd_handle), "%p", - w32_fd_to_handle(startup_p[1])) == -1 - || SetEnvironmentVariable("SSHD_STARTUPSOC", fd_handle) == FALSE - || fcntl(startup_p[0], F_SETFD, FD_CLOEXEC) == -1) { - error("unable to set environment for child"); - close(*newsock); - /* - * close child end of startup pipe. parent end will - * automatically be cleaned up on next iteration - */ - close(startup_p[1]); - free(path_utf8); - continue; - } - - pid = spawn_child(path_utf8, NULL, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, CREATE_NEW_PROCESS_GROUP); - free(path_utf8); - close(*newsock); - SetEnvironmentVariable("SSHD_REMSOC", NULL); - SetEnvironmentVariable("SSHD_STARTUPSOC", NULL); - } -#else /* !WINDOWS */ if ((pid = fork()) == 0) { /* @@ -1378,7 +1582,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) close(config_s[0]); break; } -#endif /* !WINDOWS */ + /* Parent. Stay in the loop. */ platform_post_fork_parent(pid); if (pid < 0) @@ -1386,6 +1590,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) else debug("Forked child %ld.", (long)pid); +#endif /* fork unsupported */ close(startup_p[1]); if (rexec_flag) { @@ -1552,7 +1757,7 @@ main(int ac, char **av) /* Parse command-line arguments. */ while ((opt = getopt(ac, av, - "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrt")) != -1) { + "C:E:b:c:f:g:h:k:o:p:u:46DQRTdeiqrtyz")) != -1) { switch (opt) { case '4': options.address_family = AF_INET; @@ -1653,6 +1858,18 @@ main(int ac, char **av) exit(1); free(line); break; + case 'y': + privsep_unauth_child = 1; + rexec_flag = 0; + logfile = NULL; + //Sleep(10 * 1000); + break; + case 'z': + privsep_auth_child = 1; + rexec_flag = 0; + logfile = NULL; + //Sleep(10 * 1000); + break; case '?': default: usage(); @@ -1661,10 +1878,19 @@ main(int ac, char **av) } if (rexeced_flag || inetd_flag) rexec_flag = 0; - if (!test_flag && (rexec_flag && (av[0] == NULL || *av[0] != '/'))) + if (!test_flag && !debug_flag && rexec_flag && +#ifdef WINDOWS + (av[0] == NULL || *av[0] == '\0' || av[0][1] != ':')) +#else + (av[0] == NULL || *av[0] != '/')) +#endif fatal("sshd re-exec requires execution with an absolute path"); if (rexeced_flag) closefrom(REEXEC_MIN_FREE_FD); + else if (privsep_unauth_child) + closefrom(PRIVSEP_UNAUTH_MIN_FREE_FD); + else if (privsep_auth_child) + closefrom(PRIVSEP_AUTH_MIN_FREE_FD); else closefrom(REEXEC_DEVCRYPTO_RESERVED_FD); @@ -1714,6 +1940,8 @@ main(int ac, char **av) buffer_init(&cfg); if (rexeced_flag) recv_rexec_state(REEXEC_CONFIG_PASS_FD, &cfg); + else if (privsep_unauth_child || privsep_auth_child) + recv_config_state(PRIVSEP_MONITOR_FD, &cfg); else if (strcasecmp(config_file_name, "none") != 0) load_server_config(config_file_name, &cfg); @@ -1725,15 +1953,6 @@ main(int ac, char **av) /* Fill in default values for those options not explicitly set. */ fill_default_server_options(&options); -#ifdef WINDOWS - /* - * For windows, enable logging right away to capture failures while loading private host keys. - * On Unix, logging at configured level is not done until private host keys are loaded. Why?? - */ - if (!debug_flag) - log_init(__progname, options.log_level, options.log_facility, log_stderr); -#endif // WINDOWS - /* challenge-response is implemented via keyboard interactive */ if (options.challenge_response_authentication) options.kbd_interactive_authentication = 1; @@ -1781,7 +2000,6 @@ main(int ac, char **av) #endif ); -#ifndef WINDOWS /* not applicable in Windows */ /* Store privilege separation user for later use if required. */ privsep_chroot = use_privsep && (getuid() == 0 || geteuid() == 0); if ((privsep_pw = getpwnam(SSH_PRIVSEP_USER)) == NULL) { @@ -1796,7 +2014,11 @@ main(int ac, char **av) privsep_pw->pw_passwd = xstrdup("*"); } endpwent(); -#endif /* !WINDOWS */ + + if (privsep_auth_child || privsep_unauth_child) { + recv_hostkeys_state(PRIVSEP_MONITOR_FD); + goto done_loading_hostkeys; + } /* load host keys */ sensitive_data.host_keys = xcalloc(options.num_host_key_files, @@ -1818,6 +2040,7 @@ main(int ac, char **av) for (i = 0; i < options.num_host_key_files; i++) { if (options.host_key_files[i] == NULL) continue; + if (privsep_unauth_child || privsep_auth_child) key = NULL; else /*TODO - remove this*/ key = key_load_private(options.host_key_files[i], "", NULL); pubkey = key_load_public(options.host_key_files[i], NULL); @@ -1903,7 +2126,7 @@ main(int ac, char **av) debug("host certificate: #%u type %d %s", j, key->type, key_type(key)); } - +done_loading_hostkeys: if (privsep_chroot) { struct stat st; @@ -1996,12 +2219,13 @@ main(int ac, char **av) /* Get a connection, either from inetd or a listening TCP socket */ if (inetd_flag) { server_accept_inetd(&sock_in, &sock_out); + } else if (privsep_unauth_child || privsep_auth_child) { + sock_in = sock_out = dup(STDIN_FILENO); + close(STDIN_FILENO); + close(STDOUT_FILENO); + startup_pipe = -1; } else { platform_pre_listen(); -#ifdef WINDOWS - /* For Windows child sshd, skip listener */ - if (is_child == 0) -#endif /* WINDOWS */ server_listen(); signal(SIGHUP, sighup_handler); @@ -2025,27 +2249,6 @@ main(int ac, char **av) } } -#ifdef WINDOWS - /* Windows - for sshd child, pick up the accepted socket*/ - if (is_child) { - char *stopstring; - DWORD_PTR handle; - - handle = strtol(getenv("SSHD_REMSOC"), &stopstring, 16); - SetEnvironmentVariable("SSHD_REMSOC", NULL); - debug("child socket: %d", handle); - sock_in = sock_out = newsock = w32_allocate_fd_for_handle((HANDLE)handle, TRUE); - fcntl(newsock, F_SETFD, FD_CLOEXEC); - - handle = strtol(getenv("SSHD_STARTUPSOC"), &stopstring, 16); - SetEnvironmentVariable("SSHD_STARTUPSOC", NULL); - debug("child startup_pipe: %d", handle); - startup_pipe = w32_allocate_fd_for_handle((HANDLE)handle, FALSE); - fcntl(startup_pipe, F_SETFD, FD_CLOEXEC); - } - else /* Windows and Unix sshd parent */ -#endif /* WINDOWS */ - /* Accept a connection and return in a forked child */ server_accept_loop(&sock_in, &sock_out, &newsock, config_s); @@ -2069,6 +2272,7 @@ main(int ac, char **av) error("setsid: %.100s", strerror(errno)); #endif +#ifndef FORK_NOT_SUPPORTED if (rexec_flag) { int fd; @@ -2107,7 +2311,7 @@ main(int ac, char **av) debug("rexec cleanup in %d out %d newsock %d pipe %d sock %d", sock_in, sock_out, newsock, startup_pipe, config_s[0]); } - +#endif /* Executed child processes don't need these. */ fcntl(sock_out, F_SETFD, FD_CLOEXEC); fcntl(sock_in, F_SETFD, FD_CLOEXEC); @@ -2129,6 +2333,7 @@ main(int ac, char **av) * Register our connection. This turns encryption off because we do * not have a key. */ + tmp_sock = sock_in; packet_set_connection(sock_in, sock_out); packet_set_server(); ssh = active_state; /* XXX */ @@ -2166,6 +2371,10 @@ main(int ac, char **av) rdomain = ssh_packet_rdomain_in(ssh); + if (privsep_unauth_child || privsep_auth_child) { + recv_idexch_state(PRIVSEP_MONITOR_FD); + goto idexch_done; + } /* Log the connection. */ laddr = get_local_ipaddr(sock_in); verbose("Connection from %s port %d on %s port %d%s%s%s", @@ -2188,6 +2397,7 @@ main(int ac, char **av) alarm(options.login_grace_time); sshd_exchange_identification(ssh, sock_in, sock_out); +idexch_done: packet_set_nonblocking(); /* allocate authentication context */