Implement Unix styled privilege separation in Windows (#258)

OpenSSh privilege separation model - http://www.citi.umich.edu/u/provos/ssh/privsep.html 
Posix_spawn is implemented in POSIX adapter as an alternative to fork() that is heavily used in Privilege separation. 
Additional state info is added to sshd to accommodate distinguishing the various modes (privileged monitor, unprivileged child, authenticated child).
Required service state (like config and host keys) is transmitted over pipes from monitor to child processes.
Changes to installation scripts and tests to accomodate new architectural changes
This commit is contained in:
Manoj Ampalam 2018-01-15 13:57:31 -08:00 committed by GitHub
parent 8906783fa4
commit fdd54b6334
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 1597 additions and 1873 deletions

View File

@ -230,10 +230,13 @@ 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 *tmp = NULL, *domain = NULL, *lsa_auth_pkg = NULL;
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;
@ -249,23 +252,14 @@ sys_auth_passwd_lsa(Authctxt *authctxt, const char *password)
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)
error("utf16_to_utf8 failed to convert lsa_auth_pkg_w:%ls", lsa_auth_pkg_w);
fatal("utf16_to_utf8 failed to convert lsa_auth_pkg_w:%ls", lsa_auth_pkg_w);
if ((tmp = strstr(authctxt->pw->pw_name, "@")) != NULL) {
domain_len = strlen(tmp) + 1;
domain = (char*) malloc(domain_len * sizeof(char));
memcpy(domain, tmp, domain_len);
domain[domain_len] = '\0';
}
debug("Authenticating using LSA Auth Package:%ls", lsa_auth_pkg_w);
authctxt->auth_token = mm_auth_custom_lsa(authctxt->pw->pw_name, password, domain, lsa_auth_pkg);
password_auth_token = process_custom_lsa_auth(authctxt->pw->pw_name, password, lsa_auth_pkg);
}
}
done:
if (domain)
free(domain);
if (lsa_auth_pkg_w)
free(lsa_auth_pkg_w);
@ -301,7 +295,7 @@ 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) == TRUE)
authctxt->auth_token = (void*)(INT_PTR)token;
password_auth_token = token;
else {
if (GetLastError() == ERROR_PASSWORD_MUST_CHANGE)
/*
@ -318,7 +312,7 @@ sys_auth_passwd(Authctxt *authctxt, const char *password)
}
done:
if (authctxt->auth_token)
if (password_auth_token)
r = 1;
if (user_utf16)

3
auth.h
View File

@ -93,9 +93,6 @@ struct Authctxt {
/* Information exposed to session */
struct sshbuf *session_info; /* Auth info for environment */
#ifdef WINDOWS
void *auth_token;
#endif
};
/*

View File

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

View File

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

View File

@ -345,7 +345,7 @@ function Start-OpenSSHPackage
$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 +="FixHostFilePermissions.ps1", "FixUserFilePermissions.ps1", "OpenSSHUtils.psm1", "OpenSSHUtils.psd1"
$packageName = "OpenSSH-Win64"
if ($NativeHostArch -ieq 'x86') {

View File

@ -188,11 +188,6 @@ 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
}
}
}
@ -462,11 +457,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

View File

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

View File

@ -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 _PATH_PRIVSEP_CHROOT_DIR "."
#define SSHDIR "."
#define _PATH_SFTP_SERVER "sftp-server.exe"
#define _PATH_SSH_PROGRAM "ssh.exe"
#define _PATH_LS "dir"
#define FORK_NOT_SUPPORTED 1

View File

@ -196,7 +196,6 @@
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)")</Command>
<Message>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</Message>
</PostBuildEvent>
@ -229,7 +228,6 @@ 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)")</Command>
<Message>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</Message>
</PostBuildEvent>
@ -262,7 +260,6 @@ 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)")</Command>
<Message>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</Message>
</PostBuildEvent>
@ -295,7 +292,6 @@ 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)")</Command>
<Message>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</Message>
</PostBuildEvent>
@ -332,7 +328,6 @@ 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)")</Command>
<Message>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</Message>
</PostBuildEvent>
@ -369,7 +364,6 @@ 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)")</Command>
<Message>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</Message>
</PostBuildEvent>
@ -406,7 +400,6 @@ 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)")</Command>
<Message>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</Message>
</PostBuildEvent>
@ -443,7 +436,6 @@ 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)")</Command>
<Message>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</Message>
</PostBuildEvent>

View File

@ -10,244 +10,6 @@ $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)
}
if (-not (Test-Path $sshdpath)) {
throw "sshd.exe is not present in script path"
}
@ -267,12 +29,14 @@ 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
# create sshd account with random password if it does not already exist
# New-LocalUser only exists on PS V5
# New-LocalUser -Name sshd -AccountNeverExpires -Password (ConvertTo-SecureString (new-guid).guid -AsPlainText -Force) -ErrorAction SilentlyContinue | Out-Null
# net user prompts if password is more than 14 chars long
net user sshd (new-guid).guid.substring(1,14) /add > $null 2>&1
Add-Privilege -Account $sshdSid -Privilege SeAssignPrimaryTokenPrivilege
Add-Privilege -Account $sshdSid -Privilege SeServiceLogonRight
# create logs folder and set its permissions
if(-not (test-path $logsdir -PathType Container))
@ -288,15 +52,4 @@ $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
Write-Host -ForegroundColor Green "sshd and ssh-agent services successfully installed"

View File

@ -1,96 +0,0 @@
<#
Author: manoj.ampalam@microsoft.com
Description: ssh-add.exe like Powershell utility to do host key management.
Input parameter mimic ssh-add.exe cmdline arguments.
Host keys on Windows need to be registered as SYSTEM (i.e ssh-add.exe would
need to run as SYSTEM while talking to ssh-agent). This typically requires
an external utility like psexec.
This script tries to use the Task scheduler option:
- registers a task scheduler task to run ssh-add.exe operation as SYSTEM
- actual output of ssh-add.exe is written to file (both stdout and stderr)
- Dumps the file contents to console
#>
# see https://linux.die.net/man/1/ssh-add for what the arguments mean
[CmdletBinding(DefaultParameterSetName='Add_key')]
Param(
[Parameter(ParameterSetName="List_fingerprints")]
[switch]$List_fingerprints, #ssh-add -l
[Parameter(ParameterSetName="List_pubkeys")]
[switch]$List_pubkeys, #ssh-add -L
[Parameter(ParameterSetName="Delete_key")]
[switch]$Delete_key, #ssh-add -d
[Parameter(ParameterSetName="Delete_all")]
[switch]$Delete_all, #ssh-add -D
[Parameter(Mandatory, Position=0, ParameterSetName="Delete_key")]
[Parameter(Mandatory, Position=0, ParameterSetName="Add_key")]
[ValidateNotNullOrEmpty()]
[string]$key
)
$ssh_add_cmd = get-command ssh-add.exe -ErrorAction Ignore
if($ssh_add_cmd -eq $null)
{
Throw "Cannot find ssh-add.exe."
}
#create ssh-add cmdlinet
$ssh_add_cmd_str = $ssh_add_cmd.Path
if ($List_fingerprints) { $ssh_add_cmd_str += " -l" }
elseif ($List_pubkeys) { $ssh_add_cmd_str += " -L" }
elseif ($Delete_key) { $ssh_add_cmd_str += " -d $key" }
elseif ($Delete_all) { $ssh_add_cmd_str += " -D" }
else
{
if ( ($key.Length -gt 0) -and (-not($key.Contains("host"))) ) {
Do {
$input = Read-Host -Prompt "Are you sure the provided key is a host key? [Yes] Y; [No] N (default is `"Y`")"
if([string]::IsNullOrEmpty($input))
{
$input = 'Y'
}
} until ($input -match "^(y(es)?|N(o)?)$")
$result = $Matches[0]
if (-not($result.ToLower().Startswith('y'))) { exit }
}
$ssh_add_cmd_str += " $key"
}
#globals
$taskfolder = "\OpenSSHUtils\hostkey_tasks\"
$taskname = "hostkey_task"
$ssh_add_output = Join-Path (pwd).Path "ssh-add-hostkey-tmp.txt"
$task_argument = "/c `"$ssh_add_cmd_str > $ssh_add_output 2>&1 `""
#create TaskScheduler task
$ac = New-ScheduledTaskAction -Execute "cmd.exe" -Argument $task_argument -WorkingDirectory (pwd).path
$task = Register-ScheduledTask -TaskName $taskname -User System -Action $ac -TaskPath $taskfolder -Force
#run the task
if (Test-Path $ssh_add_output) {Remove-Item $ssh_add_output -Force}
Start-ScheduledTask -TaskPath $taskfolder -TaskName $taskname
#if still running, wait a little while for task to complete
$num = 0
while ((Get-ScheduledTask -TaskName $taskname -TaskPath $taskfolder).State -eq "Running")
{
sleep 1
$num++
if($num -gt 20) { break }
}
if (-not(Test-Path $ssh_add_output)) {throw "cannot find task output file. Something went WRONG!!! "}
#dump output to console
Get-Content $ssh_add_output
#cleanup task and output file
Remove-Item $ssh_add_output -Force
Unregister-ScheduledTask -TaskPath $taskfolder -TaskName $taskname -Confirm:$false

View File

@ -382,25 +382,15 @@
</Manifest>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="$(OpenSSH-Src-Path)\servconf.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\ssh-pubkey\ssh-pubkeydefs.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agent-request.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agent.h" />
<ClInclude Include="$(OpenSSH-Src-Path)groupaccess.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="$(OpenSSH-Src-Path)\auth.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\servconf.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agent-main.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agent.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\authagent-request.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\agentconfig.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\connection.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\ssh-agent\keyagent-request.c" />
<ClCompile Include="$(OpenSSH-Src-Path)auth-options.c" />
<ClCompile Include="$(OpenSSH-Src-Path)auth2-pubkey.c" />
<ClCompile Include="$(OpenSSH-Src-Path)groupaccess.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\logonuser.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc" />

View File

@ -446,8 +446,9 @@
<ClCompile Include="$(OpenSSH-Src-Path)sshlogin.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_sshpty.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\wmain_sshd.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_monitor_wrap.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\logonuser.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_usertoken_utils.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\spawn-ext.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="version.rc" />

View File

@ -150,10 +150,13 @@
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_sshpty.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_monitor_wrap.c">
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\logonuser.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\logonuser.c">
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_usertoken_utils.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\spawn-ext.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

View File

@ -119,5 +119,4 @@ Subsystem sftp sftp-server.exe
# X11Forwarding no
# AllowTcpForwarding no
# ForceCommand cvs server
# PubkeyAcceptedKeyTypes ssh-ed25519*
hostkeyagent \\.\pipe\openssh-ssh-agent
# PubkeyAcceptedKeyTypes ssh-ed25519*

View File

@ -292,6 +292,7 @@
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tncon.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tnnet.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\utf.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\spawn.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\w32fd.h" />
@ -337,6 +338,7 @@
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\debug.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\console.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tnnet.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\inc\spawn.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -19,6 +19,7 @@
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tncon.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tnnet.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\utf.c" />
<ClCompile Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\spawn.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\w32fd.h" />
@ -138,6 +139,9 @@
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\debug.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\console.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\tnnet.h" />
<ClInclude Include="$(OpenSSH-Src-Path)\contrib\win32\win32compat\inc\spawn.h">
<Filter>inc</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="inc">

View File

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

View File

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

View File

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

View File

@ -0,0 +1,77 @@
/*
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
*
* 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);

View File

@ -7,6 +7,7 @@
#include <stddef.h>
#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

View File

@ -2,9 +2,6 @@
#include <VersionHelpers.h>
#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_)) { \
@ -39,4 +36,7 @@ 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);
int get_machine_domain_name(wchar_t *domain, int size);
HANDLE get_user_token(char* user);
int load_user_profile(HANDLE user_token, char* user);

View File

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

View File

@ -1,9 +0,0 @@
/*
* Definitions for IPC between sshd and privileged agent
*/
#pragma once
#define SSH_PRIV_AGENT_MSG_ID 0
#define PUBKEY_AUTH_REQUEST "pubkey"
#define LOAD_USER_PROFILE_REQUEST "loadprofile"
#define CUSTOM_LSA_AUTH_REQUEST "lsaauth"

