Update ssh folder permissions check in SSHD (#761)

* remove check on progdata/ssh/log folder permissions

* add pester test

* modify permissions check to log event without failing startup

* modify perm check

* update test

* uncomment code

* modify pester test

* address review feedback

* address review feedback

* fix multi-line logging

* cleanup allocations

* address review feedback

* address additional review feedback

* store value in tmp var
This commit is contained in:
Tess Gauthier 2025-01-10 10:47:23 -05:00 committed by GitHub
parent 7baad0a474
commit b36bc85f47
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 231 additions and 76 deletions

View File

@ -1419,7 +1419,7 @@ is_absolute_path(const char *path)
/* return -1 - in case of failure, 0 - success */ /* return -1 - in case of failure, 0 - success */
int int
create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w) create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w, BOOL check_permissions)
{ {
if (GetFileAttributesW(path_w) == INVALID_FILE_ATTRIBUTES) { if (GetFileAttributesW(path_w) == INVALID_FILE_ATTRIBUTES) {
PSECURITY_DESCRIPTOR pSD = NULL; PSECURITY_DESCRIPTOR pSD = NULL;
@ -1444,12 +1444,9 @@ create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w)
return -1; return -1;
} }
} }
else { else if (check_permissions) {
// directory already exists; need to confirm permissions are correct // directory already exists; need to confirm permissions are correct
if (check_secure_folder_permission(path_w, 1) != 0) { check_secure_folder_permission(path_w, 1);
error("Directory already exists but folder permissions are invalid");
return -1;
}
} }
return 0; return 0;

View File

@ -67,7 +67,7 @@ void to_lower_case(char *s);
void to_wlower_case(wchar_t *s); void to_wlower_case(wchar_t *s);
HANDLE get_user_token(const char* user, int impersonation); HANDLE get_user_token(const char* user, int impersonation);
int load_user_profile(HANDLE user_token, char* user); int load_user_profile(HANDLE user_token, char* user);
int create_directory_withsddl(wchar_t *path, wchar_t *sddl); int create_directory_withsddl(wchar_t *path, wchar_t *sddl, BOOL check_permissions);
int is_absolute_path(const char *); int is_absolute_path(const char *);
int file_in_chroot_jail(HANDLE); int file_in_chroot_jail(HANDLE);
PSID lookup_sid(const wchar_t* name_utf16, PSID psid, DWORD * psid_len); PSID lookup_sid(const wchar_t* name_utf16, PSID psid, DWORD * psid_len);

View File

