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;
ssh_buffer ident;
unsigned int count;
ssh_channel channel;
};
#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,
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);

View File

@@ -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);
ssh_channel channel = agent->channel;
socket_t fd;
pfd.fd = fd;
pfd.events = do_read ? POLLIN : POLLOUT;
/* 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) {
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;