file permission on ssh_config, authorized_keys, private keys, host keys, public keys. (#110)

1. Add file permission check when load or add ssh_config, authorized_keys, private keys, host keys,.
2. set the owner and ACE for create secure file, ex, private key in ssh-keygen.exe
3. Update script OpenSSHTestHelper.psm1 to be able to run Install-OpenSSH if the sshd is running on the machine.
4. add OpenSSHBinPath to path.
5. change indents in agentconfig.c
6. update test script to represent the changes
7. Add tests for:
* authorized_keys and ssh-keygen testing
* host keys file perm testing
* user private key file perm testing
* ssh-add test
* user ssh_config
This commit is contained in:
Yanbing 2017-05-01 14:18:20 -07:00 committed by GitHub
parent 16b0175b8e
commit 6b807ae229
30 changed files with 1478 additions and 95 deletions

13
auth.c
View File

@ -76,6 +76,7 @@
#include "authfile.h"
#include "ssherr.h"
#include "compat.h"
#include "sshfileperm.h"
/* import */
extern ServerOptions options;
@ -489,10 +490,6 @@ int
auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
uid_t uid, char *err, size_t errlen)
{
#ifdef WINDOWS
error("auth_secure_path should not be called in Windows yet");
return -1;
#else /* !WINDOWS */
char buf[PATH_MAX], homedir[PATH_MAX];
char *cp;
int comparehome = 0;
@ -545,7 +542,6 @@ auth_secure_path(const char *name, struct stat *stp, const char *pw_dir,
break;
}
return 0;
#endif /* !WINDOWS */
}
/*
@ -585,7 +581,12 @@ auth_openfile(const char *file, struct passwd *pw, int strict_modes,
strerror(errno));
return NULL;
}
/* TODO check permissions */
if (strict_modes && check_secure_file_permission(file, pw) != 0) {
fclose(f);
logit("Authentication refused.");
auth_debug_add("Ignored %s", file_type);
return NULL;
}
#else /* !WINDOWS */
if ((fd = open(file, O_RDONLY|O_NONBLOCK)) == -1) {
if (log_missing || errno != ENOENT)

View File

@ -49,6 +49,7 @@
#include "sshbuf.h"
#include "ssherr.h"
#include "krl.h"
#include "sshfileperm.h"
#define MAX_KEY_FILE_SIZE (1024 * 1024)
@ -60,6 +61,14 @@ sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
return SSH_ERR_SYSTEM_ERROR;
#ifdef WINDOWS /* WINDOWS */
/*
Set the owner of the private key file to current user and only grant
current user the full control access
*/
if (set_secure_file_permission(filename, NULL) != 0)
return SSH_ERR_SYSTEM_ERROR;
#endif /* WINDOWS */
if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf),
sshbuf_len(keybuf)) != sshbuf_len(keybuf)) {
oerrno = errno;
@ -193,19 +202,25 @@ sshkey_perm_ok(int fd, const char *filename)
#ifdef HAVE_CYGWIN
if (check_ntsec(filename))
#endif
#ifndef WINDOWS /*TODO - implement permission checks on Windows*/
#ifdef WINDOWS /*implement permission checks on Windows*/
if(check_secure_file_permission(filename, NULL) != 0) {
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @");
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
error("Permissions for '%s' are too open.", filename);
#else
if ((st.st_uid == getuid()) && (st.st_mode & 077) != 0) {
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
error("@ WARNING: UNPROTECTED PRIVATE KEY FILE! @");
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
error("Permissions 0%3.3o for '%s' are too open.",
(u_int)st.st_mode & 0777, filename);
#endif /* !WINDOWS */
error("It is required that your private key files are NOT accessible by others.");
error("This private key will be ignored.");
return SSH_ERR_KEY_BAD_PERMISSIONS;
}
#endif /* !WINDOWS */
return 0;
}

View File

@ -307,6 +307,12 @@ function Package-OpenSSH
if ($NativeHostArch -eq 'x86') {
$packageName = "OpenSSH-Win32"
}
while((($service = Get-Service ssh-agent -ErrorAction Ignore) -ne $null) -and ($service.Status -ine 'Stopped'))
{
Stop-Service ssh-agent -Force
#sleep to wait the servicelog file write
Start-Sleep 5
}
$packageDir = Join-Path $buildDir $packageName
Remove-Item $packageDir -Recurse -Force -ErrorAction SilentlyContinue
@ -320,23 +326,23 @@ function Package-OpenSSH
if ((-not(Test-Path (Join-Path $buildDir $file)))) {
Throw "Cannot find $file under $buildDir. Did you run Build-OpenSSH?"
}
Copy-Item (Join-Path $buildDir $file) $packageDir
Copy-Item (Join-Path $buildDir $file) $packageDir -Force
if ($file.EndsWith(".exe")) {
$pdb = $file.Replace(".exe", ".pdb")
Copy-Item (Join-Path $buildDir $pdb) $symbolsDir
Copy-Item (Join-Path $buildDir $pdb) $symbolsDir -Force
}
if ($file.EndsWith(".dll")) {
$pdb = $file.Replace(".dll", ".pdb")
Copy-Item (Join-Path $buildDir $pdb) $symbolsDir
Copy-Item (Join-Path $buildDir $pdb) $symbolsDir -Force
}
}
if ($DestinationPath -ne "") {
if (Test-Path $DestinationPath) {
Remove-Item $DestinationPath\* -Force
if (Test-Path $DestinationPath) {
Remove-Item $DestinationPath\* -Force -Recurse
}
else {
New-Item -ItemType Directory $DestinationPath | Out-Null
New-Item -ItemType Directory $DestinationPath -Force | Out-Null
}
Copy-Item -Path $packageDir\* -Destination $DestinationPath -Force -Recurse
}
@ -493,8 +499,12 @@ function Install-OpenSSH
Package-OpenSSH -NativeHostArch $NativeHostArch -Configuration $Configuration -DestinationPath $OpenSSHDir
Push-Location $OpenSSHDir
& ( "$OpenSSHDir\install-sshd.ps1")
.\ssh-keygen.exe -A
& "$OpenSSHDir\install-sshd.ps1"
& "$OpenSSHDir\ssh-keygen.exe" -A
$keyFiles = Get-ChildItem "$OpenSSHDir\ssh_host_*_key*" | % {
Add-PermissionToFileACL -FilePath $_.FullName -User "NT Service\sshd" -Perm "Read"
}
#machine will be reboot after Install-openssh anyway

View File

@ -28,4 +28,80 @@ function Get-RepositoryRoot
throw new-object System.IO.DirectoryNotFoundException("Could not find the root of the GIT repository")
}
Export-ModuleMember -Function Get-RepositoryRoot
<#
.Synopsis
Sets the Secure File ACL.
1. Removed all user acl except Administrators group, system, and current user
2. whether or not take the owner
.Outputs
N/A
.Inputs
FilePath - The path to the file
takeowner - if want to take the ownership
#>
function Cleanup-SecureFileACL
{
[CmdletBinding()]
param([string]$FilePath, [System.Security.Principal.NTAccount] $Owner)
$myACL = Get-ACL $filePath
$myACL.SetAccessRuleProtection($True, $True)
Set-Acl -Path $filePath -AclObject $myACL
$myACL = Get-ACL $filePath
if($owner -ne $null)
{
$myACL.SetOwner($owner)
}
if($myACL.Access)
{
$myACL.Access | % {
if (($_ -ne $null) -and ($_.IdentityReference.Value -ine "BUILTIN\Administrators") -and
($_.IdentityReference.Value -ine "NT AUTHORITY\SYSTEM") -and
($_.IdentityReference.Value -ine "$(whoami)"))
{
if(-not ($myACL.RemoveAccessRule($_)))
{
throw "failed to remove access of $($_.IdentityReference.Value) rule in setup "
}
}
}
}
Set-Acl -Path $filePath -AclObject $myACL
}
<#
.Synopsis
add a file permission to an account
.Outputs
N/A
.Inputs
FilePath - The path to the file
User - account name
Perm - The permission to grant.
#>
function Add-PermissionToFileACL
{
[CmdletBinding()]
param(
[string]$FilePath,
[System.Security.Principal.NTAccount] $User,
[System.Security.AccessControl.FileSystemRights]$Perm
)
$myACL = Get-ACL $filePath
$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `
($User, $perm, "None", "None", "Allow")
$myACL.AddAccessRule($objACE)
Set-Acl -Path $filePath -AclObject $myACL
}
Export-ModuleMember -Function Get-RepositoryRoot, Add-PermissionToFileACL, Cleanup-SecureFileACL

View File

@ -108,6 +108,10 @@ function Setup-OpenSSHTestEnvironment
}
$Global:OpenSSHTestInfo.Add("OpenSSHBinPath", $script:OpenSSHBinPath)
if (-not ($env:Path.ToLower().Contains($script:OpenSSHBinPath.ToLower())))
{
$env:Path = "$($script:OpenSSHBinPath);$($env:path)"
}
$warning = @"
WARNING: Following changes will be made to OpenSSH configuration
@ -125,15 +129,12 @@ WARNING: Following changes will be made to OpenSSH configuration
if (-not $Quiet) {
Write-Warning $warning
$continue = Read-Host -Prompt "Do you want to continue with the above changes? [Yes] Y; [No] N (default is `"Y`")"
if( ($continue -eq "") -or ($continue -ieq "Y") -or ($continue -ieq "Yes") )
{
}
elseif( ($continue -ieq "N") -or ($continue -ieq "No") )
if( ($continue -ieq "N") -or ($continue -ieq "No") )
{
Write-Host "User decided not to make the changes."
return
}
else
elseif(($continue -ne "") -and ($continue -ine "Y") -and ($continue -ine "Yes"))
{
Throw "User entered invalid option ($continue). Exit now."
}
@ -152,9 +153,21 @@ WARNING: Following changes will be made to OpenSSH configuration
Copy-Item (Join-Path $script:OpenSSHBinPath sshd_config) $backupConfigPath -Force
}
# copy new sshd_config
Copy-Item (Join-Path $Script:E2ETestDirectory sshd_config) (Join-Path $script:OpenSSHBinPath sshd_config) -Force
Copy-Item "$($Script:E2ETestDirectory)\sshtest*hostkey*" $script:OpenSSHBinPath -Force
# copy new sshd_config
Copy-Item (Join-Path $Script:E2ETestDirectory sshd_config) (Join-Path $script:OpenSSHBinPath sshd_config) -Force
#workaround for the cariggage new line added by git before copy them
Get-ChildItem "$($Script:E2ETestDirectory)\sshtest_*key*" | % {
(Get-Content $_.FullName -Raw).Replace("`r`n","`n") | Set-Content $_.FullName -Force
}
#copy sshtest keys
Copy-Item "$($Script:E2ETestDirectory)\sshtest*hostkey*" $script:OpenSSHBinPath -Force
$owner = New-Object System.Security.Principal.NTAccount($env:USERDOMAIN, $env:USERNAME)
Get-ChildItem "$($script:OpenSSHBinPath)\sshtest*hostkey*" -Exclude *.pub | % {
Cleanup-SecureFileACL -FilePath $_.FullName -Owner $owner
Add-PermissionToFileACL -FilePath $_.FullName -User "NT Service\sshd" -Perm "Read"
}
Restart-Service sshd -Force
#Backup existing known_hosts and replace with test version
@ -174,46 +187,51 @@ WARNING: Following changes will be made to OpenSSH configuration
#TODO - this is Windows specific. Need to be in PAL
foreach ($user in $OpenSSHTestAccounts)
{
try
try
{
$objUser = New-Object System.Security.Principal.NTAccount($user)
$strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
}
catch
{
{
#only add the local user when it does not exists on the machine
net user $user $Script:OpenSSHTestAccountsPassword /ADD 2>&1 >> $Script:TestSetupLogFile
}
}
}
#setup single sign on for ssouser
#TODO - this is Windows specific. Need to be in PAL
$ssousersid = Get-UserSID -User sshtest_ssouser
$ssouserProfileRegistry = Join-Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $ssousersid
if (-not (Test-Path $ssouserProfileRegistry) ) {
#setup single sign on for ssouser
$ssouserProfile = Get-LocalUserProfile -User $SSOUser
$Global:OpenSSHTestInfo.Add("SSOUserProfile", $ssouserProfile)
$Global:OpenSSHTestInfo.Add("PubKeyUserProfile", (Get-LocalUserProfile -User $PubKeyUser))
New-Item -ItemType Directory -Path (Join-Path $ssouserProfile .ssh) -Force -ErrorAction SilentlyContinue | out-null
$authorizedKeyPath = Join-Path $ssouserProfile .ssh\authorized_keys
$testPubKeyPath = Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519.pub
Copy-Item $testPubKeyPath $authorizedKeyPath -Force -ErrorAction SilentlyContinue
Add-PermissionToFileACL -FilePath $authorizedKeyPath -User "NT Service\sshd" -Perm "Read"
$testPriKeypath = Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519
Cleanup-SecureFileACL -FilePath $testPriKeypath -owner $owner
cmd /c "ssh-add $testPriKeypath 2>&1 >> $Script:TestSetupLogFile"
}
#TODO - this is Windows specific. Need to be in PAL
function Get-LocalUserProfile
{
param([string]$User)
$sid = Get-UserSID -User $User
$userProfileRegistry = Join-Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" $sid
if (-not (Test-Path $userProfileRegistry) ) {
#create profile
if (-not($env:DISPLAY)) { $env:DISPLAY = 1 }
$env:SSH_ASKPASS="$($env:ComSpec) /c echo $($OpenSSHTestAccountsPassword)"
cmd /c "ssh -p 47002 sshtest_ssouser@localhost echo %userprofile% > profile.txt"
$ret = ssh -p 47002 "$User@localhost" echo %userprofile%
if ($env:DISPLAY -eq 1) { Remove-Item env:\DISPLAY }
remove-item "env:SSH_ASKPASS" -ErrorAction SilentlyContinue
}
$ssouserProfile = (Get-ItemProperty -Path $ssouserProfileRegistry -Name 'ProfileImagePath').ProfileImagePath
New-Item -ItemType Directory -Path (Join-Path $ssouserProfile .ssh) -Force -ErrorAction SilentlyContinue | out-null
$authorizedKeyPath = Join-Path $ssouserProfile .ssh\authorized_keys
$testPubKeyPath = Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519.pub
#workaround for the cariggage new line added by git
(Get-Content $testPubKeyPath -Raw).Replace("`r`n","`n") | Set-Content $testPubKeyPath -Force
Copy-Item $testPubKeyPath $authorizedKeyPath -Force -ErrorAction SilentlyContinue
$acl = get-acl $authorizedKeyPath
$ar = New-Object System.Security.AccessControl.FileSystemAccessRule("NT Service\sshd", "Read", "Allow")
$acl.SetAccessRule($ar)
Set-Acl $authorizedKeyPath $acl
$testPriKeypath = Join-Path $Script:E2ETestDirectory sshtest_userssokey_ed25519
(Get-Content $testPriKeypath -Raw).Replace("`r`n","`n") | Set-Content $testPriKeypath -Force
cmd /c "ssh-add $testPriKeypath 2>&1 >> $Script:TestSetupLogFile"
}
(Get-ItemProperty -Path $userProfileRegistry -Name 'ProfileImagePath').ProfileImagePath
}
<#
.SYNOPSIS
This function installs the tools required by our tests

