Decouple key-agent and privileged-agent use in sshd (#173)
PowerShell/Win32-OpenSSH#766 PowerShell/Win32-OpenSSH#783
This commit is contained in:
parent
ad17ff1b74
commit
f8f5e45f02
|
@ -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 */
|
||||
|
|
4
auth.h
4
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;
|
||||
};
|
||||
|
|
|
@ -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",
|
||||
|
|
9
authfd.h
9
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 */
|
||||
|
|
|
@ -246,6 +246,7 @@
|
|||
<ClCompile Include="$(OpenSSH-Src-Path)sshlogin.c" />
|
||||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_sshpty.c" />
|
||||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\wmain_sshd.c" />
|
||||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_monitor_wrap.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="version.rc" />
|
||||
|
|
|
@ -150,6 +150,9 @@
|
|||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_sshpty.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="$(OpenSSH-Src-Path)contrib\win32\win32compat\win32_monitor_wrap.c">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="version.rc">
|
||||
|
|
|
@ -119,4 +119,5 @@ Subsystem sftp sftp-server.exe
|
|||
# X11Forwarding no
|
||||
# AllowTcpForwarding no
|
||||
# ForceCommand cvs server
|
||||
# PubkeyAcceptedKeyTypes ssh-ed25519*
|
||||
# PubkeyAcceptedKeyTypes ssh-ed25519*
|
||||
hostkeyagent \\.\pipe\openssh-ssh-agent
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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*);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Author: Manoj Ampalam <manoj.ampalam@microsoft.com>
|
||||
* 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 <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/uio.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <pwd.h>
|
||||
#include <signal.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef WITH_OPENSSL
|
||||
#include <openssl/bn.h>
|
||||
#include <openssl/dh.h>
|
||||
#include <openssl/evp.h>
|
||||
#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;
|
||||
}
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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*
|
||||
#AllowGroups allowgroup1 allowg*2 allowg?ou?3 Adm*
|
||||
hostkeyagent \\.\pipe\openssh-ssh-agent
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
4
sshd.c
4
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;
|
||||
|
|
Loading…
Reference in New Issue