View File

@ -358,3 +358,9 @@ setegid(gid_t gid)
{
return 0;
}
void
endpwent(void)
{
return;
}

View File

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

View File

@ -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, len))
return -1;
if (completed) {
@ -952,7 +955,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;

View File

@ -0,0 +1,27 @@
#include <Windows.h>
#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;
}

View File

@ -0,0 +1,104 @@
/*
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
*
* Implementation of POSIX spawn family of functions
*/
#include <Windows.h>
#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;
}

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
#include <Windows.h>
#include <stdio.h>
#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();

View File

@ -1,127 +0,0 @@
#include "includes.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/ip.h>
#include <ctype.h>
#include <netdb.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <limits.h>
#include <stdarg.h>
#include <errno.h>
#ifdef HAVE_UTIL_H
#include <util.h>
#endif
#include "openbsd-compat/sys-queue.h"
#include "xmalloc.h"
#include "ssh.h"
#include "log.h"
#include "buffer.h"
#include "misc.h"
#include "servconf.h"
#include "compat.h"
#include "pathnames.h"
#include "cipher.h"
#include "key.h"
#include "kex.h"
#include "mac.h"
#include "match.h"
#include "channels.h"
#include "groupaccess.h"
#include "canohost.h"
#include "packet.h"
#include "hostfile.h"
#include "auth.h"
#include "myproposal.h"
#include "digest.h"
#include "agent.h"
#include <utf.h>
#pragma warning(push, 3)
Buffer cfg;
ServerOptions options;
struct passwd *privsep_pw = NULL;
int auth_sock = -1;
int
auth2_key_already_used(Authctxt *authctxt, const struct sshkey *key)
{
return 0;
}
void
auth2_record_key(Authctxt *authctxt, int authenticated,
const struct sshkey *key)
{
return;
}
int
auth2_methods_valid(const char * c, int i) {
return 1;
}
int
mm_is_monitor(void) {
return 0;
}
int
mm_user_key_allowed(struct passwd *pw, Key *k, int i)
{
return 0;
}
void* mm_auth_pubkey(const char* user_name, const struct sshkey *key,
const u_char *sig, size_t slen, struct sshbuf* b)
{
return NULL;
}
int
kexgex_server(struct ssh * sh) {
return -1;
}
int
load_config() {
char *config_file_name = "sshd_config";
errno_t r = 0;
buffer_init(&cfg);
initialize_server_options(&options);
load_server_config(config_file_name, &cfg);
parse_server_config(&options, config_file_name, &cfg, NULL);
fill_default_server_options(&options);
return 0;
}
int
config_log_level() {
return options.log_level;
}
int
pubkey_allowed(struct sshkey* pubkey, char* user_utf8) {
struct passwd *pw;
if ((pw = w32_getpwnam(user_utf8)) == NULL)
return 0;
return user_key_allowed(pw, pubkey, 1);
}
#pragma warning(pop)