View File

@ -283,12 +283,14 @@
<ClCompile Include="$(OpenSSH-Src-Path)platform.c" />
<ClCompile Include="$(OpenSSH-Src-Path)sandbox-pledge.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\ttymodes_windows.c" />
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\w32-sshfileperm.c" />
<ClCompile Include="$(OpenSSH-Src-Path)digest-openssl.c">
<ExcludedFromBuild Condition="$(UseOpenSSL)==false">true</ExcludedFromBuild>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(OpenSSH-Src-Path)crypto-wrap.h" />
<ClInclude Include="$(OpenSSH-Src-Path)sshfileperm.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -285,10 +285,16 @@
<ClCompile Include="$(OpenSSH-Src-Path)digest-openssl.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\w32-sshfileperm.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="$(OpenSSH-Src-Path)crypto-wrap.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include=$(OpenSSH-Src-Path)sshfileperm.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>

View File

@ -75,14 +75,14 @@
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-bitmap</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-bitmap</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

View File

@ -75,14 +75,14 @@
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-hostkeys</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-hostkeys</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -123,7 +123,7 @@
<AdditionalManifestFiles>targetos.manifest</AdditionalManifestFiles>
</Manifest>
<PostBuildEvent>
<Command>xcopy /Y $(ProjectDir)..\..\..\regress\unittests\hostkeys\testdata\* $(OutDir)hostkeys\</Command>
<Command>xcopy /Y $(ProjectDir)..\..\..\regress\unittests\hostkeys\testdata\* $(OutDir)</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -149,7 +149,7 @@
<AdditionalManifestFiles>targetos.manifest</AdditionalManifestFiles>
</Manifest>
<PostBuildEvent>
<Command>xcopy /Y $(ProjectDir)..\..\..\regress\unittests\hostkeys\testdata\* $(OutDir)hostkeys\</Command>
<Command>xcopy /Y $(ProjectDir)..\..\..\regress\unittests\hostkeys\testdata\* $(OutDir)</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

