diff --git a/.gitignore b/.gitignore index ddffcd2..dcbf920 100644 --- a/.gitignore +++ b/.gitignore @@ -278,3 +278,4 @@ d2utmpa* configure contrib/win32/openssh/Win32-OpenSSH.VC.opendb *.opendb +*.db diff --git a/auth-passwd.c b/auth-passwd.c index 2293668..1401e96 100644 --- a/auth-passwd.c +++ b/auth-passwd.c @@ -41,13 +41,13 @@ #include "xmalloc.h" #endif -/* - * We support only client side kerberos on Windows. - */ + /* + * We support only client side kerberos on Windows. + */ #ifdef WIN32_FIXME - #undef GSSAPI - #undef KRB5 +#undef GSSAPI +#undef KRB5 #endif #include @@ -202,183 +202,43 @@ sys_auth_passwd(Authctxt *authctxt, const char *password) } #elif defined(WIN32_FIXME) +extern int auth_sock; int sys_auth_passwd(Authctxt *authctxt, const char *password) { - /* - * Authenticate on Windows - */ - - struct passwd *pw = authctxt -> pw; + /* + * Authenticate on Windows + */ - HANDLE hToken = INVALID_HANDLE_VALUE; - - BOOL worked = FALSE; - - LPWSTR user_UTF16 = NULL; - LPWSTR password_UTF16 = NULL; - LPWSTR domain_UTF16 = NULL; + { + u_char *blob = NULL; + size_t blen = 0; + DWORD token = 0; + struct sshbuf *msg = NULL; - int buffer_size = 0; - - /* - * Identify domain or local login. - */ + msg = sshbuf_new(); + if (!msg) + return 0; + if (sshbuf_put_u8(msg, 100) != 0 || + sshbuf_put_cstring(msg, "password") != 0 || + sshbuf_put_cstring(msg, authctxt->user) != 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->pw->pw_name); + return 0; + } - char *username = authctxt->user; - char *domainslash = strchr(authctxt->user, '\\'); - if (domainslash) { - // domain\username format - char *domainname = authctxt->user; - *domainslash = '\0'; - username = ++domainslash; // username is past the domain \ is the username + if (blob) + free(blob); + if (msg) + sshbuf_free(msg); - // Convert domainname from UTF-8 to UTF-16 - buffer_size = MultiByteToWideChar(CP_UTF8, 0, domainname, -1, NULL, 0); + authctxt->methoddata = token; + + } - if (buffer_size > 0) - { - domain_UTF16 = xmalloc(4 * buffer_size); - } - else - { - return 0; - } - - if (0 == MultiByteToWideChar(CP_UTF8, 0, domainname, - -1, domain_UTF16, buffer_size)) - { - free(domain_UTF16); - - return 0; - } - } - else if (domainslash = strchr(authctxt->user, '@')) { - // username@domain format - username = authctxt->user; - *domainslash = '\0'; - char *domainname = ++domainslash; // domainname is past the user@ - - // Convert domainname from UTF-8 to UTF-16 - buffer_size = MultiByteToWideChar(CP_UTF8, 0, domainname, -1, NULL, 0); - - if (buffer_size > 0) - { - domain_UTF16 = xmalloc(4 * buffer_size); - } - else - { - return 0; - } - - if (0 == MultiByteToWideChar(CP_UTF8, 0, domainname, - -1, domain_UTF16, buffer_size)) - { - free(domain_UTF16); - - return 0; - } - } - else { - domain_UTF16 = strchr(authctxt->user, '@') ? NULL : L"."; - } - - authctxt -> methoddata = hToken; - - if (domain_UTF16 == NULL) - { - debug3("Using domain logon..."); - } - - /* - * Convert username from UTF-8 to UTF-16 - */ - - buffer_size = MultiByteToWideChar(CP_UTF8, 0, username, -1, NULL, 0); - - if (buffer_size > 0) - { - user_UTF16 = xmalloc(4 * buffer_size); - } - else - { - return 0; - } - - if (0 == MultiByteToWideChar(CP_UTF8, 0, username, - -1, user_UTF16, buffer_size)) - { - free(user_UTF16); - - return 0; - } - - /* - * Convert password from UTF-8 to UTF-16 - */ - - buffer_size = MultiByteToWideChar(CP_UTF8, 0, password, -1, NULL, 0); - - if (buffer_size > 0) - { - password_UTF16 = xmalloc(4 * buffer_size); - } - else - { - return 0; - } - - if (0 == MultiByteToWideChar(CP_UTF8, 0, password, -1, - password_UTF16 , buffer_size)) - { - free(password_UTF16 ); - - return 0; - } - - worked = LogonUserW(user_UTF16, domain_UTF16, password_UTF16, - LOGON32_LOGON_NETWORK, - LOGON32_PROVIDER_DEFAULT, &hToken); - - - free(user_UTF16); - free(password_UTF16); - if (domainslash) free(domain_UTF16); - - /* - * If login still fails, go out. - */ - - if (!worked || hToken == INVALID_HANDLE_VALUE) - { - return 0; - } - - /* - * Make sure this can be inherited for when - * we start shells or commands. - */ - - worked = SetHandleInformation(hToken, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); - - if (!worked) - { - CloseHandle(hToken); - - hToken = INVALID_HANDLE_VALUE; - - authctxt -> methoddata = hToken; - - return 0; - } - - /* - * Save the handle (or invalid handle) as method-specific data. - */ - - authctxt -> methoddata = hToken; - - return 1; + return 1; } #elif !defined(CUSTOM_SYS_AUTH_PASSWD) diff --git a/auth2-pubkey.c b/auth2-pubkey.c index 4144e8f..9ab001c 100644 --- a/auth2-pubkey.c +++ b/auth2-pubkey.c @@ -86,6 +86,7 @@ extern u_int session_id2_len; #ifdef WIN32_FIXME extern char HomeDirLsaW[MAX_PATH]; + extern int auth_sock; #endif @@ -192,52 +193,24 @@ userauth_pubkey(Authctxt *authctxt) #ifdef WIN32_FIXME { #define SSH_AGENT_ROOT "SOFTWARE\\SSH\\Agent" - HKEY agent_root = 0; - DWORD agent_pid = 0, tmp_size = 4, pipe_server_pid = 0xff; - int sock = -1, r; + int r; u_char *blob = NULL; size_t blen = 0; DWORD token = 0; - HANDLE h = INVALID_HANDLE_VALUE; struct sshbuf *msg = NULL; while (1) { - RegOpenKeyEx(HKEY_LOCAL_MACHINE, SSH_AGENT_ROOT, 0, KEY_QUERY_VALUE, &agent_root); - if (agent_root) - RegQueryValueEx(agent_root, "ProcessId", 0, NULL, &agent_pid, &tmp_size); - - - h = CreateFile( - "\\\\.\\pipe\\ssh-authagent", // pipe name - GENERIC_READ | // read and write access - GENERIC_WRITE, - 0, // no sharing - NULL, // default security attributes - OPEN_EXISTING, // opens existing pipe - FILE_FLAG_OVERLAPPED, // attributes - NULL); // no template file - if (h == INVALID_HANDLE_VALUE) { - debug("cannot connect to auth agent"); - break; - } - - if (!GetNamedPipeServerProcessId(h, &pipe_server_pid) || (agent_pid != pipe_server_pid)) { - debug("auth agent pid mismatch"); - break; - } - - if ((sock = w32_allocate_fd_for_handle(h, FALSE)) < 0) - break; msg = sshbuf_new(); if (!msg) break; - if ((r = sshbuf_put_cstring(msg, "keyauthenticate")) != 0 || + if ((r = sshbuf_put_u8(msg, 100)) != 0 || + (r = sshbuf_put_cstring(msg, "pubkey")) != 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, buffer_ptr(&b), buffer_len(&b))) != 0 || - (r = ssh_request_reply(sock, msg, msg)) != 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->pw->pw_name); break; @@ -246,12 +219,8 @@ userauth_pubkey(Authctxt *authctxt) break; } - if (agent_root) - RegCloseKey(agent_root); if (blob) free(blob); - if (sock != -1) - close(sock); if (msg) sshbuf_free(msg); diff --git a/authfd.c b/authfd.c index 6bad964..786b073 100644 --- a/authfd.c +++ b/authfd.c @@ -107,7 +107,7 @@ ssh_get_authentication_socket(int *fdp) } h = CreateFile( - "\\\\.\\pipe\\ssh-keyagent", // pipe name + "\\\\.\\pipe\\ssh-agent", // pipe name GENERIC_READ | // read and write access GENERIC_WRITE, 0, // no sharing diff --git a/contrib/win32/openssh/scp.vcxproj b/contrib/win32/openssh/scp.vcxproj index e7c1201..c971d10 100644 --- a/contrib/win32/openssh/scp.vcxproj +++ b/contrib/win32/openssh/scp.vcxproj @@ -22,6 +22,9 @@ + + + {29B98ADF-1285-49CE-BF6C-AA92C5D2FB24} Win32Proj diff --git a/contrib/win32/openssh/version.rc b/contrib/win32/openssh/version.rc index a2e949a..1eb4567 100644 Binary files a/contrib/win32/openssh/version.rc and b/contrib/win32/openssh/version.rc differ diff --git a/contrib/win32/win32compat/pwd.c b/contrib/win32/win32compat/pwd.c index 035b6a8..e311d0f 100644 --- a/contrib/win32/win32compat/pwd.c +++ b/contrib/win32/win32compat/pwd.c @@ -127,6 +127,14 @@ int GetDomainFromToken ( HANDLE *hAccessToken, UCHAR *domain, DWORD dwSize) * RETURNS: pointer to static string with homedir or NULL if fails. */ +#define SET_USER_ENV(folder_id, evn_variable) do { \ + if (SHGetKnownFolderPath(&folder_id,0,token,&path) == S_OK) \ + { \ + SetEnvironmentVariableW(evn_variable, path); \ + CoTaskMemFree(path); \ + } \ +} while (0) + char *GetHomeDirFromToken(char *userName, HANDLE token) { UCHAR InfoBuffer[1000]; @@ -156,16 +164,63 @@ char *GetHomeDirFromToken(char *userName, HANDLE token) if (reg_key) RegCloseKey(reg_key); - /* TODO - populate APPDATA, LOCALADPPDATA, TEMP, etc */ - SetEnvironmentVariableW(L"LOCALAPPDATA", L""); - SetEnvironmentVariableW(L"APPDATA", L""); - SetEnvironmentVariableW(L"TEMP", L""); - SetEnvironmentVariableW(L"TMP", L""); - SetEnvironmentVariableW(L"USERDNSDOMAIN", L""); - SetEnvironmentVariableW(L"USERDOMAIN", L""); - SetEnvironmentVariableW(L"USERDOMAIN_ROAMINGPROFILE", L""); - SetEnvironmentVariableW(L"USERPROFILE", L""); - + { /* retrieve and set env variables. */ + /* TODO - Get away with fixed limits and dynamically allocate required memory, cleanup this logic*/ +#define MAX_VALUE_LEN 1000 +#define MAX_DATA_LEN 2000 +#define MAX_EXPANDED_DATA_LEN 5000 + wchar_t *path; + wchar_t value_name[MAX_VALUE_LEN]; + wchar_t value_data[MAX_DATA_LEN], value_data_expanded[MAX_EXPANDED_DATA_LEN], *to_apply; + DWORD value_type, name_len, data_len; + int i; + LONG ret; + + if (ImpersonateLoggedOnUser(token) == FALSE) + debug("Failed to impersonate user token, %d", GetLastError()); + SET_USER_ENV(FOLDERID_LocalAppData, L"LOCALAPPDATA"); + SET_USER_ENV(FOLDERID_Profile, L"USERPROFILE"); + SET_USER_ENV(FOLDERID_RoamingAppData, L"APPDATA"); + reg_key = 0; + if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Environment", 0, KEY_QUERY_VALUE, ®_key) == ERROR_SUCCESS) { + i = 0; + while (1) { + name_len = MAX_VALUE_LEN * 2; + data_len = MAX_DATA_LEN * 2; + to_apply = NULL; + if (RegEnumValueW(reg_key, i++, &value_name, &name_len, 0, &value_type, &value_data, &data_len) != ERROR_SUCCESS) + break; + if (value_type == REG_SZ) + to_apply = value_data; + else if (value_type == REG_EXPAND_SZ) { + ExpandEnvironmentStringsW(value_data, value_data_expanded, MAX_EXPANDED_DATA_LEN); + to_apply = value_data_expanded; + } + + if (wcsicmp(value_name, L"PATH") == 0) { + DWORD size; + if ((size = GetEnvironmentVariableW(L"PATH", NULL, 0)) != ERROR_ENVVAR_NOT_FOUND) { + memcpy(value_data_expanded + size, to_apply, (wcslen(to_apply) + 1)*2); + GetEnvironmentVariableW(L"PATH", value_data_expanded, MAX_EXPANDED_DATA_LEN); + value_data_expanded[size-1] = L';'; + to_apply = value_data_expanded; + } + + } + if (to_apply) + SetEnvironmentVariableW(value_name, to_apply); + + + } + RegCloseKey(reg_key); + } + + + RevertToSelf(); + } + + + debug("<- GetHomeDirFromToken()..."); return pw_homedir; diff --git a/contrib/win32/win32compat/ssh-agent/agent-main.c b/contrib/win32/win32compat/ssh-agent/agent-main.c index 952cfdf..dac972c 100644 --- a/contrib/win32/win32compat/ssh-agent/agent-main.c +++ b/contrib/win32/win32compat/ssh-agent/agent-main.c @@ -32,11 +32,11 @@ #include "agent.h" -int scm_start_servie(DWORD, LPWSTR*); +int scm_start_service(DWORD, LPWSTR*); SERVICE_TABLE_ENTRYW dispatch_table[] = { - { L"ssh-agent", (LPSERVICE_MAIN_FUNCTIONW)scm_start_servie }, + { L"ssh-agent", (LPSERVICE_MAIN_FUNCTIONW)scm_start_service }, { NULL, NULL } }; static SERVICE_STATUS_HANDLE service_status_handle; @@ -98,20 +98,20 @@ int main(int argc, char **argv) { if (GetLastError() == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) { /*todo - support debug mode*/ /* - if (debugmode) { + if (debugMode) { SetConsoleCtrlHandler(ctrl_c_handler, TRUE); log_init("ssh-agent", 7, 1, 1); - agent_start(TRUE, FALSE, 0, 0); + agent_start(TRUE, FALSE, 0); return 0; } */ - if (argc == 3) { + if (argc == 2) { /*agent process is likely a spawned child*/ char* h = 0; h += atoi(*(argv + 1)); if (h != 0) { log_init("ssh-agent", config_log_level(), 1, 0); - agent_start(FALSE, TRUE, h, atoi(*(argv + 2))); + agent_start(FALSE, TRUE, h); return 0; } } @@ -141,14 +141,14 @@ int main(int argc, char **argv) { return 0; } -int scm_start_servie(DWORD num, LPWSTR* args) { +int scm_start_service(DWORD num, LPWSTR* args) { service_status_handle = RegisterServiceCtrlHandlerW(L"ssh-agent", service_handler); ZeroMemory(&service_status, sizeof(service_status)); service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ReportSvcStatus(SERVICE_START_PENDING, NO_ERROR, 300); ReportSvcStatus(SERVICE_RUNNING, NO_ERROR, 0); log_init("ssh-agent", config_log_level(), 1, 0); - agent_start(FALSE, FALSE, 0, 0); + agent_start(FALSE, FALSE, 0); return 0; } diff --git a/contrib/win32/win32compat/ssh-agent/agent-request.h b/contrib/win32/win32compat/ssh-agent/agent-request.h index 7bd360b..3d7c3df 100644 --- a/contrib/win32/win32compat/ssh-agent/agent-request.h +++ b/contrib/win32/win32compat/ssh-agent/agent-request.h @@ -10,6 +10,12 @@ typedef unsigned __int64 u_int64_t; #include "digest.h" -int process_keyagent_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); -int process_pubkeyagent_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); -int process_authagent_request(struct sshbuf*, struct sshbuf*, struct agent_connection*); \ No newline at end of file +/* key management */ +int process_add_identity(struct sshbuf*, struct sshbuf*, struct agent_connection*); +int process_request_identities(struct sshbuf*, struct sshbuf*, struct agent_connection*); +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*); + +/* auth */ diff --git a/contrib/win32/win32compat/ssh-agent/agent.c b/contrib/win32/win32compat/ssh-agent/agent.c index deb5359..69d3e66 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.c +++ b/contrib/win32/win32compat/ssh-agent/agent.c @@ -30,44 +30,30 @@ */ #include "agent.h" #include +#include #define BUFSIZE 5 * 1024 static HANDLE ioc_port = NULL; static BOOL debug_mode = FALSE; -#define NUM_LISTENERS 3 -#define KEY_AGENT_PIPE_ID L"\\\\.\\pipe\\ssh-keyagent" -#define PUBKEY_AGENT_PIPE_ID L"\\\\.\\pipe\\ssh-pubkeyagent" -#define AUTH_AGENT_PIPE_ID L"\\\\.\\pipe\\ssh-authagent" +#define AGENT_PIPE_ID L"\\\\.\\pipe\\ssh-agent" -static wchar_t *pipe_ids[NUM_LISTENERS] = { KEY_AGENT_PIPE_ID, PUBKEY_AGENT_PIPE_ID, AUTH_AGENT_PIPE_ID }; -static enum agent_type pipe_types[NUM_LISTENERS] = { KEY_AGENT, PUBKEY_AGENT, PUBKEY_AUTH_AGENT}; -static wchar_t *pipe_sddls[NUM_LISTENERS] = { L"D:P(A;; GA;;; AU)", L"D:P(A;; GA;;; AU)", L"D:P(A;; GA;;; AU)" }; -HANDLE event_stop_agent; - -struct listener { - OVERLAPPED ol; - HANDLE pipe; - wchar_t *pipe_id; - enum agent_type type; - SECURITY_ATTRIBUTES sa; -} listeners[NUM_LISTENERS]; +static HANDLE event_stop_agent; +static OVERLAPPED ol; +static HANDLE pipe; +static SECURITY_ATTRIBUTES sa; static int -init_listeners() { - int i; - memset(listeners, 0, sizeof(listeners)); - for (i = 0; i < NUM_LISTENERS; i++) { - if ((listeners[i].ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) { +init_listener() { + { + if ((ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) { debug("cannot create event ERROR:%d", GetLastError()); return GetLastError(); } - listeners[i].pipe_id = pipe_ids[i]; - listeners[i].type = pipe_types[i]; - listeners[i].pipe = INVALID_HANDLE_VALUE; - listeners[i].sa.bInheritHandle = FALSE; - if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(pipe_sddls[i], SDDL_REVISION_1, - &listeners[i].sa.lpSecurityDescriptor, &listeners[i].sa.nLength)) { + pipe = INVALID_HANDLE_VALUE; + sa.bInheritHandle = FALSE; + if (!ConvertStringSecurityDescriptorToSecurityDescriptorW(L"D:P(A;; GA;;; AU)", SDDL_REVISION_1, + &sa.lpSecurityDescriptor, &sa.nLength)) { debug("cannot convert sddl ERROR:%d", GetLastError()); return GetLastError(); } @@ -78,12 +64,11 @@ init_listeners() { static void agent_cleanup() { - int i; - for (i = 0; i < NUM_LISTENERS; i++) { - if (listeners[i].ol.hEvent != NULL) - CloseHandle(listeners[i].ol.hEvent); - if (listeners[i].pipe != INVALID_HANDLE_VALUE) - CloseHandle(listeners[i].pipe); + { + if (ol.hEvent != NULL) + CloseHandle(ol.hEvent); + if (pipe != INVALID_HANDLE_VALUE) + CloseHandle(pipe); } if (ioc_port) CloseHandle(ioc_port); @@ -112,7 +97,7 @@ iocp_work(LPVOID lpParam) { } static void -process_connection(HANDLE pipe, int type) { +process_connection(HANDLE pipe) { struct agent_connection* con; if ((con = malloc(sizeof(struct agent_connection))) == NULL) @@ -120,7 +105,6 @@ process_connection(HANDLE pipe, int type) { memset(con, 0, sizeof(struct agent_connection)); con->connection = pipe; - con->type = type; if (CreateIoCompletionPort(pipe, ioc_port, (ULONG_PTR)con, 0) != ioc_port) fatal("failed to assign pipe to ioc_port"); @@ -130,18 +114,18 @@ process_connection(HANDLE pipe, int type) { static void agent_listen_loop() { - DWORD i, r; - HANDLE wait_events[NUM_LISTENERS + 1]; + DWORD r; + HANDLE wait_events[2]; wait_events[0] = event_stop_agent; - for (i = 0; i < NUM_LISTENERS; i++) - wait_events[i + 1] = listeners[i].ol.hEvent; + + wait_events[1] = ol.hEvent; while (1) { - for (i = 0; i < NUM_LISTENERS; i++) { - if (listeners[i].pipe == INVALID_HANDLE_VALUE) { - listeners[i].pipe = CreateNamedPipeW( - listeners[i].pipe_id, // pipe name + { + { + pipe = CreateNamedPipeW( + AGENT_PIPE_ID, // pipe name PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, // read/write access PIPE_TYPE_BYTE | // message type pipe PIPE_READMODE_BYTE | // message-read mode @@ -150,20 +134,20 @@ agent_listen_loop() { BUFSIZE, // output buffer size BUFSIZE, // input buffer size 0, // client time-out - &listeners[i].sa); + &sa); - if (listeners[i].pipe == INVALID_HANDLE_VALUE) { + if (pipe == INVALID_HANDLE_VALUE) { verbose("cannot create listener pipe ERROR:%d", GetLastError()); SetEvent(event_stop_agent); } - else if (ConnectNamedPipe(listeners[i].pipe, &listeners[i].ol) != FALSE) { + else if (ConnectNamedPipe(pipe, &ol) != FALSE) { verbose("ConnectNamedPipe returned TRUE unexpectedly "); SetEvent(event_stop_agent); } if (GetLastError() == ERROR_PIPE_CONNECTED) { - debug("Client has already connection to %d", i); - SetEvent(listeners[i].ol.hEvent); + debug("Client has already connected"); + SetEvent(ol.hEvent); } if (GetLastError() != ERROR_IO_PENDING) { @@ -174,22 +158,22 @@ agent_listen_loop() { } } - r = WaitForMultipleObjects(NUM_LISTENERS + 1, wait_events, FALSE, INFINITE); + r = WaitForMultipleObjects(2, wait_events, FALSE, INFINITE); if (r == WAIT_OBJECT_0) { //received signal to shutdown debug("shutting down"); agent_cleanup(); return; } - else if ((r > WAIT_OBJECT_0) && (r <= (WAIT_OBJECT_0 + NUM_LISTENERS))) { + else if ((r > WAIT_OBJECT_0) && (r <= (WAIT_OBJECT_0 + 1))) { /* process incoming connection */ - HANDLE con = listeners[r - 1].pipe; + HANDLE con = pipe; DWORD client_pid = 0; - listeners[r - 1].pipe = INVALID_HANDLE_VALUE; + pipe = INVALID_HANDLE_VALUE; GetNamedPipeClientProcessId(con, &client_pid); - verbose("client pid %d connected on %ls", client_pid, pipe_ids[r-1]); + verbose("client pid %d connected", client_pid); if (debug_mode) { - process_connection(con, listeners[r - 1].type); + process_connection(con); agent_cleanup(); return; } @@ -203,14 +187,14 @@ agent_listen_loop() { memset(&si, 0, sizeof(STARTUPINFOW)); GetModuleFileNameW(NULL, module_path, MAX_PATH); SetHandleInformation(con, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT); - if ((swprintf_s(path, MAX_PATH, L"%s %d %d", module_path, con, listeners[r - 1].type) == -1 ) || + if ((swprintf_s(path, MAX_PATH, L"%s %d", module_path, con) == -1 ) || (CreateProcessW(NULL, path, NULL, NULL, TRUE, DETACHED_PROCESS, NULL, NULL, &si, &pi) == FALSE)) { verbose("Failed to create child process %ls ERROR:%d", module_path, GetLastError()); } else { - debug("spawned child %d to process %d", pi.dwProcessId, i); + debug("spawned child %d ", pi.dwProcessId); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } @@ -229,6 +213,10 @@ agent_listen_loop() { void agent_cleanup_connection(struct agent_connection* con) { debug("connection %p clean up", con); CloseHandle(con->connection); + if (con->hProfile) + UnloadUserProfile(con->auth_token, con->hProfile); + if (con->auth_token) + CloseHandle(con->auth_token); free(con); CloseHandle(ioc_port); ioc_port = NULL; @@ -242,7 +230,7 @@ void agent_shutdown() { #define REG_AGENT_SDDL L"D:P(A;; GR;;; AU)(A;; GA;;; SY)(A;; GA;;; BA)" void -agent_start(BOOL dbg_mode, BOOL child, HANDLE pipe, enum agent_type type) { +agent_start(BOOL dbg_mode, BOOL child, HANDLE pipe) { int r; HKEY agent_root = NULL; DWORD process_id = GetCurrentProcessId(); @@ -266,12 +254,12 @@ agent_start(BOOL dbg_mode, BOOL child, HANDLE pipe, enum agent_type type) { fatal("cannot publish agent master process id ERROR:%d", r); if ((event_stop_agent = CreateEvent(NULL, TRUE, FALSE, NULL)) == NULL) fatal("cannot create global stop event ERROR:%d", GetLastError()); - if ((r = init_listeners()) != 0) + if ((r = init_listener()) != 0) fatal("failed to create server pipes ERROR:%d", r); agent_listen_loop(); } else { /* this is a child process that processes one connection */ - process_connection(pipe, type); + process_connection(pipe); } } diff --git a/contrib/win32/win32compat/ssh-agent/agent.h b/contrib/win32/win32compat/ssh-agent/agent.h index 639c37f..36686ff 100644 --- a/contrib/win32/win32compat/ssh-agent/agent.h +++ b/contrib/win32/win32compat/ssh-agent/agent.h @@ -9,12 +9,6 @@ #define HEADER_SIZE 4 -enum agent_type { - KEY_AGENT, - PUBKEY_AGENT, - PUBKEY_AUTH_AGENT -}; - struct agent_connection { OVERLAPPED ol; HANDLE connection; @@ -37,15 +31,16 @@ struct agent_connection { LOCAL_SYSTEM, SSHD, NETWORK_SERVICE - } client_type; - enum agent_type type; + } client_process; + HANDLE auth_token; + HANDLE hProfile; }; void agent_connection_on_io(struct agent_connection*, DWORD, OVERLAPPED*); void agent_connection_on_error(struct agent_connection* , DWORD ); void agent_connection_disconnect(struct agent_connection*); -void agent_start(BOOL, BOOL, HANDLE, enum agent_type); +void agent_start(BOOL, BOOL, HANDLE); void agent_shutdown(); void agent_cleanup_connection(struct agent_connection*); diff --git a/contrib/win32/win32compat/ssh-agent/agentconfig.c b/contrib/win32/win32compat/ssh-agent/agentconfig.c index 253a5d7..2df29b1 100644 --- a/contrib/win32/win32compat/ssh-agent/agentconfig.c +++ b/contrib/win32/win32compat/ssh-agent/agentconfig.c @@ -51,6 +51,7 @@ Buffer cfg; ServerOptions options; struct passwd *privsep_pw = NULL; static char *config_file_name = _PATH_SERVER_CONFIG_FILE; +int auth_sock = -1; int auth2_methods_valid(const char * c, int i) { return 1; diff --git a/contrib/win32/win32compat/ssh-agent/authagent-request.c b/contrib/win32/win32compat/ssh-agent/authagent-request.c index f74f70c..68c17f4 100644 --- a/contrib/win32/win32compat/ssh-agent/authagent-request.c +++ b/contrib/win32/win32compat/ssh-agent/authagent-request.c @@ -31,6 +31,7 @@ #define UMDF_USING_NTSTATUS #include +#include #include #include #include @@ -50,6 +51,54 @@ InitLsaString(LSA_STRING *lsa_string, const char *str) } } +static void +EnablePrivilege(const char *privName, int enabled) +{ + TOKEN_PRIVILEGES tp; + HANDLE hProcToken = NULL; + LUID luid; + + int exitCode = 1; + + if (LookupPrivilegeValueA(NULL, privName, &luid) == FALSE || + OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hProcToken) == FALSE) + goto done; + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + tp.Privileges[0].Attributes = enabled ? SE_PRIVILEGE_ENABLED : 0; + + AdjustTokenPrivileges(hProcToken, FALSE, &tp, sizeof(TOKEN_PRIVILEGES), NULL, NULL); + +done: + if (hProcToken) + CloseHandle(hProcToken); + + return; +} + + +void +LoadProfile(struct agent_connection* con, wchar_t* user, wchar_t* domain) { + PROFILEINFOW profileInfo; + profileInfo.dwFlags = PI_NOUI; + profileInfo.lpProfilePath = NULL; + profileInfo.lpUserName = user; + profileInfo.lpDefaultPath = NULL; + profileInfo.lpServerName = domain; + profileInfo.lpPolicyPath = NULL; + profileInfo.hProfile = NULL; + profileInfo.dwSize = sizeof(profileInfo); + EnablePrivilege("SeBackupPrivilege", 1); + EnablePrivilege("SeRestorePrivilege", 1); + if (LoadUserProfileW(con->auth_token, &profileInfo) == FALSE) + debug("Loading user (%ls,%ls) profile failed ERROR: %d", user, domain, GetLastError()); + else + con->hProfile = profileInfo.hProfile; + EnablePrivilege("SeBackupPrivilege", 0); + EnablePrivilege("SeRestorePrivilege", 0); +} + #define MAX_USER_LEN 256 static HANDLE generate_user_token(wchar_t* user) { @@ -152,7 +201,7 @@ generate_user_token(wchar_t* user) { &token, "as, &subStatus) != STATUS_SUCCESS) { - debug("LsaRegisterLogonProcess failed"); + debug("LsaLogonUser failed %d", ret); goto done; } @@ -167,13 +216,81 @@ done: return token; } -#define AUTH_REQUEST "keyauthenticate" +#define PUBKEY_AUTH_REQUEST "pubkey" +#define PASSWD_AUTH_REQUEST "password" #define MAX_USER_NAME_LEN 256 +#define MAX_PW_LEN 128 -int process_authagent_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { +int process_passwordauth_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { + char *user = NULL, *pwd = NULL; + wchar_t userW_buf[MAX_USER_NAME_LEN], pwdW_buf[MAX_PW_LEN]; + wchar_t *userW = userW_buf, *domW = NULL, *pwdW = pwdW_buf, *tmp; + size_t user_len = 0, pwd_len = 0, dom_len = 0; int r = -1; - char *opn, *key_blob, *user, *sig, *blob; - size_t opn_len, key_blob_len, user_len, sig_len, blob_len; + HANDLE token = 0, dup_token, client_proc = 0; + ULONG client_pid; + + if (sshbuf_get_cstring(request, &user, &user_len) != 0 || + sshbuf_get_cstring(request, &pwd, &pwd_len) != 0 || + user_len == 0 || + pwd_len == 0 ){ + debug("bad password auth request"); + goto done; + } + + userW[0] = L'\0'; + if (MultiByteToWideChar(CP_UTF8, 0, user, user_len + 1, userW, MAX_USER_NAME_LEN) == 0 || + MultiByteToWideChar(CP_UTF8, 0, pwd, pwd_len + 1, pwdW, MAX_PW_LEN) == 0) { + debug("unable to convert user (%s) or password to UTF-16", user); + goto done; + } + + if ((tmp = wcschr(userW, L'\\')) != NULL) { + domW = userW; + userW = tmp + 1; + *tmp = L'\0'; + + } + else if ((tmp = wcschr(userW, L'@')) != NULL) { + domW = tmp + 1; + *tmp = L'\0'; + } + + if (LogonUserW(userW, domW, pwdW, LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, &token) == FALSE) { + debug("failed to logon user"); + goto done; + } + + if ((FALSE == GetNamedPipeClientProcessId(con->connection, &client_pid)) || + ((client_proc = OpenProcess(PROCESS_DUP_HANDLE, FALSE, client_pid)) == NULL) || + (FALSE == DuplicateHandle(GetCurrentProcess(), token, client_proc, &dup_token, TOKEN_QUERY | TOKEN_IMPERSONATE, FALSE, DUPLICATE_SAME_ACCESS)) || + (sshbuf_put_u32(response, dup_token) != 0)) { + debug("failed to duplicate user token"); + goto done; + } + + con->auth_token = token; + LoadProfile(con, userW, domW); + 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 (client_proc) + CloseHandle(client_proc); + + 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, client_proc = NULL; wchar_t wuser[MAX_USER_NAME_LEN]; @@ -181,24 +298,24 @@ int process_authagent_request(struct sshbuf* request, struct sshbuf* response, s ULONG client_pid; user = NULL; - if (sshbuf_get_string_direct(request, &opn, &opn_len) != 0 || - sshbuf_get_string_direct(request, &key_blob, &key_blob_len) != 0 || + if (sshbuf_get_string_direct(request, &key_blob, &key_blob_len) != 0 || sshbuf_get_cstring(request, &user, &user_len) != 0 || sshbuf_get_string_direct(request, &sig, &sig_len) != 0 || sshbuf_get_string_direct(request, &blob, &blob_len) != 0 || - sshkey_from_blob(key_blob, key_blob_len, &key) != 0 || - opn_len != strlen(AUTH_REQUEST) || - memcmp(opn, AUTH_REQUEST, opn_len) != 0) { - debug("auth agent invalid request"); + sshkey_from_blob(key_blob, key_blob_len, &key) != 0) { + debug("invalid pubkey auth request"); goto done; } + wuser[0] = L'\0'; if (MultiByteToWideChar(CP_UTF8, 0, user, user_len + 1, wuser, MAX_USER_NAME_LEN) == 0 || (token = generate_user_token(wuser)) == 0) { debug("unable to generate token for user %ls", wuser); goto done; } + con->auth_token = token; + if (SHGetKnownFolderPath(&FOLDERID_Profile, 0, token, &wuser_home) != S_OK || pubkey_allowed(key, wuser, wuser_home) != 1) { debug("given public key is not mapped to user %ls (profile:%ls)", wuser, wuser_home); @@ -218,17 +335,54 @@ int process_authagent_request(struct sshbuf* request, struct sshbuf* response, s goto done; } + { + wchar_t *tmp, *userW, *domW; + userW = wuser; + if ((tmp = wcschr(userW, L'\\')) != NULL) { + domW = userW; + userW = tmp + 1; + *tmp = L'\0'; + + } + else if ((tmp = wcschr(userW, L'@')) != NULL) { + domW = tmp + 1; + *tmp = L'\0'; + } + LoadProfile(con, userW, domW); + } + 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 (key) sshkey_free(key); - if (token) - CloseHandle(token); if (wuser_home) CoTaskMemFree(wuser_home); if (client_proc) CloseHandle(client_proc); return r; +} + +int process_authagent_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) { + debug("invalid auth request"); + return -1; + } + + if (opn_len == strlen(PUBKEY_AUTH_REQUEST) && memcmp(opn, PUBKEY_AUTH_REQUEST, opn_len) == 0) + return process_pubkeyauth_request(request, response, con); + else if (opn_len == strlen(PASSWD_AUTH_REQUEST) && memcmp(opn, PASSWD_AUTH_REQUEST, opn_len) == 0) + return process_passwordauth_request(request, response, con); + else { + debug("unknown auth request: %s", opn); + return -1; + } + } \ No newline at end of file diff --git a/contrib/win32/win32compat/ssh-agent/connection.c b/contrib/win32/win32compat/ssh-agent/connection.c index 2a19282..b45a005 100644 --- a/contrib/win32/win32compat/ssh-agent/connection.c +++ b/contrib/win32/win32compat/ssh-agent/connection.c @@ -46,7 +46,7 @@ void agent_connection_on_error(struct agent_connection* con, DWORD error) { void agent_connection_on_io(struct agent_connection* con, DWORD bytes, OVERLAPPED* ol) { /* process error */ - debug("connection io %p #bytes:%d state:%d", con, bytes, con->state); + debug3("connection io %p #bytes:%d state:%d", con, bytes, con->state); if ((bytes == 0) && (GetOverlappedResult(con->connection, ol, &bytes, FALSE) == FALSE)) ABORT_CONNECTION_RETURN(con); @@ -155,7 +155,7 @@ get_con_client_type(HANDLE pipe) { else r = OTHER; - debug("client type: %d", r); + debug2("client type: %d", r); done: if (sshd_sid) free(sshd_sid); @@ -167,14 +167,16 @@ done: return r; } +/* TODO - move this to common header*/ +#define SSH_AGENT_AUTHENTICATE 100 static int process_request(struct agent_connection* con) { int r = -1; struct sshbuf *request = NULL, *response = NULL; - if (con->client_type == UNKNOWN) - if ((con->client_type = get_con_client_type(con->connection)) == -1) + if (con->client_process == UNKNOWN) + if ((con->client_process = get_con_client_type(con->connection)) == -1) goto done; @@ -183,12 +185,38 @@ process_request(struct agent_connection* con) { if ((request == NULL) || (response == NULL)) goto done; - if (con->type == KEY_AGENT) - r = process_keyagent_request(request, response, con); - else if (con->type == PUBKEY_AGENT) - r = process_pubkeyagent_request(request, response, con); - else if (con->type == PUBKEY_AUTH_AGENT) - r = process_authagent_request(request, response, con); + { + u_char type; + + if (sshbuf_get_u8(request, &type) != 0) + return -1; + debug("process agent request type %d", type); + + switch (type) { + case SSH2_AGENTC_ADD_IDENTITY: + r = process_add_identity(request, response, con); + break; + case SSH2_AGENTC_REQUEST_IDENTITIES: + r = process_request_identities(request, response, con); + break; + case SSH2_AGENTC_SIGN_REQUEST: + r = process_sign_request(request, response, con); + break; + case SSH2_AGENTC_REMOVE_IDENTITY: + r = process_remove_key(request, response, con); + break; + 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); + break; + default: + debug("unknown agent request %d", type); + r = -1; + break; + } + } done: if (request) diff --git a/contrib/win32/win32compat/ssh-agent/keyagent-request.c b/contrib/win32/win32compat/ssh-agent/keyagent-request.c index 50b59b8..6a34e13 100644 --- a/contrib/win32/win32compat/ssh-agent/keyagent-request.c +++ b/contrib/win32/win32compat/ssh-agent/keyagent-request.c @@ -43,7 +43,7 @@ get_user_root(struct agent_connection* con, HKEY *root){ if (ImpersonateNamedPipeClient(con->connection) == FALSE) return -1; - if (con->client_type > OTHER) + if (con->client_process > OTHER) *root = HKEY_LOCAL_MACHINE; else if (RegOpenCurrentUser(KEY_ALL_ACCESS, root) != ERROR_SUCCESS) r = -1; @@ -59,7 +59,7 @@ convert_blob(struct agent_connection* con, const char *blob, DWORD blen, char ** int success = 0; DATA_BLOB in, out; - if (con->client_type == OTHER) + if (con->client_process == OTHER) if (ImpersonateNamedPipeClient(con->connection) == FALSE) return -1; @@ -91,14 +91,14 @@ convert_blob(struct agent_connection* con, const char *blob, DWORD blen, char ** done: if (out.pbData) LocalFree(out.pbData); - if (con->client_type == OTHER) + if (con->client_process == OTHER) RevertToSelf(); return success? 0: -1; } #define REG_KEY_SDDL L"D:P(A;; GA;;; SY)(A;; GA;;; BA)" -static int +int process_add_identity(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { struct sshkey* key = NULL; int r = 0, blob_len, eblob_len, request_invalid = 0, success = 0; @@ -224,7 +224,7 @@ done: return success ? 0 : -1; } -static int +int process_sign_request(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { u_char *blob, *data, *signature = NULL; size_t blen, dlen, slen = 0; @@ -270,7 +270,7 @@ done: return r; } -static int +int process_remove_key(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { HKEY user_root = 0, root = 0; char *blob, *thumbprint = NULL; @@ -308,7 +308,7 @@ done: free(thumbprint); return r; } -static int +int process_remove_all(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { HKEY user_root = 0, root = 0; int r = 0; @@ -332,7 +332,7 @@ done: return r; } -static int +int process_request_identities(struct sshbuf* request, struct sshbuf* response, struct agent_connection* con) { int count = 0, index = 0, success = 0, r = 0; HKEY root = NULL, sub = NULL, user_root = 0;