blob: 70c2a7f65d1c5b330405952fb6efda0fdccef40a [file] [log] [blame]
Damien Miller95def091999-11-25 00:26:21 +11001/* $OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt 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
7 * Created: Wed Mar 29 03:46:59 1995 ylo
8 * The authentication agent program.
9 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +100010
11#include "includes.h"
Damien Miller95def091999-11-25 00:26:21 +110012RCSID("$OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt Exp $");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100013
14#include "ssh.h"
15#include "rsa.h"
16#include "authfd.h"
17#include "buffer.h"
18#include "bufaux.h"
19#include "xmalloc.h"
20#include "packet.h"
21#include "getput.h"
22#include "mpaux.h"
23
Damien Miller7f6ea021999-10-28 13:25:17 +100024#ifdef HAVE_OPENSSL
Damien Millerd4a8b7e1999-10-27 13:42:43 +100025#include <openssl/md5.h>
Damien Miller7f6ea021999-10-28 13:25:17 +100026#endif
27#ifdef HAVE_SSL
28#include <ssl/md5.h>
29#endif
Damien Millerd4a8b7e1999-10-27 13:42:43 +100030
Damien Miller95def091999-11-25 00:26:21 +110031typedef struct {
32 int fd;
33 enum {
34 AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION
35 } type;
36 Buffer input;
37 Buffer output;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100038} SocketEntry;
39
40unsigned int sockets_alloc = 0;
41SocketEntry *sockets = NULL;
42
Damien Miller95def091999-11-25 00:26:21 +110043typedef struct {
44 RSA *key;
45 char *comment;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100046} Identity;
47
48unsigned int num_identities = 0;
49Identity *identities = NULL;
50
51int max_fd = 0;
52
53/* pid of shell == parent of agent */
54int parent_pid = -1;
55
56/* pathname and directory for AUTH_SOCKET */
57char socket_name[1024];
58char socket_dir[1024];
59
Damien Miller95def091999-11-25 00:26:21 +110060#ifdef HAVE___PROGNAME
61extern char *__progname;
62#else /* HAVE___PROGNAME */
63const char *__progname = "ssh-agent";
64#endif /* HAVE___PROGNAME */
65
Damien Millerd4a8b7e1999-10-27 13:42:43 +100066void
67process_request_identity(SocketEntry *e)
68{
Damien Miller95def091999-11-25 00:26:21 +110069 Buffer msg;
70 int i;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100071
Damien Miller95def091999-11-25 00:26:21 +110072 buffer_init(&msg);
73 buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
74 buffer_put_int(&msg, num_identities);
75 for (i = 0; i < num_identities; i++) {
76 buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
77 buffer_put_bignum(&msg, identities[i].key->e);
78 buffer_put_bignum(&msg, identities[i].key->n);
79 buffer_put_string(&msg, identities[i].comment,
80 strlen(identities[i].comment));
81 }
82 buffer_put_int(&e->output, buffer_len(&msg));
83 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
84 buffer_free(&msg);
Damien Millerd4a8b7e1999-10-27 13:42:43 +100085}
86
87void
88process_authentication_challenge(SocketEntry *e)
89{
Damien Miller95def091999-11-25 00:26:21 +110090 int i, pub_bits, len;
91 BIGNUM *pub_e, *pub_n, *challenge;
92 Buffer msg;
93 MD5_CTX md;
94 unsigned char buf[32], mdbuf[16], session_id[16];
95 unsigned int response_type;
Damien Millerd4a8b7e1999-10-27 13:42:43 +100096
Damien Miller95def091999-11-25 00:26:21 +110097 buffer_init(&msg);
98 pub_e = BN_new();
99 pub_n = BN_new();
100 challenge = BN_new();
101 pub_bits = buffer_get_int(&e->input);
102 buffer_get_bignum(&e->input, pub_e);
103 buffer_get_bignum(&e->input, pub_n);
104 buffer_get_bignum(&e->input, challenge);
105 if (buffer_len(&e->input) == 0) {
106 /* Compatibility code for old servers. */
107 memset(session_id, 0, 16);
108 response_type = 0;
109 } else {
110 /* New code. */
111 buffer_get(&e->input, (char *) session_id, 16);
112 response_type = buffer_get_int(&e->input);
113 }
114 for (i = 0; i < num_identities; i++)
115 if (pub_bits == BN_num_bits(identities[i].key->n) &&
116 BN_cmp(pub_e, identities[i].key->e) == 0 &&
117 BN_cmp(pub_n, identities[i].key->n) == 0) {
118 /* Decrypt the challenge using the private key. */
119 rsa_private_decrypt(challenge, challenge, identities[i].key);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000120
Damien Miller95def091999-11-25 00:26:21 +1100121 /* Compute the desired response. */
122 switch (response_type) {
123 case 0:/* As of protocol 1.0 */
124 /* This response type is no longer supported. */
125 log("Compatibility with ssh protocol 1.0 no longer supported.");
126 buffer_put_char(&msg, SSH_AGENT_FAILURE);
127 goto send;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000128
Damien Miller95def091999-11-25 00:26:21 +1100129 case 1:/* As of protocol 1.1 */
130 /* The response is MD5 of decrypted challenge plus session id. */
131 len = BN_num_bytes(challenge);
Damien Millerfd7c9111999-11-08 16:15:55 +1100132
Damien Miller95def091999-11-25 00:26:21 +1100133 if (len <= 0 || len > 32) {
134 fatal("process_authentication_challenge: "
135 "bad challenge length %d", len);
136 }
137 memset(buf, 0, 32);
138 BN_bn2bin(challenge, buf + 32 - len);
139 MD5_Init(&md);
140 MD5_Update(&md, buf, 32);
141 MD5_Update(&md, session_id, 16);
142 MD5_Final(mdbuf, &md);
143 break;
Damien Millerfd7c9111999-11-08 16:15:55 +1100144
Damien Miller95def091999-11-25 00:26:21 +1100145 default:
146 fatal("process_authentication_challenge: bad response_type %d",
147 response_type);
148 break;
149 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000150
Damien Miller95def091999-11-25 00:26:21 +1100151 /* Send the response. */
152 buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
153 for (i = 0; i < 16; i++)
154 buffer_put_char(&msg, mdbuf[i]);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000155
Damien Miller95def091999-11-25 00:26:21 +1100156 goto send;
157 }
158 /* Unknown identity. Send failure. */
159 buffer_put_char(&msg, SSH_AGENT_FAILURE);
160send:
161 buffer_put_int(&e->output, buffer_len(&msg));
162 buffer_append(&e->output, buffer_ptr(&msg),
163 buffer_len(&msg));
164 buffer_free(&msg);
165 BN_clear_free(pub_e);
166 BN_clear_free(pub_n);
167 BN_clear_free(challenge);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000168}
169
170void
171process_remove_identity(SocketEntry *e)
172{
Damien Miller95def091999-11-25 00:26:21 +1100173 unsigned int bits;
174 unsigned int i;
175 BIGNUM *dummy, *n;
Damien Miller7e8e8201999-11-16 13:37:16 +1100176
Damien Miller95def091999-11-25 00:26:21 +1100177 dummy = BN_new();
178 n = BN_new();
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000179
Damien Miller95def091999-11-25 00:26:21 +1100180 /* Get the key from the packet. */
181 bits = buffer_get_int(&e->input);
182 buffer_get_bignum(&e->input, dummy);
183 buffer_get_bignum(&e->input, n);
184
185 if (bits != BN_num_bits(n))
186 error("Warning: keysize mismatch: actual %d, announced %d",
187 BN_num_bits(n), bits);
188
189 /* Check if we have the key. */
190 for (i = 0; i < num_identities; i++)
191 if (BN_cmp(identities[i].key->n, n) == 0) {
192 /* We have this key. Free the old key. Since we
193 don\'t want to leave empty slots in the middle
194 of the array, we actually free the key there
195 and copy data from the last entry. */
196 RSA_free(identities[i].key);
197 xfree(identities[i].comment);
198 if (i < num_identities - 1)
199 identities[i] = identities[num_identities - 1];
200 num_identities--;
201 BN_clear_free(dummy);
202 BN_clear_free(n);
203
204 /* Send success. */
205 buffer_put_int(&e->output, 1);
206 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
207 return;
208 }
209 /* We did not have the key. */
210 BN_clear(dummy);
211 BN_clear(n);
212
213 /* Send failure. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000214 buffer_put_int(&e->output, 1);
Damien Miller95def091999-11-25 00:26:21 +1100215 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000216}
217
Damien Miller95def091999-11-25 00:26:21 +1100218/*
219 * Removes all identities from the agent.
220 */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000221void
222process_remove_all_identities(SocketEntry *e)
223{
Damien Miller95def091999-11-25 00:26:21 +1100224 unsigned int i;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000225
Damien Miller95def091999-11-25 00:26:21 +1100226 /* Loop over all identities and clear the keys. */
227 for (i = 0; i < num_identities; i++) {
228 RSA_free(identities[i].key);
229 xfree(identities[i].comment);
230 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000231
Damien Miller95def091999-11-25 00:26:21 +1100232 /* Mark that there are no identities. */
233 num_identities = 0;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000234
235 /* Send success. */
236 buffer_put_int(&e->output, 1);
237 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
238 return;
Damien Miller95def091999-11-25 00:26:21 +1100239}
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000240
Damien Miller95def091999-11-25 00:26:21 +1100241/*
242 * Adds an identity to the agent.
243 */
244void
245process_add_identity(SocketEntry *e)
246{
247 RSA *k;
248 int i;
249 BIGNUM *aux;
250 BN_CTX *ctx;
251
252 if (num_identities == 0)
253 identities = xmalloc(sizeof(Identity));
254 else
255 identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
256
257 identities[num_identities].key = RSA_new();
258 k = identities[num_identities].key;
259 buffer_get_int(&e->input); /* bits */
260 k->n = BN_new();
261 buffer_get_bignum(&e->input, k->n);
262 k->e = BN_new();
263 buffer_get_bignum(&e->input, k->e);
264 k->d = BN_new();
265 buffer_get_bignum(&e->input, k->d);
266 k->iqmp = BN_new();
267 buffer_get_bignum(&e->input, k->iqmp);
268 /* SSH and SSL have p and q swapped */
269 k->q = BN_new();
270 buffer_get_bignum(&e->input, k->q); /* p */
271 k->p = BN_new();
272 buffer_get_bignum(&e->input, k->p); /* q */
273
274 /* Generate additional parameters */
275 aux = BN_new();
276 ctx = BN_CTX_new();
277
278 BN_sub(aux, k->q, BN_value_one());
279 k->dmq1 = BN_new();
280 BN_mod(k->dmq1, k->d, aux, ctx);
281
282 BN_sub(aux, k->p, BN_value_one());
283 k->dmp1 = BN_new();
284 BN_mod(k->dmp1, k->d, aux, ctx);
285
286 BN_clear_free(aux);
287 BN_CTX_free(ctx);
288
289 identities[num_identities].comment = buffer_get_string(&e->input, NULL);
290
291 /* Check if we already have the key. */
292 for (i = 0; i < num_identities; i++)
293 if (BN_cmp(identities[i].key->n, k->n) == 0) {
294 /* We already have this key. Clear and free the
295 new data and return success. */
296 RSA_free(k);
297 xfree(identities[num_identities].comment);
298
299 /* Send success. */
300 buffer_put_int(&e->output, 1);
301 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
302 return;
303 }
304 /* Increment the number of identities. */
305 num_identities++;
306
307 /* Send a success message. */
308 buffer_put_int(&e->output, 1);
309 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000310}
311
312void
313process_message(SocketEntry *e)
314{
Damien Miller95def091999-11-25 00:26:21 +1100315 unsigned int msg_len;
316 unsigned int type;
317 unsigned char *cp;
318 if (buffer_len(&e->input) < 5)
319 return; /* Incomplete message. */
320 cp = (unsigned char *) buffer_ptr(&e->input);
321 msg_len = GET_32BIT(cp);
322 if (msg_len > 256 * 1024) {
323 shutdown(e->fd, SHUT_RDWR);
324 close(e->fd);
325 e->type = AUTH_UNUSED;
326 return;
327 }
328 if (buffer_len(&e->input) < msg_len + 4)
329 return;
330 buffer_consume(&e->input, 4);
331 type = buffer_get_char(&e->input);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000332
Damien Miller95def091999-11-25 00:26:21 +1100333 switch (type) {
334 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
335 process_request_identity(e);
336 break;
337 case SSH_AGENTC_RSA_CHALLENGE:
338 process_authentication_challenge(e);
339 break;
340 case SSH_AGENTC_ADD_RSA_IDENTITY:
341 process_add_identity(e);
342 break;
343 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
344 process_remove_identity(e);
345 break;
346 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
347 process_remove_all_identities(e);
348 break;
349 default:
350 /* Unknown message. Respond with failure. */
351 error("Unknown message %d", type);
352 buffer_clear(&e->input);
353 buffer_put_int(&e->output, 1);
354 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
355 break;
356 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000357}
358
359void
360new_socket(int type, int fd)
361{
Damien Miller95def091999-11-25 00:26:21 +1100362 unsigned int i, old_alloc;
363 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
364 error("fcntl O_NONBLOCK: %s", strerror(errno));
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000365
Damien Miller95def091999-11-25 00:26:21 +1100366 if (fd > max_fd)
367 max_fd = fd;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000368
Damien Miller95def091999-11-25 00:26:21 +1100369 for (i = 0; i < sockets_alloc; i++)
370 if (sockets[i].type == AUTH_UNUSED) {
371 sockets[i].fd = fd;
372 sockets[i].type = type;
373 buffer_init(&sockets[i].input);
374 buffer_init(&sockets[i].output);
375 return;
376 }
377 old_alloc = sockets_alloc;
378 sockets_alloc += 10;
379 if (sockets)
380 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
381 else
382 sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
383 for (i = old_alloc; i < sockets_alloc; i++)
384 sockets[i].type = AUTH_UNUSED;
385 sockets[old_alloc].type = type;
386 sockets[old_alloc].fd = fd;
387 buffer_init(&sockets[old_alloc].input);
388 buffer_init(&sockets[old_alloc].output);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000389}
390
391void
392prepare_select(fd_set *readset, fd_set *writeset)
393{
Damien Miller95def091999-11-25 00:26:21 +1100394 unsigned int i;
395 for (i = 0; i < sockets_alloc; i++)
396 switch (sockets[i].type) {
397 case AUTH_SOCKET:
398 case AUTH_CONNECTION:
399 FD_SET(sockets[i].fd, readset);
400 if (buffer_len(&sockets[i].output) > 0)
401 FD_SET(sockets[i].fd, writeset);
402 break;
403 case AUTH_UNUSED:
404 break;
405 default:
406 fatal("Unknown socket type %d", sockets[i].type);
407 break;
408 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000409}
410
Damien Miller95def091999-11-25 00:26:21 +1100411void
412after_select(fd_set *readset, fd_set *writeset)
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000413{
Damien Miller95def091999-11-25 00:26:21 +1100414 unsigned int i;
415 int len, sock;
416 char buf[1024];
417 struct sockaddr_un sunaddr;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000418
Damien Miller95def091999-11-25 00:26:21 +1100419 for (i = 0; i < sockets_alloc; i++)
420 switch (sockets[i].type) {
421 case AUTH_UNUSED:
422 break;
423 case AUTH_SOCKET:
424 if (FD_ISSET(sockets[i].fd, readset)) {
425 len = sizeof(sunaddr);
426 sock = accept(sockets[i].fd, (struct sockaddr *) & sunaddr, &len);
427 if (sock < 0) {
428 perror("accept from AUTH_SOCKET");
429 break;
430 }
431 new_socket(AUTH_CONNECTION, sock);
432 }
433 break;
434 case AUTH_CONNECTION:
435 if (buffer_len(&sockets[i].output) > 0 &&
436 FD_ISSET(sockets[i].fd, writeset)) {
437 len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
438 buffer_len(&sockets[i].output));
439 if (len <= 0) {
440 shutdown(sockets[i].fd, SHUT_RDWR);
441 close(sockets[i].fd);
442 sockets[i].type = AUTH_UNUSED;
443 break;
444 }
445 buffer_consume(&sockets[i].output, len);
446 }
447 if (FD_ISSET(sockets[i].fd, readset)) {
448 len = read(sockets[i].fd, buf, sizeof(buf));
449 if (len <= 0) {
450 shutdown(sockets[i].fd, SHUT_RDWR);
451 close(sockets[i].fd);
452 sockets[i].type = AUTH_UNUSED;
453 break;
454 }
455 buffer_append(&sockets[i].input, buf, len);
456 process_message(&sockets[i]);
457 }
458 break;
459 default:
460 fatal("Unknown type %d", sockets[i].type);
461 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000462}
463
464void
465check_parent_exists(int sig)
466{
Damien Miller95def091999-11-25 00:26:21 +1100467 if (kill(parent_pid, 0) < 0) {
468 /* printf("Parent has died - Authentication agent exiting.\n"); */
469 exit(1);
470 }
471 signal(SIGALRM, check_parent_exists);
472 alarm(10);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000473}
474
Damien Miller792c5111999-10-29 09:47:09 +1000475void
476cleanup_socket(void)
477{
Damien Miller95def091999-11-25 00:26:21 +1100478 remove(socket_name);
479 rmdir(socket_dir);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000480}
481
Damien Miller792c5111999-10-29 09:47:09 +1000482void
483cleanup_exit(int i)
484{
Damien Miller95def091999-11-25 00:26:21 +1100485 cleanup_socket();
486 exit(i);
Damien Miller792c5111999-10-29 09:47:09 +1000487}
488
489void
490usage()
491{
Damien Miller95def091999-11-25 00:26:21 +1100492 fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
493 fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
494 __progname);
495 exit(1);
Damien Miller792c5111999-10-29 09:47:09 +1000496}
497
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000498int
499main(int ac, char **av)
500{
Damien Miller95def091999-11-25 00:26:21 +1100501 fd_set readset, writeset;
502 int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
503 struct sockaddr_un sunaddr;
504 pid_t pid;
505 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000506
Damien Miller95def091999-11-25 00:26:21 +1100507 /* check if RSA support exists */
508 if (rsa_alive() == 0) {
509 fprintf(stderr,
510 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
511 __progname);
512 exit(1);
513 }
Damien Millerb77870f1999-11-10 12:48:08 +1100514#if defined(__GNU_LIBRARY__)
Damien Miller95def091999-11-25 00:26:21 +1100515 while ((ch = getopt(ac, av, "+cks")) != -1) {
Damien Millerb77870f1999-11-10 12:48:08 +1100516#else
Damien Miller95def091999-11-25 00:26:21 +1100517 while ((ch = getopt(ac, av, "cks")) != -1) {
Damien Millerb77870f1999-11-10 12:48:08 +1100518#endif /* defined(__GNU_LIBRARY__) */
Damien Miller95def091999-11-25 00:26:21 +1100519 switch (ch) {
520 case 'c':
521 if (s_flag)
522 usage();
523 c_flag++;
524 break;
525 case 'k':
526 k_flag++;
527 break;
528 case 's':
529 if (c_flag)
530 usage();
531 s_flag++;
532 break;
533 default:
534 usage();
535 }
Damien Miller792c5111999-10-29 09:47:09 +1000536 }
Damien Miller95def091999-11-25 00:26:21 +1100537 ac -= optind;
538 av += optind;
Damien Miller792c5111999-10-29 09:47:09 +1000539
Damien Miller95def091999-11-25 00:26:21 +1100540 if (ac > 0 && (c_flag || k_flag || s_flag))
541 usage();
Damien Miller792c5111999-10-29 09:47:09 +1000542
Damien Miller95def091999-11-25 00:26:21 +1100543 if (ac == 0 && !c_flag && !k_flag && !s_flag) {
544 shell = getenv("SHELL");
545 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
546 c_flag = 1;
Damien Miller792c5111999-10-29 09:47:09 +1000547 }
Damien Miller95def091999-11-25 00:26:21 +1100548 if (k_flag) {
549 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
550 if (pidstr == NULL) {
551 fprintf(stderr, "%s not set, cannot kill agent\n",
552 SSH_AGENTPID_ENV_NAME);
553 exit(1);
554 }
555 pid = atoi(pidstr);
556 if (pid < 1) { /* XXX PID_MAX check too */
557 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
558 SSH_AGENTPID_ENV_NAME, pidstr);
559 exit(1);
560 }
561 if (kill(pid, SIGTERM) == -1) {
562 perror("kill");
563 exit(1);
564 }
565 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
566 printf(format, SSH_AUTHSOCKET_ENV_NAME);
567 printf(format, SSH_AGENTPID_ENV_NAME);
568 printf("echo Agent pid %d killed;\n", pid);
569 exit(0);
Damien Miller792c5111999-10-29 09:47:09 +1000570 }
Damien Miller95def091999-11-25 00:26:21 +1100571 parent_pid = getpid();
572
573 /* Create private directory for agent socket */
574 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
575 if (mkdtemp(socket_dir) == NULL) {
576 perror("mkdtemp: private socket dir");
577 exit(1);
Damien Miller792c5111999-10-29 09:47:09 +1000578 }
Damien Miller95def091999-11-25 00:26:21 +1100579 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
580 parent_pid);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000581
Damien Miller95def091999-11-25 00:26:21 +1100582 /* Create socket early so it will exist before command gets run
583 from the parent. */
584 sock = socket(AF_UNIX, SOCK_STREAM, 0);
585 if (sock < 0) {
586 perror("socket");
587 cleanup_exit(1);
Damien Miller792c5111999-10-29 09:47:09 +1000588 }
Damien Miller95def091999-11-25 00:26:21 +1100589 memset(&sunaddr, 0, sizeof(sunaddr));
590 sunaddr.sun_family = AF_UNIX;
591 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
592 if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) {
593 perror("bind");
594 cleanup_exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000595 }
Damien Miller95def091999-11-25 00:26:21 +1100596 if (listen(sock, 5) < 0) {
597 perror("listen");
598 cleanup_exit(1);
599 }
600 /* Fork, and have the parent execute the command, if any, or
601 present the socket data. The child continues as the
602 authentication agent. */
603 pid = fork();
604 if (pid == -1) {
605 perror("fork");
606 exit(1);
607 }
608 if (pid != 0) { /* Parent - execute the given command. */
609 close(sock);
610 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
611 if (ac == 0) {
612 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
613 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
614 SSH_AUTHSOCKET_ENV_NAME);
615 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
616 SSH_AGENTPID_ENV_NAME);
617 printf("echo Agent pid %d;\n", pid);
618 exit(0);
619 }
620 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
621 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
622 execvp(av[0], av);
623 perror(av[0]);
624 exit(1);
625 }
626 close(0);
627 close(1);
628 close(2);
629
630 if (setsid() == -1) {
631 perror("setsid");
632 cleanup_exit(1);
633 }
634 if (atexit(cleanup_socket) < 0) {
635 perror("atexit");
636 cleanup_exit(1);
637 }
638 new_socket(AUTH_SOCKET, sock);
639 if (ac > 0) {
640 signal(SIGALRM, check_parent_exists);
641 alarm(10);
642 }
643 signal(SIGINT, SIG_IGN);
644 signal(SIGPIPE, SIG_IGN);
645 while (1) {
646 FD_ZERO(&readset);
647 FD_ZERO(&writeset);
648 prepare_select(&readset, &writeset);
649 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0) {
650 if (errno == EINTR)
651 continue;
652 exit(1);
653 }
654 after_select(&readset, &writeset);
655 }
656 /* NOTREACHED */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000657}