View File

@ -75,14 +75,14 @@
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-kex</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-kex</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

View File

@ -75,14 +75,14 @@
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-match</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-match</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

View File

@ -75,14 +75,14 @@
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-sshbuf</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-sshbuf</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

View File

@ -75,14 +75,14 @@
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-sshkey</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-sshkey</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -123,7 +123,7 @@
<AdditionalManifestFiles>targetos.manifest</AdditionalManifestFiles>
</Manifest>
<PostBuildEvent>
<Command>xcopy /Y $(ProjectDir)..\..\..\regress\unittests\sshkey\testdata\* $(OutDir)sshkey\</Command>
<Command>xcopy /Y $(ProjectDir)..\..\..\regress\unittests\sshkey\testdata\* $(OutDir)</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -149,7 +149,7 @@
<AdditionalManifestFiles>targetos.manifest</AdditionalManifestFiles>
</Manifest>
<PostBuildEvent>
<Command>xcopy /Y $(ProjectDir)..\..\..\regress\unittests\sshkey\testdata\* $(OutDir)sshkey\</Command>
<Command>xcopy /Y $(ProjectDir)..\..\..\regress\unittests\sshkey\testdata\* $(OutDir)</Command>
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

View File

@ -82,14 +82,14 @@
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-win32compat</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>false</LinkIncremental>
<IntDir>$(Platform)\$(Configuration)\$(TargetName)\</IntDir>
<TargetName>unittest-win32compat</TargetName>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\</OutDir>
<OutDir>$(OpenSSH-Bin-Path)$(Platform)\$(Configuration)\$(TargetName)\</OutDir>
<IncludePath>$(OpenSSH-Src-Path)contrib\win32\win32compat\inc;$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">

View File

