3333#include <Aclapi.h>
3434#include <lm.h>
3535#include <stdio.h>
36+ #include <stdlib.h>
37+ #include <string.h>
3638
3739#include "inc\pwd.h"
3840#include "sshfileperm.h"
3941#include "debug.h"
4042#include "misc_internal.h"
4143#include "config.h"
4244
45+ #define NULL_TERMINATOR_LEN 1
46+ #define COMMA_SPACE_LEN 2
47+ #define BACKSLASH_LEN 1
48+
49+ extern int log_on_stderr ;
50+
4351/*
4452* The function is to check if current user is secure to access to the file.
4553* Check the owner of the file is one of these types: Local Administrators groups, system account, current user account
@@ -178,37 +186,38 @@ check_secure_file_permission(const char *input_path, struct passwd * pw, int rea
178186* Check the owner of the file is one of these types: Local Administrators groups or system account
179187* Check the users have access permission to the file don't violate the following rules:
180188 1. no user other than local administrators group and system account have write permission on the folder
181- * Returns 0 on success and -1 on failure
189+ * Logs a message if the rules are violated, but does not prevent further execution
182190*/
183- int
191+ void
184192check_secure_folder_permission (const wchar_t * path_utf16 , int read_ok )
185193{
186194 PSECURITY_DESCRIPTOR pSD = NULL ;
187195 PSID owner_sid = NULL , ti_sid = NULL ;
188196 PACL dacl = NULL ;
189197 DWORD error_code = ERROR_SUCCESS ;
190- BOOL is_valid_sid = FALSE, is_valid_acl = FALSE;
198+ BOOL is_valid_sid = FALSE, is_valid_acl = FALSE, need_log_msg = FALSE, is_first = TRUE ;
191199 wchar_t * bad_user = NULL ;
192- int ret = 0 ;
200+ size_t log_msg_len = (DNLEN + BACKSLASH_LEN + UNLEN ) * 2 + COMMA_SPACE_LEN + NULL_TERMINATOR_LEN ;
201+ wchar_t * log_msg = (wchar_t * )malloc (log_msg_len * sizeof (wchar_t ));
202+ if (log_msg != NULL ) {
203+ log_msg [0 ] = '\0' ;
204+ }
193205
194206 /*Get the owner sid of the file.*/
195207 if ((error_code = GetNamedSecurityInfoW (path_utf16 , SE_FILE_OBJECT ,
196208 OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION ,
197209 & owner_sid , NULL , & dacl , NULL , & pSD )) != ERROR_SUCCESS ) {
198210 printf ("failed to retrieve the owner sid and dacl of file %S with error code: %d" , path_utf16 , error_code );
199211 errno = EOTHER ;
200- ret = -1 ;
201212 goto cleanup ;
202213 }
203214 if (((is_valid_sid = IsValidSid (owner_sid )) == FALSE) || ((is_valid_acl = IsValidAcl (dacl )) == FALSE)) {
204215 printf ("IsValidSid: %d; is_valid_acl: %d" , is_valid_sid , is_valid_acl );
205- ret = -1 ;
206216 goto cleanup ;
207217 }
208218 if (!IsWellKnownSid (owner_sid , WinBuiltinAdministratorsSid ) &&
209219 !IsWellKnownSid (owner_sid , WinLocalSystemSid )) {
210220 printf ("Bad owner on %S" , path_utf16 );
211- ret = -1 ;
212221 goto cleanup ;
213222 }
214223 /*
@@ -224,7 +233,6 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
224233 if (!GetAce (dacl , i , & current_ace )) {
225234 printf ("GetAce() failed" );
226235 errno = EOTHER ;
227- ret = -1 ;
228236 goto cleanup ;
229237 }
230238
@@ -247,15 +255,112 @@ check_secure_folder_permission(const wchar_t* path_utf16, int read_ok)
247255 continue ;
248256 }
249257 else {
250- ret = -1 ;
258+ /* collect all SIDs with write permissions */
259+ wchar_t resolved_trustee [UNLEN + NULL_TERMINATOR_LEN ] = L"UNKNOWN" ;
260+ wchar_t resolved_trustee_domain [DNLEN + NULL_TERMINATOR_LEN ] = L"UNKNOWN" ;
261+ DWORD resolved_trustee_len = _countof (resolved_trustee ), resolved_trustee_domain_len = _countof (resolved_trustee_domain );
262+ SID_NAME_USE resolved_trustee_type ;
263+
264+ need_log_msg = TRUE;
265+
266+ if (log_msg != NULL &&
267+ LookupAccountSidW (NULL , current_trustee_sid , resolved_trustee , & resolved_trustee_len ,
268+ resolved_trustee_domain , & resolved_trustee_domain_len , & resolved_trustee_type ) != 0 ) {
269+ if (is_first ) {
270+ _snwprintf_s (log_msg , log_msg_len , _TRUNCATE , L"%ls\\%ls" , resolved_trustee_domain , resolved_trustee );
271+ is_first = FALSE;
272+ }
273+ else {
274+ size_t currentLength = wcslen (log_msg );
275+ size_t userLength = resolved_trustee_domain_len + BACKSLASH_LEN + resolved_trustee_len + COMMA_SPACE_LEN ;
276+ if (wcslen (log_msg ) + userLength + NULL_TERMINATOR_LEN > log_msg_len ) {
277+ log_msg_len *= 2 ;
278+ wchar_t * temp_log_msg = (wchar_t * )malloc (log_msg_len * sizeof (wchar_t ));
279+ if (temp_log_msg == NULL ) {
280+ break ;
281+ }
282+ wcscpy_s (temp_log_msg , log_msg_len , log_msg );
283+ if (log_msg )
284+ free (log_msg );
285+ log_msg = temp_log_msg ;
286+ }
287+ _snwprintf_s (log_msg + currentLength , log_msg_len - currentLength , _TRUNCATE ,
288+ L", %ls\\%ls" , resolved_trustee_domain , resolved_trustee );
289+ }
290+ }
251291 }
252292 }
293+
294+ if (need_log_msg ) {
295+ log_folder_perms_msg_etw (path_utf16 , log_msg );
296+ }
253297cleanup :
254- if (bad_user )
298+ if (bad_user ) {
255299 LocalFree (bad_user );
256- if (pSD )
300+ }
301+ if (log_msg ) {
302+ free (log_msg );
303+ }
304+ if (pSD ) {
257305 LocalFree (pSD );
258- if (ti_sid )
306+ }
307+ if (ti_sid ) {
259308 free (ti_sid );
260- return ret ;
309+ }
310+ }
311+
312+ /*
313+ * This function takes in the full path to the ProgramData\ssh folder
314+ * and a string of comma-separated domain\usernames. The function converts
315+ * the well-known built-in Administrators group sid and the Local System
316+ * sid to their corresponding names. With these names, and the input string,
317+ * it logs a message to the Event Viewer. If logging the detailed message fails,
318+ * a generic log message is written to the Event Viewer instead.
319+ */
320+ void log_folder_perms_msg_etw (const wchar_t * path_utf16 , wchar_t * log_msg ) {
321+ PSID adminSid = NULL ;
322+ WCHAR adminName [UNLEN + NULL_TERMINATOR_LEN ];
323+ WCHAR adminDomain [DNLEN + NULL_TERMINATOR_LEN ];
324+ DWORD adminNameSize = UNLEN + NULL_TERMINATOR_LEN ;
325+ DWORD adminDomainSize = DNLEN + NULL_TERMINATOR_LEN ;
326+ DWORD adminSidSize = SECURITY_MAX_SID_SIZE ;
327+ PSID systemSid = NULL ;
328+ WCHAR systemName [UNLEN + NULL_TERMINATOR_LEN ];
329+ WCHAR systemDomain [DNLEN + NULL_TERMINATOR_LEN ];
330+ DWORD systemNameSize = UNLEN + NULL_TERMINATOR_LEN ;
331+ DWORD systemDomainSize = DNLEN + NULL_TERMINATOR_LEN ;
332+ DWORD systemSidSize = SECURITY_MAX_SID_SIZE ;
333+ SID_NAME_USE sidType ;
334+ BOOL needLog = TRUE;
335+ int temp_log_on_stderr = log_on_stderr ;
336+ log_on_stderr = 0 ;
337+
338+ adminSid = (PSID )malloc (SECURITY_MAX_SID_SIZE );
339+ if (log_msg != NULL && adminSid != NULL &&
340+ CreateWellKnownSid (WinBuiltinAdministratorsSid , NULL , adminSid , & adminSidSize ) != 0 &&
341+ LookupAccountSidW (NULL , adminSid , adminName , & adminNameSize , adminDomain , & adminDomainSize , & sidType ) != 0 ) {
342+ systemSid = (PSID )malloc (SECURITY_MAX_SID_SIZE );
343+ if (systemSid != NULL &&
344+ CreateWellKnownSid (WinLocalSystemSid , NULL , systemSid , & systemSidSize ) != 0 &&
345+ LookupAccountSidW (NULL , systemSid , systemName , & systemNameSize , systemDomain , & systemDomainSize , & sidType ) != 0 ) {
346+ logit ("For '%S' folder, write access is granted to the following users: %S. "
347+ "Consider reviewing users to ensure that only %S\\%S, and the %S\\%S group, and its members, have write access." ,
348+ path_utf16 , log_msg , systemDomain , systemName , adminDomain , adminName );
349+ needLog = FALSE;
350+ }
351+ }
352+
353+ if (needLog ) {
354+ /* log generic warning message in unlikely case that lookup for either well-known SID fails or user list is empty */
355+ logit ("for '%S' folder, consider downgrading permissions for any users with unnecessary write access." , path_utf16 );
356+ }
357+
358+ log_on_stderr = temp_log_on_stderr ;
359+
360+ if (adminSid ) {
361+ free (adminSid );
362+ }
363+ if (systemSid ) {
364+ free (systemSid );
365+ }
261366}
0 commit comments