From f8f5e45f0212b8180c4c9c5ed9891ac786ec2241 Mon Sep 17 00:00:00 2001 From: Manoj Ampalam Date: Mon, 26 Jun 2017 21:57:24 -0700 Subject: [PATCH] Decouple key-agent and privileged-agent use in sshd (#173) PowerShell/Win32-OpenSSH#766 PowerShell/Win32-OpenSSH#783 --- auth-passwd.c | 53 +++-- auth.h | 4 +- auth2-pubkey.c | 51 +---- authfd.h | 9 +- contrib/win32/openssh/sshd.vcxproj | 1 + contrib/win32/openssh/sshd.vcxproj.filters | 3 + contrib/win32/openssh/sshd_config | 3 +- contrib/win32/win32compat/fileio.c | 12 +- contrib/win32/win32compat/priv-agent.h | 8 + .../win32compat/ssh-agent/agent-request.h | 2 +- contrib/win32/win32compat/ssh-agent/agent.c | 32 +-- contrib/win32/win32compat/ssh-agent/agent.h | 12 +- .../win32/win32compat/ssh-agent/agentconfig.c | 6 + .../win32compat/ssh-agent/authagent-request.c | 146 ++++++------ .../win32/win32compat/ssh-agent/connection.c | 5 +- .../win32/win32compat/win32_monitor_wrap.c | 214 ++++++++++++++++++ contrib/win32/win32compat/wmain_common.c | 2 +- contrib/win32/win32compat/wmain_sshd.c | 3 - monitor_wrap.h | 5 + regress/pesterTests/SSHD_Config | 3 +- servconf.c | 5 +- session.c | 5 +- sshd.c | 4 +- 23 files changed, 390 insertions(+), 198 deletions(-) create mode 100644 contrib/win32/win32compat/priv-agent.h create mode 100644 contrib/win32/win32compat/win32_monitor_wrap.c diff --git a/auth-passwd.c b/auth-passwd.c index cfb829ccf..8804dc4eb 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -226,38 +226,45 @@ sys_auth_passwd(Authctxt *authctxt, const char *password) #elif defined(WINDOWS) /* -* Authenticate on Windows - Pass credentials to ssh-agent and retrieve token -* upon successful authentication -* TODO - password is sent in plain text over IPC. Consider implications. +* Authenticate on Windows - Call LogonUser and retrieve user token */ int sys_auth_passwd(Authctxt *authctxt, const char *password) { - struct sshbuf *msg = NULL; - size_t blen = 0; - DWORD token = 0; - extern int auth_sock; + wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *pwd_utf16 = NULL, *tmp; + HANDLE token = NULL; int r = 0; - int ssh_request_reply(int, struct sshbuf *, struct sshbuf *); - msg = sshbuf_new(); - if (!msg) - fatal("%s: out of memory", __func__); - - if (sshbuf_put_u8(msg, SSH_AGENT_AUTHENTICATE) != 0 || - sshbuf_put_cstring(msg, PASSWD_AUTH_REQUEST) != 0 || - sshbuf_put_cstring(msg, authctxt->pw->pw_name) != 0 || - sshbuf_put_cstring(msg, password) != 0 || - ssh_request_reply(auth_sock, msg, msg) != 0 || - sshbuf_get_u32(msg, &token) != 0) { - debug("auth agent did not authorize client %s", authctxt->user); - r = 0; + if ((user_utf16 = utf8_to_utf16(authctxt->pw->pw_name)) == NULL || + (pwd_utf16 = utf8_to_utf16(password)) == NULL) { + fatal("out of memory"); goto done; } - authctxt->methoddata = (void*)(INT_PTR)token; + + if ((tmp = wcschr(user_utf16, L'@')) != NULL) { + udom_utf16 = tmp + 1; + *tmp = L'\0'; + } + + if (LogonUserW(user_utf16, udom_utf16, pwd_utf16, LOGON32_LOGON_NETWORK_CLEARTEXT, + LOGON32_PROVIDER_DEFAULT, &token) == FALSE) { + 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; + } + + authctxt->auth_token = (void*)(INT_PTR)token; r = 1; done: - if (msg) - sshbuf_free(msg); + 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/auth.h b/auth.h index 677935463..a5cd75049 100644 --- a/auth.h +++ b/auth.h @@ -78,7 +78,9 @@ struct Authctxt { #endif Buffer *loginmsg; void *methoddata; - +#ifdef WINDOWS + void *auth_token; +#endif struct sshkey **prev_userkeys; u_int nprev_userkeys; }; diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 084cbf0ed..db4403672 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -200,54 +200,14 @@ userauth_pubkey(struct ssh *ssh) /* test for correct signature */ authenticated = 0; -#ifdef WINDOWS - /* Pass key challenge material to ssh-agent to retrieve token upon successful authentication */ - { - struct sshbuf *msg = NULL; - u_char *blob = NULL; - size_t blen = 0; - DWORD token = 0; - extern int auth_sock; - int r = 0; - int ssh_request_reply(int , struct sshbuf *, struct sshbuf *); - - while (1) { - msg = sshbuf_new(); - if (!msg) - fatal("%s: out of memory", __func__); - if ((r = sshbuf_put_u8(msg, SSH_AGENT_AUTHENTICATE)) != 0 || - (r = sshbuf_put_cstring(msg, PUBKEY_AUTH_REQUEST)) != 0 || - (r = sshkey_to_blob(key, &blob, &blen)) != 0 || - (r = sshbuf_put_string(msg, blob, blen)) != 0 || - (r = sshbuf_put_cstring(msg, authctxt->pw->pw_name)) != 0 || - (r = sshbuf_put_string(msg, sig, slen)) != 0 || - (r = sshbuf_put_string(msg, sshbuf_ptr(b), sshbuf_len(b))) != 0 || - (r = ssh_request_reply(auth_sock, msg, msg)) != 0 || - (r = sshbuf_get_u32(msg, &token)) != 0) { - debug("auth agent did not authorize client %s", authctxt->user); - break; - } - - debug3("auth agent authenticated %s", authctxt->user); - break; - - } - if (blob) - free(blob); - if (msg) - sshbuf_free(msg); - - if (token) { - authenticated = 1; - authctxt->methoddata = (void*)(INT_PTR)token; - } - - } - -#else /* !WINDOWS */ if (PRIVSEP(user_key_allowed(authctxt->pw, key, 1)) && +#ifdef WINDOWS + (authctxt->auth_token = mm_auth_pubkey(authctxt->pw->pw_name, + key, sig, slen, b)) != NULL) { +#else PRIVSEP(sshkey_verify(key, sig, slen, sshbuf_ptr(b), sshbuf_len(b), ssh->compat)) == 0) { +#endif authenticated = 1; /* Record the successful key to prevent reuse */ auth2_record_userkey(authctxt, key); @@ -255,7 +215,6 @@ userauth_pubkey(struct ssh *ssh) } sshbuf_free(b); free(sig); -#endif /* !WINDOWS */ } else { debug("%s: test whether pkalg/pkblob are acceptable for %s %s", diff --git a/authfd.h b/authfd.h index a9874e134..5804f18d9 100644 --- a/authfd.h +++ b/authfd.h @@ -43,6 +43,7 @@ int ssh_agent_sign(int sock, struct sshkey *key, const u_char *data, size_t datalen, const char *alg, u_int compat); /* Messages for the authentication agent connection. */ +/* Message Id 0 is reserved */ #define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 #define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 #define SSH_AGENTC_RSA_CHALLENGE 3 @@ -88,12 +89,4 @@ int ssh_agent_sign(int sock, struct sshkey *key, #define SSH_AGENT_RSA_SHA2_256 0x02 #define SSH_AGENT_RSA_SHA2_512 0x04 -/* -* Following are used in Windows implementation -* ssh-agent in Windows also serves user authentication -*/ -#define SSH_AGENT_AUTHENTICATE 200 -#define PUBKEY_AUTH_REQUEST "pubkey" -#define PASSWD_AUTH_REQUEST "password" - #endif /* AUTHFD_H */ diff --git a/contrib/win32/openssh/sshd.vcxproj b/contrib/win32/openssh/sshd.vcxproj index 206140822..c68b4e11d 100644 --- a/contrib/win32/openssh/sshd.vcxproj +++ b/contrib/win32/openssh/sshd.vcxproj @@ -246,6 +246,7 @@ + diff --git a/contrib/win32/openssh/sshd.vcxproj.filters b/contrib/win32/openssh/sshd.vcxproj.filters index e3254ed82..f5c22820d 100644 --- a/contrib/win32/openssh/sshd.vcxproj.filters +++ b/contrib/win32/openssh/sshd.vcxproj.filters @@ -150,6 +150,9 @@ Source Files + + Source Files + diff --git a/contrib/win32/openssh/sshd_config b/contrib/win32/openssh/sshd_config index 651af0be6..7a25ba87c 100644 --- a/contrib/win32/openssh/sshd_config +++ b/contrib/win32/openssh/sshd_config @@ -119,4 +119,5 @@ Subsystem sftp sftp-server.exe # X11Forwarding no # AllowTcpForwarding no # ForceCommand cvs server -# PubkeyAcceptedKeyTypes ssh-ed25519* \ No newline at end of file +# PubkeyAcceptedKeyTypes ssh-ed25519* +hostkeyagent \\.\pipe\openssh-ssh-agent \ No newline at end of file diff --git a/contrib/win32/win32compat/fileio.c b/contrib/win32/win32compat/fileio.c index de9fbe874..d67745366 100644 --- a/contrib/win32/win32compat/fileio.c +++ b/contrib/win32/win32compat/fileio.c @@ -114,7 +114,6 @@ int fileio_connect(struct w32_io* pio, char* name) { wchar_t* name_w = NULL; - wchar_t pipe_name[PATH_MAX]; HANDLE h = INVALID_HANDLE_VALUE; int ret = 0, r; @@ -129,16 +128,9 @@ fileio_connect(struct w32_io* pio, char* name) errno = ENOMEM; return -1; } - r = _snwprintf_s(pipe_name, PATH_MAX, PATH_MAX, L"\\\\.\\pipe\\%ls", name_w); - if (r < 0 || r >= PATH_MAX) { - debug3("cannot create pipe name with %s", name); - errno = EOTHER; - return -1; - } - - + do { - h = CreateFileW(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, + h = CreateFileW(name_w, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED | SECURITY_SQOS_PRESENT | SECURITY_IDENTIFICATION, NULL); if (h != INVALID_HANDLE_VALUE) diff --git a/contrib/win32/win32compat/priv-agent.h b/contrib/win32/win32compat/priv-agent.h new file mode 100644 index 000000000..f98ac15d9 --- /dev/null +++ b/contrib/win32/win32compat/priv-agent.h @@ -0,0 +1,8 @@ +/* + * Definitions for IPC between sshd and privileged agent + */ +#pragma once + +#define SSH_PRIV_AGENT_MSG_ID 0 +#define PUBKEY_AUTH_REQUEST "pubkey" +#define LOAD_USER_PROFILE_REQUEST "loadprofile" diff --git a/contrib/win32/win32compat/ssh-agent/agent-request.h b/contrib/win32/win32compat/ssh-agent/agent-request.h index 3d7c3df9f..2ee62222a 100644 --- a/contrib/win32/win32compat/ssh-agent/agent-request.h +++ b/contrib/win32/win32compat/ssh-agent/agent-request.h @@ -16,6 +16,6 @@ int process_request_identities(struct sshbuf*, struct sshbuf*, struct agent_conn int process_sign_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); int process_remove_key(struct sshbuf*, struct sshbuf*, struct agent_connection*); int process_remove_all(struct sshbuf*, struct sshbuf*, struct agent_connection*); -int process_authagent_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); +int process_privagent_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); /* auth */ diff --git a/contrib/win32/win32compat/ssh-agent/agent.c b/contrib/win32/win32compat/ssh-agent/agent.c index d1042d4f2..54f4a5f6c 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.c +++ b/contrib/win32/win32compat/ssh-agent/agent.c @@ -37,7 +37,7 @@ static HANDLE ioc_port = NULL; static BOOL debug_mode = FALSE; -#define AGENT_PIPE_ID L"\\\\.\\pipe\\ssh-agent" +#define AGENT_PIPE_ID L"\\\\.\\pipe\\openssh-ssh-agent" static HANDLE event_stop_agent; static OVERLAPPED ol; @@ -168,12 +168,14 @@ agent_cleanup_connection(struct agent_connection* con) { debug("connection %p clean up", con); CloseHandle(con->pipe_handle); - if (con->hProfile) - UnloadUserProfile(con->auth_token, con->hProfile); - if (con->auth_token) - CloseHandle(con->auth_token); + if (con->profile_handle) + UnloadUserProfile(con->profile_token, con->profile_handle); + if (con->profile_token) + CloseHandle(con->profile_token); if (con->client_impersonation_token) CloseHandle(con->client_impersonation_token); + if (con->client_process_handle) + CloseHandle(con->client_process_handle); free(con); CloseHandle(ioc_port); ioc_port = NULL; @@ -251,13 +253,13 @@ get_con_client_info(struct agent_connection* con) DWORD sshd_sid_len = 0; PSID sshd_sid = NULL; SID_NAME_USE nuse; - HANDLE client_primary_token = NULL, client_impersonation_token = NULL, client_proc_handle = NULL; + HANDLE client_primary_token = NULL, client_impersonation_token = NULL, client_process_handle = NULL; TOKEN_USER* info = NULL; BOOL isMember = FALSE; if (GetNamedPipeClientProcessId(con->pipe_handle, &client_pid) == FALSE || - (client_proc_handle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, client_pid)) == NULL || - OpenProcessToken(client_proc_handle, TOKEN_QUERY | TOKEN_DUPLICATE, &client_primary_token) == FALSE || + (client_process_handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_DUP_HANDLE, FALSE, client_pid)) == NULL || + OpenProcessToken(client_process_handle, TOKEN_QUERY | TOKEN_DUPLICATE, &client_primary_token) == FALSE || DuplicateToken(client_primary_token, SecurityImpersonation, &client_impersonation_token) == FALSE) { error("cannot retrieve client impersonatin token"); goto done; @@ -341,15 +343,19 @@ done: free(ref_dom); if (info) free(info); - if (client_proc_handle) - CloseHandle(client_proc_handle); if (client_primary_token) CloseHandle(client_primary_token); - if (r == 0) + if (r == 0) { + con->client_process_handle = client_process_handle; con->client_impersonation_token = client_impersonation_token; - else if (client_impersonation_token) - CloseHandle(client_impersonation_token); + } + else { + if (client_process_handle) + CloseHandle(client_process_handle); + if (client_impersonation_token) + CloseHandle(client_impersonation_token); + } return r; } diff --git a/contrib/win32/win32compat/ssh-agent/agent.h b/contrib/win32/win32compat/ssh-agent/agent.h index ea2da0a38..f73290893 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.h +++ b/contrib/win32/win32compat/ssh-agent/agent.h @@ -4,17 +4,18 @@ #include "log.h" #define MAX_MESSAGE_SIZE 256 * 1024 -#define SSH_ROOT L"SOFTWARE\\SSH" +#define SSH_ROOT L"SOFTWARE\\OpenSSH" #define SSH_AGENT_ROOT SSH_ROOT L"\\Agent" #define SSH_KEYS_KEY L"Keys" -#define SSH_KEYS_ROOT SSH_ROOT L"\\" SSH_KEYS_KEY +#define SSH_KEYS_ROOT SSH_AGENT_ROOT L"\\" SSH_KEYS_KEY #define HEADER_SIZE 4 struct agent_connection { OVERLAPPED ol; HANDLE pipe_handle; - HANDLE client_impersonation_token; + HANDLE client_impersonation_token; + HANDLE client_process_handle; struct { DWORD num_bytes; DWORD transferred; @@ -36,8 +37,9 @@ struct agent_connection { SYSTEM, /* client is running as System */ SERVICE, /* client is running as LS or NS */ } client_type; - HANDLE auth_token; - HANDLE hProfile; + /* user profile related members */ + HANDLE profile_token; + HANDLE profile_handle; }; void agent_connection_on_io(struct agent_connection*, DWORD, OVERLAPPED*); diff --git a/contrib/win32/win32compat/ssh-agent/agentconfig.c b/contrib/win32/win32compat/ssh-agent/agentconfig.c index 836616eb0..954e4e062 100644 --- a/contrib/win32/win32compat/ssh-agent/agentconfig.c +++ b/contrib/win32/win32compat/ssh-agent/agentconfig.c @@ -72,6 +72,12 @@ mm_user_key_allowed(struct passwd *pw, Key *k, int i) return 0; } +void* mm_auth_pubkey(const char* user_name, const struct sshkey *key, + const u_char *sig, size_t slen, struct sshbuf* b) +{ + return NULL; +} + int kexgex_server(struct ssh * sh) { return -1; diff --git a/contrib/win32/win32compat/ssh-agent/authagent-request.c b/contrib/win32/win32compat/ssh-agent/authagent-request.c index 32603b9c9..3aafdf8aa 100644 --- a/contrib/win32/win32compat/ssh-agent/authagent-request.c +++ b/contrib/win32/win32compat/ssh-agent/authagent-request.c @@ -39,6 +39,7 @@ #include "agent-request.h" #include "key.h" #include "inc\utf.h" +#include "..\priv-agent.h" #pragma warning(push, 3) @@ -83,9 +84,11 @@ done: } -void -LoadProfile(struct agent_connection* con, wchar_t* user, wchar_t* domain) { +static HANDLE +LoadProfile(HANDLE user_token, wchar_t* user, wchar_t* domain) { PROFILEINFOW profileInfo; + HANDLE ret = NULL; + profileInfo.dwFlags = PI_NOUI; profileInfo.lpProfilePath = NULL; profileInfo.lpUserName = user; @@ -96,12 +99,16 @@ LoadProfile(struct agent_connection* con, wchar_t* user, wchar_t* domain) { profileInfo.dwSize = sizeof(profileInfo); EnablePrivilege("SeBackupPrivilege", 1); EnablePrivilege("SeRestorePrivilege", 1); - if (LoadUserProfileW(con->auth_token, &profileInfo) == FALSE) + if (LoadUserProfileW(user_token, &profileInfo) == FALSE) { debug("Loading user (%ls,%ls) profile failed ERROR: %d", user, domain, GetLastError()); + goto done; + } else - con->hProfile = profileInfo.hProfile; + ret = profileInfo.hProfile; +done: EnablePrivilege("SeBackupPrivilege", 0); EnablePrivilege("SeRestorePrivilege", 0); + return ret; } #define MAX_USER_LEN 64 @@ -238,73 +245,13 @@ done: return dup_t; } -/* TODO - SecureZeroMemory password */ -int process_passwordauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { - char *user = NULL, *domain = NULL, *pwd = NULL; - size_t user_len, pwd_len; - wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *pwd_utf16 = NULL, *tmp; - int r = -1; - HANDLE token = 0, dup_token; - - if (sshbuf_get_cstring(request, &user, &user_len) != 0 || - sshbuf_get_cstring(request, &pwd, &pwd_len) != 0 || - user_len == 0 || - pwd_len == 0 || - user_len > MAX_USER_LEN + MAX_FQDN_LEN || - pwd_len > MAX_PW_LEN) { - debug("bad password auth request"); - goto done; - } - - if ((user_utf16 = utf8_to_utf16(user)) == NULL || - (pwd_utf16 = utf8_to_utf16(pwd)) == NULL) { - debug("out of memory"); - goto done; - } - - if ((tmp = wcschr(user_utf16, L'@') ) != NULL ) { - udom_utf16 = tmp + 1; - *tmp = L'\0'; - } - - if (LogonUserW(user_utf16, udom_utf16, pwd_utf16, LOGON32_LOGON_NETWORK_CLEARTEXT, LOGON32_PROVIDER_DEFAULT, &token) == FALSE) { - debug("failed to logon user: %ls domain: %ls", user_utf16, udom_utf16); - 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; - - con->auth_token = token; - LoadProfile(con, user_utf16, udom_utf16); - r = 0; -done: - /* TODO Fix this hacky protocol*/ - if ((r == -1) && (sshbuf_put_u8(response, SSH_AGENT_FAILURE) == 0)) - r = 0; - - if (user) - free(user); - if (pwd) - free(pwd); - if (user_utf16) - free(user_utf16); - if (pwd_utf16) - free(pwd_utf16); - - return r; -} - int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { int r = -1; char *key_blob, *user, *sig, *blob; size_t key_blob_len, user_len, sig_len, blob_len; struct sshkey *key = NULL; HANDLE token = NULL, dup_token = NULL; - wchar_t *user_utf16 = NULL, *udom_utf16 = NULL, *tmp; + wchar_t *user_utf16 = NULL; PWSTR wuser_home = NULL; @@ -346,14 +293,6 @@ int process_pubkeyauth_request(struct sshbuf* request, struct sshbuf* response, if (sshbuf_put_u32(response, (int)(intptr_t)dup_token) != 0) goto done; - con->auth_token = token; - token = NULL; - if ((tmp = wcschr(user_utf16, L'@')) != NULL) { - udom_utf16 = tmp + 1; - *tmp = L'\0'; - } - LoadProfile(con, user_utf16, udom_utf16); - r = 0; done: /* TODO Fix this hacky protocol*/ @@ -373,7 +312,60 @@ done: return r; } -int process_authagent_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { +int process_loadprofile_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { + int r = 0, success = 0; + char *user; + size_t user_len; + u_int32_t user_token_int = 0; + HANDLE user_token = NULL; + wchar_t *user_utf16 = NULL, *dom_utf16 = NULL, *tmp; + + /* is profile already loaded */ + if (con->profile_handle) { + success = 1; + goto done; + } + + if (sshbuf_get_cstring(request, &user, &user_len) != 0 || + user_len > MAX_USER_LEN || + sshbuf_get_u32(request, &user_token_int) != 0){ + debug("invalid loadprofile request"); + goto done; + } + + if (DuplicateHandle(con->client_process_handle, (HANDLE)(INT_PTR)user_token_int, GetCurrentProcess(), + &user_token, TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, FALSE, 0) == FALSE) { + debug("cannot duplicate user token, error: %d", GetLastError()); + goto done; + } + + if ((user_utf16 = utf8_to_utf16(user)) == NULL) { + debug("out of memory"); + goto done; + } + + /* split user and domain */ + if ((tmp = wcschr(user_utf16, L'@')) != NULL) { + dom_utf16 = tmp + 1; + *tmp = L'\0'; + } + + if ((con->profile_handle = LoadProfile(user_token, user_utf16, dom_utf16)) == NULL) + goto done; + + con->profile_token = user_token; + user_token = NULL; + success = 1; +done: + if (sshbuf_put_u8(response, success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE) != 0) + r = -1; + + if (user_token) + CloseHandle(user_token); + return r; +} + +int process_privagent_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { char *opn; size_t opn_len; if (sshbuf_get_string_direct(request, &opn, &opn_len) != 0) { @@ -383,14 +375,14 @@ int process_authagent_request(struct sshbuf* request, struct sshbuf* response, s /* allow only admins and NT Service\sshd to send auth requests */ if (con->client_type != SSHD_SERVICE && con->client_type != ADMIN_USER) { - error("cannot authenticate: client process is not admin or sshd"); + error("cannot process request: client process is not admin or sshd"); return -1; } if (memcmp(opn, PUBKEY_AUTH_REQUEST, opn_len) == 0) return process_pubkeyauth_request(request, response, con); - else if (memcmp(opn, PASSWD_AUTH_REQUEST, opn_len) == 0) - return process_passwordauth_request(request, response, con); + else if (memcmp(opn, LOAD_USER_PROFILE_REQUEST, opn_len) == 0) + return process_loadprofile_request(request, response, con); else { debug("unknown auth request: %s", opn); return -1; diff --git a/contrib/win32/win32compat/ssh-agent/connection.c b/contrib/win32/win32compat/ssh-agent/connection.c index d6ae7c23b..ecb71e2b6 100644 --- a/contrib/win32/win32compat/ssh-agent/connection.c +++ b/contrib/win32/win32compat/ssh-agent/connection.c @@ -30,6 +30,7 @@ */ #include "agent.h" #include "agent-request.h" +#include "..\priv-agent.h" #pragma warning(push, 3) @@ -168,8 +169,8 @@ process_request(struct agent_connection* con) case SSH2_AGENTC_REMOVE_ALL_IDENTITIES: r = process_remove_all(request, response, con); break; - case SSH_AGENT_AUTHENTICATE: - r = process_authagent_request(request, response, con); + case SSH_PRIV_AGENT_MSG_ID: + r = process_privagent_request(request, response, con); break; default: debug("unknown agent request %d", type); diff --git a/contrib/win32/win32compat/win32_monitor_wrap.c b/contrib/win32/win32compat/win32_monitor_wrap.c new file mode 100644 index 000000000..01893c24b --- /dev/null +++ b/contrib/win32/win32compat/win32_monitor_wrap.c @@ -0,0 +1,214 @@ +/* +* Author: Manoj Ampalam +* mm_* routines that delegate privileged operations to privileged +* agent. +* +* Copyright (c) 2015 Microsoft Corp. +* All rights reserved +* +* Microsoft openssh win32 port +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions +* are met: +* +* 1. Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* 2. Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#include "includes.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#ifdef WITH_OPENSSL +#include +#include +#include +#endif + +#include "openbsd-compat/sys-queue.h" +#include "xmalloc.h" +#include "ssh.h" +#ifdef WITH_OPENSSL +#include "dh.h" +#endif +#include "buffer.h" +#include "key.h" +#include "cipher.h" +#include "kex.h" +#include "hostfile.h" +#include "auth.h" +#include "auth-options.h" +#include "packet.h" +#include "mac.h" +#include "log.h" +#include "auth-pam.h" +#include "monitor_wrap.h" +#include "atomicio.h" +#include "monitor_fdpass.h" +#include "misc.h" +#include "uuencode.h" + +#include "channels.h" +#include "session.h" +#include "servconf.h" + +#include "ssherr.h" +#include "priv-agent.h" +#include "authfd.h" + +int priv_agent_sock = -1; +int ssh_request_reply(int, struct sshbuf *, struct sshbuf *); + +/* + * Get socket connected to privileged agent + * In Windows, this is implemented within ssh-agent + * that server both as a key-agent (like in Unix) and + * privileged agent. + * This is a temporary accomodation until Windows has + * Unix like privilege separation (monitor and less + * privileged worker) + */ +int get_priv_agent_sock() +{ + extern int auth_sock; + char env_value[12]; /* enough to accomodate "ssh-agent"*/ + size_t tmp; + + if (priv_agent_sock != -1) + return priv_agent_sock; + + /* check if auth_sock is populated and connected to "ssh-agent"*/ + if (auth_sock != -1 && + getenv_s(&tmp, env_value, 12, SSH_AUTHSOCKET_ENV_NAME) == 0 && + strncmp(env_value, "ssh-agent", 12) == 0 ) + priv_agent_sock = auth_sock; + else { + struct sockaddr_un sunaddr; + int sock; + + memset(&sunaddr, 0, sizeof(sunaddr)); + sunaddr.sun_family = AF_UNIX; + strlcpy(sunaddr.sun_path, "\\\\.\\pipe\\openssh-ssh-agent", sizeof(sunaddr.sun_path)); + + if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) { + debug("%s: unable to create AF_UNIX socket, errno:%d", __func__, errno); + return -1; + } + + /* close on exec */ + if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1 || + connect(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0) { + close(sock); + debug("%s: unable to connect to privileged agent, errno:%d", __func__, errno); + return -1; + } + + priv_agent_sock = sock; + } + + return priv_agent_sock; +} + + +void* mm_auth_pubkey(const char* user_name, const struct sshkey *key, + const u_char *sig, size_t slen, struct sshbuf* b) +{ + /* Pass key challenge material to privileged agent to retrieve token upon successful authentication */ + struct sshbuf *msg = NULL; + u_char *blob = NULL; + size_t blen = 0; + DWORD 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, 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"); + break; + } + + if (sshbuf_get_u32(msg, &token) != 0) + break; + + debug3("%s authenticated via pubkey", user_name); + break; + + } + if (blob) + free(blob); + if (msg) + sshbuf_free(msg); + + return (void*)(INT_PTR)token; +} + +int mm_load_profile(const char* user_name, u_int token) +{ + struct sshbuf *msg = NULL; + int agent_fd; + u_char result = 0; + + 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, LOAD_USER_PROFILE_REQUEST) != 0 || + sshbuf_put_cstring(msg, user_name) != 0 || + sshbuf_put_u32(msg, token) != 0 || + ssh_request_reply(agent_fd, msg, msg) != 0) { + debug("unable to send loadprofile request %s", user_name); + break; + } + + if (sshbuf_get_u8(msg, &result) != 0 || result == SSH_AGENT_FAILURE) { + debug("agent failed to load profile for user %s", user_name); + break; + } + + break; + + } + + return result; +} \ No newline at end of file diff --git a/contrib/win32/win32compat/wmain_common.c b/contrib/win32/win32compat/wmain_common.c index 585442dde..b78d8cfe5 100644 --- a/contrib/win32/win32compat/wmain_common.c +++ b/contrib/win32/win32compat/wmain_common.c @@ -51,7 +51,7 @@ wmain(int argc, wchar_t **wargv) { } if (getenv("SSH_AUTH_SOCK") == NULL) - _putenv("SSH_AUTH_SOCK=ssh-agent"); + _putenv("SSH_AUTH_SOCK=\\\\.\\pipe\\openssh-ssh-agent"); w32posix_initialize(); diff --git a/contrib/win32/win32compat/wmain_sshd.c b/contrib/win32/win32compat/wmain_sshd.c index 3f1bc583f..5e75d01b2 100644 --- a/contrib/win32/win32compat/wmain_sshd.c +++ b/contrib/win32/win32compat/wmain_sshd.c @@ -108,9 +108,6 @@ int sshd_main(int argc, wchar_t **wargv) { argv[i] = utf16_to_utf8(wargv[i]); } - if (getenv("SSH_AUTH_SOCK") == NULL) - _putenv("SSH_AUTH_SOCK=ssh-agent"); - w32posix_initialize(); if (getenv("SSHD_REMSOC")) is_child = 1; diff --git a/monitor_wrap.h b/monitor_wrap.h index 9e032d204..72ba1cd24 100644 --- a/monitor_wrap.h +++ b/monitor_wrap.h @@ -98,4 +98,9 @@ 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 */ +void* mm_auth_pubkey(const char*, const struct sshkey *, const u_char *, size_t, + struct sshbuf*); +int mm_load_profile(const char*, u_int ); + #endif /* _MM_WRAP_H_ */ diff --git a/regress/pesterTests/SSHD_Config b/regress/pesterTests/SSHD_Config index 4bab2a822..244471f36 100644 --- a/regress/pesterTests/SSHD_Config +++ b/regress/pesterTests/SSHD_Config @@ -125,4 +125,5 @@ PubkeyAcceptedKeyTypes ssh-ed25519* #DenyUsers denyuser1 deny*2 denyuse?3, #AllowUsers allowuser1 allowu*r2 allow?se?3 allowuser4 localuser1 localu*r2 loc?lu?er3 localadmin #DenyGroups denygroup1 denygr*p2 deny?rou?3 -#AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm* \ No newline at end of file +#AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm* +hostkeyagent \\.\pipe\openssh-ssh-agent diff --git a/servconf.c b/servconf.c index 39a00a1b3..32d6ea386 100644 --- a/servconf.c +++ b/servconf.c @@ -605,8 +605,9 @@ derelativise_path(const char *path) return xstrdup("none"); expanded = tilde_expand_filename(path, getuid()); #ifdef WINDOWS - /* Windows absolute paths have a drive letter followed by :*/ - if (*expanded != '\0' && expanded[1] == ':') + /* Windows absolute paths - \abc, /abc, c:\abc, c:/abc*/ + if (*expanded == '/' || *expanded == '\\' || + (*expanded != '\0' && expanded[1] == ':')) #else /* !WINDOWS */ if (*expanded == '/') #endif /* !WINDOWS */ diff --git a/session.c b/session.c index 67720e797..1b40cb947 100644 --- a/session.c +++ b/session.c @@ -477,6 +477,9 @@ int do_exec_windows(Session *s, const char *command, int pty) { *cmd = '\0'; } + /* load user profile */ + mm_load_profile(s->pw->pw_name, ((INT_PTR)s->authctxt->auth_token) & 0xffffffff); + /* start the process */ { memset(&si, 0, sizeof(STARTUPINFO)); @@ -492,7 +495,7 @@ int do_exec_windows(Session *s, const char *command, int pty) { si.hStdError = (HANDLE)w32_fd_to_handle(pipeerr[1]); si.lpDesktop = NULL; - hToken = s->authctxt->methoddata; + hToken = s->authctxt->auth_token; debug("Executing command: %s", exec_command); UTF8_TO_UTF16_FATAL(exec_command_w, exec_command); diff --git a/sshd.c b/sshd.c index 552f12ed0..f19275e22 100644 --- a/sshd.c +++ b/sshd.c @@ -1765,9 +1765,7 @@ main(int ac, char **av) error("Could not connect to agent \"%s\": %s", options.host_key_agent, ssh_err(r)); } -#ifdef WINDOWS /* Windows version always needs and has agent running */ - have_agent = 1; -#endif + for (i = 0; i < options.num_host_key_files; i++) { if (options.host_key_files[i] == NULL) continue;