@ -54,7 +54,8 @@ struct passwd *privsep_pw = NULL;
static char *config_file_name = _PATH_SERVER_CONFIG_FILE;
int auth_sock = -1;
int auth2_methods_valid(const char * c, int i) {
int
auth2_methods_valid(const char * c, int i) {
return 1;
}
@ -69,21 +70,21 @@ mm_user_key_allowed(struct passwd *pw, Key *k, int i)
return 0;
}
int kexgex_server(struct ssh * sh) {
int
kexgex_server(struct ssh * sh) {
return -1;
}
static
int GetCurrentModulePath(wchar_t *path, int pathSize)
static int
GetCurrentModulePath(wchar_t *path, int pathSize)
{
if (GetModuleFileNameW(NULL, path, pathSize)) {
int i;
int lastSlashPos = 0;
for (i = 0; path[i]; i++) {
if (path[i] == L'/' || path[i] == L'\\') {
lastSlashPos = i;
}
if (path[i] == L'/' || path[i] == L'\\')
lastSlashPos = i;
}
path[lastSlashPos] = 0;
@ -92,20 +93,21 @@ int GetCurrentModulePath(wchar_t *path, int pathSize)
return -1;
}
int load_config() {
int
load_config() {
wchar_t basePath[PATH_MAX] = { 0 };
wchar_t path[PATH_MAX] = { 0 };
if (GetCurrentModulePath(basePath, PATH_MAX) == -1)
return -1;
if (GetCurrentModulePath(basePath, PATH_MAX) == -1)
return -1;
wcsncpy(path, basePath, PATH_MAX);
wcsncat(path, L"/sshd_config", PATH_MAX);
wcsncat(path, L"/sshd_config", PATH_MAX);
if ((config_file_name = utf16_to_utf8(path)) == NULL)
return -1;
if ((config_file_name = utf16_to_utf8(path)) == NULL)
return -1;
buffer_init(&cfg);
buffer_init(&cfg);
initialize_server_options(&options);
load_server_config(config_file_name, &cfg);
parse_server_config(&options, config_file_name, &cfg, NULL);
@ -114,17 +116,17 @@ int load_config() {
return 0;
}
int config_log_level() {
int
config_log_level() {
return options.log_level;
}
int pubkey_allowed(struct sshkey* pubkey, HANDLE user_token) {
int
pubkey_allowed(struct sshkey* pubkey, HANDLE user_token) {
struct passwd *pw;
int ret;
char *user = NULL, *user_home = NULL;
if ((pw = w32_getpwtoken(user_token)) == NULL)
return 0;
return user_key_allowed(pw, pubkey, 1);
return user_key_allowed(pw, pubkey, 1);
}

View File

@ -0,0 +1,348 @@
/*
* Author: Yanbing Wang <yawang@microsoft.com>
*
* Copyright (c) 2009, 2011 NoMachine
* All rights reserved
*
* Support file permission check on Win32 based operating systems.
*
* 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 <Windows.h>
#include <Sddl.h>
#include <Aclapi.h>
#include <Ntsecapi.h>
#include <lm.h>
#include <stdio.h>
#include "inc\pwd.h"
#include "sshfileperm.h"
#include "misc_internal.h"
#include "debug.h"
#define SSHD_ACCOUNT L"NT Service\\sshd"
/*
* The function is to check if user prepresented by pw is secure to access to the file.
* Check the owner of the file is one of these types: Local Administrators groups, system account,
* direct user accounts in local administrators, or user represented by pw
* Check the users have access permission to the file don't voilate the following rules:
1. no user other than local administrators group, system account, user represented by pw,
and owner accounts have write permission on the file
2. sshd account can only have read permission
3. user represented by pw and file owner should at least have read permission.
* Returns 0 on success and -1 on failure
*/
int
check_secure_file_permission(const char *name, struct passwd * pw)
{
PSECURITY_DESCRIPTOR pSD = NULL;
wchar_t * name_utf16 = NULL;
PSID owner_sid = NULL, user_sid = NULL;
PACL dacl = NULL;
DWORD error_code = ERROR_SUCCESS;
BOOL is_valid_sid = FALSE, is_valid_acl = FALSE;
struct passwd * pwd = pw;
int ret = 0;
if (pwd == NULL)
if ((pwd = getpwuid(0)) == NULL)
fatal("getpwuid failed.");
if (ConvertStringSidToSid(pwd->pw_sid, &user_sid) == FALSE ||
(IsValidSid(user_sid) == FALSE)) {
debug3("failed to retrieve the sid of the pwd");
ret = -1;
goto cleanup;
}
if ((name_utf16 = utf8_to_utf16(name)) == NULL) {
errno = ENOMEM;
goto cleanup;
}
/*Get the owner sid of the file.*/
if ((error_code = GetNamedSecurityInfoW(name_utf16, SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
&owner_sid, NULL, &dacl, NULL, &pSD)) != ERROR_SUCCESS) {
debug3("failed to retrieve the owner sid and dacl of file %s with error code: %d", name, error_code);
errno = EOTHER;
ret = -1;
goto cleanup;
}
if (((is_valid_sid = IsValidSid(owner_sid)) == FALSE) || ((is_valid_acl = IsValidAcl(dacl)) == FALSE)) {
debug3("IsValidSid: %d; is_valid_acl: %d", is_valid_sid, is_valid_acl);
ret = -1;
goto cleanup;
}
if (!IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) &&
!IsWellKnownSid(owner_sid, WinLocalSystemSid) &&
!EqualSid(owner_sid, user_sid) &&
!is_admin_account(owner_sid)) {
debug3("Bad owner on %s", name);
ret = -1;
goto cleanup;
}
/*
iterate all aces of the file to find out if there is voilation of the following rules:
1. no others than administrators group, system account, and current user, owner accounts have write permission on the file
2. sshd account can only have read permission
3. this user and file owner should at least have read permission
*/
for (DWORD i = 0; i < dacl->AceCount; i++) {
PVOID current_ace = NULL;
PACE_HEADER current_aceHeader = NULL;
PSID current_trustee_sid = NULL;
ACCESS_MASK current_access_mask = 0;
if (!GetAce(dacl, i, &current_ace)) {
debug3("GetAce() failed");
errno = EOTHER;
ret = -1;
goto cleanup;
}
current_aceHeader = (PACE_HEADER)current_ace;
// Determine the location of the trustee's sid and the value of the access mask
switch (current_aceHeader->AceType) {
case ACCESS_ALLOWED_ACE_TYPE: {
PACCESS_ALLOWED_ACE pAllowedAce = (PACCESS_ALLOWED_ACE)current_ace;
current_trustee_sid = &(pAllowedAce->SidStart);
current_access_mask = pAllowedAce->Mask;
break;
}
case ACCESS_DENIED_ACE_TYPE: {
PACCESS_DENIED_ACE pDeniedAce = (PACCESS_DENIED_ACE)current_ace;
current_trustee_sid = &(pDeniedAce->SidStart);
if(pDeniedAce->Mask & (FILE_GENERIC_READ & ~(SYNCHRONIZE | READ_CONTROL)) != 0) {
if (EqualSid(current_trustee_sid, owner_sid)){
debug3("Bad permission on %s. The owner of the file should at least have read permission.", name);
ret = -1;
goto cleanup;
}
else if (EqualSid(current_trustee_sid, user_sid)) {
debug3("Bad permission on %s. The user should at least have read permission.", name);
ret = -1;
goto cleanup;
}
}
continue;
}
default: {
// Not interested ACE
continue;
}
}
/*no need to check administrators group, owner account, user account and system account*/
if (IsWellKnownSid(current_trustee_sid, WinBuiltinAdministratorsSid) ||
IsWellKnownSid(current_trustee_sid, WinLocalSystemSid) ||
EqualSid(current_trustee_sid, owner_sid) ||
EqualSid(current_trustee_sid, user_sid) ||
is_admin_account(current_trustee_sid)) {
continue;
}
else if(is_sshd_account(current_trustee_sid)){
if ((current_access_mask & ~FILE_GENERIC_READ) != 0){
debug3("Bad permission. %s can only read access to %s", SSHD_ACCOUNT, name);
ret = -1;
break;
}
}
else {
ret = -1;
debug3("Bad permission. Other user or group than owner, admin user and local system have access to file %s.", name);
break;
}
}
cleanup:
if (pSD)
LocalFree(pSD);
if (user_sid)
FreeSid(user_sid);
if(name_utf16)
free(name_utf16);
return ret;
}
static BOOL
is_sshd_account(PSID user_sid) {
wchar_t user_name[UNCLEN], full_name[UNCLEN + DNLEN + 2];
DWORD name_length = UNCLEN, domain_name_length = 0, full_name_len = UNCLEN + DNLEN + 2;
SID_NAME_USE sid_type = SidTypeInvalid;
BOOL ret = FALSE;
if (LookupAccountSidLocalW(user_sid, user_name, &name_length, full_name, &full_name_len, &sid_type) == FALSE)
{
debug3("LookupAccountSidLocalW() failed with error: %d. ", GetLastError());
errno = ENOENT;
return FALSE;
}
domain_name_length = wcslen(full_name);
full_name[domain_name_length] = L'\\';
wmemcpy(full_name + domain_name_length + 1, user_name, wcslen(user_name)+1);
return (wcsicmp(full_name, SSHD_ACCOUNT) == 0);
}
/*
* Check if the user is in local administrators group
* currently only check if the user is directly in the group
* Returns TRUE if the user is in administrators group; otherwise false;
*/
static BOOL
is_admin_account(PSID user_sid)
{
DWORD entries_read = 0, total_entries = 0, i = 0, name_length = UNCLEN, domain_name_length = DNLEN;
LPLOCALGROUP_MEMBERS_INFO_1 local_groups_member_info = NULL;
PSID admins_sid = NULL;
wchar_t admins_group_name[UNCLEN], domain_name[DNLEN];
SID_NAME_USE sid_type = SidTypeInvalid;
NET_API_STATUS status;
BOOL ret = FALSE;
if (ConvertStringSidToSidW(L"S-1-5-32-544", &admins_sid) == FALSE ||
(IsValidSid(user_sid) == FALSE)) {
debug3("ConvertStringSidToSidW failed with error code: %d.", GetLastError());
goto done;
}
if (LookupAccountSidLocalW(admins_sid, admins_group_name, &name_length,
domain_name, &domain_name_length, &sid_type) == FALSE) {
debug3("LookupAccountSidLocalW() failed with error: %d. ", GetLastError());
errno = ENOENT;
goto done;
}
status = NetLocalGroupGetMembers(NULL, admins_group_name, 1, (LPBYTE*)&local_groups_member_info,
MAX_PREFERRED_LENGTH, &entries_read, &total_entries, NULL);
if (status != NERR_Success) {
debug3("NetLocalGroupGetMembers failed with error code: %d.", status);
goto done;
}
for (i = 0; i < entries_read; i++) {
if (local_groups_member_info[i].lgrmi1_sidusage == SidTypeDeletedAccount)
continue;
else if(EqualSid(local_groups_member_info[i].lgrmi1_sid, user_sid)) {
ret = TRUE;
break;
}
}
done:
if (local_groups_member_info)
NetApiBufferFree(local_groups_member_info);
if(admins_sid)
LocalFree(admins_sid);
return ret;
}
/*
* Set the owner of the secure file to the user represented by pw and only grant
* it the full control access
*/
int
set_secure_file_permission(const char *name, struct passwd * pw)
{
PSECURITY_DESCRIPTOR pSD = NULL;
PSID owner_sid = NULL;
PACL dacl = NULL;
wchar_t *name_utf16 = NULL, *sid_utf16 = NULL, sddl[256];
DWORD error_code = ERROR_SUCCESS;
struct passwd * pwd = pw;
BOOL present, defaulted;
int ret = 0;
if (pwd == NULL)
if ((pwd = getpwuid(0)) == NULL)
fatal("getpwuid failed.");
if (ConvertStringSidToSid(pwd->pw_sid, &owner_sid) == FALSE) {
debug3("failed to retrieve the sid of the pwd with error code: %d", GetLastError());
ret = -1;
goto cleanup;
}
if((IsValidSid(owner_sid) == FALSE)) {
debug3("IsValidSid(owner_sid): FALSE");
ret = -1;
goto cleanup;
}
if ((sid_utf16 = utf8_to_utf16(pwd->pw_sid)) == NULL) {
debug3("Failed to get utf16 of the sid string");
errno = ENOMEM;
ret = -1;
goto cleanup;
}
swprintf(sddl, sizeof(sddl) - 1, L"D:P(A;;FA;;;%s)", sid_utf16);
if(ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl, SDDL_REVISION, &pSD, NULL) == FALSE) {
debug3("ConvertStringSecurityDescriptorToSecurityDescriptorW failed with error code %d", GetLastError());
ret = -1;
goto cleanup;
}
if (IsValidSecurityDescriptor(pSD) == FALSE) {
debug3("IsValidSecurityDescriptor return FALSE");
ret = -1;
goto cleanup;
}
if (GetSecurityDescriptorDacl(pSD, &present, &dacl, &defaulted) == FALSE) {
debug3("GetSecurityDescriptorDacl failed with error code %d", GetLastError());
ret = -1;
goto cleanup;
}
if (!present || dacl == NULL) {
debug3("failed to find the acl from security descriptior.");
ret = -1;
goto cleanup;
}
if ((name_utf16 = utf8_to_utf16(name)) == NULL) {
debug3("Failed to get utf16 of the name");
errno = ENOMEM;
ret = -1;
goto cleanup;
}
/*Set the owner sid and acl of the file.*/
if ((error_code = SetNamedSecurityInfoW(name_utf16, SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
owner_sid, NULL, dacl, NULL)) != ERROR_SUCCESS) {
debug3("failed to set the owner sid and dacl of file %s with error code: %d", name, error_code);
errno = EOTHER;
ret = -1;
goto cleanup;
}
cleanup:
if (pSD)
LocalFree(pSD);
if (name_utf16)
free(name_utf16);
if(sid_utf16)
free(sid_utf16);
if (owner_sid)
FreeSid(owner_sid);
return ret;
}

View File

@ -39,6 +39,7 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sshfileperm.h>
#ifdef USE_SYSTEM_GLOB
# include <glob.h>
#else
@ -1735,8 +1736,16 @@ read_config_file_depth(const char *filename, struct passwd *pw,
if ((f = fopen(filename, "r")) == NULL)
return 0;
#ifndef WINDOWS /* TODO - implement permission checks for Windows */
if (flags & SSHCONF_CHECKPERM) {
if (flags & SSHCONF_CHECKPERM) {
#if WINDOWS
/*
file permissions are designed differently on windows
implementation on windows to make sure the config file is owned by the user and
nobody else except Administrators group and current user of calling process, and SYSTEM account has the write permission
*/
if (check_secure_file_permission(filename, pw) != 0)
fatal("Bad owner or permissions on %s", filename);
#else
struct stat sb;
if (fstat(fileno(f), &sb) == -1)
@ -1744,8 +1753,9 @@ read_config_file_depth(const char *filename, struct passwd *pw,
if (((sb.st_uid != 0 && sb.st_uid != getuid()) ||
(sb.st_mode & 022) != 0))
fatal("Bad owner or permissions on %s", filename);
}
#endif /* !WINDOWS */
}
debug("Reading configuration data %.200s", filename);

View File

@ -0,0 +1,168 @@
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
Describe "Tests for authorized_keys file permission" -Tags "CI" {
BeforeAll {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment."
}
$testDir = "$($OpenSSHTestInfo["TestDataPath"])\authorized_keys_fileperm"
if( -not (Test-path $testDir -PathType Container))
{
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
}
$fileName = "test.txt"
$filePath = Join-Path $testDir $fileName
$logName = "log.txt"
$logPath = Join-Path $testDir $logName
$server = $OpenSSHTestInfo["Target"]
$port = 47003
$ssouser = $OpenSSHTestInfo["SSOUser"]
$PwdUser = $OpenSSHTestInfo["PasswdUser"]
$ssouserProfile = $OpenSSHTestInfo["SSOUserProfile"]
$script:logNum = 0
# for the first time, delete the existing log files.
if ($OpenSSHTestInfo['DebugMode'])
{
Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore
}
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
AfterEach {
if( $OpenSSHTestInfo["DebugMode"])
{
Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\failedagent$script:logNum.log" -Force -ErrorAction ignore
Copy-Item $logPath "$($script:logNum)$($logPath)" -Force -ErrorAction ignore
Clear-Content $logPath -Force -ErrorAction ignore
$script:logNum++
# clear the ssh-agent so that next testcase will get fresh logs.
Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore
}
}
Context "Authorized key file permission" {
BeforeAll {
$ssouserSSHProfilePath = Join-Path $ssouserProfile .testssh
if(-not (Test-Path $ssouserSSHProfilePath -PathType Container)) {
New-Item $ssouserSSHProfilePath -ItemType directory -Force -ErrorAction Stop | Out-Null
}
$authorizedkeyPath = Join-Path $ssouserProfile .testssh\authorized_keys
$Source = Join-Path $ssouserProfile .ssh\authorized_keys
$testknownhosts = Join-path $PSScriptRoot testdata\test_known_hosts
if(Test-Path $authorizedkeyPath) {
Set-SecureFileACL -filepath $authorizedkeyPath
}
Copy-Item $Source $ssouserSSHProfilePath -Force -ErrorAction Stop
Remove-Item $filePath -Force -ErrorAction Ignore
Get-Process -Name sshd | Where-Object {$_.SI -ne 0} | Stop-process
#add wrong password so ssh does not prompt password if failed with authorized keys
Add-PasswordSetting -Pass "WrongPass"
}
AfterAll {
if(Test-Path $authorizedkeyPath) {
Set-SecureFileACL -filepath $authorizedkeyPath
Remove-Item $authorizedkeyPath -Force -ErrorAction Ignore
}
if(Test-Path $ssouserSSHProfilePath) {
Remove-Item $ssouserSSHProfilePath -Force -ErrorAction Ignore
}
Remove-PasswordSetting
}
AfterEach {
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
It 'Authorized key file -- positive (authorized_keys is owned by current user and running process can access to the file)' {
#setup to have current user (admin user) as owner and grant it full control
Set-SecureFileACL -filepath $authorizedkeyPath
#Run
Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow
$o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234
$o | Should Be "1234"
#Cleanup
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
}
It 'Authorized key file -- positive (Secured file and sshd can access to the file)' {
#setup to have ssouser as owner and grant it full control
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Set-SecureFileACL -filepath $authorizedkeyPath -Owner $objUser
#add running process account Read access the file authorized_keys
$currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME))
Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $currentUser -Perm "Read"
#Run
Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-o `"AuthorizedKeysFile .testssh/authorized_keys`"", "-E $logPath") -NoNewWindow
$o = ssh -p $port $ssouser@$server -o "UserKnownHostsFile $testknownhosts" echo 1234
$o | Should Be "1234"
#Cleanup
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
}
It 'Authorized key file -- negative (other account can access private key file)' {
#setup to have current user as owner and grant it full control
Set-SecureFileACL -filepath $authorizedkeyPath
#add $PwdUser to access the file authorized_keys
$objPwdUser = New-Object System.Security.Principal.NTAccount($PwdUser)
Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $objPwdUser -Perm "Read"
#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 Not Be 0
#Cleanup
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
}
It 'Authorized key file -- negative (the authorized_keys has wrong owner)' {
#setup to have ssouser as owner and grant it full control
$objPwdUser = New-Object System.Security.Principal.NTAccount($PwdUser)
Set-SecureFileACL -filepath $authorizedkeyPath -owner $objPwdUser
#add current user full access the file authorized_keys
$currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME))
Add-PermissionToFileACL -FilePath $authorizedkeyPath -User $currentUser -Perm "FullControl"
#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 Not Be 0
#Cleanup
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
}
It 'Authorized key file -- negative (the running process does not have read access to the authorized_keys)' {
#setup to have ssouser as owner and grant it full control
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Set-SecureFileACL -filepath $authorizedkeyPath -Owner $objUser
#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 Not Be 0
#Cleanup
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
}
}
}

View File

@ -0,0 +1,152 @@
Describe "Tests for ssh config" -Tags "CI" {
BeforeAll {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment."
}
if(-not (Test-Path $OpenSSHTestInfo["TestDataPath"]))
{
$null = New-Item $OpenSSHTestInfo["TestDataPath"] -ItemType directory -Force -ErrorAction SilentlyContinue
}
$testDir = "$($OpenSSHTestInfo["TestDataPath"])\cfginclude"
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
$fileName = "test.txt"
$filePath = Join-Path $testDir $fileName
$logName = "log.txt"
$logPath = Join-Path $testDir $logName
$server = $OpenSSHTestInfo["Target"]
$port = $OpenSSHTestInfo["Port"]
$ssouser = $OpenSSHTestInfo["SSOUser"]
$script:logNum = 0
# for the first time, delete the existing log files.
if ($OpenSSHTestInfo['DebugMode'])
{
Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore
Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction ignore
}
Remove-Item -Path $filePath -Force -ErrorAction ignore
function Set-SecureFileACL
{
[CmdletBinding()]
param(
[string]$FilePath,
[System.Security.Principal.NTAccount]$Owner = $null,
[System.Security.AccessControl.FileSystemAccessRule]$ACE = $null
)
$myACL = Get-ACL -Path $FilePath
$myACL.SetAccessRuleProtection($True, $True)
Set-Acl -Path $FilePath -AclObject $myACL
$myACL = Get-ACL $FilePath
if($owner -ne $null)
{
$myACL.SetOwner($Owner)
}
if($myACL.Access)
{
$myACL.Access | % {
if (($_ -ne $null) -and ($_.IdentityReference.Value -ine "BUILTIN\Administrators") -and
($_.IdentityReference.Value -ine "NT AUTHORITY\SYSTEM") -and
($_.IdentityReference.Value -ine "$(whoami)"))
{
if(-not ($myACL.RemoveAccessRule($_)))
{
throw "failed to remove access of $($_.IdentityReference.Value) rule in setup "
}
}
}
}
if($ACE -ne $null)
{
$myACL.AddAccessRule($ACE)
}
Set-Acl -Path $FilePath -AclObject $myACL
}
}
AfterAll {
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
AfterEach {
if( $OpenSSHTestInfo["DebugMode"])
{
Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\failedagent$script:logNum.log" -Force -ErrorAction ignore
Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\failedsshd$script:logNum.log" -Force -ErrorAction ignore
Copy-Item $logPath "$($script:logNum)$($logPath)" -Force -ErrorAction ignore
Clear-Content $logPath -Force -ErrorAction ignore
$script:logNum++
# clear the ssh-agent, sshd logs so that next testcase will get fresh logs.
Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore
Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\sshd.log" -Force -ErrorAction ignore
}
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
Context "User SSHConfig -- ReadConfig" {
BeforeAll {
$userConfigFile = Join-Path $home ".ssh\config"
Copy-item "$PSScriptRoot\testdata\ssh_config" $userConfigFile -force
$oldACL = Get-ACL $userConfigFile
}
AfterEach {
Set-Acl -Path $userConfigFile -AclObject $oldACL
}
AfterAll {
Remove-Item -Path $userConfigFile -Force -ErrorAction ignore
}
It 'User SSHConfig -- ReadConfig (admin user is the owner)' {
#setup
Set-SecureFileACL -filepath $userConfigFile
#Run
$str = "ssh -p $($port) -E $logPath $($Options) $($ssouser)@$($server) hostname > $filePath"
cmd /c $str
$LASTEXITCODE | Should Be 0
#validate file content.
Get-Content $filePath | Should be $env:COMPUTERNAME
#clean up
Set-Acl -Path $userConfigFile -AclObject $oldACL
}
It 'User SSHConfig -- ReadConfig (wrong owner)' {
#setup
Set-SecureFileACL -filepath $userConfigFile -owner $ssouser
#Run
$str = "ssh -p $($port) -E $logPath $($Options) $($ssouser)@$($server) hostname > $filePath"
cmd /c $str
#clean up
$LASTEXITCODE | Should Not Be 0
}
It 'User SSHConfig -- ReadConfig (wrong permission)' {
#setup
$owner = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME))
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `
($objUser, "Read, Write", "None", "None", "Allow")
Set-SecureFileACL -filepath $userConfigFile -owner $owner -Ace $objACE
#Run
$str = "ssh -p $($port) -E $logPath $($Options) $($ssouser)@$($server) hostname > $filePath"
cmd /c $str
#clean up
$LASTEXITCODE | Should Not Be 0
}
}
}

View File

@ -0,0 +1,106 @@
Enum PlatformType {
Windows
Linux
OSX
}
function Get-Platform {
# Use the .NET Core APIs to determine the current platform; if a runtime
# exception is thrown, we are on FullCLR, not .NET Core.
try {
$Runtime = [System.Runtime.InteropServices.RuntimeInformation]
$OSPlatform = [System.Runtime.InteropServices.OSPlatform]
$IsLinux = $Runtime::IsOSPlatform($OSPlatform::Linux)
$IsOSX = $Runtime::IsOSPlatform($OSPlatform::OSX)
$IsWindows = $Runtime::IsOSPlatform($OSPlatform::Windows)
} catch {
try {
$IsLinux = $false
$IsOSX = $false
$IsWindows = $true
}
catch { }
}
if($IsOSX) {
[PlatformType]::OSX
} elseif($IsLinux) {
[PlatformType]::Linux
} else {
[PlatformType]::Windows
}
}
function Set-SecureFileACL
{
param(
[string]$FilePath,
[System.Security.Principal.NTAccount]$Owner = $null
)
$myACL = Get-ACL -Path $FilePath
$myACL.SetAccessRuleProtection($True, $True)
Set-Acl -Path $FilePath -AclObject $myACL
$myACL = Get-ACL $FilePath
$actualOwner = $null
if($owner -eq $null)
{
$actualOwner = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME))
}
else
{
$actualOwner = $Owner
}
$myACL.SetOwner($actualOwner)
if($myACL.Access)
{
$myACL.Access | % {
if(-not ($myACL.RemoveAccessRule($_)))
{
throw "failed to remove access of $($_.IdentityReference.Value) rule in setup "
}
}
}
$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `
($actualOwner, "FullControl", "None", "None", "Allow")
$myACL.AddAccessRule($objACE)
Set-Acl -Path $FilePath -AclObject $myACL
}
function Add-PermissionToFileACL
{
param(
[string]$FilePath,
[System.Security.Principal.NTAccount] $User,
[System.Security.AccessControl.FileSystemRights]$Perm
)
$myACL = Get-ACL $filePath
$objACE = New-Object System.Security.AccessControl.FileSystemAccessRule `
($User, $perm, "None", "None", "Allow")
$myACL.AddAccessRule($objACE)
Set-Acl -Path $filePath -AclObject $myACL
}
function Add-PasswordSetting
{
param([string] $pass)
$platform = Get-Platform
if ($platform -eq [PlatformType]::Windows) {
if (-not($env:DISPLAY)) {$env:DISPLAY = 1}
$env:SSH_ASKPASS="$($env:ComSpec) /c echo $pass"
}
}
function Remove-PasswordSetting
{
if ($env:DISPLAY -eq 1) { Remove-Item env:\DISPLAY }
Remove-item "env:SSH_ASKPASS" -ErrorAction SilentlyContinue
}

View File

@ -0,0 +1,138 @@
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
Describe "Tests for host keys file permission" -Tags "CI" {
BeforeAll {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment."
}
$testDir = "$($OpenSSHTestInfo["TestDataPath"])\hostkey_fileperm"
if( -not (Test-path $testDir -PathType Container))
{
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
}
$fileName = "test.txt"
$filePath = Join-Path $testDir $fileName
$logName = "log.txt"
$logPath = Join-Path $testDir $logName
$port = 47003
$ssouser = $OpenSSHTestInfo["SSOUser"]
$script:logNum = 0
# for the first time, delete the existing log files.
if ($OpenSSHTestInfo['DebugMode'])
{
Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore
}
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
AfterEach {
if( $OpenSSHTestInfo["DebugMode"])
{
Copy-Item "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\failedagent$script:logNum.log" -Force -ErrorAction ignore
Copy-Item $logPath "$($script:logNum)$($logPath)" -Force -ErrorAction ignore
Clear-Content $logPath -Force -ErrorAction ignore
$script:logNum++
# clear the ssh-agent so that next testcase will get fresh logs.
Clear-Content "$($OpenSSHTestInfo['OpenSSHBinPath'])\logs\ssh-agent.log" -Force -ErrorAction ignore
}
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
Context "Host key files permission" {
BeforeAll {
$hostKeyFilePath = "$($OpenSSHTestInfo['OpenSSHBinPath'])\hostkeyFilePermTest_ed25519_key"
if(Test-path $hostKeyFilePath -PathType Leaf){
Set-SecureFileACL -filepath $hostKeyFilePath
}
if(Test-path "$hostKeyFilePath.pub" -PathType Leaf){
Set-SecureFileACL -filepath "$hostKeyFilePath.pub"
}
Remove-Item -path "$hostKeyFilePath*" -Force -ErrorAction Ignore
ssh-keygen.exe -t ed25519 -f $hostKeyFilePath -P `"`"
Remove-Item $filePath -Force -ErrorAction Ignore
Get-Process -Name sshd | Where-Object {$_.SI -ne 0} | Stop-process
}
AfterEach {
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
It 'Host keys -- positive (Secured private key and sshd can access to public key file)' {
#setup to have current user as owner and grant it full control
Set-SecureFileACL -filepath $hostKeyFilePath
#grant sshd Read permission to public key
Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User "NT Service\sshd" -Perm "Read"
#Run
Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $filePath") -NoNewWindow
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
#validate file content does not contain unprotected.
$matches = Get-Content $filePath | Select-String -pattern "UNPROTECTED PRIVATE KEY FILE!"
$matches.Count | Should Be 0
}
It 'Host keys -- negative (other account can access private key file)' {
#setup to have current user as owner and grant it full control
Set-SecureFileACL -filepath $hostKeyFilePath
#add ssouser to access the private key
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $objUser -Perm "Read"
#grant sshd Read permission to the public key
Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User "NT Service\sshd" -Perm "Read"
#Run
Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $filePath") -NoNewWindow
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
#validate file content does not contain unprotected.
$matches = Get-Content $filePath | Select-String -pattern "key_load_private: bad permissions"
$matches.Count | Should Be 1
}
It 'Host keys -- negative (the private key has wrong owner)' {
#setup to have ssouser as owner and grant it full control
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Set-SecureFileACL -filepath $hostKeyFilePath -owner $objUser
$currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME))
Add-PermissionToFileACL -FilePath $hostKeyFilePath -User $currentUser -Perm "FullControl"
#grant sshd Read permission to the public key
Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User "NT Service\sshd" -Perm "Read"
#Run
Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $filePath") -NoNewWindow
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
#validate file content does not contain unprotected.
$matches = Get-Content $filePath | Select-String -pattern "key_load_private: bad permissions"
$matches.Count | Should Be 1
}
It 'Host keys -- negative (the running process does not have read access to pub key)' {
#setup to have ssouser as owner and grant it full control
Set-SecureFileACL -filepath $hostKeyFilePath
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Set-SecureFileACL -filepath "$hostKeyFilePath.pub" -owner $objUser
#grant current user Read permission to public key
Add-PermissionToFileACL -FilePath "$hostKeyFilePath.pub" -User $objUser -Perm "Read"
#Run
Start-Process -FilePath sshd.exe -WorkingDirectory $($OpenSSHTestInfo['OpenSSHBinPath']) -ArgumentList @("-d", "-p $port", "-h $hostKeyFilePath", "-E $filePath") -NoNewWindow
Get-Process -Name sshd | % { if($_.SI -ne 0) { Start-sleep 1; Stop-Process $_; Start-sleep 1 } }
#validate file content does not contain unprotected.
$matches = Get-Content $filePath | Select-String -pattern "key_load_public: Permission denied"
$matches.Count | Should Be 1
}
}
}

