From 234706548121cb9acf609b6cce71d53144143968 Mon Sep 17 00:00:00 2001 From: Andreas Schneider Date: Mon, 2 Feb 2009 14:43:48 +0000 Subject: Add missing agent files. git-svn-id: svn+ssh://svn.berlios.de/svnroot/repos/libssh/trunk@202 7dcaeef0-15fb-0310-b436-a5af3365683c --- include/libssh/agent.h | 48 ++++++++ libssh/agent.c | 315 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 363 insertions(+) create mode 100644 include/libssh/agent.h create mode 100644 libssh/agent.c diff --git a/include/libssh/agent.h b/include/libssh/agent.h new file mode 100644 index 0000000..bbe9595 --- /dev/null +++ b/include/libssh/agent.h @@ -0,0 +1,48 @@ +#ifndef __AGENT_H +#define __AGENT_H + +/* Messages for the authentication agent connection. */ +#define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 +#define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 +#define SSH_AGENTC_RSA_CHALLENGE 3 +#define SSH_AGENT_RSA_RESPONSE 4 +#define SSH_AGENT_FAILURE 5 +#define SSH_AGENT_SUCCESS 6 +#define SSH_AGENTC_ADD_RSA_IDENTITY 7 +#define SSH_AGENTC_REMOVE_RSA_IDENTITY 8 +#define SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES 9 + +/* private OpenSSH extensions for SSH2 */ +#define SSH2_AGENTC_REQUEST_IDENTITIES 11 +#define SSH2_AGENT_IDENTITIES_ANSWER 12 +#define SSH2_AGENTC_SIGN_REQUEST 13 +#define SSH2_AGENT_SIGN_RESPONSE 14 +#define SSH2_AGENTC_ADD_IDENTITY 17 +#define SSH2_AGENTC_REMOVE_IDENTITY 18 +#define SSH2_AGENTC_REMOVE_ALL_IDENTITIES 19 + +/* smartcard */ +#define SSH_AGENTC_ADD_SMARTCARD_KEY 20 +#define SSH_AGENTC_REMOVE_SMARTCARD_KEY 21 + +/* lock/unlock the agent */ +#define SSH_AGENTC_LOCK 22 +#define SSH_AGENTC_UNLOCK 23 + +/* add key with constraints */ +#define SSH_AGENTC_ADD_RSA_ID_CONSTRAINED 24 +#define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 +#define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 + +#define SSH_AGENT_CONSTRAIN_LIFETIME 1 +#define SSH_AGENT_CONSTRAIN_CONFIRM 2 + +/* extended failure messages */ +#define SSH2_AGENT_FAILURE 30 + +/* additional error code for ssh.com's ssh-agent2 */ +#define SSH_COM_AGENT2_FAILURE 102 + +#define SSH_AGENT_OLD_SIGNATURE 0x01 + +#endif /* __AGENT_H */ diff --git a/libssh/agent.c b/libssh/agent.c new file mode 100644 index 0000000..0c3ee44 --- /dev/null +++ b/libssh/agent.c @@ -0,0 +1,315 @@ +/* + * agent.c - ssh agent functions + * + * This file is part of the SSH Library + * + * Copyright (c) 2008-2009 by Andreas Schneider + * + * The SSH Library is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 of the License, or (at your + * option) any later version. + * + * The SSH Library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + * License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the SSH Library; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + * MA 02111-1307, USA. + */ + +/* This file is based on authfd.c from OpenSSH */ + +#ifndef _WIN32 + +#include +#include +#include +#include + +#include +#include + +#include "libssh/agent.h" +#include "libssh/priv.h" + +/* macro to check for "agent failure" message */ +#define agent_failed(x) \ + (((x) == SSH_AGENT_FAILURE) || ((x) == SSH_COM_AGENT2_FAILURE) || \ + ((x) == SSH2_AGENT_FAILURE)) + +static u32 agent_get_u32(const void *vp) { + const u8 *p = (const u8 *)vp; + u32 v; + + v = (u32)p[0] << 24; + v |= (u32)p[1] << 16; + v |= (u32)p[2] << 8; + v |= (u32)p[3]; + + return (v); +} + +static void agent_put_u32(void *vp, u32 v) { + u8 *p = (u8 *)vp; + + p[0] = (u8)(v >> 24) & 0xff; + p[1] = (u8)(v >> 16) & 0xff; + p[2] = (u8)(v >> 8) & 0xff; + p[3] = (u8)v & 0xff; +} + +static size_t atomicio(struct socket *s, void *buf, size_t n, int do_read) { + char *b = buf; + size_t pos = 0; + ssize_t res; + struct pollfd pfd; + int fd = ssh_socket_get_fd(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) { + case -1: + /* TODO: set error */ + if (errno == EINTR) { + continue; + } +#ifdef EWOULDBLOCK + if (errno == EAGAIN || errno == EWOULDBLOCK) { +#else + if (errno == EAGAIN) { +#endif + (void) poll(&pfd, 1, -1); + continue; + } + return 0; + case 0: + errno = EPIPE; + return pos; + default: + pos += (size_t) res; + } + } + + return pos; +} + +AGENT *agent_new(struct ssh_session *session) { + AGENT *agent = NULL; + + agent = malloc(sizeof(*agent)); + if (agent) { + agent->count = 0; + agent->sock = ssh_socket_new(session); + } + + return agent; +} + +void agent_close(struct agent_struct *agent) { + if (getenv("SSH_AUTH_SOCK")) { + ssh_socket_close(agent->sock); + } +} + +void agent_free(AGENT *agent) { + if (agent) { + string_free(agent->ident); + if (agent->sock) { + agent_close(agent); + ssh_socket_free(agent->sock); + } + SAFE_FREE(agent); + } +} + +static int agent_connect(SSH_SESSION *session) { + const char *auth_sock = NULL; + + if (session == NULL || session->agent == NULL) { + return -1; + } + + auth_sock = getenv("SSH_AUTH_SOCK"); + + if (auth_sock && *auth_sock) { + if (ssh_socket_unix(session->agent->sock, auth_sock) < 0) { + return -1; + } + return 0; + } + + return -1; +} + +static int agent_decode_reply(int type) { + switch (type) { + case SSH_AGENT_FAILURE: + case SSH2_AGENT_FAILURE: + case SSH_COM_AGENT2_FAILURE: + ssh_say(1, "SSH_AGENT_FAILURE\n"); + return 0; + case SSH_AGENT_SUCCESS: + return 1; + default: + /* TODO: fatal */ + break; + } + + return -1; +} + +static int agent_talk(struct ssh_session *session, + struct buffer_struct *request, struct buffer_struct *reply) { + size_t len = 0; + unsigned char payload[1024] = {0}; + + len = buffer_get_len(request); + ssh_say(2, "agent_talk - len of request: %u\n", len); + agent_put_u32(payload, len); + +#if 0 + /* send length and then the request packet */ + if (ssh_socket_completewrite(session->agent->sock, payload, 4) == SSH_OK) { + buffer_get_data(request, payload, len); + fprintf(stderr, "agent_talk - sending request, payload = %u\n", payload[0]); + if (ssh_socket_completewrite(session->agent->sock, payload, len) + != SSH_OK) { + return -1; + } + } else { + return -1; + } +#endif + /* send length and then the request packet */ + if (atomicio(session->agent->sock, payload, 4, 0) == 4) { + buffer_get_data(request, payload, len); + ssh_say(2, "agent_talk - sending request, payload = %u\n", payload[0]); + if (atomicio(session->agent->sock, payload, len, 0) + != len) { + return -1; + } + } else { + return -1; + } + + session->blocking = 0; + +#if 0 + /* wait for response, read the length of the response packet */ + if (ssh_socket_read(session->agent->sock, payload, 4) != SSH_OK) { + fprintf(stderr, "agent_talk - error: %s\n", ssh_get_error(session)); + return -1; + } +#endif + /* wait for response, read the length of the response packet */ + if (atomicio(session->agent->sock, payload, 4, 1) != 4) { + return -1; + } + + len = agent_get_u32(payload); + if (len > 256 * 1024) { + return -1; + } + ssh_say(2, "agent_talk - response length: %u\n", len); + + while (len > 0) { + size_t n = len; + if (n > sizeof(payload)) { + n = sizeof(payload); + } + if (atomicio(session->agent->sock, payload, n, 1) != n) { + ssh_say(1, "Error reading response from authentication socket."); + return -1; + } + buffer_add_data(reply, payload, n); + len -= n; + } + + return 0; +} + +int agent_ident_count(SSH_SESSION *session) { + BUFFER *request = NULL; + BUFFER *reply = NULL; + unsigned int type = 0; + unsigned int c1 = 0, c2 = 0; + unsigned char buf[4] = {0}; + + switch (session->version) { + case 1: + c1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES; + c2 = SSH_AGENT_RSA_IDENTITIES_ANSWER; + break; + case 2: + c1 = SSH2_AGENTC_REQUEST_IDENTITIES; + c2 = SSH2_AGENT_IDENTITIES_ANSWER; + break; + default: + return 0; + } + + /* send message to the agent requesting the list of identities */ + request = buffer_new(); + buffer_add_u8(request, c1); + + reply = buffer_new(); + + if (agent_talk(session, request, reply) < 0) { + buffer_free(request); + return 0; + } + buffer_free(request); + + /* get message type and verify the answer */ + buffer_get_u8(reply, (u8 *) &type); + ssh_say(2, "agent_ident_count - answer type: %d, expected answer: %d\n", + type, c2); + if (agent_failed(type)) { + return 0; + } else if (type != c2) { + /* TODO: fatal, set ssh error? */ + return -1; + } + + buffer_get_u32(reply, (u32 *) buf); + session->agent->count = agent_get_u32(buf); + ssh_say(2, "agent_ident_count - count: %d\n", session->agent->count); + if (session->agent->count > 1024) { + /* TODO: fatal, set ssh error? */ + return -1; + } + + return session->agent->count; +} + +int agent_running(SSH_SESSION *session) { + if (session == NULL || session->agent == NULL) { + return 0; + } + + if (ssh_socket_is_open(session->agent->sock)) { + return 1; + } else { + if (agent_connect(session) < 0) { + return 0; + } else { + return 1; + } + } + + return 0; +} + +#endif /* _WIN32 */ + -- cgit v1.2.3