# @manojampalam - authored initial script # @friism - Fixed issue with invalid SDDL on Set-Acl # @manojampalam - removed ntrights.exe dependency # @bingbing8 - removed secedit.exe dependency # @tessgauthier - added permissions check for %programData%/ssh # @tessgauthier - added update to system path for scp/sftp discoverability [CmdletBinding(SupportsShouldProcess=$true, ConfirmImpact="High")] param () Set-StrictMode -Version 2.0 $ErrorActionPreference = 'Stop' if (!([bool]([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))) { throw "You must be running as an administrator, please restart as administrator" } $scriptpath = $MyInvocation.MyCommand.Path $scriptdir = Split-Path $scriptpath $sshdpath = Join-Path $scriptdir "sshd.exe" $sshagentpath = Join-Path $scriptdir "ssh-agent.exe" $etwman = Join-Path $scriptdir "openssh-events.man" if (-not (Test-Path $sshdpath)) { throw "sshd.exe is not present in script path" } if (Get-Service sshd -ErrorAction SilentlyContinue) { Stop-Service sshd sc.exe delete sshd 1>$null } if (Get-Service ssh-agent -ErrorAction SilentlyContinue) { Stop-Service ssh-agent sc.exe delete ssh-agent 1>$null } # Unregister etw provider # PowerShell 7.3+ has new/different native command argument parsing if ($PSVersiontable.PSVersion -le '7.2.9') { wevtutil um `"$etwman`" } else { wevtutil um "$etwman" } # adjust provider resource path in instrumentation manifest [XML]$xml = Get-Content $etwman $xml.instrumentationManifest.instrumentation.events.provider.resourceFileName = "$sshagentpath" $xml.instrumentationManifest.instrumentation.events.provider.messageFileName = "$sshagentpath" $streamWriter = $null $xmlWriter = $null try { $streamWriter = new-object System.IO.StreamWriter($etwman) $xmlWriter = [System.Xml.XmlWriter]::Create($streamWriter) $xml.Save($xmlWriter) } finally { if($streamWriter) { $streamWriter.Close() } } # Fix the registry permissions If ($PSVersiontable.PSVersion.Major -le 2) {$PSScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path} Import-Module $PSScriptRoot\OpenSSHUtils -Force Enable-Privilege SeRestorePrivilege | out-null $sshRootRegPath="HKLM:SOFTWARE/Openssh" if (Test-Path $sshRootRegPath) { $sshRootAcl=Get-Acl $sshRootRegPath # SDDL - FullAcess to System and Builtin/Admins and read only access to Authenticated users $sshRootAcl.SetSecurityDescriptorSddlForm("O:BAG:SYD:P(A;OICI;KR;;;AU)(A;OICI;KA;;;SY)(A;OICI;KA;;;BA)") Set-Acl $sshRootRegPath $sshRootAcl } $sshAgentRegPath="HKLM:SOFTWARE/Openssh/agent" if (Test-Path $sshAgentRegPath) { $sshAgentAcl=Get-Acl $sshAgentRegPath # SDDL - FullAcess to System and Builtin/Admins. $sshAgentAcl.SetSecurityDescriptorSddlForm("O:BAG:SYD:P(A;OICI;KA;;;SY)(A;OICI;KA;;;BA)") Set-Acl $sshAgentRegPath $sshAgentAcl } #Fix permissions for moduli file $moduliPath = Join-Path $PSScriptRoot "moduli" if (Test-Path $moduliPath -PathType Leaf) { # if user calls .\install-sshd.ps1 with -confirm, use that # otherwise, need to preserve legacy behavior if (-not $PSBoundParameters.ContainsKey('confirm')) { $PSBoundParameters.add('confirm', $false) } Repair-ModuliFilePermission -FilePath $moduliPath @psBoundParameters } # If %programData%/ssh folder already exists, verify and, if necessary and approved by user, fix permissions $sshProgDataPath = Join-Path $env:ProgramData "ssh" if (Test-Path $sshProgDataPath) { # SSH Folder - owner: System or Admins; full access: System, Admins; read or readandexecute/synchronize permissible: Authenticated Users Repair-SSHFolderPermission -FilePath $sshProgDataPath @psBoundParameters # Files in SSH Folder (excluding private key files) # owner: System or Admins; full access: System, Admins; read/readandexecute/synchronize permissable: Authenticated Users $privateKeyFiles = @("ssh_host_dsa_key", "ssh_host_ecdsa_key", "ssh_host_ed25519_key", "ssh_host_rsa_key") Get-ChildItem -Path (Join-Path $sshProgDataPath '*') -Recurse -Exclude ($privateKeyFiles) -Force | ForEach-Object { Repair-SSHFolderFilePermission -FilePath $_.FullName @psBoundParameters } # Private key files - owner: System or Admins; full access: System, Admins Get-ChildItem -Path (Join-Path $sshProgDataPath '*') -Recurse -Include $privateKeyFiles -Force | ForEach-Object { Repair-SSHFolderPrivateKeyPermission -FilePath $_.FullName @psBoundParameters } } # Register etw provider # PowerShell 7.3+ has new/different native command argument parsing if ($PSVersiontable.PSVersion -le '7.2.9') { wevtutil im `"$etwman`" } else { wevtutil im "$etwman" } $agentDesc = "Agent to hold private keys used for public key authentication." New-Service -Name ssh-agent -DisplayName "OpenSSH Authentication Agent" -BinaryPathName "$sshagentpath" -Description $agentDesc -StartupType Manual | Out-Null sc.exe sdset ssh-agent "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;RP;;;AU)" sc.exe privs ssh-agent SeAssignPrimaryTokenPrivilege/SeTcbPrivilege/SeBackupPrivilege/SeRestorePrivilege/SeImpersonatePrivilege $sshdDesc = "SSH protocol based service to provide secure encrypted communications between two untrusted hosts over an insecure network." New-Service -Name sshd -DisplayName "OpenSSH SSH Server" -BinaryPathName "$sshdpath" -Description $sshdDesc -StartupType Manual | Out-Null sc.exe privs sshd SeAssignPrimaryTokenPrivilege/SeTcbPrivilege/SeBackupPrivilege/SeRestorePrivilege/SeImpersonatePrivilege Write-Host -ForegroundColor Green "sshd and ssh-agent services successfully installed" # add folder to system PATH Add-MachinePath -FilePath $scriptdir @psBoundParameters