View File

@ -0,0 +1,62 @@
Describe "Tests for ssh-keygen" -Tags "CI" {
BeforeAll {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment."
}
$testDir = "$($OpenSSHTestInfo["TestDataPath"])\keygen"
if( -not (Test-path $testDir -PathType Container))
{
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
}
#only validate owner and ACE of the file
function ValidKeyFile {
param($Path)
$currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME))
$myACL = Get-ACL $Path
$myACL.Owner.Equals($currentUser.Value) | Should Be $true
$myACL.Access | Should Not Be $null
$myACL.Access.Count | Should Be 1
$myACL.Access[0].IdentityReference.Equals($currentUser) | Should Be $true
$myACL.Access[0].AccessControlType | Should Be ([System.Security.AccessControl.AccessControlType]::Allow)
$myACL.Access[0].FileSystemRights | Should Be ([System.Security.AccessControl.FileSystemRights]::FullControl)
$myACL.Access[0].IsInherited | Should Be $false
$myACL.Access[0].InheritanceFlags | Should Be ([System.Security.AccessControl.InheritanceFlags]::None)
$myACL.Access[0].PropagationFlags | Should Be ([System.Security.AccessControl.PropagationFlags]::None)
}
}
Context "Keygen key files" {
BeforeEach {
Remove-Item $testDir\* -Force -ErrorAction ignore
Remove-Item "$PSScriptRoot\ssh_host_*_key*" -Force -ErrorAction ignore
}
It 'Keygen -A' {
ssh-keygen -A
Get-ChildItem "$PSScriptRoot\ssh_host_*_key" | % {
ValidKeyFile -Path $_.FullName
}
Get-ChildItem "$PSScriptRoot\ssh_host_*_key.pub" | % {
ValidKeyFile -Path $_.FullName
}
}
It 'Keygen -t -f' {
$pwd = "testpassword"
foreach($type in @("rsa","dsa","ecdsa","ed25519"))
{
$keyPath = Join-Path $testDir "id_$type"
ssh-keygen -t $type -P $pwd -f $keyPath
ValidKeyFile -Path $keyPath
ValidKeyFile -Path "$keyPath.pub"
}
}
}
}