@ -33,6 +33,8 @@
#include <Aclapi.h> #include <Aclapi.h>
#include <lm.h> #include <lm.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "inc\pwd.h" #include "inc\pwd.h"
#include "sshfileperm.h" #include "sshfileperm.h"
@ -40,6 +42,12 @@
#include "misc_internal.h" #include "misc_internal.h"
#include "config.h" #include "config.h"
#define NULL_TERMINATOR_LEN 1
#define COMMA_SPACE_LEN 2
#define BACKSLASH_LEN 1
extern int log_on_stderr;
/* /*
* The function is to check if current user is secure to access to the file. * The function is to check if current user is secure to access to the file.
* Check the owner of the file is one of these types: Local Administrators groups, system account, current user account * Check the owner of the file is one of these types: Local Administrators groups, system account, current user account
@ -178,18 +186,22 @@ cleanup:
* Check the owner of the file is one of these types: Local Administrators groups or system account * Check the owner of the file is one of these types: Local Administrators groups or system account
* Check the users have access permission to the file don't violate the following rules: * Check the users have access permission to the file don't violate the following rules:
1. no user other than local administrators group and system account have write permission on the folder 1. no user other than local administrators group and system account have write permission on the folder
* Returns 0 on success and -1 on failure * Logs a message if the rules are violated, but does not prevent further execution
*/ */
int void
check_secure_folder_permission(const wchar_t* path_utf16, int read_ok) check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
{ {
PSECURITY_DESCRIPTOR pSD = NULL; PSECURITY_DESCRIPTOR pSD = NULL;
PSID owner_sid = NULL, ti_sid = NULL; PSID owner_sid = NULL, ti_sid = NULL;
PACL dacl = NULL; PACL dacl = NULL;
DWORD error_code = ERROR_SUCCESS; DWORD error_code = ERROR_SUCCESS;
BOOL is_valid_sid = FALSE, is_valid_acl = FALSE; BOOL is_valid_sid = FALSE, is_valid_acl = FALSE, need_log_msg = FALSE, is_first = TRUE;
wchar_t* bad_user = NULL; wchar_t* bad_user = NULL;
int ret = 0; size_t log_msg_len = (DNLEN + BACKSLASH_LEN + UNLEN) * 2 + COMMA_SPACE_LEN + NULL_TERMINATOR_LEN;
wchar_t* log_msg = (wchar_t*)malloc(log_msg_len * sizeof(wchar_t));
if (log_msg != NULL) {
log_msg[0] = '\0';
}
/*Get the owner sid of the file.*/ /*Get the owner sid of the file.*/
if ((error_code = GetNamedSecurityInfoW(path_utf16, SE_FILE_OBJECT, if ((error_code = GetNamedSecurityInfoW(path_utf16, SE_FILE_OBJECT,
@ -197,18 +209,15 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
&owner_sid, NULL, &dacl, NULL, &pSD)) != ERROR_SUCCESS) { &owner_sid, NULL, &dacl, NULL, &pSD)) != ERROR_SUCCESS) {
printf("failed to retrieve the owner sid and dacl of file %S with error code: %d", path_utf16, error_code); printf("failed to retrieve the owner sid and dacl of file %S with error code: %d", path_utf16, error_code);
errno = EOTHER; errno = EOTHER;
ret = -1;
goto cleanup; goto cleanup;
} }
if (((is_valid_sid = IsValidSid(owner_sid)) == FALSE) || ((is_valid_acl = IsValidAcl(dacl)) == FALSE)) { if (((is_valid_sid = IsValidSid(owner_sid)) == FALSE) || ((is_valid_acl = IsValidAcl(dacl)) == FALSE)) {
printf("IsValidSid: %d; is_valid_acl: %d", is_valid_sid, is_valid_acl); printf("IsValidSid: %d; is_valid_acl: %d", is_valid_sid, is_valid_acl);
ret = -1;
goto cleanup; goto cleanup;
} }
if (!IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) && if (!IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) &&
!IsWellKnownSid(owner_sid, WinLocalSystemSid)) { !IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
printf("Bad owner on %S", path_utf16); printf("Bad owner on %S", path_utf16);
ret = -1;
goto cleanup; goto cleanup;
} }
/* /*
@ -224,7 +233,6 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
if (!GetAce(dacl, i, &current_ace)) { if (!GetAce(dacl, i, &current_ace)) {
printf("GetAce() failed"); printf("GetAce() failed");
errno = EOTHER; errno = EOTHER;
ret = -1;
goto cleanup; goto cleanup;
} }
@ -247,15 +255,112 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
continue; continue;
} }
else { else {
ret = -1; /* collect all SIDs with write permissions */
wchar_t resolved_trustee[UNLEN + NULL_TERMINATOR_LEN] = L"UNKNOWN";
wchar_t resolved_trustee_domain[DNLEN + NULL_TERMINATOR_LEN] = L"UNKNOWN";
DWORD resolved_trustee_len = _countof(resolved_trustee), resolved_trustee_domain_len = _countof(resolved_trustee_domain);
SID_NAME_USE resolved_trustee_type;
need_log_msg = TRUE;
if (log_msg != NULL &&
LookupAccountSidW(NULL, current_trustee_sid, resolved_trustee, &resolved_trustee_len,
resolved_trustee_domain, &resolved_trustee_domain_len, &resolved_trustee_type) != 0) {
if (is_first) {
_snwprintf_s(log_msg, log_msg_len, _TRUNCATE, L"%ls\\%ls", resolved_trustee_domain, resolved_trustee);
is_first = FALSE;
}
else {
size_t currentLength = wcslen(log_msg);
size_t userLength = resolved_trustee_domain_len + BACKSLASH_LEN + resolved_trustee_len + COMMA_SPACE_LEN;
if (wcslen(log_msg) + userLength + NULL_TERMINATOR_LEN > log_msg_len) {
log_msg_len *= 2;
wchar_t* temp_log_msg = (wchar_t*)malloc(log_msg_len * sizeof(wchar_t));
if (temp_log_msg == NULL) {
break;
}
wcscpy_s(temp_log_msg, log_msg_len, log_msg);
if (log_msg)
free(log_msg);
log_msg = temp_log_msg;
}
_snwprintf_s(log_msg + currentLength, log_msg_len - currentLength, _TRUNCATE,
L", %ls\\%ls", resolved_trustee_domain, resolved_trustee);
}
}
} }
} }
if (need_log_msg) {
log_folder_perms_msg_etw(path_utf16, log_msg);
}
cleanup: cleanup:
if (bad_user) if (bad_user) {
LocalFree(bad_user); LocalFree(bad_user);
if (pSD) }
if (log_msg) {
free(log_msg);
}
if (pSD) {
LocalFree(pSD); LocalFree(pSD);
if (ti_sid) }
if (ti_sid) {
free(ti_sid); free(ti_sid);
return ret; }
}
/*
* This function takes in the full path to the ProgramData\ssh folder
* and a string of comma-separated domain\usernames. The function converts
* the well-known built-in Administrators group sid and the Local System
* sid to their corresponding names. With these names, and the input string,
* it logs a message to the Event Viewer. If logging the detailed message fails,
* a generic log message is written to the Event Viewer instead.
*/
void log_folder_perms_msg_etw(const wchar_t* path_utf16, wchar_t* log_msg) {
PSID adminSid = NULL;
WCHAR adminName[UNLEN + NULL_TERMINATOR_LEN];
WCHAR adminDomain[DNLEN + NULL_TERMINATOR_LEN];
DWORD adminNameSize = UNLEN + NULL_TERMINATOR_LEN;
DWORD adminDomainSize = DNLEN + NULL_TERMINATOR_LEN;
DWORD adminSidSize = SECURITY_MAX_SID_SIZE;
PSID systemSid = NULL;
WCHAR systemName[UNLEN + NULL_TERMINATOR_LEN];
WCHAR systemDomain[DNLEN + NULL_TERMINATOR_LEN];
DWORD systemNameSize = UNLEN + NULL_TERMINATOR_LEN;
DWORD systemDomainSize = DNLEN + NULL_TERMINATOR_LEN;
DWORD systemSidSize = SECURITY_MAX_SID_SIZE;
SID_NAME_USE sidType;
BOOL needLog = TRUE;
int temp_log_on_stderr = log_on_stderr;
log_on_stderr = 0;
adminSid = (PSID)malloc(SECURITY_MAX_SID_SIZE);
if (log_msg != NULL && adminSid != NULL &&
CreateWellKnownSid(WinBuiltinAdministratorsSid, NULL, adminSid, &adminSidSize) != 0 &&
LookupAccountSidW(NULL, adminSid, adminName, &adminNameSize, adminDomain, &adminDomainSize, &sidType) != 0) {
systemSid = (PSID)malloc(SECURITY_MAX_SID_SIZE);
if (systemSid != NULL &&
CreateWellKnownSid(WinLocalSystemSid, NULL, systemSid, &systemSidSize) != 0 &&
LookupAccountSidW(NULL, systemSid, systemName, &systemNameSize, systemDomain, &systemDomainSize, &sidType) != 0) {
logit("For '%S' folder, write access is granted to the following users: %S. "
"Consider reviewing users to ensure that only %S\\%S, and the %S\\%S group, and its members, have write access.",
path_utf16, log_msg, systemDomain, systemName, adminDomain, adminName);
needLog = FALSE;
}
}
if (needLog) {
/* log generic warning message in unlikely case that lookup for either well-known SID fails or user list is empty */
logit("for '%S' folder, consider downgrading permissions for any users with unnecessary write access.", path_utf16);
}
log_on_stderr = temp_log_on_stderr;
if (adminSid) {
free(adminSid);
}
if (systemSid) {
free(systemSid);
}
} }

