diff --git a/auth-passwd.c b/auth-passwd.c index 997daa025..fb9850204 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -326,7 +326,7 @@ sys_auth_passwd(struct ssh *ssh, const char *password) */ error("password for user %s has expired", authctxt->pw->pw_name); else { - debug("Windows authentication failed for user: %ls domain: %ls error:%d", user_utf16, udom_utf16, GetLastError()); + debug("Windows authentication failed for user: %ls domain: %ls error:%d", unam_utf16, udom_utf16, GetLastError()); /* If LSA authentication package is configured then it will return the auth_token */ sys_auth_passwd_lsa(authctxt, password); diff --git a/contrib/win32/win32compat/misc.c b/contrib/win32/win32compat/misc.c index 36fd8bedc..5bd32870d 100644 --- a/contrib/win32/win32compat/misc.c +++ b/contrib/win32/win32compat/misc.c @@ -1638,6 +1638,37 @@ chroot(const char *path) return 0; } +/* + * Am I running as SYSTEM ? + * a security sensitive call - fatal exits if it cannot definitively conclude + */ +int +am_system() +{ + HANDLE proc_token = NULL; + DWORD info_len; + TOKEN_USER* info = NULL; + static int running_as_system = -1; + + if (running_as_system != -1) + return running_as_system; + + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &proc_token) == FALSE || + GetTokenInformation(proc_token, TokenUser, NULL, 0, &info_len) == TRUE || + (info = (TOKEN_USER*)malloc(info_len)) == NULL || + GetTokenInformation(proc_token, TokenUser, info, info_len, &info_len) == FALSE) + fatal("unable to know if I am running as system"); + + if (IsWellKnownSid(info->User.Sid, WinLocalSystemSid)) + running_as_system = 1; + else + running_as_system = 0; + + CloseHandle(proc_token); + free(info); + return running_as_system; +} + /* returns SID of user or current user if (user = NULL) */ PSID get_user_sid(char* name) diff --git a/contrib/win32/win32compat/misc_internal.h b/contrib/win32/win32compat/misc_internal.h index 376755f5a..24fa9001a 100644 --- a/contrib/win32/win32compat/misc_internal.h +++ b/contrib/win32/win32compat/misc_internal.h @@ -52,4 +52,5 @@ int load_user_profile(HANDLE user_token, char* user); int create_directory_withsddl(wchar_t *path, wchar_t *sddl); int is_absolute_path(const char *); int file_in_chroot_jail(HANDLE, const char*); -PSID get_user_sid(char*); \ No newline at end of file +PSID get_user_sid(char*); +int am_system(); diff --git a/contrib/win32/win32compat/win32_usertoken_utils.c b/contrib/win32/win32compat/win32_usertoken_utils.c index b8f023ffd..0b1133773 100644 --- a/contrib/win32/win32compat/win32_usertoken_utils.c +++ b/contrib/win32/win32compat/win32_usertoken_utils.c @@ -51,6 +51,7 @@ #include "misc_internal.h" #include "lsa_missingdefs.h" #include "Debug.h" +#include "inc\pwd.h" #pragma warning(push, 3) @@ -307,6 +308,7 @@ done: } HANDLE generate_sshd_virtual_token(); +HANDLE generate_sshd_token_as_nonsystem(); HANDLE get_user_token(char* user, int impersonation) { @@ -319,13 +321,28 @@ get_user_token(char* user, int impersonation) { } if (wcscmp(user_utf16, L"sshd") == 0) { - if ((token = generate_sshd_virtual_token()) != 0) + /* not running as system, try generating sshd token as admin */ + if (!am_system() && (token = generate_sshd_token_as_nonsystem()) != 0) goto done; - debug3("unable to generate sshd virtual token, falling back to s4u"); + + if ((token = generate_sshd_virtual_token()) == 0) + error("unable to generate sshd virtual token, ensure sshd service has TCB privileges"); + + goto done; + } + + if (!am_system()) { + struct passwd* pwd = w32_getpwuid(0); + if (strcmp(pwd->pw_name, user) == 0) + OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS_P, &token); + else + debug("unable to generate user token for %s as I am not running as system", user); + + goto done; } if ((token = generate_s4u_user_token(user_utf16, impersonation)) == 0) { - error("unable to generate token for user %ls", user_utf16); + debug3("unable to generate token for user %ls", user_utf16); /* work around for https://github.com/PowerShell/Win32-OpenSSH/issues/727 by doing a fake login */ LogonUserExExWHelper(L"FakeUser", L"FakeDomain", L"FakePasswd", LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL); @@ -346,6 +363,12 @@ int load_user_profile(HANDLE user_token, char* user) { wchar_t * user_utf16 = NULL; + + if (!am_system()) { + debug("Not running as SYSTEM: skipping loading user profile"); + return 0; + } + if ((user_utf16 = utf8_to_utf16(user)) == NULL) { fatal("out of memory"); return -1; @@ -476,6 +499,33 @@ InitUnicodeString(PUNICODE_STRING dest, PWSTR source) dest->MaximumLength = dest->Length + 2; } +HANDLE generate_sshd_token_as_nonsystem() +{ + /* + * This logic tries to reset sshd account password and generate sshd token via logon user + * however this token cannot be used to spawn child processes in typical interactive + * scenarios, without modifying ACLs on desktop station. + * Since sshd is run in interactive mode primarily for debugging/testing purposes, we are + * simply returing the process token (to be used for spawning unprivileged worker) + { + UUID uuid; + RPC_CWSTR rpc_str; + USER_INFO_1003 info; + HANDLE token = 0; + UuidCreate(&uuid); + UuidToStringW(&uuid, (RPC_WSTR*)&rpc_str); + + info.usri1003_password = (LPWSTR)rpc_str; + NetUserSetInfo(NULL, L"sshd", 1003, (LPBYTE)&info, NULL); + + LogonUserW(L"sshd", NULL, (LPCWSTR)rpc_str, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &token); + } + */ + HANDLE token = 0; + OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS_P , &token); + return token; +} + HANDLE generate_sshd_virtual_token() { SID_IDENTIFIER_AUTHORITY nt_authority = SECURITY_NT_AUTHORITY; diff --git a/contrib/win32/win32compat/wmain_sshd.c b/contrib/win32/win32compat/wmain_sshd.c index 8a14c705d..a210195e1 100644 --- a/contrib/win32/win32compat/wmain_sshd.c +++ b/contrib/win32/win32compat/wmain_sshd.c @@ -103,9 +103,7 @@ static VOID WINAPI service_handler(DWORD dwControl) static void generate_host_keys() { - TOKEN_USER* info = NULL; - DWORD info_len = 0, dwError = 0; - HANDLE proc_token = NULL; + DWORD dwError = 0; UUID uuid; RPC_CWSTR rpc_str; USER_INFO_1 ui; @@ -114,13 +112,8 @@ generate_host_keys() PROCESS_INFORMATION pi; wchar_t cmdline[MAX_PATH]; - if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &proc_token) == FALSE || - GetTokenInformation(proc_token, TokenUser, NULL, 0, &info_len) == TRUE || - (info = (TOKEN_USER*)malloc(info_len)) == NULL || - GetTokenInformation(proc_token, TokenUser, info, info_len, &info_len) == FALSE) - goto cleanup; - if (IsWellKnownSid(info->User.Sid, WinLocalSystemSid)) { + if (am_system()) { /* create sshd account if it does not exist */ UuidCreate(&uuid); UuidToStringW(&uuid, (RPC_WSTR*)&rpc_str); @@ -146,11 +139,6 @@ generate_host_keys() CloseHandle(pi.hProcess); } } -cleanup: - if (proc_token) - CloseHandle(proc_token); - if (info) - free(info); } /* @@ -259,13 +247,13 @@ int wmain(int argc, wchar_t **wargv) { wchar_t* path_utf16; argc_original = argc; wargv_original = wargv; - + /* change current directory to sshd.exe root */ if ( (path_utf16 = utf8_to_utf16(w32_programdir())) == NULL) return -1; _wchdir(path_utf16); free(path_utf16); - + if (!StartServiceCtrlDispatcherW(dispatch_table)) { if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) return sshd_main(argc, wargv); /* sshd running NOT as service*/