View File

@ -66,13 +66,13 @@ Describe "Tests for scp command" -Tags "CI" {
Source = $SourceFilePath
Destination = "$($ssouser)@$($server):$DestinationDir"
Options = "-P $port -C -q"
},
}<#,
@{
Title = 'simple copy remote file to local dir'
Source = "$($ssouser)@$($server):$SourceFilePath"
Destination = $DestinationDir
Options = "-P $port "
}
}#>
)
$testData1 = @(

View File

@ -0,0 +1,185 @@
Import-Module $PSScriptRoot\CommonUtils.psm1 -Force
Describe "Tests for user Key file permission" -Tags "CI" {
BeforeAll {
if($OpenSSHTestInfo -eq $null)
{
Throw "`$OpenSSHTestInfo is null. Please run Setup-OpenSSHTestEnvironment to setup test environment."
}
$testDir = "$($OpenSSHTestInfo["TestDataPath"])\usertkey_fileperm"
if( -not (Test-path $testDir -PathType Container))
{
$null = New-Item $testDir -ItemType directory -Force -ErrorAction SilentlyContinue
}
$fileName = "test.txt"
$filePath = Join-Path $testDir $fileName
$port = $OpenSSHTestInfo["Port"]
$ssouser = $OpenSSHTestInfo["SSOUser"]
$pubKeyUser = $OpenSSHTestInfo["PubKeyUser"]
$pubKeyUserProfile = $OpenSSHTestInfo["PubKeyUserProfile"]
$server = $OpenSSHTestInfo["Target"]
$keyFileName = "sshtest_userPermTestkey_ed25519"
$keyFilePath = Join-Path $testDir $keyFileName
Remove-Item -path "$keyFilePath*" -Force -ErrorAction Ignore
ssh-keygen.exe -t ed25519 -f $keyFilePath -P `"`"
$userName = "$env:USERNAME@$env:USERDOMAIN"
if(Test-Path $keyFilePath) {
Set-SecureFileACL -filepath $keyFilePath
}
#add wrong password so ssh does not prompt password if failed with authorized keys
Add-PasswordSetting -Pass "WrongPass"
}
AfterAll {
Remove-PasswordSetting
}
<# comment the test out since ssh-add have impact on
existing default test environment.
Context "ssh-add key files" {
BeforeEach {
ssh-add -D 2>&1 > $fileName
}
AfterEach {
ssh-add -D 2>&1 > $fileName
if(Test-Path $keyFilePath) {
Set-SecureFileACL -filepath $keyFilePath
}
}
It 'ssh-add positive (Secured private key owned by current user)' {
#setup to have current user as owner and grant it full control
Set-SecureFileACL -filepath $keyFilePath
ssh-add $keyFilePath 2>&1 > $fileName
$LASTEXITCODE | Should Be 0
$o = ssh-add -l
$matches = $o | Select-String -pattern "no identities"
$matches.Count | Should Be 0
$matches = $o | Select-String -pattern $userName
$matches.Count | Should Be 1
}
It 'ssh-add positive (Secured private key owned by Administrators group)' {
#setup to have local admin group as owner and grant it full control
$objAdmin = New-Object System.Security.Principal.NTAccount("BUILTIN", "Administrators")
Set-SecureFileACL -filepath $keyFilePath -Owner $objAdmin
ssh-add $keyFilePath 2>&1 > $fileName
$LASTEXITCODE | Should Be 0
$o = ssh-add -l
$matches = $o | Select-String -pattern "no identities"
$matches.Count | Should Be 0
$matches = $o | Select-String -pattern $userName
$matches.Count | Should Be 1
}
It 'ssh-add -- negative (other account can access private key file)' {
#setup to have current user as owner and grant it full control
Set-SecureFileACL -filepath $keyFilePath
#add ssouser to access the private key
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Add-PermissionToFileACL -FilePath $keyFilePath -User $objUser -Perm "Read"
ssh-add $keyFilePath 2>&1 > $fileName
$LASTEXITCODE | Should Not Be 0
$o = ssh-add -l
$matches = $o | Select-String -pattern "no identities"
$matches.Count | Should Be 1
}
It 'ssh-add -- negative (the private key has wrong owner)' {
#setup to have ssouser as owner and grant it full control
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Set-SecureFileACL -filepath $keyFilePath -owner $objUser
$currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME))
Add-PermissionToFileACL -FilePath $keyFilePath -User $currentUser -Perm "FullControl"
ssh-add $keyFilePath 2>&1 > $fileName
$LASTEXITCODE | Should Not Be 0
$o = ssh-add -l
$matches = $o | Select-String -pattern "no identities"
$matches.Count | Should Be 1
}
}#>
Context "ssh with private key file" {
BeforeAll {
$pubKeyUserProfilePath = Join-Path $pubKeyUserProfile .ssh
if(-not (Test-Path $pubKeyUserProfilePath -PathType Container)) {
New-Item $pubKeyUserProfilePath -ItemType directory -Force -ErrorAction Stop | Out-Null
}
Set-SecureFileACL -filepath $keyFilePath
$testAuthorizedKeyPath = Join-Path $pubKeyUserProfilePath authorized_keys
Copy-Item "$keyFilePath.pub" $testAuthorizedKeyPath -Force -ErrorAction SilentlyContinue
Add-PermissionToFileACL -FilePath $testAuthorizedKeyPath -User "NT Service\sshd" -Perm "Read"
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
AfterAll {
if(Test-Path $testAuthorizedKeyPath) {
Set-SecureFileACL -filepath $testAuthorizedKeyPath
Remove-Item $testAuthorizedKeyPath -Force -ErrorAction Ignore
}
if(Test-Path $pubKeyUserProfilePath) {
Remove-Item $pubKeyUserProfilePath -Force -ErrorAction Ignore
}
}
AfterEach {
if(Test-Path $keyFilePath) {
Set-SecureFileACL -filepath $keyFilePath
}
Remove-Item -Path $filePath -Force -ErrorAction ignore
}
It 'ssh with private key file -- positive (Secured private key owned by current user)' {
Set-SecureFileACL -filepath $keyFilePath
#Run
$o = ssh -p $port -i $keyFilePath $pubKeyUser@$server echo 1234
$o | Should Be "1234"
}
It 'ssh with private key file -- positive (Secured private key owned by Administrators group)' {
#setup to have local admin group as owner and grant it full control
$objAdmin = New-Object System.Security.Principal.NTAccount("BUILTIN", "Administrators")
Set-SecureFileACL -filepath $keyFilePath -Owner $objAdmin
#Run
$o = ssh -p $port -i $keyFilePath $pubKeyUser@$server echo 1234
$o | Should Be "1234"
}
It 'ssh with private key file -- negative (other account can access private key file)' {
#setup to have current user as owner and grant it full control
Set-SecureFileACL -filepath $keyFilePath
#add ssouser to access the private key
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Add-PermissionToFileACL -FilePath $keyFilePath -User $objUser -Perm "Read"
#Run
$o = ssh -p $port -i $keyFilePath -E $filePath $pubKeyUser@$server echo 1234
$LASTEXITCODE | Should Not Be 0
$matches = Get-Content $filePath | Select-String -pattern "UNPROTECTED PRIVATE KEY FILE!"
$matches.Count | Should Be 1
}
It 'ssh with private key file -- (the private key has wrong owner)' {
#setup to have ssouser as owner and grant it full control
$objUser = New-Object System.Security.Principal.NTAccount($ssouser)
Set-SecureFileACL -filepath $keyFilePath -owner $objUser
$currentUser = New-Object System.Security.Principal.NTAccount($($env:USERDOMAIN), $($env:USERNAME))
Add-PermissionToFileACL -FilePath $keyFilePath -User $currentUser -Perm "FullControl"
$o = ssh -p $port -i $keyFilePath -E $filePath $pubKeyUser@$server echo 1234
$LASTEXITCODE | Should Not Be 0
$matches = Get-Content $filePath | Select-String -pattern "UNPROTECTED PRIVATE KEY FILE!"
$matches.Count | Should Be 1
}
}
}

