blob: 3b5d06c5117ef9cb71abe29f558787eadd87f47f [file] [log] [blame]
Damien Millerc653b892002-02-08 22:05:41 +11001/* $OpenBSD: ssh-agent.c,v 1.81 2002/02/05 15:50:12 stevesk Exp $ */
Damien Miller792c5111999-10-29 09:47:09 +10002
Damien Millerd4a8b7e1999-10-27 13:42:43 +10003/*
Damien Miller95def091999-11-25 00:26:21 +11004 * Author: Tatu Ylonen <ylo@cs.hut.fi>
5 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6 * All rights reserved
Damien Miller95def091999-11-25 00:26:21 +11007 * The authentication agent program.
Damien Millerad833b32000-08-23 10:46:23 +10008 *
Damien Millere4340be2000-09-16 13:29:08 +11009 * As far as I am concerned, the code I have written for this software
10 * can be used freely for any purpose. Any derived versions of this
11 * software must be clearly marked as such, and if the derived work is
12 * incompatible with the protocol description in the RFC file, it must be
13 * called by a name other than "ssh" or "Secure Shell".
14 *
Ben Lindstrom44697232001-07-04 03:32:30 +000015 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
Damien Millere4340be2000-09-16 13:29:08 +110016 *
17 * Redistribution and use in source and binary forms, with or without
18 * modification, are permitted provided that the following conditions
19 * are met:
20 * 1. Redistributions of source code must retain the above copyright
21 * notice, this list of conditions and the following disclaimer.
22 * 2. Redistributions in binary form must reproduce the above copyright
23 * notice, this list of conditions and the following disclaimer in the
24 * documentation and/or other materials provided with the distribution.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Damien Miller95def091999-11-25 00:26:21 +110036 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100037
38#include "includes.h"
Damien Millerc653b892002-02-08 22:05:41 +110039RCSID("$OpenBSD: ssh-agent.c,v 1.81 2002/02/05 15:50:12 stevesk Exp $");
Ben Lindstrom226cfa02001-01-22 05:34:40 +000040
Damien Millerec52d7c2002-01-22 23:52:17 +110041#if defined(HAVE_SYS_QUEUE_H) && !defined(HAVE_BOGUS_SYS_QUEUE_H)
42#include <sys/queue.h>
43#else
44#include "openbsd-compat/fake-queue.h"
45#endif
46
47
Ben Lindstrom226cfa02001-01-22 05:34:40 +000048#include <openssl/evp.h>
49#include <openssl/md5.h>
Damien Millerd4a8b7e1999-10-27 13:42:43 +100050
51#include "ssh.h"
52#include "rsa.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100053#include "buffer.h"
54#include "bufaux.h"
55#include "xmalloc.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100056#include "getput.h"
Damien Miller994cf142000-07-21 10:19:44 +100057#include "key.h"
58#include "authfd.h"
Damien Miller62cee002000-09-23 17:15:56 +110059#include "compat.h"
Ben Lindstrom226cfa02001-01-22 05:34:40 +000060#include "log.h"
Damien Millerd4a8b7e1999-10-27 13:42:43 +100061
Ben Lindstrom3f471632001-07-04 03:53:15 +000062#ifdef SMARTCARD
63#include <openssl/engine.h>
64#include "scard.h"
Ben Lindstrombcc18082001-08-06 21:59:25 +000065#endif
Ben Lindstrom3f471632001-07-04 03:53:15 +000066
Ben Lindstrom65366a82001-12-06 16:32:47 +000067typedef enum {
68 AUTH_UNUSED,
69 AUTH_SOCKET,
70 AUTH_CONNECTION
71} sock_type;
72
Damien Miller95def091999-11-25 00:26:21 +110073typedef struct {
74 int fd;
Ben Lindstrom65366a82001-12-06 16:32:47 +000075 sock_type type;
Damien Miller95def091999-11-25 00:26:21 +110076 Buffer input;
77 Buffer output;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100078} SocketEntry;
79
Ben Lindstrom46c16222000-12-22 01:43:59 +000080u_int sockets_alloc = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100081SocketEntry *sockets = NULL;
82
Damien Miller1a534ae2002-01-22 23:26:13 +110083typedef struct identity {
84 TAILQ_ENTRY(identity) next;
Damien Millerad833b32000-08-23 10:46:23 +100085 Key *key;
Damien Miller95def091999-11-25 00:26:21 +110086 char *comment;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100087} Identity;
88
Damien Millerad833b32000-08-23 10:46:23 +100089typedef struct {
90 int nentries;
Damien Miller1a534ae2002-01-22 23:26:13 +110091 TAILQ_HEAD(idqueue, identity) idlist;
Damien Millerad833b32000-08-23 10:46:23 +100092} Idtab;
93
94/* private key table, one per protocol version */
95Idtab idtable[3];
Damien Millerd4a8b7e1999-10-27 13:42:43 +100096
97int max_fd = 0;
98
99/* pid of shell == parent of agent */
Damien Miller166fca82000-04-20 07:42:21 +1000100pid_t parent_pid = -1;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000101
102/* pathname and directory for AUTH_SOCKET */
103char socket_name[1024];
104char socket_dir[1024];
105
Damien Miller95def091999-11-25 00:26:21 +1100106#ifdef HAVE___PROGNAME
107extern char *__progname;
Ben Lindstrom49a79c02000-11-17 03:47:20 +0000108#else
109char *__progname;
110#endif
Damien Miller95def091999-11-25 00:26:21 +1100111
Ben Lindstrombba81212001-06-25 05:01:22 +0000112static void
Damien Millerad833b32000-08-23 10:46:23 +1000113idtab_init(void)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000114{
Damien Millerad833b32000-08-23 10:46:23 +1000115 int i;
Ben Lindstrom1c37c6a2001-12-06 18:00:18 +0000116 for (i = 0; i <=2; i++) {
Damien Miller1a534ae2002-01-22 23:26:13 +1100117 TAILQ_INIT(&idtable[i].idlist);
Damien Millerad833b32000-08-23 10:46:23 +1000118 idtable[i].nentries = 0;
119 }
120}
121
122/* return private key table for requested protocol version */
Ben Lindstrombba81212001-06-25 05:01:22 +0000123static Idtab *
Damien Millerad833b32000-08-23 10:46:23 +1000124idtab_lookup(int version)
125{
126 if (version < 1 || version > 2)
127 fatal("internal error, bad protocol version %d", version);
128 return &idtable[version];
129}
130
131/* return matching private key for given public key */
Damien Miller1a534ae2002-01-22 23:26:13 +1100132static Identity *
133lookup_identity(Key *key, int version)
Damien Millerad833b32000-08-23 10:46:23 +1000134{
Damien Miller1a534ae2002-01-22 23:26:13 +1100135 Identity *id;
136
Damien Millerad833b32000-08-23 10:46:23 +1000137 Idtab *tab = idtab_lookup(version);
Damien Miller1a534ae2002-01-22 23:26:13 +1100138 TAILQ_FOREACH(id, &tab->idlist, next) {
139 if (key_equal(key, id->key))
140 return (id);
Damien Millerad833b32000-08-23 10:46:23 +1000141 }
Damien Miller1a534ae2002-01-22 23:26:13 +1100142 return (NULL);
143}
144
145static void
146free_identity(Identity *id)
147{
148 key_free(id->key);
149 xfree(id->comment);
150 xfree(id);
Damien Millerad833b32000-08-23 10:46:23 +1000151}
152
153/* send list of supported public keys to 'client' */
Ben Lindstrombba81212001-06-25 05:01:22 +0000154static void
Damien Millerad833b32000-08-23 10:46:23 +1000155process_request_identities(SocketEntry *e, int version)
156{
157 Idtab *tab = idtab_lookup(version);
Damien Miller95def091999-11-25 00:26:21 +1100158 Buffer msg;
Damien Miller1a534ae2002-01-22 23:26:13 +1100159 Identity *id;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000160
Damien Miller95def091999-11-25 00:26:21 +1100161 buffer_init(&msg);
Damien Millerad833b32000-08-23 10:46:23 +1000162 buffer_put_char(&msg, (version == 1) ?
163 SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER);
164 buffer_put_int(&msg, tab->nentries);
Damien Miller1a534ae2002-01-22 23:26:13 +1100165 TAILQ_FOREACH(id, &tab->idlist, next) {
Damien Miller0bc1bd82000-11-13 22:57:25 +1100166 if (id->key->type == KEY_RSA1) {
Damien Millerad833b32000-08-23 10:46:23 +1000167 buffer_put_int(&msg, BN_num_bits(id->key->rsa->n));
168 buffer_put_bignum(&msg, id->key->rsa->e);
169 buffer_put_bignum(&msg, id->key->rsa->n);
170 } else {
Ben Lindstrom46c16222000-12-22 01:43:59 +0000171 u_char *blob;
172 u_int blen;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100173 key_to_blob(id->key, &blob, &blen);
Damien Millerad833b32000-08-23 10:46:23 +1000174 buffer_put_string(&msg, blob, blen);
175 xfree(blob);
176 }
177 buffer_put_cstring(&msg, id->comment);
Damien Miller95def091999-11-25 00:26:21 +1100178 }
179 buffer_put_int(&e->output, buffer_len(&msg));
180 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
181 buffer_free(&msg);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000182}
183
Damien Millerad833b32000-08-23 10:46:23 +1000184/* ssh1 only */
Ben Lindstrombba81212001-06-25 05:01:22 +0000185static void
Damien Millerad833b32000-08-23 10:46:23 +1000186process_authentication_challenge1(SocketEntry *e)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000187{
Damien Miller1a534ae2002-01-22 23:26:13 +1100188 Identity *id;
189 Key *key;
Damien Millerad833b32000-08-23 10:46:23 +1000190 BIGNUM *challenge;
191 int i, len;
Damien Miller95def091999-11-25 00:26:21 +1100192 Buffer msg;
193 MD5_CTX md;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000194 u_char buf[32], mdbuf[16], session_id[16];
195 u_int response_type;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000196
Damien Miller95def091999-11-25 00:26:21 +1100197 buffer_init(&msg);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100198 key = key_new(KEY_RSA1);
Damien Millerda755162002-01-22 23:09:22 +1100199 if ((challenge = BN_new()) == NULL)
200 fatal("process_authentication_challenge1: BN_new failed");
Damien Millerad833b32000-08-23 10:46:23 +1000201
202 buffer_get_int(&e->input); /* ignored */
203 buffer_get_bignum(&e->input, key->rsa->e);
204 buffer_get_bignum(&e->input, key->rsa->n);
Damien Miller95def091999-11-25 00:26:21 +1100205 buffer_get_bignum(&e->input, challenge);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000206
Damien Millerad833b32000-08-23 10:46:23 +1000207 /* Only protocol 1.1 is supported */
208 if (buffer_len(&e->input) == 0)
209 goto failure;
Damien Miller4a8ed542002-01-22 23:33:31 +1100210 buffer_get(&e->input, session_id, 16);
Damien Millerad833b32000-08-23 10:46:23 +1000211 response_type = buffer_get_int(&e->input);
212 if (response_type != 1)
213 goto failure;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000214
Damien Miller1a534ae2002-01-22 23:26:13 +1100215 id = lookup_identity(key, 1);
216 if (id != NULL) {
217 Key *private = id->key;
Damien Millerad833b32000-08-23 10:46:23 +1000218 /* Decrypt the challenge using the private key. */
Damien Miller7650bc62001-01-30 09:27:26 +1100219 if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0)
220 goto failure;
Damien Millerfd7c9111999-11-08 16:15:55 +1100221
Damien Millerad833b32000-08-23 10:46:23 +1000222 /* The response is MD5 of decrypted challenge plus session id. */
223 len = BN_num_bytes(challenge);
224 if (len <= 0 || len > 32) {
225 log("process_authentication_challenge: bad challenge length %d", len);
226 goto failure;
Damien Miller95def091999-11-25 00:26:21 +1100227 }
Damien Millerad833b32000-08-23 10:46:23 +1000228 memset(buf, 0, 32);
229 BN_bn2bin(challenge, buf + 32 - len);
230 MD5_Init(&md);
231 MD5_Update(&md, buf, 32);
232 MD5_Update(&md, session_id, 16);
233 MD5_Final(mdbuf, &md);
234
235 /* Send the response. */
236 buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
237 for (i = 0; i < 16; i++)
238 buffer_put_char(&msg, mdbuf[i]);
239 goto send;
240 }
241
242failure:
243 /* Unknown identity or protocol error. Send failure. */
Damien Miller95def091999-11-25 00:26:21 +1100244 buffer_put_char(&msg, SSH_AGENT_FAILURE);
245send:
246 buffer_put_int(&e->output, buffer_len(&msg));
Damien Millerad833b32000-08-23 10:46:23 +1000247 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
248 key_free(key);
Damien Miller95def091999-11-25 00:26:21 +1100249 BN_clear_free(challenge);
Damien Millerad833b32000-08-23 10:46:23 +1000250 buffer_free(&msg);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000251}
252
Damien Millerad833b32000-08-23 10:46:23 +1000253/* ssh2 only */
Ben Lindstrombba81212001-06-25 05:01:22 +0000254static void
Damien Millerad833b32000-08-23 10:46:23 +1000255process_sign_request2(SocketEntry *e)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000256{
Damien Millerad833b32000-08-23 10:46:23 +1000257 extern int datafellows;
Damien Miller1a534ae2002-01-22 23:26:13 +1100258 Key *key;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000259 u_char *blob, *data, *signature = NULL;
260 u_int blen, dlen, slen = 0;
Damien Miller62cee002000-09-23 17:15:56 +1100261 int flags;
Damien Millerad833b32000-08-23 10:46:23 +1000262 Buffer msg;
263 int ok = -1;
264
265 datafellows = 0;
Kevin Stevesfa72dda2000-12-15 18:39:12 +0000266
Damien Millerad833b32000-08-23 10:46:23 +1000267 blob = buffer_get_string(&e->input, &blen);
268 data = buffer_get_string(&e->input, &dlen);
Damien Miller62cee002000-09-23 17:15:56 +1100269
270 flags = buffer_get_int(&e->input);
271 if (flags & SSH_AGENT_OLD_SIGNATURE)
272 datafellows = SSH_BUG_SIGBLOB;
Damien Millerad833b32000-08-23 10:46:23 +1000273
Damien Miller0bc1bd82000-11-13 22:57:25 +1100274 key = key_from_blob(blob, blen);
Damien Millerad833b32000-08-23 10:46:23 +1000275 if (key != NULL) {
Damien Miller1a534ae2002-01-22 23:26:13 +1100276 Identity *id = lookup_identity(key, 2);
277 if (id != NULL)
278 ok = key_sign(id->key, &signature, &slen, data, dlen);
Damien Millerad833b32000-08-23 10:46:23 +1000279 }
280 key_free(key);
281 buffer_init(&msg);
282 if (ok == 0) {
283 buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE);
284 buffer_put_string(&msg, signature, slen);
285 } else {
286 buffer_put_char(&msg, SSH_AGENT_FAILURE);
287 }
288 buffer_put_int(&e->output, buffer_len(&msg));
289 buffer_append(&e->output, buffer_ptr(&msg),
290 buffer_len(&msg));
291 buffer_free(&msg);
292 xfree(data);
293 xfree(blob);
294 if (signature != NULL)
295 xfree(signature);
296}
297
298/* shared */
Ben Lindstrombba81212001-06-25 05:01:22 +0000299static void
Damien Millerad833b32000-08-23 10:46:23 +1000300process_remove_identity(SocketEntry *e, int version)
301{
Damien Miller1a534ae2002-01-22 23:26:13 +1100302 Key *key = NULL;
Ben Lindstrom46c16222000-12-22 01:43:59 +0000303 u_char *blob;
304 u_int blen;
305 u_int bits;
Damien Millerad833b32000-08-23 10:46:23 +1000306 int success = 0;
Damien Miller7e8e8201999-11-16 13:37:16 +1100307
Ben Lindstrom1c37c6a2001-12-06 18:00:18 +0000308 switch (version) {
Damien Millerad833b32000-08-23 10:46:23 +1000309 case 1:
Damien Miller0bc1bd82000-11-13 22:57:25 +1100310 key = key_new(KEY_RSA1);
Damien Millerad833b32000-08-23 10:46:23 +1000311 bits = buffer_get_int(&e->input);
312 buffer_get_bignum(&e->input, key->rsa->e);
313 buffer_get_bignum(&e->input, key->rsa->n);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000314
Damien Millerad833b32000-08-23 10:46:23 +1000315 if (bits != key_size(key))
316 log("Warning: identity keysize mismatch: actual %d, announced %d",
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000317 key_size(key), bits);
Damien Millerad833b32000-08-23 10:46:23 +1000318 break;
319 case 2:
320 blob = buffer_get_string(&e->input, &blen);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100321 key = key_from_blob(blob, blen);
Damien Millerad833b32000-08-23 10:46:23 +1000322 xfree(blob);
323 break;
324 }
325 if (key != NULL) {
Damien Miller1a534ae2002-01-22 23:26:13 +1100326 Identity *id = lookup_identity(key, version);
327 if (id != NULL) {
Damien Miller5428f641999-11-25 11:54:57 +1100328 /*
329 * We have this key. Free the old key. Since we
330 * don\'t want to leave empty slots in the middle of
Ben Lindstrom14920292000-11-21 21:24:55 +0000331 * the array, we actually free the key there and move
332 * all the entries between the empty slot and the end
333 * of the array.
Damien Miller5428f641999-11-25 11:54:57 +1100334 */
Damien Millerad833b32000-08-23 10:46:23 +1000335 Idtab *tab = idtab_lookup(version);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100336 if (tab->nentries < 1)
337 fatal("process_remove_identity: "
338 "internal error: tab->nentries %d",
339 tab->nentries);
Damien Miller1a534ae2002-01-22 23:26:13 +1100340 TAILQ_REMOVE(&tab->idlist, id, next);
341 free_identity(id);
Damien Millerad833b32000-08-23 10:46:23 +1000342 tab->nentries--;
343 success = 1;
Damien Miller95def091999-11-25 00:26:21 +1100344 }
Damien Millerad833b32000-08-23 10:46:23 +1000345 key_free(key);
346 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000347 buffer_put_int(&e->output, 1);
Damien Millerad833b32000-08-23 10:46:23 +1000348 buffer_put_char(&e->output,
349 success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000350}
351
Ben Lindstrombba81212001-06-25 05:01:22 +0000352static void
Damien Millerad833b32000-08-23 10:46:23 +1000353process_remove_all_identities(SocketEntry *e, int version)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000354{
Damien Millerad833b32000-08-23 10:46:23 +1000355 Idtab *tab = idtab_lookup(version);
Damien Miller1a534ae2002-01-22 23:26:13 +1100356 Identity *id;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000357
Damien Miller95def091999-11-25 00:26:21 +1100358 /* Loop over all identities and clear the keys. */
Damien Miller1a534ae2002-01-22 23:26:13 +1100359 for (id = TAILQ_FIRST(&tab->idlist); id;
360 id = TAILQ_FIRST(&tab->idlist)) {
361 TAILQ_REMOVE(&tab->idlist, id, next);
362 free_identity(id);
Damien Miller95def091999-11-25 00:26:21 +1100363 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000364
Damien Miller95def091999-11-25 00:26:21 +1100365 /* Mark that there are no identities. */
Damien Millerad833b32000-08-23 10:46:23 +1000366 tab->nentries = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000367
368 /* Send success. */
369 buffer_put_int(&e->output, 1);
370 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
371 return;
Damien Miller95def091999-11-25 00:26:21 +1100372}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000373
Ben Lindstrombba81212001-06-25 05:01:22 +0000374static void
Damien Millerad833b32000-08-23 10:46:23 +1000375process_add_identity(SocketEntry *e, int version)
Damien Miller95def091999-11-25 00:26:21 +1100376{
Damien Millerad833b32000-08-23 10:46:23 +1000377 Key *k = NULL;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100378 char *type_name;
Damien Millerad833b32000-08-23 10:46:23 +1000379 char *comment;
Damien Miller0bc1bd82000-11-13 22:57:25 +1100380 int type, success = 0;
Damien Millerad833b32000-08-23 10:46:23 +1000381 Idtab *tab = idtab_lookup(version);
Damien Miller95def091999-11-25 00:26:21 +1100382
Damien Millerad833b32000-08-23 10:46:23 +1000383 switch (version) {
384 case 1:
Damien Miller0bc1bd82000-11-13 22:57:25 +1100385 k = key_new_private(KEY_RSA1);
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000386 buffer_get_int(&e->input); /* ignored */
Damien Miller0bc1bd82000-11-13 22:57:25 +1100387 buffer_get_bignum(&e->input, k->rsa->n);
388 buffer_get_bignum(&e->input, k->rsa->e);
389 buffer_get_bignum(&e->input, k->rsa->d);
390 buffer_get_bignum(&e->input, k->rsa->iqmp);
Damien Miller95def091999-11-25 00:26:21 +1100391
Damien Millerad833b32000-08-23 10:46:23 +1000392 /* SSH and SSL have p and q swapped */
Damien Miller0bc1bd82000-11-13 22:57:25 +1100393 buffer_get_bignum(&e->input, k->rsa->q); /* p */
394 buffer_get_bignum(&e->input, k->rsa->p); /* q */
Damien Miller95def091999-11-25 00:26:21 +1100395
Damien Millerad833b32000-08-23 10:46:23 +1000396 /* Generate additional parameters */
Ben Lindstromf7297dd2001-07-04 05:02:23 +0000397 rsa_generate_additional_parameters(k->rsa);
Damien Millerad833b32000-08-23 10:46:23 +1000398 break;
399 case 2:
Damien Miller0bc1bd82000-11-13 22:57:25 +1100400 type_name = buffer_get_string(&e->input, NULL);
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000401 type = key_type_from_name(type_name);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100402 xfree(type_name);
Ben Lindstrom1c37c6a2001-12-06 18:00:18 +0000403 switch (type) {
Damien Miller0bc1bd82000-11-13 22:57:25 +1100404 case KEY_DSA:
405 k = key_new_private(type);
406 buffer_get_bignum2(&e->input, k->dsa->p);
407 buffer_get_bignum2(&e->input, k->dsa->q);
408 buffer_get_bignum2(&e->input, k->dsa->g);
409 buffer_get_bignum2(&e->input, k->dsa->pub_key);
410 buffer_get_bignum2(&e->input, k->dsa->priv_key);
411 break;
412 case KEY_RSA:
413 k = key_new_private(type);
414 buffer_get_bignum2(&e->input, k->rsa->n);
415 buffer_get_bignum2(&e->input, k->rsa->e);
416 buffer_get_bignum2(&e->input, k->rsa->d);
417 buffer_get_bignum2(&e->input, k->rsa->iqmp);
418 buffer_get_bignum2(&e->input, k->rsa->p);
419 buffer_get_bignum2(&e->input, k->rsa->q);
420
421 /* Generate additional parameters */
Ben Lindstromf7297dd2001-07-04 05:02:23 +0000422 rsa_generate_additional_parameters(k->rsa);
Damien Miller0bc1bd82000-11-13 22:57:25 +1100423 break;
424 default:
Damien Millerad833b32000-08-23 10:46:23 +1000425 buffer_clear(&e->input);
Damien Millerad833b32000-08-23 10:46:23 +1000426 goto send;
Damien Miller95def091999-11-25 00:26:21 +1100427 }
Damien Millerad833b32000-08-23 10:46:23 +1000428 break;
429 }
Damien Millerad833b32000-08-23 10:46:23 +1000430 comment = buffer_get_string(&e->input, NULL);
431 if (k == NULL) {
432 xfree(comment);
433 goto send;
434 }
435 success = 1;
Damien Miller1a534ae2002-01-22 23:26:13 +1100436 if (lookup_identity(k, version) == NULL) {
437 Identity *id = xmalloc(sizeof(Identity));
438 id->key = k;
439 id->comment = comment;
440 TAILQ_INSERT_TAIL(&tab->idlist, id, next);
Damien Millerad833b32000-08-23 10:46:23 +1000441 /* Increment the number of identities. */
442 tab->nentries++;
443 } else {
444 key_free(k);
445 xfree(comment);
446 }
447send:
Damien Miller95def091999-11-25 00:26:21 +1100448 buffer_put_int(&e->output, 1);
Damien Millerad833b32000-08-23 10:46:23 +1000449 buffer_put_char(&e->output,
450 success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000451}
452
Ben Lindstrom3f471632001-07-04 03:53:15 +0000453
454#ifdef SMARTCARD
455static void
456process_add_smartcard_key (SocketEntry *e)
457{
458 Idtab *tab;
459 Key *n = NULL, *k = NULL;
Ben Lindstromf7db3bb2001-08-06 21:35:51 +0000460 char *sc_reader_id = NULL;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000461 int success = 0;
Damien Miller9f0f5c62001-12-21 14:45:46 +1100462
Ben Lindstromf7db3bb2001-08-06 21:35:51 +0000463 sc_reader_id = buffer_get_string(&e->input, NULL);
464 k = sc_get_key(sc_reader_id);
465 xfree(sc_reader_id);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000466
Ben Lindstrom3f471632001-07-04 03:53:15 +0000467 if (k == NULL) {
468 error("sc_get_pubkey failed");
469 goto send;
470 }
471 success = 1;
472
473 tab = idtab_lookup(1);
Damien Millerf3512d92001-07-14 12:14:27 +1000474 k->type = KEY_RSA1;
Damien Miller1a534ae2002-01-22 23:26:13 +1100475 if (lookup_identity(k, 1) == NULL) {
476 Identity *id = xmalloc(sizeof(Identity));
Ben Lindstrom3f471632001-07-04 03:53:15 +0000477 n = key_new(KEY_RSA1);
478 BN_copy(n->rsa->n, k->rsa->n);
479 BN_copy(n->rsa->e, k->rsa->e);
480 RSA_set_method(n->rsa, sc_get_engine());
Damien Miller1a534ae2002-01-22 23:26:13 +1100481 id->key = n;
482 id->comment = xstrdup("rsa1 smartcard");
483 TAILQ_INSERT_TAIL(&tab->idlist, id, next);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000484 tab->nentries++;
485 }
Damien Millerf3512d92001-07-14 12:14:27 +1000486 k->type = KEY_RSA;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000487 tab = idtab_lookup(2);
Damien Miller1a534ae2002-01-22 23:26:13 +1100488 if (lookup_identity(k, 2) == NULL) {
489 Identity *id = xmalloc(sizeof(Identity));
Ben Lindstrom3f471632001-07-04 03:53:15 +0000490 n = key_new(KEY_RSA);
491 BN_copy(n->rsa->n, k->rsa->n);
492 BN_copy(n->rsa->e, k->rsa->e);
493 RSA_set_method(n->rsa, sc_get_engine());
Damien Miller1a534ae2002-01-22 23:26:13 +1100494 id->key = n;
495 id->comment = xstrdup("rsa smartcard");
496 TAILQ_INSERT_TAIL(&tab->idlist, id, next);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000497 tab->nentries++;
498 }
499 key_free(k);
500send:
501 buffer_put_int(&e->output, 1);
502 buffer_put_char(&e->output,
503 success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
504}
505
506static void
507process_remove_smartcard_key(SocketEntry *e)
508{
Damien Miller1a534ae2002-01-22 23:26:13 +1100509 Key *k = NULL;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000510 int success = 0;
Ben Lindstromf7db3bb2001-08-06 21:35:51 +0000511 char *sc_reader_id = NULL;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000512
Ben Lindstromf7db3bb2001-08-06 21:35:51 +0000513 sc_reader_id = buffer_get_string(&e->input, NULL);
514 k = sc_get_key(sc_reader_id);
515 xfree(sc_reader_id);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000516
Ben Lindstromf7db3bb2001-08-06 21:35:51 +0000517 if (k == NULL) {
Ben Lindstrom3f471632001-07-04 03:53:15 +0000518 error("sc_get_pubkey failed");
519 } else {
Damien Miller1a534ae2002-01-22 23:26:13 +1100520 Identity *id;
Damien Miller8d4bf172001-07-14 12:13:49 +1000521 k->type = KEY_RSA1;
Damien Miller1a534ae2002-01-22 23:26:13 +1100522 id = lookup_identity(k, 1);
523 if (id != NULL) {
Ben Lindstrom3f471632001-07-04 03:53:15 +0000524 Idtab *tab = idtab_lookup(1);
Damien Miller1a534ae2002-01-22 23:26:13 +1100525 TAILQ_REMOVE(&tab->idlist, id, next);
526 free_identity(id);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000527 tab->nentries--;
528 success = 1;
529 }
Damien Miller8d4bf172001-07-14 12:13:49 +1000530 k->type = KEY_RSA;
Damien Miller1a534ae2002-01-22 23:26:13 +1100531 id = lookup_identity(k, 2);
532 if (id != NULL) {
Ben Lindstrom3f471632001-07-04 03:53:15 +0000533 Idtab *tab = idtab_lookup(2);
Damien Miller1a534ae2002-01-22 23:26:13 +1100534 TAILQ_REMOVE(&tab->idlist, id, next);
535 free_identity(id);
Ben Lindstrom3f471632001-07-04 03:53:15 +0000536 tab->nentries--;
537 success = 1;
538 }
539 key_free(k);
540 }
541
542 buffer_put_int(&e->output, 1);
543 buffer_put_char(&e->output,
544 success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
545}
Ben Lindstromffce1472001-08-06 21:57:31 +0000546#endif /* SMARTCARD */
Ben Lindstrom3f471632001-07-04 03:53:15 +0000547
Damien Millerad833b32000-08-23 10:46:23 +1000548/* dispatch incoming messages */
549
Ben Lindstrombba81212001-06-25 05:01:22 +0000550static void
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000551process_message(SocketEntry *e)
552{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000553 u_int msg_len;
554 u_int type;
555 u_char *cp;
Damien Miller95def091999-11-25 00:26:21 +1100556 if (buffer_len(&e->input) < 5)
557 return; /* Incomplete message. */
Damien Miller708d21c2002-01-22 23:18:15 +1100558 cp = buffer_ptr(&e->input);
Damien Miller95def091999-11-25 00:26:21 +1100559 msg_len = GET_32BIT(cp);
560 if (msg_len > 256 * 1024) {
561 shutdown(e->fd, SHUT_RDWR);
562 close(e->fd);
563 e->type = AUTH_UNUSED;
564 return;
565 }
566 if (buffer_len(&e->input) < msg_len + 4)
567 return;
568 buffer_consume(&e->input, 4);
569 type = buffer_get_char(&e->input);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000570
Ben Lindstrom3f471632001-07-04 03:53:15 +0000571 debug("type %d", type);
Damien Miller95def091999-11-25 00:26:21 +1100572 switch (type) {
Damien Millerad833b32000-08-23 10:46:23 +1000573 /* ssh1 */
Damien Miller95def091999-11-25 00:26:21 +1100574 case SSH_AGENTC_RSA_CHALLENGE:
Damien Millerad833b32000-08-23 10:46:23 +1000575 process_authentication_challenge1(e);
576 break;
577 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
578 process_request_identities(e, 1);
Damien Miller95def091999-11-25 00:26:21 +1100579 break;
580 case SSH_AGENTC_ADD_RSA_IDENTITY:
Damien Millerad833b32000-08-23 10:46:23 +1000581 process_add_identity(e, 1);
Damien Miller95def091999-11-25 00:26:21 +1100582 break;
583 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
Damien Millerad833b32000-08-23 10:46:23 +1000584 process_remove_identity(e, 1);
Damien Miller95def091999-11-25 00:26:21 +1100585 break;
586 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
Damien Millerad833b32000-08-23 10:46:23 +1000587 process_remove_all_identities(e, 1);
588 break;
589 /* ssh2 */
590 case SSH2_AGENTC_SIGN_REQUEST:
591 process_sign_request2(e);
592 break;
593 case SSH2_AGENTC_REQUEST_IDENTITIES:
594 process_request_identities(e, 2);
595 break;
596 case SSH2_AGENTC_ADD_IDENTITY:
597 process_add_identity(e, 2);
598 break;
599 case SSH2_AGENTC_REMOVE_IDENTITY:
600 process_remove_identity(e, 2);
601 break;
602 case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
603 process_remove_all_identities(e, 2);
Damien Miller95def091999-11-25 00:26:21 +1100604 break;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000605#ifdef SMARTCARD
606 case SSH_AGENTC_ADD_SMARTCARD_KEY:
607 process_add_smartcard_key(e);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100608 break;
Ben Lindstrom3f471632001-07-04 03:53:15 +0000609 case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
610 process_remove_smartcard_key(e);
Damien Miller9f0f5c62001-12-21 14:45:46 +1100611 break;
Ben Lindstromffce1472001-08-06 21:57:31 +0000612#endif /* SMARTCARD */
Damien Miller95def091999-11-25 00:26:21 +1100613 default:
614 /* Unknown message. Respond with failure. */
615 error("Unknown message %d", type);
616 buffer_clear(&e->input);
617 buffer_put_int(&e->output, 1);
618 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
619 break;
620 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000621}
622
Ben Lindstrombba81212001-06-25 05:01:22 +0000623static void
Ben Lindstrom65366a82001-12-06 16:32:47 +0000624new_socket(sock_type type, int fd)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000625{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000626 u_int i, old_alloc;
Damien Miller95def091999-11-25 00:26:21 +1100627 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
628 error("fcntl O_NONBLOCK: %s", strerror(errno));
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000629
Damien Miller95def091999-11-25 00:26:21 +1100630 if (fd > max_fd)
631 max_fd = fd;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000632
Damien Miller95def091999-11-25 00:26:21 +1100633 for (i = 0; i < sockets_alloc; i++)
634 if (sockets[i].type == AUTH_UNUSED) {
635 sockets[i].fd = fd;
636 sockets[i].type = type;
637 buffer_init(&sockets[i].input);
638 buffer_init(&sockets[i].output);
639 return;
640 }
641 old_alloc = sockets_alloc;
642 sockets_alloc += 10;
643 if (sockets)
644 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
645 else
646 sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
647 for (i = old_alloc; i < sockets_alloc; i++)
648 sockets[i].type = AUTH_UNUSED;
649 sockets[old_alloc].type = type;
650 sockets[old_alloc].fd = fd;
651 buffer_init(&sockets[old_alloc].input);
652 buffer_init(&sockets[old_alloc].output);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000653}
654
Ben Lindstrombba81212001-06-25 05:01:22 +0000655static int
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +0000656prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, int *nallocp)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000657{
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000658 u_int i, sz;
659 int n = 0;
660
661 for (i = 0; i < sockets_alloc; i++) {
Damien Miller95def091999-11-25 00:26:21 +1100662 switch (sockets[i].type) {
663 case AUTH_SOCKET:
664 case AUTH_CONNECTION:
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000665 n = MAX(n, sockets[i].fd);
Damien Miller95def091999-11-25 00:26:21 +1100666 break;
667 case AUTH_UNUSED:
668 break;
669 default:
670 fatal("Unknown socket type %d", sockets[i].type);
671 break;
672 }
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000673 }
674
675 sz = howmany(n+1, NFDBITS) * sizeof(fd_mask);
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +0000676 if (*fdrp == NULL || sz > *nallocp) {
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000677 if (*fdrp)
Ben Lindstrom86ebcb62001-04-04 01:53:20 +0000678 xfree(*fdrp);
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000679 if (*fdwp)
Ben Lindstrom86ebcb62001-04-04 01:53:20 +0000680 xfree(*fdwp);
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000681 *fdrp = xmalloc(sz);
682 *fdwp = xmalloc(sz);
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +0000683 *nallocp = sz;
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000684 }
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +0000685 if (n < *fdl)
686 debug("XXX shrink: %d < %d", n, *fdl);
687 *fdl = n;
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000688 memset(*fdrp, 0, sz);
689 memset(*fdwp, 0, sz);
690
691 for (i = 0; i < sockets_alloc; i++) {
692 switch (sockets[i].type) {
693 case AUTH_SOCKET:
694 case AUTH_CONNECTION:
695 FD_SET(sockets[i].fd, *fdrp);
696 if (buffer_len(&sockets[i].output) > 0)
697 FD_SET(sockets[i].fd, *fdwp);
698 break;
699 default:
700 break;
701 }
702 }
703 return (1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000704}
705
Ben Lindstrombba81212001-06-25 05:01:22 +0000706static void
Damien Miller95def091999-11-25 00:26:21 +1100707after_select(fd_set *readset, fd_set *writeset)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000708{
Ben Lindstrom46c16222000-12-22 01:43:59 +0000709 u_int i;
Damien Miller95def091999-11-25 00:26:21 +1100710 int len, sock;
Damien Miller7684ee12000-03-17 23:40:15 +1100711 socklen_t slen;
Damien Miller95def091999-11-25 00:26:21 +1100712 char buf[1024];
713 struct sockaddr_un sunaddr;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000714
Damien Miller95def091999-11-25 00:26:21 +1100715 for (i = 0; i < sockets_alloc; i++)
716 switch (sockets[i].type) {
717 case AUTH_UNUSED:
718 break;
719 case AUTH_SOCKET:
720 if (FD_ISSET(sockets[i].fd, readset)) {
Damien Miller7684ee12000-03-17 23:40:15 +1100721 slen = sizeof(sunaddr);
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000722 sock = accept(sockets[i].fd,
723 (struct sockaddr *) &sunaddr, &slen);
Damien Miller95def091999-11-25 00:26:21 +1100724 if (sock < 0) {
Damien Millerc653b892002-02-08 22:05:41 +1100725 error("accept from AUTH_SOCKET: %s",
726 strerror(errno));
Damien Miller95def091999-11-25 00:26:21 +1100727 break;
728 }
729 new_socket(AUTH_CONNECTION, sock);
730 }
731 break;
732 case AUTH_CONNECTION:
733 if (buffer_len(&sockets[i].output) > 0 &&
734 FD_ISSET(sockets[i].fd, writeset)) {
Ben Lindstromb3144e52001-03-06 03:31:34 +0000735 do {
736 len = write(sockets[i].fd,
737 buffer_ptr(&sockets[i].output),
738 buffer_len(&sockets[i].output));
739 if (len == -1 && (errno == EAGAIN ||
740 errno == EINTR))
741 continue;
742 break;
743 } while (1);
Damien Miller95def091999-11-25 00:26:21 +1100744 if (len <= 0) {
745 shutdown(sockets[i].fd, SHUT_RDWR);
746 close(sockets[i].fd);
747 sockets[i].type = AUTH_UNUSED;
Damien Millera552faf2000-04-21 15:55:20 +1000748 buffer_free(&sockets[i].input);
749 buffer_free(&sockets[i].output);
Damien Miller95def091999-11-25 00:26:21 +1100750 break;
751 }
752 buffer_consume(&sockets[i].output, len);
753 }
754 if (FD_ISSET(sockets[i].fd, readset)) {
Ben Lindstromb3144e52001-03-06 03:31:34 +0000755 do {
756 len = read(sockets[i].fd, buf, sizeof(buf));
757 if (len == -1 && (errno == EAGAIN ||
758 errno == EINTR))
759 continue;
760 break;
761 } while (1);
Damien Miller95def091999-11-25 00:26:21 +1100762 if (len <= 0) {
763 shutdown(sockets[i].fd, SHUT_RDWR);
764 close(sockets[i].fd);
765 sockets[i].type = AUTH_UNUSED;
Damien Millera552faf2000-04-21 15:55:20 +1000766 buffer_free(&sockets[i].input);
767 buffer_free(&sockets[i].output);
Damien Miller95def091999-11-25 00:26:21 +1100768 break;
769 }
770 buffer_append(&sockets[i].input, buf, len);
771 process_message(&sockets[i]);
772 }
773 break;
774 default:
775 fatal("Unknown type %d", sockets[i].type);
776 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000777}
778
Ben Lindstrombba81212001-06-25 05:01:22 +0000779static void
Damien Millerc653b892002-02-08 22:05:41 +1100780cleanup_socket(void *p)
Damien Miller792c5111999-10-29 09:47:09 +1000781{
Ben Lindstrom77808ab2001-01-26 05:10:34 +0000782 if (socket_name[0])
783 unlink(socket_name);
784 if (socket_dir[0])
785 rmdir(socket_dir);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000786}
787
Ben Lindstrombba81212001-06-25 05:01:22 +0000788static void
Damien Miller792c5111999-10-29 09:47:09 +1000789cleanup_exit(int i)
790{
Damien Millerc653b892002-02-08 22:05:41 +1100791 cleanup_socket(NULL);
Damien Miller95def091999-11-25 00:26:21 +1100792 exit(i);
Damien Miller792c5111999-10-29 09:47:09 +1000793}
794
Ben Lindstrombba81212001-06-25 05:01:22 +0000795static void
Ben Lindstrom77808ab2001-01-26 05:10:34 +0000796cleanup_handler(int sig)
797{
Damien Millerc653b892002-02-08 22:05:41 +1100798 cleanup_socket(NULL);
Ben Lindstrom77808ab2001-01-26 05:10:34 +0000799 _exit(2);
800}
801
Ben Lindstrombba81212001-06-25 05:01:22 +0000802static void
Ben Lindstrom0250da02001-07-22 20:44:00 +0000803check_parent_exists(int sig)
804{
805 int save_errno = errno;
806
807 if (parent_pid != -1 && kill(parent_pid, 0) < 0) {
808 /* printf("Parent has died - Authentication agent exiting.\n"); */
809 cleanup_handler(sig); /* safe */
810 }
811 signal(SIGALRM, check_parent_exists);
812 alarm(10);
813 errno = save_errno;
814}
815
816static void
Ben Lindstrom28072eb2001-02-10 23:13:41 +0000817usage(void)
Damien Miller792c5111999-10-29 09:47:09 +1000818{
Ben Lindstromddfb1e32001-08-06 22:06:35 +0000819 fprintf(stderr, "Usage: %s [options] [command [args ...]]\n",
Kevin Steves29265862001-01-24 14:06:28 +0000820 __progname);
Ben Lindstromddfb1e32001-08-06 22:06:35 +0000821 fprintf(stderr, "Options:\n");
822 fprintf(stderr, " -c Generate C-shell commands on stdout.\n");
823 fprintf(stderr, " -s Generate Bourne shell commands on stdout.\n");
824 fprintf(stderr, " -k Kill the current agent.\n");
825 fprintf(stderr, " -d Debug mode.\n");
Damien Miller95def091999-11-25 00:26:21 +1100826 exit(1);
Damien Miller792c5111999-10-29 09:47:09 +1000827}
828
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000829int
830main(int ac, char **av)
831{
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +0000832 int sock, c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0, ch, nalloc;
Damien Miller95def091999-11-25 00:26:21 +1100833 struct sockaddr_un sunaddr;
Ben Lindstrom2c467a22000-12-27 04:57:41 +0000834#ifdef HAVE_SETRLIMIT
Ben Lindstromc72745a2000-12-02 19:03:54 +0000835 struct rlimit rlim;
Ben Lindstrom2c467a22000-12-27 04:57:41 +0000836#endif
Ben Lindstrom3524d692001-05-03 22:59:24 +0000837#ifdef HAVE_CYGWIN
838 int prev_mask;
839#endif
Damien Miller95def091999-11-25 00:26:21 +1100840 pid_t pid;
841 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
Damien Miller615f9392000-05-17 22:53:33 +1000842 extern int optind;
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000843 fd_set *readsetp = NULL, *writesetp = NULL;
Kevin Stevesde41bc62000-12-14 00:04:08 +0000844
Ben Lindstromd09fcf52001-03-29 00:29:54 +0000845 SSLeay_add_all_algorithms();
846
Ben Lindstrom49a79c02000-11-17 03:47:20 +0000847 __progname = get_progname(av[0]);
Damien Millerf9b625c2000-07-09 22:42:32 +1000848 init_rng();
Damien Miller60bc5172001-03-19 09:38:15 +1100849 seed_rng();
Kevin Stevesef4eea92001-02-05 12:42:17 +0000850
Damien Millerd0cff3e2000-04-20 23:12:58 +1000851#ifdef __GNU_LIBRARY__
Ben Lindstromd94580c2001-07-04 03:48:02 +0000852 while ((ch = getopt(ac, av, "+cdks")) != -1) {
Damien Millerd0cff3e2000-04-20 23:12:58 +1000853#else /* __GNU_LIBRARY__ */
Ben Lindstromd94580c2001-07-04 03:48:02 +0000854 while ((ch = getopt(ac, av, "cdks")) != -1) {
Damien Millerd0cff3e2000-04-20 23:12:58 +1000855#endif /* __GNU_LIBRARY__ */
Damien Miller95def091999-11-25 00:26:21 +1100856 switch (ch) {
857 case 'c':
858 if (s_flag)
859 usage();
860 c_flag++;
861 break;
862 case 'k':
863 k_flag++;
864 break;
865 case 's':
866 if (c_flag)
867 usage();
868 s_flag++;
869 break;
Ben Lindstromd94580c2001-07-04 03:48:02 +0000870 case 'd':
871 if (d_flag)
872 usage();
873 d_flag++;
874 break;
Damien Miller95def091999-11-25 00:26:21 +1100875 default:
876 usage();
877 }
Damien Miller792c5111999-10-29 09:47:09 +1000878 }
Damien Miller95def091999-11-25 00:26:21 +1100879 ac -= optind;
880 av += optind;
Damien Miller792c5111999-10-29 09:47:09 +1000881
Ben Lindstromd94580c2001-07-04 03:48:02 +0000882 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag))
Damien Miller95def091999-11-25 00:26:21 +1100883 usage();
Damien Miller792c5111999-10-29 09:47:09 +1000884
Ben Lindstromd94580c2001-07-04 03:48:02 +0000885 if (ac == 0 && !c_flag && !k_flag && !s_flag && !d_flag) {
Damien Miller95def091999-11-25 00:26:21 +1100886 shell = getenv("SHELL");
887 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
888 c_flag = 1;
Damien Miller792c5111999-10-29 09:47:09 +1000889 }
Damien Miller95def091999-11-25 00:26:21 +1100890 if (k_flag) {
891 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
892 if (pidstr == NULL) {
893 fprintf(stderr, "%s not set, cannot kill agent\n",
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000894 SSH_AGENTPID_ENV_NAME);
Damien Miller95def091999-11-25 00:26:21 +1100895 exit(1);
896 }
897 pid = atoi(pidstr);
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000898 if (pid < 1) {
Damien Miller95def091999-11-25 00:26:21 +1100899 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000900 SSH_AGENTPID_ENV_NAME, pidstr);
Damien Miller95def091999-11-25 00:26:21 +1100901 exit(1);
902 }
903 if (kill(pid, SIGTERM) == -1) {
904 perror("kill");
905 exit(1);
906 }
907 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
908 printf(format, SSH_AUTHSOCKET_ENV_NAME);
909 printf(format, SSH_AGENTPID_ENV_NAME);
910 printf("echo Agent pid %d killed;\n", pid);
911 exit(0);
Damien Miller792c5111999-10-29 09:47:09 +1000912 }
Damien Miller95def091999-11-25 00:26:21 +1100913 parent_pid = getpid();
914
915 /* Create private directory for agent socket */
916 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
917 if (mkdtemp(socket_dir) == NULL) {
918 perror("mkdtemp: private socket dir");
919 exit(1);
Damien Miller792c5111999-10-29 09:47:09 +1000920 }
Damien Miller95def091999-11-25 00:26:21 +1100921 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000922 parent_pid);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000923
Damien Miller5428f641999-11-25 11:54:57 +1100924 /*
925 * Create socket early so it will exist before command gets run from
926 * the parent.
927 */
Damien Miller95def091999-11-25 00:26:21 +1100928 sock = socket(AF_UNIX, SOCK_STREAM, 0);
929 if (sock < 0) {
930 perror("socket");
931 cleanup_exit(1);
Damien Miller792c5111999-10-29 09:47:09 +1000932 }
Damien Miller95def091999-11-25 00:26:21 +1100933 memset(&sunaddr, 0, sizeof(sunaddr));
934 sunaddr.sun_family = AF_UNIX;
935 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
Ben Lindstrom3524d692001-05-03 22:59:24 +0000936#ifdef HAVE_CYGWIN
937 prev_mask = umask(0177);
938#endif
Damien Miller95def091999-11-25 00:26:21 +1100939 if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) {
940 perror("bind");
Ben Lindstrom3524d692001-05-03 22:59:24 +0000941#ifdef HAVE_CYGWIN
942 umask(prev_mask);
943#endif
Damien Miller95def091999-11-25 00:26:21 +1100944 cleanup_exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000945 }
Ben Lindstrom3524d692001-05-03 22:59:24 +0000946#ifdef HAVE_CYGWIN
947 umask(prev_mask);
948#endif
Damien Miller95def091999-11-25 00:26:21 +1100949 if (listen(sock, 5) < 0) {
950 perror("listen");
951 cleanup_exit(1);
952 }
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000953
Damien Miller5428f641999-11-25 11:54:57 +1100954 /*
955 * Fork, and have the parent execute the command, if any, or present
956 * the socket data. The child continues as the authentication agent.
957 */
Ben Lindstromd94580c2001-07-04 03:48:02 +0000958 if (d_flag) {
959 log_init(__progname, SYSLOG_LEVEL_DEBUG1, SYSLOG_FACILITY_AUTH, 1);
960 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
961 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
962 SSH_AUTHSOCKET_ENV_NAME);
963 printf("echo Agent pid %d;\n", parent_pid);
964 goto skip;
965 }
Damien Miller95def091999-11-25 00:26:21 +1100966 pid = fork();
967 if (pid == -1) {
968 perror("fork");
Damien Millerc653b892002-02-08 22:05:41 +1100969 cleanup_exit(1);
Damien Miller95def091999-11-25 00:26:21 +1100970 }
971 if (pid != 0) { /* Parent - execute the given command. */
972 close(sock);
973 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
974 if (ac == 0) {
975 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
976 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000977 SSH_AUTHSOCKET_ENV_NAME);
Damien Miller95def091999-11-25 00:26:21 +1100978 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
Ben Lindstrom226cfa02001-01-22 05:34:40 +0000979 SSH_AGENTPID_ENV_NAME);
Damien Miller95def091999-11-25 00:26:21 +1100980 printf("echo Agent pid %d;\n", pid);
981 exit(0);
982 }
Damien Millere4340be2000-09-16 13:29:08 +1100983 if (setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1) == -1 ||
984 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1) == -1) {
985 perror("setenv");
986 exit(1);
987 }
Damien Miller95def091999-11-25 00:26:21 +1100988 execvp(av[0], av);
989 perror(av[0]);
990 exit(1);
991 }
Damien Millerc653b892002-02-08 22:05:41 +1100992 /* child */
993 log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0);
Ben Lindstrom3fdf8762001-07-22 20:40:24 +0000994
995 if (setsid() == -1) {
Damien Millerc653b892002-02-08 22:05:41 +1100996 error("setsid: %s", strerror(errno));
Ben Lindstrom3fdf8762001-07-22 20:40:24 +0000997 cleanup_exit(1);
998 }
999
1000 (void)chdir("/");
Damien Miller95def091999-11-25 00:26:21 +11001001 close(0);
1002 close(1);
1003 close(2);
1004
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001005#ifdef HAVE_SETRLIMIT
Ben Lindstromc72745a2000-12-02 19:03:54 +00001006 /* deny core dumps, since memory contains unencrypted private keys */
1007 rlim.rlim_cur = rlim.rlim_max = 0;
1008 if (setrlimit(RLIMIT_CORE, &rlim) < 0) {
Damien Millerc653b892002-02-08 22:05:41 +11001009 error("setrlimit RLIMIT_CORE: %s", strerror(errno));
Ben Lindstromc72745a2000-12-02 19:03:54 +00001010 cleanup_exit(1);
1011 }
Ben Lindstrom2c467a22000-12-27 04:57:41 +00001012#endif
Ben Lindstromd94580c2001-07-04 03:48:02 +00001013
1014skip:
Damien Millerc653b892002-02-08 22:05:41 +11001015 fatal_add_cleanup(cleanup_socket, NULL);
Damien Miller95def091999-11-25 00:26:21 +11001016 new_socket(AUTH_SOCKET, sock);
1017 if (ac > 0) {
1018 signal(SIGALRM, check_parent_exists);
1019 alarm(10);
1020 }
Damien Millerad833b32000-08-23 10:46:23 +10001021 idtab_init();
Damien Miller48bfa9c2001-07-14 12:12:55 +10001022 if (!d_flag)
Ben Lindstromd94580c2001-07-04 03:48:02 +00001023 signal(SIGINT, SIG_IGN);
Damien Miller48bfa9c2001-07-14 12:12:55 +10001024 signal(SIGPIPE, SIG_IGN);
Ben Lindstrom77808ab2001-01-26 05:10:34 +00001025 signal(SIGHUP, cleanup_handler);
1026 signal(SIGTERM, cleanup_handler);
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +00001027 nalloc = 0;
1028
Damien Miller95def091999-11-25 00:26:21 +11001029 while (1) {
Ben Lindstroma3d5a4c2001-07-18 15:58:08 +00001030 prepare_select(&readsetp, &writesetp, &max_fd, &nalloc);
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001031 if (select(max_fd + 1, readsetp, writesetp, NULL, NULL) < 0) {
Damien Miller95def091999-11-25 00:26:21 +11001032 if (errno == EINTR)
1033 continue;
Damien Millerc653b892002-02-08 22:05:41 +11001034 fatal("select: %s", strerror(errno));
Damien Miller95def091999-11-25 00:26:21 +11001035 }
Ben Lindstrom226cfa02001-01-22 05:34:40 +00001036 after_select(readsetp, writesetp);
Damien Miller95def091999-11-25 00:26:21 +11001037 }
1038 /* NOTREACHED */
Damien Millerd4a8b7e1999-10-27 13:42:43 +10001039}