Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update ssh folder permissions check in SSHD #761

Open
wants to merge 13 commits into
base: latestw_all
Choose a base branch
from
9 changes: 3 additions & 6 deletions contrib/win32/win32compat/misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1419,7 +1419,7 @@ is_absolute_path(const char *path)

/* return -1 - in case of failure, 0 - success */
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) {
PSECURITY_DESCRIPTOR pSD = NULL;
Expand All @@ -1444,12 +1444,9 @@ create_directory_withsddl(wchar_t *path_w, wchar_t *sddl_w)
return -1;
}
}
else {
else if (check_permissions) {
// directory already exists; need to confirm permissions are correct
if (check_secure_folder_permission(path_w, 1) != 0) {
error("Directory already exists but folder permissions are invalid");
return -1;
}
check_secure_folder_permission(path_w, 1);
}

return 0;
Expand Down
2 changes: 1 addition & 1 deletion contrib/win32/win32compat/misc_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void to_lower_case(char *s);
void to_wlower_case(wchar_t *s);
HANDLE get_user_token(const char* user, int impersonation);
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 file_in_chroot_jail(HANDLE);
PSID lookup_sid(const wchar_t* name_utf16, PSID psid, DWORD * psid_len);
Expand Down
103 changes: 95 additions & 8 deletions contrib/win32/win32compat/w32-sshfileperm.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@
#include <Aclapi.h>
#include <lm.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "inc\pwd.h"
#include "sshfileperm.h"
#include "debug.h"
#include "misc_internal.h"
#include "config.h"

extern int log_on_stderr;
SteveL-MSFT marked this conversation as resolved.
Show resolved Hide resolved

/*
* 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
Expand Down Expand Up @@ -178,37 +182,39 @@ check_secure_file_permission(const char *input_path, struct passwd * pw, int rea
* 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:
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)
{
PSECURITY_DESCRIPTOR pSD = NULL;
PSID owner_sid = NULL, ti_sid = NULL;
PACL dacl = NULL;
DWORD error_code = ERROR_SUCCESS;
BOOL is_valid_sid = FALSE, is_valid_acl = FALSE;
BOOL is_valid_sid = FALSE, is_valid_acl = FALSE, is_first = TRUE;
wchar_t* bad_user = NULL;
int ret = 0;
size_t log_msg_len = (DNLEN + 1 + UNLEN) * 2 + 3; // +3 for ", " and null terminator
tgauth marked this conversation as resolved.
Show resolved Hide resolved
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.*/
if ((error_code = GetNamedSecurityInfoW(path_utf16, SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
&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);
errno = EOTHER;
ret = -1;
goto cleanup;
}
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);
ret = -1;
goto cleanup;
}
if (!IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) &&
!IsWellKnownSid(owner_sid, WinLocalSystemSid)) {
printf("Bad owner on %S", path_utf16);
ret = -1;
goto cleanup;
}
/*
Expand Down Expand Up @@ -247,15 +253,96 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
continue;
}
else {
ret = -1;
/* collect all SIDs with write permissions */
wchar_t resolved_trustee[UNLEN + 1] = L"UNKNOWN", resolved_trustee_domain[DNLEN + 1] = L"UNKNOWN";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These +1 is for the null terminator? Use NULL_TERMINATOR_LEN

DWORD resolved_trustee_len = _countof(resolved_trustee), resolved_trustee_domain_len = _countof(resolved_trustee_domain);
SID_NAME_USE resolved_trustee_type;

ret = -1; // set ret to -1 to indicate that there are bad permissions so message will be logged

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 + 1 + resolved_trustee_len + 2; // +1 for '\\' and +2 for ', '
if (wcslen(log_msg) + userLength + 1 > log_msg_len) { // +1 for null terminator
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);
Comment on lines +283 to +284
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Braces make the code more clear and also avoids problems with future changes

Suggested change
if (log_msg)
free(log_msg);
if (log_msg) {
free(log_msg);
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I get that but on other hand, this follows the existing convention in the code. Also, this is in the win32 compat project so future merge conflicts due to pulling in upstream changes are not a concern here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think since it's in the win32 compat only side, it makes a stronger case to fix it throughout the code (separate PR for changes outside the scope of this fix).

log_msg = temp_log_msg;
}
_snwprintf_s(log_msg + currentLength, log_msg_len - currentLength, _TRUNCATE,
L", %ls\\%ls", resolved_trustee_domain, resolved_trustee);
}
}
}
}

if (ret != 0) {
log_folder_permissions_message(path_utf16, log_msg);
}
cleanup:
if (bad_user)
LocalFree(bad_user);
if (log_msg)
free(log_msg);
if (pSD)
LocalFree(pSD);
if (ti_sid)
free(ti_sid);
return ret;
}

/* Helper function used by check_secure_folder_permission */
tgauth marked this conversation as resolved.
Show resolved Hide resolved
void log_folder_permissions_message(const wchar_t* path_utf16, wchar_t* log_msg) {
log_on_stderr = 0;

PSID adminSid = NULL;
WCHAR adminName[UNLEN + 1];
WCHAR adminDomain[DNLEN + 1];
DWORD adminNameSize = UNLEN + 1;
DWORD adminDomainSize = DNLEN + 1;
DWORD adminSidSize = SECURITY_MAX_SID_SIZE;
PSID systemSid = NULL;
WCHAR systemName[UNLEN + 1];
WCHAR systemDomain[DNLEN + 1];
DWORD systemNameSize = UNLEN + 1;
DWORD systemDomainSize = DNLEN + 1;
DWORD systemSidSize = SECURITY_MAX_SID_SIZE;
SID_NAME_USE sidType;

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);
log_on_stderr = 1;
}
}

if (log_on_stderr == 0) {
/* 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);
SteveL-MSFT marked this conversation as resolved.
Show resolved Hide resolved
log_on_stderr = 1;
}

if (adminSid)
free(adminSid);
if (systemSid)
free(systemSid);
Comment on lines +354 to +357
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (adminSid)
free(adminSid);
if (systemSid)
free(systemSid);
if (adminSid) {
free(adminSid);
}
if (systemSid) {
free(systemSid);
}

}
4 changes: 2 additions & 2 deletions contrib/win32/win32compat/wmain_sshd.c
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ create_prgdata_ssh_folder()
wchar_t ssh_cfg_dir[PATH_MAX] = { 0, };
wcscpy_s(ssh_cfg_dir, _countof(ssh_cfg_dir), __wprogdata);
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);
exit(255);
}
Expand All @@ -144,7 +144,7 @@ create_prgdata_ssh_folder()
wchar_t logs_dir[PATH_MAX] = { 0, };
wcscat_s(logs_dir, _countof(logs_dir), ssh_cfg_dir);
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);
exit(255);
}
Expand Down
4 changes: 4 additions & 0 deletions log.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@
#include "match.h"

static LogLevel log_level = SYSLOG_LEVEL_INFO;
#ifdef WINDOWS
int log_on_stderr = 1;
#else
static int log_on_stderr = 1;
#endif /* WINDOWS */
static int log_stderr_fd = STDERR_FILENO;
static int log_facility = LOG_AUTH;
static const char *argv0;
Expand Down
Loading