View File

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

View File

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

View File

@ -60,65 +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;
size_t len = 0;
_dupenv_s(&envValue, &len, 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, &len, 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, &len, 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;
}
@ -372,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;
}
@ -396,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;
@ -800,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)
{
@ -911,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
@ -921,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;
@ -999,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) {
@ -1038,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);
}

View File

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

View File

@ -1,261 +0,0 @@
/*
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
* mm_* routines that delegate privileged operations to privileged
* agent.
*
* 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.
*/
#include "includes.h"
#include <sys/types.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <errno.h>
#include <pwd.h>
#include <signal.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#ifdef WITH_OPENSSL
#include <openssl/bn.h>
#include <openssl/dh.h>
#include <openssl/evp.h>
#endif
#include "openbsd-compat/sys-queue.h"
#include "xmalloc.h"
#include "ssh.h"
#ifdef WITH_OPENSSL
#include "dh.h"
#endif
#include "buffer.h"
#include "key.h"
#include "cipher.h"
#include "kex.h"
#include "hostfile.h"
#include "auth.h"
#include "auth-options.h"
#include "packet.h"
#include "mac.h"
#include "log.h"
#include "auth-pam.h"
#include "monitor_wrap.h"
#include "atomicio.h"
#include "monitor_fdpass.h"
#include "misc.h"
#include "uuencode.h"
#include "channels.h"
#include "session.h"
#include "servconf.h"
#include "ssherr.h"
#include "priv-agent.h"
#include "authfd.h"
int priv_agent_sock = -1;
int ssh_request_reply(int, struct sshbuf *, struct sshbuf *);
/*
* Get socket connected to privileged agent
* In Windows, this is implemented within ssh-agent
* that server both as a key-agent (like in Unix) and
* privileged agent.
* This is a temporary accomodation until Windows has
* Unix like privilege separation (monitor and less
* privileged worker)
*/
int get_priv_agent_sock()
{
extern int auth_sock;
char env_value[12]; /* enough to accomodate "ssh-agent"*/
size_t tmp;
if (priv_agent_sock != -1)
return priv_agent_sock;
/* check if auth_sock is populated and connected to "ssh-agent"*/
if (auth_sock != -1 &&
getenv_s(&tmp, env_value, 12, SSH_AUTHSOCKET_ENV_NAME) == 0 &&
strncmp(env_value, "ssh-agent", 12) == 0 )
priv_agent_sock = auth_sock;
else {
struct sockaddr_un sunaddr;
int sock;
memset(&sunaddr, 0, sizeof(sunaddr));
sunaddr.sun_family = AF_UNIX;
strlcpy(sunaddr.sun_path, "\\\\.\\pipe\\openssh-ssh-agent", sizeof(sunaddr.sun_path));
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
debug("%s: unable to create AF_UNIX socket, errno:%d", __func__, errno);
return -1;
}
/* close on exec */
if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 ||
connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) {
close(sock);
debug("%s: unable to connect to privileged agent, errno:%d", __func__, errno);
return -1;
}
priv_agent_sock = sock;
}
return priv_agent_sock;
}
void* mm_auth_pubkey(const char* user_name, const struct sshkey *key,
const u_char *sig, size_t slen, struct sshbuf* b)
{
/* Pass key challenge material to privileged agent to retrieve token upon successful authentication */
struct sshbuf *msg = NULL;
u_char *blob = NULL;
size_t blen = 0;
DWORD token = 0;
int agent_fd;
while (1) {
if ((agent_fd = get_priv_agent_sock()) == -1)
break;
msg = sshbuf_new();
if (!msg)
fatal("%s: out of memory", __func__);
if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 ||
sshbuf_put_cstring(msg, PUBKEY_AUTH_REQUEST) != 0 ||
sshkey_to_blob(key, &blob, &blen) != 0 ||
sshbuf_put_string(msg, blob, blen) != 0 ||
sshbuf_put_cstring(msg, user_name) != 0 ||
sshbuf_put_string(msg, sig, slen) != 0 ||
sshbuf_put_string(msg, sshbuf_ptr(b), sshbuf_len(b)) != 0) {
debug("unable to add data to ssh buffer");
break;
}
if(ssh_request_reply(agent_fd, msg, msg) != 0) {
error("unable to send pubkeyauth request to agent");
break;
}
if (sshbuf_get_u32(msg, &token) != 0)
break;
debug3("%s authenticated via pubkey", user_name);
break;
}
if (blob)
free(blob);
if (msg)
sshbuf_free(msg);
return (void*)(INT_PTR)token;
}
int mm_load_profile(const char* user_name, u_int token)
{
struct sshbuf *msg = NULL;
int agent_fd;
u_char result = 0;
while (1) {
if ((agent_fd = get_priv_agent_sock()) == -1)
break;
msg = sshbuf_new();
if (!msg)
fatal("%s: out of memory", __func__);
if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 ||
sshbuf_put_cstring(msg, LOAD_USER_PROFILE_REQUEST) != 0 ||
sshbuf_put_cstring(msg, user_name) != 0 ||
sshbuf_put_u32(msg, token) != 0 ||
ssh_request_reply(agent_fd, msg, msg) != 0) {
debug("unable to send loadprofile request %s", user_name);
break;
}
if (sshbuf_get_u8(msg, &result) != 0 || result == SSH_AGENT_FAILURE) {
debug("agent failed to load profile for user %s", user_name);
break;
}
break;
}
return result;
}
void*
mm_auth_custom_lsa(const char* user, const char* password, const char* domain, const char* lsa_auth_pkg)
{
/* Pass credentials to privileged agent to retrieve token upon successful authentication */
struct sshbuf *msg = NULL;
HANDLE token = 0;
int agent_fd;
while (1) {
if ((agent_fd = get_priv_agent_sock()) == -1)
break;
msg = sshbuf_new();
if (!msg)
fatal("%s: out of memory", __func__);
if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 ||
sshbuf_put_cstring(msg, CUSTOM_LSA_AUTH_REQUEST) != 0 ||
sshbuf_put_cstring(msg, user) != 0 ||
sshbuf_put_cstring(msg, password) != 0 ||
sshbuf_put_cstring(msg, domain) != 0 ||
sshbuf_put_cstring(msg, lsa_auth_pkg) != 0 ||
ssh_request_reply(agent_fd, msg, msg) != 0) {
debug("unable to send LSA Auth request");
break;
}
if (sshbuf_get_u32(msg, (u_int32_t *)&token) != 0) {
debug("failed to get the token from the agent");
break;
}
debug3("user:%s authenticated using LSA auth pkg:%s", user, lsa_auth_pkg);
break;
}
if (msg)
sshbuf_free(msg);
return (void*)(INT_PTR)token;
}

