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 <asn@cryptomilk.org>
This commit is contained in:
Aris Adamantiadis
2013-03-06 20:44:13 +01:00
committed by Andreas Schneider
parent 9bdb546852
commit e76442b650
3 changed files with 75 additions and 28 deletions

View File

@@ -71,6 +71,7 @@ struct ssh_agent_struct {
struct ssh_socket_struct *sock; struct ssh_socket_struct *sock;
ssh_buffer ident; ssh_buffer ident;
unsigned int count; unsigned int count;
ssh_channel channel;
}; };
#ifndef _WIN32 #ifndef _WIN32

View File

@@ -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, LIBSSH_API int ssh_select(ssh_channel *channels, ssh_channel *outchannels, socket_t maxfd,
fd_set *readfds, struct timeval *timeout); fd_set *readfds, struct timeval *timeout);
LIBSSH_API int ssh_service_request(ssh_session session, const char *service); 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_blocking(ssh_session session, int blocking);
LIBSSH_API void ssh_set_fd_except(ssh_session session); LIBSSH_API void ssh_set_fd_except(ssh_session session);
LIBSSH_API void ssh_set_fd_toread(ssh_session session); LIBSSH_API void ssh_set_fd_toread(ssh_session session);

View File

@@ -83,23 +83,27 @@ static void agent_put_u32(void *vp, uint32_t v) {
p[3] = (uint8_t)v & 0xff; 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; char *b = buf;
size_t pos = 0; size_t pos = 0;
ssize_t res; ssize_t res;
ssh_pollfd_t pfd; ssh_pollfd_t pfd;
socket_t fd = ssh_socket_get_fd_in(s); ssh_channel channel = agent->channel;
socket_t fd;
pfd.fd = fd; /* Using a socket ? */
pfd.events = do_read ? POLLIN : POLLOUT; if (channel == NULL) {
fd = ssh_socket_get_fd_in(agent->sock);
pfd.fd = fd;
pfd.events = do_read ? POLLIN : POLLOUT;
while (n > pos) { while (n > pos) {
if (do_read) { if (do_read) {
res = read(fd, b + pos, n - pos); res = read(fd, b + pos, n - pos);
} else { } else {
res = write(fd, b + pos, n - pos); res = write(fd, b + pos, n - pos);
} }
switch (res) { switch (res) {
case -1: case -1:
if (errno == EINTR) { if (errno == EINTR) {
continue; continue;
@@ -107,22 +111,36 @@ static size_t atomicio(ssh_socket s, void *buf, size_t n, int do_read) {
#ifdef EWOULDBLOCK #ifdef EWOULDBLOCK
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
#else #else
if (errno == EAGAIN) { if (errno == EAGAIN) {
#endif #endif
(void) ssh_poll(&pfd, 1, -1); (void) ssh_poll(&pfd, 1, -1);
continue; 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: return pos;
/* read returns 0 on end-of-file */ } else {
errno = do_read ? 0 : EPIPE; /* 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; return pos;
default:
pos += (size_t) res;
} }
}
return pos;
} }
ssh_agent agent_new(struct ssh_session_struct *session) { 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); SAFE_FREE(agent);
return NULL; return NULL;
} }
agent->channel = NULL;
return agent; 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) { void agent_close(struct ssh_agent_struct *agent) {
if (agent == NULL) { if (agent == NULL) {
return; return;
@@ -174,6 +216,9 @@ static int agent_connect(ssh_session session) {
return -1; return -1;
} }
if (session->agent->channel != NULL)
return 0;
auth_sock = getenv("SSH_AUTH_SOCK"); auth_sock = getenv("SSH_AUTH_SOCK");
if (auth_sock && *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); agent_put_u32(payload, len);
/* send length and then the request packet */ /* send length and then the request packet */
if (atomicio(session->agent->sock, payload, 4, 0) == 4) { if (atomicio(session->agent, payload, 4, 0) == 4) {
if (atomicio(session->agent->sock, buffer_get_rest(request), len, 0) if (atomicio(session->agent, buffer_get_rest(request), len, 0)
!= len) { != len) {
SSH_LOG(session, SSH_LOG_WARN, "atomicio sending request failed: %s", SSH_LOG(session, SSH_LOG_WARN, "atomicio sending request failed: %s",
strerror(errno)); 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 */ /* 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", SSH_LOG(session, SSH_LOG_WARN, "atomicio read response length failed: %s",
strerror(errno)); strerror(errno));
return -1; return -1;
@@ -250,7 +295,7 @@ static int agent_talk(struct ssh_session_struct *session,
if (n > sizeof(payload)) { if (n > sizeof(payload)) {
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, SSH_LOG(session, SSH_LOG_WARN,
"Error reading response from authentication socket."); "Error reading response from authentication socket.");
return -1; return -1;