View File

@ -135,7 +135,7 @@ create_prgdata_ssh_folder()
wchar_t ssh_cfg_dir[PATH_MAX] = { 0, }; wchar_t ssh_cfg_dir[PATH_MAX] = { 0, };
wcscpy_s(ssh_cfg_dir, _countof(ssh_cfg_dir), __wprogdata); wcscpy_s(ssh_cfg_dir, _countof(ssh_cfg_dir), __wprogdata);
wcscat_s(ssh_cfg_dir, _countof(ssh_cfg_dir), L"\\ssh"); wcscat_s(ssh_cfg_dir, _countof(ssh_cfg_dir), L"\\ssh");
if (create_directory_withsddl(ssh_cfg_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)") < 0) { if (create_directory_withsddl(ssh_cfg_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;0x1200a9;;;AU)", TRUE) < 0) {
printf("failed to create %S", ssh_cfg_dir); printf("failed to create %S", ssh_cfg_dir);
exit(255); exit(255);
} }
@ -144,7 +144,7 @@ create_prgdata_ssh_folder()
wchar_t logs_dir[PATH_MAX] = { 0, }; wchar_t logs_dir[PATH_MAX] = { 0, };
wcscat_s(logs_dir, _countof(logs_dir), ssh_cfg_dir); wcscat_s(logs_dir, _countof(logs_dir), ssh_cfg_dir);
wcscat_s(logs_dir, _countof(logs_dir), L"\\logs"); wcscat_s(logs_dir, _countof(logs_dir), L"\\logs");
if (create_directory_withsddl(logs_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)") < 0) { if (create_directory_withsddl(logs_dir, L"O:BAD:PAI(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)", FALSE) < 0) {
printf("failed to create %S", logs_dir); printf("failed to create %S", logs_dir);
exit(255); exit(255);
} }

4
log.c
View File

@ -54,7 +54,11 @@
#include "match.h" #include "match.h"
static LogLevel log_level = SYSLOG_LEVEL_INFO; static LogLevel log_level = SYSLOG_LEVEL_INFO;
#ifdef WINDOWS
int log_on_stderr = 1;
#else
static int log_on_stderr = 1; static int log_on_stderr = 1;
#endif /* WINDOWS */
static int log_stderr_fd = STDERR_FILENO; static int log_stderr_fd = STDERR_FILENO;
static int log_facility = LOG_AUTH; static int log_facility = LOG_AUTH;
static const char *argv0; static const char *argv0;

View File

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

View File

@ -26,5 +26,6 @@
#define _SSH_FILE_PERM_H #define _SSH_FILE_PERM_H
int check_secure_file_permission(const char *, struct passwd *, int); int check_secure_file_permission(const char *, struct passwd *, int);
int check_secure_folder_permission(const wchar_t*, int); void check_secure_folder_permission(const wchar_t*, int);
void log_folder_perms_msg_etw(const wchar_t*, wchar_t*);
#endif /* _SSH_FILE_PERM_H */ #endif /* _SSH_FILE_PERM_H */