View File

@ -1,529 +1,381 @@
/*
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
* ssh-agent implementation on Windows
*
* 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 <Windows.h>
#include <UserEnv.h>
#include <Ntsecapi.h>
#include <ntstatus.h>
#include <Shlobj.h>
#include "agent.h"
#include "agent-request.h"
#include "key.h"
#include "inc\utf.h"
#include "..\priv-agent.h"
#include "logonuser.h"
#include <Ntsecapi.h>
#include <ntstatus.h>
#pragma warning(push, 3)
int pubkey_allowed(struct sshkey* pubkey, char* user_utf8);
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, "ssh-agent");
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, "sshagent", 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,
&quotas,
&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;
}
static HANDLE
duplicate_token_for_client(struct agent_connection* con, HANDLE t) {
ULONG client_pid;
HANDLE client_proc = NULL, dup_t = NULL;
/* Should the token match client's session id?
ULONG client_sessionId;
if (GetNamedPipeClientSessionId(con->pipe_handle, &client_sessionId) == FALSE ||
SetTokenInformation(t, TokenSessionId, &client_sessionId, sizeof(client_sessionId)) == FALSE) {
error("unable to set token session id, error: %d", GetLastError());
goto done;
}*/
if ((FALSE == GetNamedPipeClientProcessId(con->pipe_handle, &client_pid)) ||
((client_proc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, client_pid)) == NULL) ||
DuplicateHandle(GetCurrentProcess(), t, client_proc, &dup_t, TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, DUPLICATE_SAME_ACCESS) == FALSE ) {
error("failed to duplicate user token");
goto done;
}
done:
if (client_proc)
CloseHandle(client_proc);
return dup_t;
}
int
process_custom_lsa_auth_req(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con)
{
char *user = NULL, *pwd = NULL, *domain = NULL, *lsa_pkg = NULL;
size_t user_len = 0, pwd_len = 0, domain_len = 0, lsa_pkg_len = 0;
wchar_t *userw = NULL, *pwdw = NULL, *domw = NULL, *providerw = NULL;
HANDLE token = NULL, dup_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;
if (sshbuf_get_string_direct(request, &user, &user_len) != 0 ||
sshbuf_get_cstring(request, &pwd, &pwd_len) != 0 ||
sshbuf_get_string_direct(request, &domain, &domain_len) != 0 ||
user_len > MAX_USER_LEN || pwd_len == 0 ||
sshbuf_get_string_direct(request, &lsa_pkg, &lsa_pkg_len) != 0) {
debug("invalid LSA auth request");
goto done;
}
debug("LSA auth request, user:%s domain:%s lsa_pkg:%s ", user, domain, lsa_pkg);
/* convert everything to utf16 only if its not NULL */
if ((userw = utf8_to_utf16(user)) == NULL ||
(pwdw = utf8_to_utf16(pwd)) == NULL ||
(domain && (domw = utf8_to_utf16(domain)) == NULL)) {
debug("%s: out of memory", __func__);
goto done;
}
/* call into LSA provider , get and duplicate token */
InitLsaString(&logon_process_name, "ssh-agent");
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,
&quotas,
&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;
}
if ((dup_token = duplicate_token_for_client(con, token)) == NULL)
goto done;
if (sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0)
goto done;
retVal = 0;
done:
/* delete allocated memory*/
if ((retVal == -1) && (sshbuf_put_u8(response, SSH_AGENT_FAILURE) == 0))
retVal = 0;
if (lsa_handle)
LsaDeregisterLogonProcess(lsa_handle);
if (logon_info)
free(logon_info);
if (pProfile)
LsaFreeReturnBuffer(pProfile);
if (userw)
free(userw);
if (pwdw)
free(pwdw);
if (domw)
free(domw);
if (token)
CloseHandle(token);
return retVal;
}
int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
int r = -1;
char *key_blob, *user, *sig, *blob;
size_t key_blob_len, user_len, sig_len, blob_len;
struct sshkey *key = NULL;
HANDLE token = NULL, dup_token = NULL;
wchar_t *user_utf16 = NULL;
PWSTR wuser_home = NULL;
user = NULL;
if (sshbuf_get_string_direct(request, &key_blob, &key_blob_len) != 0 ||
sshbuf_get_cstring(request, &user, &user_len) != 0 ||
user_len > MAX_USER_LEN ||
sshbuf_get_string_direct(request, &sig, &sig_len) != 0 ||
sshbuf_get_string_direct(request, &blob, &blob_len) != 0 ||
sshkey_from_blob(key_blob, key_blob_len, &key) != 0) {
debug("invalid pubkey auth request");
goto done;
}
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;
}
}
if (pubkey_allowed(key, user) != 1) {
debug("unable to verify public key for user %ls (profile:%ls)", user_utf16, wuser_home);
goto done;
}
if (key_verify(key, sig, (u_int)sig_len, blob, (u_int)blob_len) != 1) {
debug("signature verification failed");
goto done;
}
if ((dup_token = duplicate_token_for_client(con, token)) == NULL)
goto done;
if (sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0)
goto done;
r = 0;
done:
/* TODO Fix this hacky protocol*/
if ((r == -1) && (sshbuf_put_u8(response, SSH_AGENT_FAILURE) == 0))
r = 0;
if (user)
free(user);
if (user_utf16)
free(user_utf16);
if (key)
sshkey_free(key);
if (wuser_home)
CoTaskMemFree(wuser_home);
if (token)
CloseHandle(token);
return r;
}
int process_loadprofile_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
int r = 0, success = 0;
char *user;
size_t user_len;
u_int32_t user_token_int = 0;
HANDLE user_token = NULL;
wchar_t *user_utf16 = NULL, *dom_utf16 = NULL, *tmp;
/* is profile already loaded */
if (con->profile_handle) {
success = 1;
goto done;
}
if (sshbuf_get_cstring(request, &user, &user_len) != 0 ||
user_len > MAX_USER_LEN ||
sshbuf_get_u32(request, &user_token_int) != 0){
debug("invalid loadprofile request");
goto done;
}
if (DuplicateHandle(con->client_process_handle, (HANDLE)(INT_PTR)user_token_int, GetCurrentProcess(),
&user_token, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, FALSE, 0) == FALSE) {
debug("cannot duplicate user token, error: %d", GetLastError());
goto done;
}
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 ((con->profile_handle = LoadProfile(user_token, user_utf16, dom_utf16)) == NULL)
goto done;
con->profile_token = user_token;
user_token = NULL;
success = 1;
done:
if (sshbuf_put_u8(response, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE) != 0)
r = -1;
if (user_token)
CloseHandle(user_token);
return r;
}
int process_privagent_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) {
char *opn;
size_t opn_len;
if (sshbuf_get_string_direct(request, &opn, &opn_len) != 0) {
debug("invalid auth request");
return -1;
}
/* allow only admins and NT Service\sshd to send auth requests */
if (con->client_type != SSHD_SERVICE && con->client_type != ADMIN_USER) {
error("cannot process request: client process is not admin or sshd");
return -1;
}
if (memcmp(opn, PUBKEY_AUTH_REQUEST, opn_len) == 0)
return process_pubkeyauth_request(request, response, con);
else if (memcmp(opn, LOAD_USER_PROFILE_REQUEST, opn_len) == 0)
return process_loadprofile_request(request, response, con);
else if (memcmp(opn, CUSTOM_LSA_AUTH_REQUEST, opn_len) == 0)
return process_custom_lsa_auth_req(request, response, con);
else {
debug("unknown auth request: %s", opn);
return -1;
}
}
/*
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
* 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 <Windows.h>
#include <UserEnv.h>
#include <Ntsecapi.h>
#include <ntstatus.h>
#include <Shlobj.h>
#include "inc\utf.h"
#include "logonuser.h"
#include <Ntsecapi.h>
#include <ntstatus.h>
#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,
&quotas,
&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,
&quotas,
&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)

View File

@ -39,7 +39,6 @@
int main(int, char **);
extern HANDLE main_thread;
extern int is_child;
int scm_start_service(DWORD, LPWSTR*);
@ -110,8 +109,6 @@ 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());
@ -123,7 +120,12 @@ int sshd_main(int argc, wchar_t **wargv) {
return r;
}
int argc_original = 0;
wchar_t **wargv_original = NULL;
int wmain(int argc, wchar_t **wargv) {
argc_original = argc;
wargv_original = wargv;
if (!StartServiceCtrlDispatcherW(dispatch_table)) {
if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT)
return sshd_main(argc, wargv); /* sshd running NOT as service*/
@ -140,7 +142,7 @@ int 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);
return sshd_main(num, args);
return sshd_main(argc_original, wargv_original);
}

5
misc.c
View File

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

View File

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

View File

@ -98,10 +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 functions */
void* mm_auth_pubkey(const char*, const struct sshkey *, const u_char *, size_t,
struct sshbuf*);
int mm_load_profile(const char*, u_int );
void* mm_auth_custom_lsa(const char* user, const char* password, const char* dom, const char* lsa_auth_pkg);
#endif /* _MM_WRAP_H_ */

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

55
scp.c
View File

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

View File

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

32
sftp.c
View File

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

458
sshd.c
View File

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