17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * Author: Tatu Ylonen <ylo@cs.hut.fi>
37c478bd9Sstevel@tonic-gate * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
47c478bd9Sstevel@tonic-gate * All rights reserved
57c478bd9Sstevel@tonic-gate * Functions for connecting the local authentication agent.
67c478bd9Sstevel@tonic-gate *
77c478bd9Sstevel@tonic-gate * As far as I am concerned, the code I have written for this software
87c478bd9Sstevel@tonic-gate * can be used freely for any purpose. Any derived versions of this
97c478bd9Sstevel@tonic-gate * software must be clearly marked as such, and if the derived work is
107c478bd9Sstevel@tonic-gate * incompatible with the protocol description in the RFC file, it must be
117c478bd9Sstevel@tonic-gate * called by a name other than "ssh" or "Secure Shell".
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * SSH2 implementation,
147c478bd9Sstevel@tonic-gate * Copyright (c) 2000 Markus Friedl. All rights reserved.
157c478bd9Sstevel@tonic-gate *
167c478bd9Sstevel@tonic-gate * Redistribution and use in source and binary forms, with or without
177c478bd9Sstevel@tonic-gate * modification, are permitted provided that the following conditions
187c478bd9Sstevel@tonic-gate * are met:
197c478bd9Sstevel@tonic-gate * 1. Redistributions of source code must retain the above copyright
207c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer.
217c478bd9Sstevel@tonic-gate * 2. Redistributions in binary form must reproduce the above copyright
227c478bd9Sstevel@tonic-gate * notice, this list of conditions and the following disclaimer in the
237c478bd9Sstevel@tonic-gate * documentation and/or other materials provided with the distribution.
247c478bd9Sstevel@tonic-gate *
257c478bd9Sstevel@tonic-gate * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
267c478bd9Sstevel@tonic-gate * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
277c478bd9Sstevel@tonic-gate * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
287c478bd9Sstevel@tonic-gate * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
297c478bd9Sstevel@tonic-gate * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
307c478bd9Sstevel@tonic-gate * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
317c478bd9Sstevel@tonic-gate * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
327c478bd9Sstevel@tonic-gate * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
337c478bd9Sstevel@tonic-gate * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
347c478bd9Sstevel@tonic-gate * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
357c478bd9Sstevel@tonic-gate */
367c478bd9Sstevel@tonic-gate
377c478bd9Sstevel@tonic-gate #include "includes.h"
387c478bd9Sstevel@tonic-gate RCSID("$OpenBSD: authfd.c,v 1.57 2002/09/11 18:27:26 stevesk Exp $");
397c478bd9Sstevel@tonic-gate
407c478bd9Sstevel@tonic-gate #include <openssl/evp.h>
417c478bd9Sstevel@tonic-gate
427c478bd9Sstevel@tonic-gate #include "ssh.h"
437c478bd9Sstevel@tonic-gate #include "rsa.h"
447c478bd9Sstevel@tonic-gate #include "buffer.h"
457c478bd9Sstevel@tonic-gate #include "bufaux.h"
467c478bd9Sstevel@tonic-gate #include "xmalloc.h"
477c478bd9Sstevel@tonic-gate #include "getput.h"
487c478bd9Sstevel@tonic-gate #include "key.h"
497c478bd9Sstevel@tonic-gate #include "authfd.h"
507c478bd9Sstevel@tonic-gate #include "cipher.h"
517c478bd9Sstevel@tonic-gate #include "kex.h"
527c478bd9Sstevel@tonic-gate #include "compat.h"
537c478bd9Sstevel@tonic-gate #include "log.h"
547c478bd9Sstevel@tonic-gate #include "atomicio.h"
557c478bd9Sstevel@tonic-gate
567c478bd9Sstevel@tonic-gate static int agent_present = 0;
577c478bd9Sstevel@tonic-gate
587c478bd9Sstevel@tonic-gate /* helper */
597c478bd9Sstevel@tonic-gate int decode_reply(int type);
607c478bd9Sstevel@tonic-gate
617c478bd9Sstevel@tonic-gate /* macro to check for "agent failure" message */
627c478bd9Sstevel@tonic-gate #define agent_failed(x) \
637c478bd9Sstevel@tonic-gate ((x == SSH_AGENT_FAILURE) || (x == SSH_COM_AGENT2_FAILURE) || \
647c478bd9Sstevel@tonic-gate (x == SSH2_AGENT_FAILURE))
657c478bd9Sstevel@tonic-gate
667c478bd9Sstevel@tonic-gate int
ssh_agent_present(void)677c478bd9Sstevel@tonic-gate ssh_agent_present(void)
687c478bd9Sstevel@tonic-gate {
697c478bd9Sstevel@tonic-gate int authfd;
707c478bd9Sstevel@tonic-gate
717c478bd9Sstevel@tonic-gate if (agent_present)
727c478bd9Sstevel@tonic-gate return 1;
737c478bd9Sstevel@tonic-gate if ((authfd = ssh_get_authentication_socket()) == -1)
747c478bd9Sstevel@tonic-gate return 0;
757c478bd9Sstevel@tonic-gate else {
767c478bd9Sstevel@tonic-gate ssh_close_authentication_socket(authfd);
777c478bd9Sstevel@tonic-gate return 1;
787c478bd9Sstevel@tonic-gate }
797c478bd9Sstevel@tonic-gate }
807c478bd9Sstevel@tonic-gate
817c478bd9Sstevel@tonic-gate /* Returns the number of the authentication fd, or -1 if there is none. */
827c478bd9Sstevel@tonic-gate
837c478bd9Sstevel@tonic-gate int
ssh_get_authentication_socket(void)847c478bd9Sstevel@tonic-gate ssh_get_authentication_socket(void)
857c478bd9Sstevel@tonic-gate {
867c478bd9Sstevel@tonic-gate const char *authsocket;
877c478bd9Sstevel@tonic-gate int sock;
887c478bd9Sstevel@tonic-gate struct sockaddr_un sunaddr;
897c478bd9Sstevel@tonic-gate
907c478bd9Sstevel@tonic-gate authsocket = getenv(SSH_AUTHSOCKET_ENV_NAME);
917c478bd9Sstevel@tonic-gate if (!authsocket)
927c478bd9Sstevel@tonic-gate return -1;
937c478bd9Sstevel@tonic-gate
947c478bd9Sstevel@tonic-gate sunaddr.sun_family = AF_UNIX;
957c478bd9Sstevel@tonic-gate strlcpy(sunaddr.sun_path, authsocket, sizeof(sunaddr.sun_path));
967c478bd9Sstevel@tonic-gate
977c478bd9Sstevel@tonic-gate sock = socket(AF_UNIX, SOCK_STREAM, 0);
987c478bd9Sstevel@tonic-gate if (sock < 0)
997c478bd9Sstevel@tonic-gate return -1;
1007c478bd9Sstevel@tonic-gate
1017c478bd9Sstevel@tonic-gate /* close on exec */
102*b9aa66a7SJan Pechanec if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) {
1037c478bd9Sstevel@tonic-gate close(sock);
1047c478bd9Sstevel@tonic-gate return -1;
1057c478bd9Sstevel@tonic-gate }
1067c478bd9Sstevel@tonic-gate if (connect(sock, (struct sockaddr *) &sunaddr, sizeof sunaddr) < 0) {
1077c478bd9Sstevel@tonic-gate close(sock);
1087c478bd9Sstevel@tonic-gate return -1;
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate agent_present = 1;
1117c478bd9Sstevel@tonic-gate return sock;
1127c478bd9Sstevel@tonic-gate }
1137c478bd9Sstevel@tonic-gate
1147c478bd9Sstevel@tonic-gate static int
ssh_request_reply(AuthenticationConnection * auth,Buffer * request,Buffer * reply)1157c478bd9Sstevel@tonic-gate ssh_request_reply(AuthenticationConnection *auth, Buffer *request, Buffer *reply)
1167c478bd9Sstevel@tonic-gate {
1177c478bd9Sstevel@tonic-gate int l, len;
1187c478bd9Sstevel@tonic-gate char buf[1024];
1197c478bd9Sstevel@tonic-gate
1207c478bd9Sstevel@tonic-gate /* Get the length of the message, and format it in the buffer. */
1217c478bd9Sstevel@tonic-gate len = buffer_len(request);
1227c478bd9Sstevel@tonic-gate PUT_32BIT(buf, len);
1237c478bd9Sstevel@tonic-gate
1247c478bd9Sstevel@tonic-gate /* Send the length and then the packet to the agent. */
1257c478bd9Sstevel@tonic-gate if (atomicio(write, auth->fd, buf, 4) != 4 ||
1267c478bd9Sstevel@tonic-gate atomicio(write, auth->fd, buffer_ptr(request),
1277c478bd9Sstevel@tonic-gate buffer_len(request)) != buffer_len(request)) {
1287c478bd9Sstevel@tonic-gate error("Error writing to authentication socket.");
1297c478bd9Sstevel@tonic-gate return 0;
1307c478bd9Sstevel@tonic-gate }
1317c478bd9Sstevel@tonic-gate /*
1327c478bd9Sstevel@tonic-gate * Wait for response from the agent. First read the length of the
1337c478bd9Sstevel@tonic-gate * response packet.
1347c478bd9Sstevel@tonic-gate */
1357c478bd9Sstevel@tonic-gate len = 4;
1367c478bd9Sstevel@tonic-gate while (len > 0) {
1377c478bd9Sstevel@tonic-gate l = read(auth->fd, buf + 4 - len, len);
1387c478bd9Sstevel@tonic-gate if (l == -1 && (errno == EAGAIN || errno == EINTR))
1397c478bd9Sstevel@tonic-gate continue;
1407c478bd9Sstevel@tonic-gate if (l <= 0) {
1417c478bd9Sstevel@tonic-gate error("Error reading response length from authentication socket.");
1427c478bd9Sstevel@tonic-gate return 0;
1437c478bd9Sstevel@tonic-gate }
1447c478bd9Sstevel@tonic-gate len -= l;
1457c478bd9Sstevel@tonic-gate }
1467c478bd9Sstevel@tonic-gate
1477c478bd9Sstevel@tonic-gate /* Extract the length, and check it for sanity. */
1487c478bd9Sstevel@tonic-gate len = GET_32BIT(buf);
1497c478bd9Sstevel@tonic-gate if (len > 256 * 1024)
1507c478bd9Sstevel@tonic-gate fatal("Authentication response too long: %d", len);
1517c478bd9Sstevel@tonic-gate
1527c478bd9Sstevel@tonic-gate /* Read the rest of the response in to the buffer. */
1537c478bd9Sstevel@tonic-gate buffer_clear(reply);
1547c478bd9Sstevel@tonic-gate while (len > 0) {
1557c478bd9Sstevel@tonic-gate l = len;
1567c478bd9Sstevel@tonic-gate if (l > sizeof(buf))
1577c478bd9Sstevel@tonic-gate l = sizeof(buf);
1587c478bd9Sstevel@tonic-gate l = read(auth->fd, buf, l);
1597c478bd9Sstevel@tonic-gate if (l == -1 && (errno == EAGAIN || errno == EINTR))
1607c478bd9Sstevel@tonic-gate continue;
1617c478bd9Sstevel@tonic-gate if (l <= 0) {
1627c478bd9Sstevel@tonic-gate error("Error reading response from authentication socket.");
1637c478bd9Sstevel@tonic-gate return 0;
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate buffer_append(reply, buf, l);
1667c478bd9Sstevel@tonic-gate len -= l;
1677c478bd9Sstevel@tonic-gate }
1687c478bd9Sstevel@tonic-gate return 1;
1697c478bd9Sstevel@tonic-gate }
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate /*
1727c478bd9Sstevel@tonic-gate * Closes the agent socket if it should be closed (depends on how it was
1737c478bd9Sstevel@tonic-gate * obtained). The argument must have been returned by
1747c478bd9Sstevel@tonic-gate * ssh_get_authentication_socket().
1757c478bd9Sstevel@tonic-gate */
1767c478bd9Sstevel@tonic-gate
1777c478bd9Sstevel@tonic-gate void
ssh_close_authentication_socket(int sock)1787c478bd9Sstevel@tonic-gate ssh_close_authentication_socket(int sock)
1797c478bd9Sstevel@tonic-gate {
1807c478bd9Sstevel@tonic-gate if (getenv(SSH_AUTHSOCKET_ENV_NAME))
1817c478bd9Sstevel@tonic-gate close(sock);
1827c478bd9Sstevel@tonic-gate }
1837c478bd9Sstevel@tonic-gate
1847c478bd9Sstevel@tonic-gate /*
1857c478bd9Sstevel@tonic-gate * Opens and connects a private socket for communication with the
1867c478bd9Sstevel@tonic-gate * authentication agent. Returns the file descriptor (which must be
1877c478bd9Sstevel@tonic-gate * shut down and closed by the caller when no longer needed).
1887c478bd9Sstevel@tonic-gate * Returns NULL if an error occurred and the connection could not be
1897c478bd9Sstevel@tonic-gate * opened.
1907c478bd9Sstevel@tonic-gate */
1917c478bd9Sstevel@tonic-gate
1927c478bd9Sstevel@tonic-gate AuthenticationConnection *
ssh_get_authentication_connection(void)1937c478bd9Sstevel@tonic-gate ssh_get_authentication_connection(void)
1947c478bd9Sstevel@tonic-gate {
1957c478bd9Sstevel@tonic-gate AuthenticationConnection *auth;
1967c478bd9Sstevel@tonic-gate int sock;
1977c478bd9Sstevel@tonic-gate
1987c478bd9Sstevel@tonic-gate sock = ssh_get_authentication_socket();
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate /*
2017c478bd9Sstevel@tonic-gate * Fail if we couldn't obtain a connection. This happens if we
2027c478bd9Sstevel@tonic-gate * exited due to a timeout.
2037c478bd9Sstevel@tonic-gate */
2047c478bd9Sstevel@tonic-gate if (sock < 0)
2057c478bd9Sstevel@tonic-gate return NULL;
2067c478bd9Sstevel@tonic-gate
2077c478bd9Sstevel@tonic-gate auth = xmalloc(sizeof(*auth));
2087c478bd9Sstevel@tonic-gate auth->fd = sock;
2097c478bd9Sstevel@tonic-gate buffer_init(&auth->identities);
2107c478bd9Sstevel@tonic-gate auth->howmany = 0;
2117c478bd9Sstevel@tonic-gate
2127c478bd9Sstevel@tonic-gate return auth;
2137c478bd9Sstevel@tonic-gate }
2147c478bd9Sstevel@tonic-gate
2157c478bd9Sstevel@tonic-gate /*
2167c478bd9Sstevel@tonic-gate * Closes the connection to the authentication agent and frees any associated
2177c478bd9Sstevel@tonic-gate * memory.
2187c478bd9Sstevel@tonic-gate */
2197c478bd9Sstevel@tonic-gate
2207c478bd9Sstevel@tonic-gate void
ssh_close_authentication_connection(AuthenticationConnection * auth)2217c478bd9Sstevel@tonic-gate ssh_close_authentication_connection(AuthenticationConnection *auth)
2227c478bd9Sstevel@tonic-gate {
2237c478bd9Sstevel@tonic-gate buffer_free(&auth->identities);
2247c478bd9Sstevel@tonic-gate close(auth->fd);
2257c478bd9Sstevel@tonic-gate xfree(auth);
2267c478bd9Sstevel@tonic-gate }
2277c478bd9Sstevel@tonic-gate
2287c478bd9Sstevel@tonic-gate /* Lock/unlock agent */
2297c478bd9Sstevel@tonic-gate int
ssh_lock_agent(AuthenticationConnection * auth,int lock,const char * password)2307c478bd9Sstevel@tonic-gate ssh_lock_agent(AuthenticationConnection *auth, int lock, const char *password)
2317c478bd9Sstevel@tonic-gate {
2327c478bd9Sstevel@tonic-gate int type;
2337c478bd9Sstevel@tonic-gate Buffer msg;
2347c478bd9Sstevel@tonic-gate
2357c478bd9Sstevel@tonic-gate buffer_init(&msg);
2367c478bd9Sstevel@tonic-gate buffer_put_char(&msg, lock ? SSH_AGENTC_LOCK : SSH_AGENTC_UNLOCK);
2377c478bd9Sstevel@tonic-gate buffer_put_cstring(&msg, password);
2387c478bd9Sstevel@tonic-gate
2397c478bd9Sstevel@tonic-gate if (ssh_request_reply(auth, &msg, &msg) == 0) {
2407c478bd9Sstevel@tonic-gate buffer_free(&msg);
2417c478bd9Sstevel@tonic-gate return 0;
2427c478bd9Sstevel@tonic-gate }
2437c478bd9Sstevel@tonic-gate type = buffer_get_char(&msg);
2447c478bd9Sstevel@tonic-gate buffer_free(&msg);
2457c478bd9Sstevel@tonic-gate return decode_reply(type);
2467c478bd9Sstevel@tonic-gate }
2477c478bd9Sstevel@tonic-gate
2487c478bd9Sstevel@tonic-gate /*
2497c478bd9Sstevel@tonic-gate * Returns the first authentication identity held by the agent.
2507c478bd9Sstevel@tonic-gate */
2517c478bd9Sstevel@tonic-gate
2527c478bd9Sstevel@tonic-gate int
ssh_get_num_identities(AuthenticationConnection * auth,int version)2537c478bd9Sstevel@tonic-gate ssh_get_num_identities(AuthenticationConnection *auth, int version)
2547c478bd9Sstevel@tonic-gate {
2557c478bd9Sstevel@tonic-gate int type, code1 = 0, code2 = 0;
2567c478bd9Sstevel@tonic-gate Buffer request;
2577c478bd9Sstevel@tonic-gate
2587c478bd9Sstevel@tonic-gate switch (version) {
2597c478bd9Sstevel@tonic-gate case 1:
2607c478bd9Sstevel@tonic-gate code1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES;
2617c478bd9Sstevel@tonic-gate code2 = SSH_AGENT_RSA_IDENTITIES_ANSWER;
2627c478bd9Sstevel@tonic-gate break;
2637c478bd9Sstevel@tonic-gate case 2:
2647c478bd9Sstevel@tonic-gate code1 = SSH2_AGENTC_REQUEST_IDENTITIES;
2657c478bd9Sstevel@tonic-gate code2 = SSH2_AGENT_IDENTITIES_ANSWER;
2667c478bd9Sstevel@tonic-gate break;
2677c478bd9Sstevel@tonic-gate default:
2687c478bd9Sstevel@tonic-gate return 0;
2697c478bd9Sstevel@tonic-gate }
2707c478bd9Sstevel@tonic-gate
2717c478bd9Sstevel@tonic-gate /*
2727c478bd9Sstevel@tonic-gate * Send a message to the agent requesting for a list of the
2737c478bd9Sstevel@tonic-gate * identities it can represent.
2747c478bd9Sstevel@tonic-gate */
2757c478bd9Sstevel@tonic-gate buffer_init(&request);
2767c478bd9Sstevel@tonic-gate buffer_put_char(&request, code1);
2777c478bd9Sstevel@tonic-gate
2787c478bd9Sstevel@tonic-gate buffer_clear(&auth->identities);
2797c478bd9Sstevel@tonic-gate if (ssh_request_reply(auth, &request, &auth->identities) == 0) {
2807c478bd9Sstevel@tonic-gate buffer_free(&request);
2817c478bd9Sstevel@tonic-gate return 0;
2827c478bd9Sstevel@tonic-gate }
2837c478bd9Sstevel@tonic-gate buffer_free(&request);
2847c478bd9Sstevel@tonic-gate
2857c478bd9Sstevel@tonic-gate /* Get message type, and verify that we got a proper answer. */
2867c478bd9Sstevel@tonic-gate type = buffer_get_char(&auth->identities);
2877c478bd9Sstevel@tonic-gate if (agent_failed(type)) {
2887c478bd9Sstevel@tonic-gate return 0;
2897c478bd9Sstevel@tonic-gate } else if (type != code2) {
2907c478bd9Sstevel@tonic-gate fatal("Bad authentication reply message type: %d", type);
2917c478bd9Sstevel@tonic-gate }
2927c478bd9Sstevel@tonic-gate
2937c478bd9Sstevel@tonic-gate /* Get the number of entries in the response and check it for sanity. */
2947c478bd9Sstevel@tonic-gate auth->howmany = buffer_get_int(&auth->identities);
2957c478bd9Sstevel@tonic-gate if (auth->howmany > 1024)
2967c478bd9Sstevel@tonic-gate fatal("Too many identities in authentication reply: %d",
2977c478bd9Sstevel@tonic-gate auth->howmany);
2987c478bd9Sstevel@tonic-gate
2997c478bd9Sstevel@tonic-gate return auth->howmany;
3007c478bd9Sstevel@tonic-gate }
3017c478bd9Sstevel@tonic-gate
3027c478bd9Sstevel@tonic-gate Key *
ssh_get_first_identity(AuthenticationConnection * auth,char ** comment,int version)3037c478bd9Sstevel@tonic-gate ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version)
3047c478bd9Sstevel@tonic-gate {
3057c478bd9Sstevel@tonic-gate /* get number of identities and return the first entry (if any). */
3067c478bd9Sstevel@tonic-gate if (ssh_get_num_identities(auth, version) > 0)
3077c478bd9Sstevel@tonic-gate return ssh_get_next_identity(auth, comment, version);
3087c478bd9Sstevel@tonic-gate return NULL;
3097c478bd9Sstevel@tonic-gate }
3107c478bd9Sstevel@tonic-gate
3117c478bd9Sstevel@tonic-gate Key *
ssh_get_next_identity(AuthenticationConnection * auth,char ** comment,int version)3127c478bd9Sstevel@tonic-gate ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version)
3137c478bd9Sstevel@tonic-gate {
3147c478bd9Sstevel@tonic-gate u_int bits;
3157c478bd9Sstevel@tonic-gate u_char *blob;
3167c478bd9Sstevel@tonic-gate u_int blen;
3177c478bd9Sstevel@tonic-gate Key *key = NULL;
3187c478bd9Sstevel@tonic-gate
3197c478bd9Sstevel@tonic-gate /* Return failure if no more entries. */
3207c478bd9Sstevel@tonic-gate if (auth->howmany <= 0)
3217c478bd9Sstevel@tonic-gate return NULL;
3227c478bd9Sstevel@tonic-gate
3237c478bd9Sstevel@tonic-gate /*
3247c478bd9Sstevel@tonic-gate * Get the next entry from the packet. These will abort with a fatal
3257c478bd9Sstevel@tonic-gate * error if the packet is too short or contains corrupt data.
3267c478bd9Sstevel@tonic-gate */
3277c478bd9Sstevel@tonic-gate switch (version) {
3287c478bd9Sstevel@tonic-gate case 1:
3297c478bd9Sstevel@tonic-gate key = key_new(KEY_RSA1);
3307c478bd9Sstevel@tonic-gate bits = buffer_get_int(&auth->identities);
3317c478bd9Sstevel@tonic-gate buffer_get_bignum(&auth->identities, key->rsa->e);
3327c478bd9Sstevel@tonic-gate buffer_get_bignum(&auth->identities, key->rsa->n);
3337c478bd9Sstevel@tonic-gate *comment = buffer_get_string(&auth->identities, NULL);
3347c478bd9Sstevel@tonic-gate if (bits != BN_num_bits(key->rsa->n))
3357c478bd9Sstevel@tonic-gate log("Warning: identity keysize mismatch: actual %d, announced %u",
3367c478bd9Sstevel@tonic-gate BN_num_bits(key->rsa->n), bits);
3377c478bd9Sstevel@tonic-gate break;
3387c478bd9Sstevel@tonic-gate case 2:
3397c478bd9Sstevel@tonic-gate blob = buffer_get_string(&auth->identities, &blen);
3407c478bd9Sstevel@tonic-gate *comment = buffer_get_string(&auth->identities, NULL);
3417c478bd9Sstevel@tonic-gate key = key_from_blob(blob, blen);
3427c478bd9Sstevel@tonic-gate xfree(blob);
3437c478bd9Sstevel@tonic-gate break;
3447c478bd9Sstevel@tonic-gate default:
3457c478bd9Sstevel@tonic-gate return NULL;
3467c478bd9Sstevel@tonic-gate break;
3477c478bd9Sstevel@tonic-gate }
3487c478bd9Sstevel@tonic-gate /* Decrement the number of remaining entries. */
3497c478bd9Sstevel@tonic-gate auth->howmany--;
3507c478bd9Sstevel@tonic-gate return key;
3517c478bd9Sstevel@tonic-gate }
3527c478bd9Sstevel@tonic-gate
3537c478bd9Sstevel@tonic-gate /*
3547c478bd9Sstevel@tonic-gate * Generates a random challenge, sends it to the agent, and waits for
3557c478bd9Sstevel@tonic-gate * response from the agent. Returns true (non-zero) if the agent gave the
3567c478bd9Sstevel@tonic-gate * correct answer, zero otherwise. Response type selects the style of
3577c478bd9Sstevel@tonic-gate * response desired, with 0 corresponding to protocol version 1.0 (no longer
3587c478bd9Sstevel@tonic-gate * supported) and 1 corresponding to protocol version 1.1.
3597c478bd9Sstevel@tonic-gate */
3607c478bd9Sstevel@tonic-gate
3617c478bd9Sstevel@tonic-gate int
ssh_decrypt_challenge(AuthenticationConnection * auth,Key * key,BIGNUM * challenge,u_char session_id[16],u_int response_type,u_char response[16])3627c478bd9Sstevel@tonic-gate ssh_decrypt_challenge(AuthenticationConnection *auth,
3637c478bd9Sstevel@tonic-gate Key* key, BIGNUM *challenge,
3647c478bd9Sstevel@tonic-gate u_char session_id[16],
3657c478bd9Sstevel@tonic-gate u_int response_type,
3667c478bd9Sstevel@tonic-gate u_char response[16])
3677c478bd9Sstevel@tonic-gate {
3687c478bd9Sstevel@tonic-gate Buffer buffer;
3697c478bd9Sstevel@tonic-gate int success = 0;
3707c478bd9Sstevel@tonic-gate int i;
3717c478bd9Sstevel@tonic-gate int type;
3727c478bd9Sstevel@tonic-gate
3737c478bd9Sstevel@tonic-gate if (key->type != KEY_RSA1)
3747c478bd9Sstevel@tonic-gate return 0;
3757c478bd9Sstevel@tonic-gate if (response_type == 0) {
3767c478bd9Sstevel@tonic-gate log("Compatibility with ssh protocol version 1.0 no longer supported.");
3777c478bd9Sstevel@tonic-gate return 0;
3787c478bd9Sstevel@tonic-gate }
3797c478bd9Sstevel@tonic-gate buffer_init(&buffer);
3807c478bd9Sstevel@tonic-gate buffer_put_char(&buffer, SSH_AGENTC_RSA_CHALLENGE);
3817c478bd9Sstevel@tonic-gate buffer_put_int(&buffer, BN_num_bits(key->rsa->n));
3827c478bd9Sstevel@tonic-gate buffer_put_bignum(&buffer, key->rsa->e);
3837c478bd9Sstevel@tonic-gate buffer_put_bignum(&buffer, key->rsa->n);
3847c478bd9Sstevel@tonic-gate buffer_put_bignum(&buffer, challenge);
3857c478bd9Sstevel@tonic-gate buffer_append(&buffer, session_id, 16);
3867c478bd9Sstevel@tonic-gate buffer_put_int(&buffer, response_type);
3877c478bd9Sstevel@tonic-gate
3887c478bd9Sstevel@tonic-gate if (ssh_request_reply(auth, &buffer, &buffer) == 0) {
3897c478bd9Sstevel@tonic-gate buffer_free(&buffer);
3907c478bd9Sstevel@tonic-gate return 0;
3917c478bd9Sstevel@tonic-gate }
3927c478bd9Sstevel@tonic-gate type = buffer_get_char(&buffer);
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gate if (agent_failed(type)) {
3957c478bd9Sstevel@tonic-gate log("Agent admitted failure to authenticate using the key.");
3967c478bd9Sstevel@tonic-gate } else if (type != SSH_AGENT_RSA_RESPONSE) {
3977c478bd9Sstevel@tonic-gate fatal("Bad authentication response: %d", type);
3987c478bd9Sstevel@tonic-gate } else {
3997c478bd9Sstevel@tonic-gate success = 1;
4007c478bd9Sstevel@tonic-gate /*
4017c478bd9Sstevel@tonic-gate * Get the response from the packet. This will abort with a
4027c478bd9Sstevel@tonic-gate * fatal error if the packet is corrupt.
4037c478bd9Sstevel@tonic-gate */
4047c478bd9Sstevel@tonic-gate for (i = 0; i < 16; i++)
4057c478bd9Sstevel@tonic-gate response[i] = buffer_get_char(&buffer);
4067c478bd9Sstevel@tonic-gate }
4077c478bd9Sstevel@tonic-gate buffer_free(&buffer);
4087c478bd9Sstevel@tonic-gate return success;
4097c478bd9Sstevel@tonic-gate }
4107c478bd9Sstevel@tonic-gate
4117c478bd9Sstevel@tonic-gate /* ask agent to sign data, returns -1 on error, 0 on success */
4127c478bd9Sstevel@tonic-gate int
ssh_agent_sign(AuthenticationConnection * auth,Key * key,u_char ** sigp,u_int * lenp,u_char * data,u_int datalen)4137c478bd9Sstevel@tonic-gate ssh_agent_sign(AuthenticationConnection *auth,
4147c478bd9Sstevel@tonic-gate Key *key,
4157c478bd9Sstevel@tonic-gate u_char **sigp, u_int *lenp,
4167c478bd9Sstevel@tonic-gate u_char *data, u_int datalen)
4177c478bd9Sstevel@tonic-gate {
4187c478bd9Sstevel@tonic-gate Buffer msg;
4197c478bd9Sstevel@tonic-gate u_char *blob;
4207c478bd9Sstevel@tonic-gate u_int blen;
4217c478bd9Sstevel@tonic-gate int type, flags = 0;
4227c478bd9Sstevel@tonic-gate int ret = -1;
4237c478bd9Sstevel@tonic-gate
4247c478bd9Sstevel@tonic-gate if (key_to_blob(key, &blob, &blen) == 0)
4257c478bd9Sstevel@tonic-gate return -1;
4267c478bd9Sstevel@tonic-gate
4277c478bd9Sstevel@tonic-gate if (datafellows & SSH_BUG_SIGBLOB)
4287c478bd9Sstevel@tonic-gate flags = SSH_AGENT_OLD_SIGNATURE;
4297c478bd9Sstevel@tonic-gate
4307c478bd9Sstevel@tonic-gate buffer_init(&msg);
4317c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
4327c478bd9Sstevel@tonic-gate buffer_put_string(&msg, blob, blen);
4337c478bd9Sstevel@tonic-gate buffer_put_string(&msg, data, datalen);
4347c478bd9Sstevel@tonic-gate buffer_put_int(&msg, flags);
4357c478bd9Sstevel@tonic-gate xfree(blob);
4367c478bd9Sstevel@tonic-gate
4377c478bd9Sstevel@tonic-gate if (ssh_request_reply(auth, &msg, &msg) == 0) {
4387c478bd9Sstevel@tonic-gate buffer_free(&msg);
4397c478bd9Sstevel@tonic-gate return -1;
4407c478bd9Sstevel@tonic-gate }
4417c478bd9Sstevel@tonic-gate type = buffer_get_char(&msg);
4427c478bd9Sstevel@tonic-gate if (agent_failed(type)) {
4437c478bd9Sstevel@tonic-gate log("Agent admitted failure to sign using the key.");
4447c478bd9Sstevel@tonic-gate } else if (type != SSH2_AGENT_SIGN_RESPONSE) {
4457c478bd9Sstevel@tonic-gate fatal("Bad authentication response: %d", type);
4467c478bd9Sstevel@tonic-gate } else {
4477c478bd9Sstevel@tonic-gate ret = 0;
4487c478bd9Sstevel@tonic-gate *sigp = buffer_get_string(&msg, lenp);
4497c478bd9Sstevel@tonic-gate }
4507c478bd9Sstevel@tonic-gate buffer_free(&msg);
4517c478bd9Sstevel@tonic-gate return ret;
4527c478bd9Sstevel@tonic-gate }
4537c478bd9Sstevel@tonic-gate
4547c478bd9Sstevel@tonic-gate /* Encode key for a message to the agent. */
4557c478bd9Sstevel@tonic-gate
4567c478bd9Sstevel@tonic-gate static void
ssh_encode_identity_rsa1(Buffer * b,RSA * key,const char * comment)4577c478bd9Sstevel@tonic-gate ssh_encode_identity_rsa1(Buffer *b, RSA *key, const char *comment)
4587c478bd9Sstevel@tonic-gate {
4597c478bd9Sstevel@tonic-gate buffer_put_int(b, BN_num_bits(key->n));
4607c478bd9Sstevel@tonic-gate buffer_put_bignum(b, key->n);
4617c478bd9Sstevel@tonic-gate buffer_put_bignum(b, key->e);
4627c478bd9Sstevel@tonic-gate buffer_put_bignum(b, key->d);
4637c478bd9Sstevel@tonic-gate /* To keep within the protocol: p < q for ssh. in SSL p > q */
4647c478bd9Sstevel@tonic-gate buffer_put_bignum(b, key->iqmp); /* ssh key->u */
4657c478bd9Sstevel@tonic-gate buffer_put_bignum(b, key->q); /* ssh key->p, SSL key->q */
4667c478bd9Sstevel@tonic-gate buffer_put_bignum(b, key->p); /* ssh key->q, SSL key->p */
4677c478bd9Sstevel@tonic-gate buffer_put_cstring(b, comment);
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate
4707c478bd9Sstevel@tonic-gate static void
ssh_encode_identity_ssh2(Buffer * b,Key * key,const char * comment)4717c478bd9Sstevel@tonic-gate ssh_encode_identity_ssh2(Buffer *b, Key *key, const char *comment)
4727c478bd9Sstevel@tonic-gate {
4737c478bd9Sstevel@tonic-gate buffer_put_cstring(b, key_ssh_name(key));
4747c478bd9Sstevel@tonic-gate switch (key->type) {
4757c478bd9Sstevel@tonic-gate case KEY_RSA:
4767c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->rsa->n);
4777c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->rsa->e);
4787c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->rsa->d);
4797c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->rsa->iqmp);
4807c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->rsa->p);
4817c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->rsa->q);
4827c478bd9Sstevel@tonic-gate break;
4837c478bd9Sstevel@tonic-gate case KEY_DSA:
4847c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->dsa->p);
4857c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->dsa->q);
4867c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->dsa->g);
4877c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->dsa->pub_key);
4887c478bd9Sstevel@tonic-gate buffer_put_bignum2(b, key->dsa->priv_key);
4897c478bd9Sstevel@tonic-gate break;
4907c478bd9Sstevel@tonic-gate }
4917c478bd9Sstevel@tonic-gate buffer_put_cstring(b, comment);
4927c478bd9Sstevel@tonic-gate }
4937c478bd9Sstevel@tonic-gate
4947c478bd9Sstevel@tonic-gate /*
4957c478bd9Sstevel@tonic-gate * Adds an identity to the authentication server. This call is not meant to
4967c478bd9Sstevel@tonic-gate * be used by normal applications.
4977c478bd9Sstevel@tonic-gate */
4987c478bd9Sstevel@tonic-gate
4997c478bd9Sstevel@tonic-gate int
ssh_add_identity_constrained(AuthenticationConnection * auth,Key * key,const char * comment,u_int life)5007c478bd9Sstevel@tonic-gate ssh_add_identity_constrained(AuthenticationConnection *auth, Key *key,
5017c478bd9Sstevel@tonic-gate const char *comment, u_int life)
5027c478bd9Sstevel@tonic-gate {
5037c478bd9Sstevel@tonic-gate Buffer msg;
5047c478bd9Sstevel@tonic-gate int type, constrained = (life != 0);
5057c478bd9Sstevel@tonic-gate
5067c478bd9Sstevel@tonic-gate buffer_init(&msg);
5077c478bd9Sstevel@tonic-gate
5087c478bd9Sstevel@tonic-gate switch (key->type) {
5097c478bd9Sstevel@tonic-gate case KEY_RSA1:
5107c478bd9Sstevel@tonic-gate type = constrained ?
5117c478bd9Sstevel@tonic-gate SSH_AGENTC_ADD_RSA_ID_CONSTRAINED :
5127c478bd9Sstevel@tonic-gate SSH_AGENTC_ADD_RSA_IDENTITY;
5137c478bd9Sstevel@tonic-gate buffer_put_char(&msg, type);
5147c478bd9Sstevel@tonic-gate ssh_encode_identity_rsa1(&msg, key->rsa, comment);
5157c478bd9Sstevel@tonic-gate break;
5167c478bd9Sstevel@tonic-gate case KEY_RSA:
5177c478bd9Sstevel@tonic-gate case KEY_DSA:
5187c478bd9Sstevel@tonic-gate type = constrained ?
5197c478bd9Sstevel@tonic-gate SSH2_AGENTC_ADD_ID_CONSTRAINED :
5207c478bd9Sstevel@tonic-gate SSH2_AGENTC_ADD_IDENTITY;
5217c478bd9Sstevel@tonic-gate buffer_put_char(&msg, type);
5227c478bd9Sstevel@tonic-gate ssh_encode_identity_ssh2(&msg, key, comment);
5237c478bd9Sstevel@tonic-gate break;
5247c478bd9Sstevel@tonic-gate default:
5257c478bd9Sstevel@tonic-gate buffer_free(&msg);
5267c478bd9Sstevel@tonic-gate return 0;
5277c478bd9Sstevel@tonic-gate break;
5287c478bd9Sstevel@tonic-gate }
5297c478bd9Sstevel@tonic-gate if (constrained) {
5307c478bd9Sstevel@tonic-gate if (life != 0) {
5317c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH_AGENT_CONSTRAIN_LIFETIME);
5327c478bd9Sstevel@tonic-gate buffer_put_int(&msg, life);
5337c478bd9Sstevel@tonic-gate }
5347c478bd9Sstevel@tonic-gate }
5357c478bd9Sstevel@tonic-gate if (ssh_request_reply(auth, &msg, &msg) == 0) {
5367c478bd9Sstevel@tonic-gate buffer_free(&msg);
5377c478bd9Sstevel@tonic-gate return 0;
5387c478bd9Sstevel@tonic-gate }
5397c478bd9Sstevel@tonic-gate type = buffer_get_char(&msg);
5407c478bd9Sstevel@tonic-gate buffer_free(&msg);
5417c478bd9Sstevel@tonic-gate return decode_reply(type);
5427c478bd9Sstevel@tonic-gate }
5437c478bd9Sstevel@tonic-gate
5447c478bd9Sstevel@tonic-gate int
ssh_add_identity(AuthenticationConnection * auth,Key * key,const char * comment)5457c478bd9Sstevel@tonic-gate ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment)
5467c478bd9Sstevel@tonic-gate {
5477c478bd9Sstevel@tonic-gate return ssh_add_identity_constrained(auth, key, comment, 0);
5487c478bd9Sstevel@tonic-gate }
5497c478bd9Sstevel@tonic-gate
5507c478bd9Sstevel@tonic-gate /*
5517c478bd9Sstevel@tonic-gate * Removes an identity from the authentication server. This call is not
5527c478bd9Sstevel@tonic-gate * meant to be used by normal applications.
5537c478bd9Sstevel@tonic-gate */
5547c478bd9Sstevel@tonic-gate
5557c478bd9Sstevel@tonic-gate int
ssh_remove_identity(AuthenticationConnection * auth,Key * key)5567c478bd9Sstevel@tonic-gate ssh_remove_identity(AuthenticationConnection *auth, Key *key)
5577c478bd9Sstevel@tonic-gate {
5587c478bd9Sstevel@tonic-gate Buffer msg;
5597c478bd9Sstevel@tonic-gate int type;
5607c478bd9Sstevel@tonic-gate u_char *blob;
5617c478bd9Sstevel@tonic-gate u_int blen;
5627c478bd9Sstevel@tonic-gate
5637c478bd9Sstevel@tonic-gate buffer_init(&msg);
5647c478bd9Sstevel@tonic-gate
5657c478bd9Sstevel@tonic-gate if (key->type == KEY_RSA1) {
5667c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH_AGENTC_REMOVE_RSA_IDENTITY);
5677c478bd9Sstevel@tonic-gate buffer_put_int(&msg, BN_num_bits(key->rsa->n));
5687c478bd9Sstevel@tonic-gate buffer_put_bignum(&msg, key->rsa->e);
5697c478bd9Sstevel@tonic-gate buffer_put_bignum(&msg, key->rsa->n);
5707c478bd9Sstevel@tonic-gate } else if (key->type == KEY_DSA || key->type == KEY_RSA) {
5717c478bd9Sstevel@tonic-gate key_to_blob(key, &blob, &blen);
5727c478bd9Sstevel@tonic-gate buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY);
5737c478bd9Sstevel@tonic-gate buffer_put_string(&msg, blob, blen);
5747c478bd9Sstevel@tonic-gate xfree(blob);
5757c478bd9Sstevel@tonic-gate } else {
5767c478bd9Sstevel@tonic-gate buffer_free(&msg);
5777c478bd9Sstevel@tonic-gate return 0;
5787c478bd9Sstevel@tonic-gate }
5797c478bd9Sstevel@tonic-gate if (ssh_request_reply(auth, &msg, &msg) == 0) {
5807c478bd9Sstevel@tonic-gate buffer_free(&msg);
5817c478bd9Sstevel@tonic-gate return 0;
5827c478bd9Sstevel@tonic-gate }
5837c478bd9Sstevel@tonic-gate type = buffer_get_char(&msg);
5847c478bd9Sstevel@tonic-gate buffer_free(&msg);
5857c478bd9Sstevel@tonic-gate return decode_reply(type);
5867c478bd9Sstevel@tonic-gate }
5877c478bd9Sstevel@tonic-gate
5887c478bd9Sstevel@tonic-gate
5897c478bd9Sstevel@tonic-gate /*
5907c478bd9Sstevel@tonic-gate * Removes all identities from the agent. This call is not meant to be used
5917c478bd9Sstevel@tonic-gate * by normal applications.
5927c478bd9Sstevel@tonic-gate */
5937c478bd9Sstevel@tonic-gate
5947c478bd9Sstevel@tonic-gate int
ssh_remove_all_identities(AuthenticationConnection * auth,int version)5957c478bd9Sstevel@tonic-gate ssh_remove_all_identities(AuthenticationConnection *auth, int version)
5967c478bd9Sstevel@tonic-gate {
5977c478bd9Sstevel@tonic-gate Buffer msg;
5987c478bd9Sstevel@tonic-gate int type;
5997c478bd9Sstevel@tonic-gate int code = (version==1) ?
6007c478bd9Sstevel@tonic-gate SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES :
6017c478bd9Sstevel@tonic-gate SSH2_AGENTC_REMOVE_ALL_IDENTITIES;
6027c478bd9Sstevel@tonic-gate
6037c478bd9Sstevel@tonic-gate buffer_init(&msg);
6047c478bd9Sstevel@tonic-gate buffer_put_char(&msg, code);
6057c478bd9Sstevel@tonic-gate
6067c478bd9Sstevel@tonic-gate if (ssh_request_reply(auth, &msg, &msg) == 0) {
6077c478bd9Sstevel@tonic-gate buffer_free(&msg);
6087c478bd9Sstevel@tonic-gate return 0;
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate type = buffer_get_char(&msg);
6117c478bd9Sstevel@tonic-gate buffer_free(&msg);
6127c478bd9Sstevel@tonic-gate return decode_reply(type);
6137c478bd9Sstevel@tonic-gate }
6147c478bd9Sstevel@tonic-gate
6157c478bd9Sstevel@tonic-gate int
decode_reply(int type)6167c478bd9Sstevel@tonic-gate decode_reply(int type)
6177c478bd9Sstevel@tonic-gate {
6187c478bd9Sstevel@tonic-gate switch (type) {
6197c478bd9Sstevel@tonic-gate case SSH_AGENT_FAILURE:
6207c478bd9Sstevel@tonic-gate case SSH_COM_AGENT2_FAILURE:
6217c478bd9Sstevel@tonic-gate case SSH2_AGENT_FAILURE:
6227c478bd9Sstevel@tonic-gate log("SSH_AGENT_FAILURE");
6237c478bd9Sstevel@tonic-gate return 0;
6247c478bd9Sstevel@tonic-gate case SSH_AGENT_SUCCESS:
6257c478bd9Sstevel@tonic-gate return 1;
6267c478bd9Sstevel@tonic-gate default:
6277c478bd9Sstevel@tonic-gate fatal("Bad response from authentication agent: %d", type);
6287c478bd9Sstevel@tonic-gate }
6297c478bd9Sstevel@tonic-gate /* NOTREACHED */
6307c478bd9Sstevel@tonic-gate return 0;
6317c478bd9Sstevel@tonic-gate }
632