View File

@ -0,0 +1,4 @@
# test usage of ssh_config
PubkeyAcceptedKeyTypes ssh-ed25519*

View File

@ -0,0 +1,8 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACAN1tdRDiL3ZAZMPT3c3/3Gg/XbWPK3M0gAZPhIFHivHgAAALBPa9N1T2vT
dQAAAAtzc2gtZWQyNTUxOQAAACAN1tdRDiL3ZAZMPT3c3/3Gg/XbWPK3M0gAZPhIFHivHg
AAAEAkxz77KuyYDchGmc6owF2ykq2rMzRqqQaEpJgyTrsLVA3W11EOIvdkBkw9Pdzf/caD
9dtY8rczSABk+EgUeK8eAAAAJm5ld2xvZ2luQFlBTkJJTkdXMksxMlIyQFlhbmJpbmd3Mm
sxMnIyAQIDBAUGBw==
-----END OPENSSH PRIVATE KEY-----

View File

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIA3W11EOIvdkBkw9Pdzf/caD9dtY8rczSABk+EgUeK8e newlogin@YANBINGW2K12R2@Yanbingw2k12r2

View File

@ -0,0 +1 @@
[localhost]:47003 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMtJMxwn+iJU0X4+EC7PSj/cfcMbdP6ahhodtXx+6RHv sshtest_hostkey_ed25519

