diff --git a/auth-passwd.c b/auth-passwd.c index f9dfd273f..a8145ac31 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -41,9 +41,6 @@ #include #include -#ifdef WINDOWS -#include -#endif #include #include #include @@ -59,6 +56,11 @@ #include "auth-options.h" #include "authfd.h" +#ifdef WINDOWS +#include "logonuser.h" +#include "monitor_wrap.h" +#endif + extern Buffer loginmsg; extern ServerOptions options; @@ -228,10 +230,59 @@ sys_auth_passwd(Authctxt *authctxt, const char *password) } #elif defined(WINDOWS) +void +sys_auth_passwd_lsa(Authctxt *authctxt, const char *password) +{ + char *tmp = NULL, *domain = NULL, *lsa_auth_pkg = NULL; + wchar_t *lsa_auth_pkg_w = NULL; + int domain_len = 0, lsa_auth_pkg_len = 0; + HKEY reg_key = 0; + REGSAM mask = STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_WOW64_64KEY; + + if ((RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\OpenSSH", 0, mask, ®_key) == ERROR_SUCCESS) && + (RegQueryValueExW(reg_key, L"LSAAuthenticationPackage", 0, NULL, NULL, &lsa_auth_pkg_len) == ERROR_SUCCESS)) { + lsa_auth_pkg_w = (wchar_t *) malloc(lsa_auth_pkg_len); // lsa_auth_pkg_len includes the null terminating character. + if (!lsa_auth_pkg_w) + fatal("%s: out of memory", __func__); + + memset(lsa_auth_pkg_w, 0, lsa_auth_pkg_len); + if (RegQueryValueExW(reg_key, L"LSAAuthenticationPackage", 0, NULL, (LPBYTE)lsa_auth_pkg_w, &lsa_auth_pkg_len) == ERROR_SUCCESS) { + lsa_auth_pkg = utf16_to_utf8(lsa_auth_pkg_w); + if (!lsa_auth_pkg) + error("utf16_to_utf8 failed to convert lsa_auth_pkg_w:%ls", lsa_auth_pkg_w); + + if ((tmp = strstr(authctxt->pw->pw_name, "@")) != NULL) { + domain_len = strlen(tmp) + 1; + domain = (char*) malloc(domain_len * sizeof(char)); + memcpy(domain, tmp, domain_len); + domain[domain_len] = '\0'; + } + debug("Authenticating using LSA Auth Package:%ls", lsa_auth_pkg_w); + authctxt->auth_token = mm_auth_custom_lsa(authctxt->pw->pw_name, password, domain, lsa_auth_pkg); + } + } + +done: + if (domain) + free(domain); + + if (lsa_auth_pkg_w) + free(lsa_auth_pkg_w); + + if (lsa_auth_pkg) + free(lsa_auth_pkg); + + if (reg_key) + RegCloseKey(reg_key); +} + /* -* Authenticate on Windows - Call LogonUser and retrieve user token +* Authenticate on Windows +* - Call LogonUser and retrieve user token +* - If LogonUser fails, then try the LSA (Local Security Authority) authentication. */ -int sys_auth_passwd(Authctxt *authctxt, const char *password) +int +sys_auth_passwd(Authctxt *authctxt, const char *password) { wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *pwd_utf16 = NULL, *tmp; HANDLE token = NULL; @@ -249,25 +300,33 @@ int sys_auth_passwd(Authctxt *authctxt, const char *password) } if (LogonUserExExWHelper(user_utf16, udom_utf16, pwd_utf16, LOGON32_LOGON_NETWORK_CLEARTEXT, - LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL) == FALSE) { - if (GetLastError() == ERROR_PASSWORD_MUST_CHANGE) - /* - * TODO - need to add support to force password change - * by sending back SSH_MSG_USERAUTH_PASSWD_CHANGEREQ - */ + LOGON32_PROVIDER_DEFAULT, NULL, &token, NULL, NULL, NULL, NULL) == TRUE) + authctxt->auth_token = (void*)(INT_PTR)token; + else { + if (GetLastError() == ERROR_PASSWORD_MUST_CHANGE) + /* + * TODO - need to add support to force password change + * by sending back SSH_MSG_USERAUTH_PASSWD_CHANGEREQ + */ error("password for user %s has expired", authctxt->pw->pw_name); - else - debug("failed to logon user: %ls domain: %ls error:%d", user_utf16, udom_utf16, GetLastError()); - goto done; - } + else { + debug("Windows authentication failed for user: %ls domain: %ls error:%d", user_utf16, udom_utf16, GetLastError()); - authctxt->auth_token = (void*)(INT_PTR)token; - r = 1; + /* If LSA authentication package is configured then it will return the auth_token */ + sys_auth_passwd_lsa(authctxt, password); + } + } + done: + if (authctxt->auth_token) + r = 1; + if (user_utf16) free(user_utf16); + if (pwd_utf16) SecureZeroMemory(pwd_utf16, sizeof(wchar_t) * wcslen(pwd_utf16)); + return r; } #endif /* WINDOWS */ diff --git a/contrib/win32/win32compat/console.c b/contrib/win32/win32compat/console.c index 49d295776..a21609e40 100644 --- a/contrib/win32/win32compat/console.c +++ b/contrib/win32/win32compat/console.c @@ -118,7 +118,8 @@ ConEnterRawMode() dwAttributes |= (DWORD)ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; char *envValue = NULL; - _dupenv_s(&envValue, NULL, "SSH_TERM_CONHOST_PARSER"); + size_t len = 0; + _dupenv_s(&envValue, &len, "SSH_TERM_CONHOST_PARSER"); if (NULL != envValue) { isConHostParserEnabled = atoi(envValue); diff --git a/contrib/win32/win32compat/priv-agent.h b/contrib/win32/win32compat/priv-agent.h index f98ac15d9..127cd75e4 100644 --- a/contrib/win32/win32compat/priv-agent.h +++ b/contrib/win32/win32compat/priv-agent.h @@ -3,6 +3,7 @@ */ #pragma once -#define SSH_PRIV_AGENT_MSG_ID 0 -#define PUBKEY_AUTH_REQUEST "pubkey" -#define LOAD_USER_PROFILE_REQUEST "loadprofile" +#define SSH_PRIV_AGENT_MSG_ID 0 +#define PUBKEY_AUTH_REQUEST "pubkey" +#define LOAD_USER_PROFILE_REQUEST "loadprofile" +#define CUSTOM_LSA_AUTH_REQUEST "lsaauth" \ No newline at end of file diff --git a/contrib/win32/win32compat/shell-host.c b/contrib/win32/win32compat/shell-host.c index cfc04a4b9..689a5b92c 100644 --- a/contrib/win32/win32compat/shell-host.c +++ b/contrib/win32/win32compat/shell-host.c @@ -1204,7 +1204,7 @@ get_default_shell_path() wchar_t *tmp = malloc(PATH_MAX + 1); if (!tmp) { - printf_s("get_default_shell_path(), Unable to allocate memory"); + printf_s("%s: out of memory", __func__); exit(255); } @@ -1257,6 +1257,9 @@ get_default_shell_path() if (tmp) free(tmp); + + if (reg_key) + RegCloseKey(reg_key); return default_shell_path; } diff --git a/contrib/win32/win32compat/ssh-agent/authagent-request.c b/contrib/win32/win32compat/ssh-agent/authagent-request.c index 1e9ace5b4..2ff7aa488 100644 --- a/contrib/win32/win32compat/ssh-agent/authagent-request.c +++ b/contrib/win32/win32compat/ssh-agent/authagent-request.c @@ -41,6 +41,8 @@ #include "inc\utf.h" #include "..\priv-agent.h" #include "logonuser.h" +#include +#include #pragma warning(push, 3) @@ -49,7 +51,7 @@ int pubkey_allowed(struct sshkey* pubkey, char* user_utf8); static void InitLsaString(LSA_STRING *lsa_string, const char *str) { - if (str == NULL) + if (!str) memset(lsa_string, 0, sizeof(LSA_STRING)); else { lsa_string->Buffer = (char *)str; @@ -250,6 +252,128 @@ done: return dup_t; } +int +process_custom_lsa_auth_req(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) +{ + char *user = NULL, *pwd = NULL, *domain = NULL, *lsa_pkg = NULL; + size_t user_len = 0, pwd_len = 0, domain_len = 0, lsa_pkg_len = 0; + wchar_t *userw = NULL, *pwdw = NULL, *domw = NULL, *providerw = NULL; + HANDLE token = NULL, dup_token = NULL, lsa_handle = NULL; + LSA_OPERATIONAL_MODE mode; + ULONG auth_package_id, logon_info_size = 0; + NTSTATUS ret, subStatus; + wchar_t *logon_info = NULL; + LSA_STRING logon_process_name, lsa_auth_package_name, originName; + TOKEN_SOURCE sourceContext; + PVOID pProfile = NULL; + LUID logonId; + QUOTA_LIMITS quotas; + DWORD cbProfile; + int retVal = -1; + + if (sshbuf_get_string_direct(request, &user, &user_len) != 0 || + sshbuf_get_cstring(request, &pwd, &pwd_len) != 0 || + sshbuf_get_string_direct(request, &domain, &domain_len) != 0 || + user_len > MAX_USER_LEN || pwd_len == 0 || + sshbuf_get_string_direct(request, &lsa_pkg, &lsa_pkg_len) != 0) { + debug("invalid LSA auth request"); + goto done; + } + + debug("LSA auth request, user:%s domain:%s lsa_pkg:%s ", user, domain, lsa_pkg); + + /* convert everything to utf16 only if its not NULL */ + if ((userw = utf8_to_utf16(user)) == NULL || + (pwdw = utf8_to_utf16(pwd)) == NULL || + (domain && (domw = utf8_to_utf16(domain)) == NULL)) { + debug("%s: out of memory", __func__); + goto done; + } + + /* call into LSA provider , get and duplicate token */ + InitLsaString(&logon_process_name, "ssh-agent"); + InitLsaString(&lsa_auth_package_name, lsa_pkg); + InitLsaString(&originName, "sshd"); + + if ((ret = LsaRegisterLogonProcess(&logon_process_name, &lsa_handle, &mode)) != STATUS_SUCCESS) { + error("LsaRegisterLogonProcess failed, error:%ld", LsaNtStatusToWinError(ret)); + goto done; + } + + if ((ret = LsaLookupAuthenticationPackage(lsa_handle, &lsa_auth_package_name, &auth_package_id)) != STATUS_SUCCESS) { + error("LsaLookupAuthenticationPackage failed, lsa auth pkg:%ls error:%ld", lsa_pkg, LsaNtStatusToWinError(ret)); + goto done; + } + + logon_info_size = (ULONG)((wcslen(userw) + wcslen(pwdw) + wcslen(domw) + 3) * sizeof(wchar_t)); + logon_info = (wchar_t *)malloc(logon_info_size); + if (NULL == logon_info) + fatal("%s:out of memory", __func__); + + wcscpy_s(logon_info, logon_info_size, userw); + wcscat_s(logon_info, logon_info_size, L";"); + wcscat_s(logon_info, logon_info_size, pwdw); + wcscat_s(logon_info, logon_info_size, L";"); + wcscat_s(logon_info, logon_info_size, domw); + + memcpy(sourceContext.SourceName, "sshd", sizeof(sourceContext.SourceName)); + + if (!AllocateLocallyUniqueId(&sourceContext.SourceIdentifier)) { + error("AllocateLocallyUniqueId failed, error:%d", GetLastError()); + goto done; + } + + if ((ret = LsaLogonUser(lsa_handle, + &originName, + Network, + auth_package_id, + logon_info, + logon_info_size, + NULL, + &sourceContext, + &pProfile, + &cbProfile, + &logonId, + &token, + "as, + &subStatus)) != STATUS_SUCCESS) { + if(ret == STATUS_ACCOUNT_RESTRICTION) + error("LsaLogonUser failed, error:%ld subStatus:%ld", LsaNtStatusToWinError(ret), subStatus); + else + error("LsaLogonUser failed error:%ld", LsaNtStatusToWinError(ret)); + + goto done; + } + + if ((dup_token = duplicate_token_for_client(con, token)) == NULL) + goto done; + + if (sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0) + goto done; + + retVal = 0; +done: + /* delete allocated memory*/ + if ((retVal == -1) && (sshbuf_put_u8(response, SSH_AGENT_FAILURE) == 0)) + retVal = 0; + if (lsa_handle) + LsaDeregisterLogonProcess(lsa_handle); + if (logon_info) + free(logon_info); + if (pProfile) + LsaFreeReturnBuffer(pProfile); + if (userw) + free(userw); + if (pwdw) + free(pwdw); + if (domw) + free(domw); + if (token) + CloseHandle(token); + + return retVal; +} + int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { int r = -1; char *key_blob, *user, *sig, *blob; @@ -394,6 +518,8 @@ int process_privagent_request(struct sshbuf* request, struct sshbuf* response, s return process_pubkeyauth_request(request, response, con); else if (memcmp(opn, LOAD_USER_PROFILE_REQUEST, opn_len) == 0) return process_loadprofile_request(request, response, con); + else if (memcmp(opn, CUSTOM_LSA_AUTH_REQUEST, opn_len) == 0) + return process_custom_lsa_auth_req(request, response, con); else { debug("unknown auth request: %s", opn); return -1; diff --git a/contrib/win32/win32compat/w32fd.c b/contrib/win32/win32compat/w32fd.c index 91394dc6a..f12143f3b 100644 --- a/contrib/win32/win32compat/w32fd.c +++ b/contrib/win32/win32compat/w32fd.c @@ -78,7 +78,8 @@ fd_table_initialize() w32_io_stdin.type = NONSOCK_SYNC_FD; char *envValue = NULL; - _dupenv_s(&envValue, NULL, SSH_ASYNC_STDIN); + size_t len = 0; + _dupenv_s(&envValue, &len, SSH_ASYNC_STDIN); if (NULL != envValue) { if(strcmp(envValue, "1") == 0) w32_io_stdin.type = NONSOCK_FD; @@ -93,7 +94,7 @@ fd_table_initialize() w32_io_stdout.type = NONSOCK_SYNC_FD; envValue = NULL; - _dupenv_s(&envValue, NULL, SSH_ASYNC_STDOUT); + _dupenv_s(&envValue, &len, SSH_ASYNC_STDOUT); if (NULL != envValue) { if(strcmp(envValue, "1") == 0) w32_io_stdout.type = NONSOCK_FD; @@ -108,7 +109,7 @@ fd_table_initialize() w32_io_stderr.type = NONSOCK_SYNC_FD; envValue = NULL; - _dupenv_s(&envValue, NULL, SSH_ASYNC_STDERR); + _dupenv_s(&envValue, &len, SSH_ASYNC_STDERR); if (NULL != envValue) { if(strcmp(envValue, "1") == 0) w32_io_stderr.type = NONSOCK_FD; diff --git a/contrib/win32/win32compat/win32_monitor_wrap.c b/contrib/win32/win32compat/win32_monitor_wrap.c index 01893c24b..2e198aef8 100644 --- a/contrib/win32/win32compat/win32_monitor_wrap.c +++ b/contrib/win32/win32compat/win32_monitor_wrap.c @@ -152,15 +152,20 @@ void* mm_auth_pubkey(const char* user_name, const struct sshkey *key, msg = sshbuf_new(); if (!msg) fatal("%s: out of memory", __func__); + if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 || sshbuf_put_cstring(msg, PUBKEY_AUTH_REQUEST) != 0 || sshkey_to_blob(key, &blob, &blen) != 0 || sshbuf_put_string(msg, blob, blen) != 0 || sshbuf_put_cstring(msg, user_name) != 0 || sshbuf_put_string(msg, sig, slen) != 0 || - sshbuf_put_string(msg, sshbuf_ptr(b), sshbuf_len(b)) != 0 || - ssh_request_reply(agent_fd, msg, msg) != 0) { - debug("unable to send pubkeyauth request"); + sshbuf_put_string(msg, sshbuf_ptr(b), sshbuf_len(b)) != 0) { + debug("unable to add data to ssh buffer"); + break; + } + + if(ssh_request_reply(agent_fd, msg, msg) != 0) { + error("unable to send pubkeyauth request to agent"); break; } @@ -211,4 +216,46 @@ int mm_load_profile(const char* user_name, u_int token) } return result; +} + +void* +mm_auth_custom_lsa(const char* user, const char* password, const char* domain, const char* lsa_auth_pkg) +{ + /* Pass credentials to privileged agent to retrieve token upon successful authentication */ + struct sshbuf *msg = NULL; + HANDLE token = 0; + int agent_fd; + + while (1) { + if ((agent_fd = get_priv_agent_sock()) == -1) + break; + + msg = sshbuf_new(); + if (!msg) + fatal("%s: out of memory", __func__); + + if (sshbuf_put_u8(msg, SSH_PRIV_AGENT_MSG_ID) != 0 || + sshbuf_put_cstring(msg, CUSTOM_LSA_AUTH_REQUEST) != 0 || + sshbuf_put_cstring(msg, user) != 0 || + sshbuf_put_cstring(msg, password) != 0 || + sshbuf_put_cstring(msg, domain) != 0 || + sshbuf_put_cstring(msg, lsa_auth_pkg) != 0 || + ssh_request_reply(agent_fd, msg, msg) != 0) { + debug("unable to send LSA Auth request"); + break; + } + + if (sshbuf_get_u32(msg, (u_int32_t *)&token) != 0) { + debug("failed to get the token from the agent"); + break; + } + + debug3("user:%s authenticated using LSA auth pkg:%s", user, lsa_auth_pkg); + break; + } + + if (msg) + sshbuf_free(msg); + + return (void*)(INT_PTR)token; } \ No newline at end of file diff --git a/monitor_wrap.h b/monitor_wrap.h index 72ba1cd24..c1f6d9b96 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h @@ -98,9 +98,10 @@ int mm_bsdauth_respond(void *, u_int, char **); int mm_skey_query(void *, char **, char **, u_int *, char ***, u_int **); int mm_skey_respond(void *, u_int, char **); -/* Windows specific */ +/* Windows specific functions */ void* mm_auth_pubkey(const char*, const struct sshkey *, const u_char *, size_t, struct sshbuf*); int mm_load_profile(const char*, u_int ); +void* mm_auth_custom_lsa(const char* user, const char* password, const char* dom, const char* lsa_auth_pkg); #endif /* _MM_WRAP_H_ */