From e76442b650428425a1fbb97d621420216145a3bf Mon Sep 17 00:00:00 2001 From: Aris Adamantiadis Date: Wed, 6 Mar 2013 20:44:13 +0100 Subject: ssh-agent: implement the clientside for agent forwarding auth. This can only be used to authenticate the client, not to allow the connected server to transfer agent requests Reviewed-by: Andreas Schneider --- include/libssh/agent.h | 1 + include/libssh/libssh.h | 1 + src/agent.c | 105 ++++++++++++++++++++++++++++++++++-------------- 3 files changed, 77 insertions(+), 30 deletions(-) diff --git a/include/libssh/agent.h b/include/libssh/agent.h index 4641c9e2..77209d0f 100644 --- a/include/libssh/agent.h +++ b/include/libssh/agent.h @@ -71,6 +71,7 @@ struct ssh_agent_struct { struct ssh_socket_struct *sock; ssh_buffer ident; unsigned int count; + ssh_channel channel; }; #ifndef _WIN32 diff --git a/include/libssh/libssh.h b/include/libssh/libssh.h index 0ec64ab5..20e87595 100644 --- a/include/libssh/libssh.h +++ b/include/libssh/libssh.h @@ -521,6 +521,7 @@ LIBSSH_API int ssh_scp_write(ssh_scp scp, const void *buffer, size_t len); LIBSSH_API int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd, fd_set *readfds, struct timeval *timeout); LIBSSH_API int ssh_service_request(ssh_session session, const char *service); +LIBSSH_API int ssh_set_agent_channel(ssh_session session, ssh_channel channel); LIBSSH_API void ssh_set_blocking(ssh_session session, int blocking); LIBSSH_API void ssh_set_fd_except(ssh_session session); LIBSSH_API void ssh_set_fd_toread(ssh_session session); diff --git a/src/agent.c b/src/agent.c index b962a8e3..9c62ea42 100644 --- a/src/agent.c +++ b/src/agent.c @@ -83,23 +83,27 @@ static void agent_put_u32(void *vp, uint32_t v) { p[3] = (uint8_t)v & 0xff; } -static size_t atomicio(ssh_socket s, void *buf, size_t n, int do_read) { +static size_t atomicio(struct ssh_agent_struct *agent, void *buf, size_t n, int do_read) { char *b = buf; size_t pos = 0; ssize_t res; ssh_pollfd_t pfd; - socket_t fd = ssh_socket_get_fd_in(s); - - pfd.fd = fd; - pfd.events = do_read ? POLLIN : POLLOUT; - - while (n > pos) { - if (do_read) { - res = read(fd, b + pos, n - pos); - } else { - res = write(fd, b + pos, n - pos); - } - switch (res) { + ssh_channel channel = agent->channel; + socket_t fd; + + /* Using a socket ? */ + if (channel == NULL) { + fd = ssh_socket_get_fd_in(agent->sock); + pfd.fd = fd; + pfd.events = do_read ? POLLIN : POLLOUT; + + while (n > pos) { + if (do_read) { + res = read(fd, b + pos, n - pos); + } else { + res = write(fd, b + pos, n - pos); + } + switch (res) { case -1: if (errno == EINTR) { continue; @@ -107,22 +111,36 @@ static size_t atomicio(ssh_socket s, void *buf, size_t n, int do_read) { #ifdef EWOULDBLOCK if (errno == EAGAIN || errno == EWOULDBLOCK) { #else - if (errno == EAGAIN) { + if (errno == EAGAIN) { #endif - (void) ssh_poll(&pfd, 1, -1); - continue; + (void) ssh_poll(&pfd, 1, -1); + continue; + } + return 0; + case 0: + /* read returns 0 on end-of-file */ + errno = do_read ? 0 : EPIPE; + return pos; + default: + pos += (size_t) res; } - return 0; - case 0: - /* read returns 0 on end-of-file */ - errno = do_read ? 0 : EPIPE; + } + return pos; + } else { + /* using an SSH channel */ + while (n > pos){ + if (do_read) + res = ssh_channel_read(channel,b + pos, n-pos, 0); + else + res = ssh_channel_write(channel, b+pos, n-pos); + if (res == SSH_AGAIN) + continue; + if (res == SSH_ERROR) + return 0; + pos += (size_t)res; + } return pos; - default: - pos += (size_t) res; } - } - - return pos; } ssh_agent agent_new(struct ssh_session_struct *session) { @@ -140,10 +158,34 @@ ssh_agent agent_new(struct ssh_session_struct *session) { SAFE_FREE(agent); return NULL; } - + agent->channel = NULL; return agent; } +static void agent_set_channel(struct ssh_agent_struct *agent, ssh_channel channel){ + agent->channel = channel; +} + +/** @brief sets the SSH agent channel. + * The SSH agent channel will be used to authenticate this client using + * an agent through a channel, from another session. The most likely use + * is to implement SSH Agent forwarding into a SSH proxy. + * @param[in] channel a SSH channel from another session. + * @returns SSH_OK in case of success + * SSH_ERROR in case of an error + */ +int ssh_set_agent_channel(ssh_session session, ssh_channel channel){ + if (!session) + return SSH_ERROR; + if (!session->agent){ + ssh_set_error(session, SSH_REQUEST_DENIED, "Session has no active agent"); + return SSH_ERROR; + } + agent_set_channel(session->agent, channel); + return SSH_OK; +} + + void agent_close(struct ssh_agent_struct *agent) { if (agent == NULL) { return; @@ -174,6 +216,9 @@ static int agent_connect(ssh_session session) { return -1; } + if (session->agent->channel != NULL) + return 0; + auth_sock = getenv("SSH_AUTH_SOCK"); if (auth_sock && *auth_sock) { @@ -216,8 +261,8 @@ static int agent_talk(struct ssh_session_struct *session, agent_put_u32(payload, len); /* send length and then the request packet */ - if (atomicio(session->agent->sock, payload, 4, 0) == 4) { - if (atomicio(session->agent->sock, buffer_get_rest(request), len, 0) + if (atomicio(session->agent, payload, 4, 0) == 4) { + if (atomicio(session->agent, buffer_get_rest(request), len, 0) != len) { SSH_LOG(session, SSH_LOG_WARN, "atomicio sending request failed: %s", strerror(errno)); @@ -231,7 +276,7 @@ static int agent_talk(struct ssh_session_struct *session, } /* wait for response, read the length of the response packet */ - if (atomicio(session->agent->sock, payload, 4, 1) != 4) { + if (atomicio(session->agent, payload, 4, 1) != 4) { SSH_LOG(session, SSH_LOG_WARN, "atomicio read response length failed: %s", strerror(errno)); return -1; @@ -250,7 +295,7 @@ static int agent_talk(struct ssh_session_struct *session, if (n > sizeof(payload)) { n = sizeof(payload); } - if (atomicio(session->agent->sock, payload, n, 1) != n) { + if (atomicio(session->agent, payload, n, 1) != n) { SSH_LOG(session, SSH_LOG_WARN, "Error reading response from authentication socket."); return -1; -- cgit v1.2.3