View File

@ -59,6 +59,7 @@
#include "krl.h"
#include "digest.h"
#include "utf8.h"
#include "sshfileperm.h"
#ifdef WITH_OPENSSL
# define DEFAULT_KEY_TYPE_NAME "rsa"
@ -1046,7 +1047,16 @@ do_gen_all_hostkeys(struct passwd *pw)
/* Windows POSIX adpater does not support fdopen() on open(file)*/
if ((f = fopen(identity_file, "w")) == NULL) {
error("fopen %s failed: %s", identity_file, strerror(errno));
/* TODO - set permissions on file */
sshkey_free(public);
first = 0;
continue;
}
/*
Set the owner of the private key file to the user represented by pw
and only grant it the full control access
*/
if (set_secure_file_permission(identity_file, pw) !=0) {
error("set_secure_file_permission on %s failed!", identity_file);
#else /* !WINDOWS */
fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd == -1) {
@ -1497,6 +1507,14 @@ do_change_comment(struct passwd *pw)
strlcat(identity_file, ".pub", sizeof(identity_file));
fd = open(identity_file, O_WRONLY | O_CREAT | O_TRUNC, 0644);
#ifdef WINDOWS
/*
Set the owner of the private key file to user represented by pw and only grant
it the full control access
*/
if (set_secure_file_permission(identity_file, pw) != 0)
fatal("set_secure_file_permission on %s failed!", identity_file);
#endif /* WINDOWS*/
if (fd == -1)
fatal("Could not save your public key in %s", identity_file);
f = fdopen(fd, "w");
@ -1680,6 +1698,15 @@ do_ca_sign(struct passwd *pw, int argc, char **argv)
if ((fd = open(out, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
fatal("Could not open \"%s\" for writing: %s", out,
strerror(errno));
#ifdef WINDOWS
/*
Set the owner of the private key file to user represented by pw and only grant
it the full control access
*/
if (set_secure_file_permission(out, pw) != 0)
fatal("set_secure_file_permission on %s failed!", identity_file);
#endif /* WINDOWS */
if ((f = fdopen(fd, "w")) == NULL)
fatal("%s: fdopen: %s", __func__, strerror(errno));
if ((r = sshkey_write(public, f)) != 0)
@ -2189,6 +2216,14 @@ do_gen_krl(struct passwd *pw, int updating, int argc, char **argv)
fatal("Couldn't generate KRL");
if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
fatal("open %s: %s", identity_file, strerror(errno));
#ifdef WINDOWS
/*
Set the owner of the private key file to user represented by pw and only grant
it the full control access
*/
if (set_secure_file_permission(identity_file, pw) != 0)
fatal("set_secure_file_permission on %s failed!", identity_file);
#endif /* WINDOWS */
if (atomicio(vwrite, fd, (void *)sshbuf_ptr(kbuf), sshbuf_len(kbuf)) !=
sshbuf_len(kbuf))
fatal("write %s: %s", identity_file, strerror(errno));
@ -2758,7 +2793,12 @@ passphrase_again:
/* Windows POSIX adpater does not support fdopen() on open(file)*/
if ((f = fopen(identity_file, "w")) == NULL)
fatal("fopen %s failed: %s", identity_file, strerror(errno));
/* TODO - set permissions on file */
/*
Set the owner of the private key file to the user represented by pw and only grant
it the full control access
*/
if (set_secure_file_permission(identity_file, pw) != 0)
error("set_secure_file_permission on %s failed!", identity_file);
#else /* !WINDOWS */
if ((fd = open(identity_file, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1)
fatal("Unable to save public key to %s: %s",

30
sshfileperm.h Normal file
View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 2004, 2005 Darren Tucker. All rights reserved.
*
* 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.
*/
#ifndef _SSH_FILE_PERM_H
#define _SSH_FILE_PERM_H
int check_secure_file_permission(const char *, struct passwd *);
int set_secure_file_permission(const char *, struct passwd *);
#endif /* _SSH_FILE_PERM_H */