]> andersk Git - openssh.git/blame - ssh-agent.c
- stevesk@cvs.openbsd.org 2002/02/05 15:50:12
[openssh.git] / ssh-agent.c
CommitLineData
fccfbe3b 1/* $OpenBSD: ssh-agent.c,v 1.81 2002/02/05 15:50:12 stevesk Exp $ */
dae3fa13 2
8efc0c15 3/*
5260325f 4 * Author: Tatu Ylonen <ylo@cs.hut.fi>
5 * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
6 * All rights reserved
5260325f 7 * The authentication agent program.
2e73a022 8 *
bcbf86ec 9 * 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 *
a96070d4 15 * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
bcbf86ec 16 *
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.
5260325f 36 */
8efc0c15 37
38#include "includes.h"
fccfbe3b 39RCSID("$OpenBSD: ssh-agent.c,v 1.81 2002/02/05 15:50:12 stevesk Exp $");
42f11eb2 40
187cd1fa 41#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
42f11eb2 48#include <openssl/evp.h>
49#include <openssl/md5.h>
8efc0c15 50
51#include "ssh.h"
52#include "rsa.h"
8efc0c15 53#include "buffer.h"
54#include "bufaux.h"
55#include "xmalloc.h"
8efc0c15 56#include "getput.h"
4c8722d9 57#include "key.h"
58#include "authfd.h"
188adeb2 59#include "compat.h"
42f11eb2 60#include "log.h"
8efc0c15 61
2b5fe3b8 62#ifdef SMARTCARD
63#include <openssl/engine.h>
64#include "scard.h"
dd2495cb 65#endif
2b5fe3b8 66
17a3011c 67typedef enum {
68 AUTH_UNUSED,
69 AUTH_SOCKET,
70 AUTH_CONNECTION
71} sock_type;
72
5260325f 73typedef struct {
74 int fd;
17a3011c 75 sock_type type;
5260325f 76 Buffer input;
77 Buffer output;
8efc0c15 78} SocketEntry;
79
1e3b8b07 80u_int sockets_alloc = 0;
8efc0c15 81SocketEntry *sockets = NULL;
82
95f0a918 83typedef struct identity {
84 TAILQ_ENTRY(identity) next;
2e73a022 85 Key *key;
5260325f 86 char *comment;
8efc0c15 87} Identity;
88
2e73a022 89typedef struct {
90 int nentries;
95f0a918 91 TAILQ_HEAD(idqueue, identity) idlist;
2e73a022 92} Idtab;
93
94/* private key table, one per protocol version */
95Idtab idtable[3];
8efc0c15 96
97int max_fd = 0;
98
99/* pid of shell == parent of agent */
9da5c3c9 100pid_t parent_pid = -1;
8efc0c15 101
102/* pathname and directory for AUTH_SOCKET */
103char socket_name[1024];
104char socket_dir[1024];
105
5260325f 106#ifdef HAVE___PROGNAME
107extern char *__progname;
260d427b 108#else
109char *__progname;
110#endif
5260325f 111
396c147e 112static void
2e73a022 113idtab_init(void)
114{
115 int i;
6aacefa7 116 for (i = 0; i <=2; i++) {
95f0a918 117 TAILQ_INIT(&idtable[i].idlist);
2e73a022 118 idtable[i].nentries = 0;
119 }
120}
121
122/* return private key table for requested protocol version */
396c147e 123static Idtab *
2e73a022 124idtab_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 */
95f0a918 132static Identity *
133lookup_identity(Key *key, int version)
2e73a022 134{
95f0a918 135 Identity *id;
136
2e73a022 137 Idtab *tab = idtab_lookup(version);
95f0a918 138 TAILQ_FOREACH(id, &tab->idlist, next) {
139 if (key_equal(key, id->key))
140 return (id);
2e73a022 141 }
95f0a918 142 return (NULL);
143}
144
145static void
146free_identity(Identity *id)
147{
148 key_free(id->key);
149 xfree(id->comment);
150 xfree(id);
2e73a022 151}
152
153/* send list of supported public keys to 'client' */
396c147e 154static void
2e73a022 155process_request_identities(SocketEntry *e, int version)
8efc0c15 156{
2e73a022 157 Idtab *tab = idtab_lookup(version);
5260325f 158 Buffer msg;
95f0a918 159 Identity *id;
5260325f 160
161 buffer_init(&msg);
2e73a022 162 buffer_put_char(&msg, (version == 1) ?
163 SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER);
164 buffer_put_int(&msg, tab->nentries);
95f0a918 165 TAILQ_FOREACH(id, &tab->idlist, next) {
fa08c86b 166 if (id->key->type == KEY_RSA1) {
2e73a022 167 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 {
1e3b8b07 171 u_char *blob;
172 u_int blen;
fa08c86b 173 key_to_blob(id->key, &blob, &blen);
2e73a022 174 buffer_put_string(&msg, blob, blen);
175 xfree(blob);
176 }
177 buffer_put_cstring(&msg, id->comment);
5260325f 178 }
179 buffer_put_int(&e->output, buffer_len(&msg));
180 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
181 buffer_free(&msg);
8efc0c15 182}
183
2e73a022 184/* ssh1 only */
396c147e 185static void
2e73a022 186process_authentication_challenge1(SocketEntry *e)
8efc0c15 187{
95f0a918 188 Identity *id;
189 Key *key;
2e73a022 190 BIGNUM *challenge;
191 int i, len;
5260325f 192 Buffer msg;
193 MD5_CTX md;
1e3b8b07 194 u_char buf[32], mdbuf[16], session_id[16];
195 u_int response_type;
5260325f 196
197 buffer_init(&msg);
fa08c86b 198 key = key_new(KEY_RSA1);
b775c6f2 199 if ((challenge = BN_new()) == NULL)
200 fatal("process_authentication_challenge1: BN_new failed");
5260325f 201
2e73a022 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);
205 buffer_get_bignum(&e->input, challenge);
5260325f 206
2e73a022 207 /* Only protocol 1.1 is supported */
208 if (buffer_len(&e->input) == 0)
209 goto failure;
e6207598 210 buffer_get(&e->input, session_id, 16);
2e73a022 211 response_type = buffer_get_int(&e->input);
212 if (response_type != 1)
213 goto failure;
214
95f0a918 215 id = lookup_identity(key, 1);
216 if (id != NULL) {
217 Key *private = id->key;
2e73a022 218 /* Decrypt the challenge using the private key. */
46aa2d1f 219 if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0)
220 goto failure;
2e73a022 221
222 /* 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;
5260325f 227 }
2e73a022 228 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. */
5260325f 244 buffer_put_char(&msg, SSH_AGENT_FAILURE);
245send:
2e73a022 246 buffer_put_int(&e->output, buffer_len(&msg));
247 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
248 key_free(key);
249 BN_clear_free(challenge);
250 buffer_free(&msg);
251}
252
253/* ssh2 only */
396c147e 254static void
2e73a022 255process_sign_request2(SocketEntry *e)
256{
257 extern int datafellows;
95f0a918 258 Key *key;
1e3b8b07 259 u_char *blob, *data, *signature = NULL;
260 u_int blen, dlen, slen = 0;
188adeb2 261 int flags;
2e73a022 262 Buffer msg;
263 int ok = -1;
264
265 datafellows = 0;
227e8e86 266
2e73a022 267 blob = buffer_get_string(&e->input, &blen);
268 data = buffer_get_string(&e->input, &dlen);
188adeb2 269
270 flags = buffer_get_int(&e->input);
271 if (flags & SSH_AGENT_OLD_SIGNATURE)
272 datafellows = SSH_BUG_SIGBLOB;
2e73a022 273
fa08c86b 274 key = key_from_blob(blob, blen);
2e73a022 275 if (key != NULL) {
95f0a918 276 Identity *id = lookup_identity(key, 2);
277 if (id != NULL)
278 ok = key_sign(id->key, &signature, &slen, data, dlen);
2e73a022 279 }
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 }
5260325f 288 buffer_put_int(&e->output, buffer_len(&msg));
289 buffer_append(&e->output, buffer_ptr(&msg),
2e73a022 290 buffer_len(&msg));
5260325f 291 buffer_free(&msg);
2e73a022 292 xfree(data);
293 xfree(blob);
294 if (signature != NULL)
295 xfree(signature);
8efc0c15 296}
297
2e73a022 298/* shared */
396c147e 299static void
2e73a022 300process_remove_identity(SocketEntry *e, int version)
8efc0c15 301{
95f0a918 302 Key *key = NULL;
1e3b8b07 303 u_char *blob;
304 u_int blen;
305 u_int bits;
2e73a022 306 int success = 0;
307
6aacefa7 308 switch (version) {
2e73a022 309 case 1:
fa08c86b 310 key = key_new(KEY_RSA1);
2e73a022 311 bits = buffer_get_int(&e->input);
312 buffer_get_bignum(&e->input, key->rsa->e);
313 buffer_get_bignum(&e->input, key->rsa->n);
314
315 if (bits != key_size(key))
316 log("Warning: identity keysize mismatch: actual %d, announced %d",
42f11eb2 317 key_size(key), bits);
2e73a022 318 break;
319 case 2:
320 blob = buffer_get_string(&e->input, &blen);
fa08c86b 321 key = key_from_blob(blob, blen);
2e73a022 322 xfree(blob);
323 break;
324 }
325 if (key != NULL) {
95f0a918 326 Identity *id = lookup_identity(key, version);
327 if (id != NULL) {
aa3378df 328 /*
329 * We have this key. Free the old key. Since we
330 * don\'t want to leave empty slots in the middle of
d343d900 331 * 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.
aa3378df 334 */
2e73a022 335 Idtab *tab = idtab_lookup(version);
fa08c86b 336 if (tab->nentries < 1)
337 fatal("process_remove_identity: "
338 "internal error: tab->nentries %d",
339 tab->nentries);
95f0a918 340 TAILQ_REMOVE(&tab->idlist, id, next);
341 free_identity(id);
2e73a022 342 tab->nentries--;
343 success = 1;
5260325f 344 }
2e73a022 345 key_free(key);
346 }
8efc0c15 347 buffer_put_int(&e->output, 1);
2e73a022 348 buffer_put_char(&e->output,
349 success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
8efc0c15 350}
351
396c147e 352static void
2e73a022 353process_remove_all_identities(SocketEntry *e, int version)
8efc0c15 354{
2e73a022 355 Idtab *tab = idtab_lookup(version);
95f0a918 356 Identity *id;
8efc0c15 357
5260325f 358 /* Loop over all identities and clear the keys. */
95f0a918 359 for (id = TAILQ_FIRST(&tab->idlist); id;
360 id = TAILQ_FIRST(&tab->idlist)) {
361 TAILQ_REMOVE(&tab->idlist, id, next);
362 free_identity(id);
5260325f 363 }
8efc0c15 364
5260325f 365 /* Mark that there are no identities. */
2e73a022 366 tab->nentries = 0;
8efc0c15 367
368 /* Send success. */
369 buffer_put_int(&e->output, 1);
370 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
371 return;
5260325f 372}
373
396c147e 374static void
fa08c86b 375process_add_identity(SocketEntry *e, int version)
376{
377 Key *k = NULL;
378 char *type_name;
2e73a022 379 char *comment;
fa08c86b 380 int type, success = 0;
2e73a022 381 Idtab *tab = idtab_lookup(version);
5260325f 382
2e73a022 383 switch (version) {
384 case 1:
fa08c86b 385 k = key_new_private(KEY_RSA1);
42f11eb2 386 buffer_get_int(&e->input); /* ignored */
fa08c86b 387 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);
2e73a022 391
392 /* SSH and SSL have p and q swapped */
fa08c86b 393 buffer_get_bignum(&e->input, k->rsa->q); /* p */
394 buffer_get_bignum(&e->input, k->rsa->p); /* q */
2e73a022 395
396 /* Generate additional parameters */
2b63e803 397 rsa_generate_additional_parameters(k->rsa);
2e73a022 398 break;
399 case 2:
fa08c86b 400 type_name = buffer_get_string(&e->input, NULL);
42f11eb2 401 type = key_type_from_name(type_name);
fa08c86b 402 xfree(type_name);
6aacefa7 403 switch (type) {
fa08c86b 404 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 */
2b63e803 422 rsa_generate_additional_parameters(k->rsa);
fa08c86b 423 break;
424 default:
2e73a022 425 buffer_clear(&e->input);
2e73a022 426 goto send;
5260325f 427 }
2e73a022 428 break;
429 }
2e73a022 430 comment = buffer_get_string(&e->input, NULL);
431 if (k == NULL) {
432 xfree(comment);
433 goto send;
434 }
435 success = 1;
95f0a918 436 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);
2e73a022 441 /* Increment the number of identities. */
442 tab->nentries++;
443 } else {
444 key_free(k);
445 xfree(comment);
446 }
447send:
5260325f 448 buffer_put_int(&e->output, 1);
2e73a022 449 buffer_put_char(&e->output,
450 success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
8efc0c15 451}
452
2b5fe3b8 453
454#ifdef SMARTCARD
455static void
456process_add_smartcard_key (SocketEntry *e)
457{
458 Idtab *tab;
459 Key *n = NULL, *k = NULL;
9ff6f66f 460 char *sc_reader_id = NULL;
2b5fe3b8 461 int success = 0;
184eed6a 462
9ff6f66f 463 sc_reader_id = buffer_get_string(&e->input, NULL);
464 k = sc_get_key(sc_reader_id);
465 xfree(sc_reader_id);
2b5fe3b8 466
2b5fe3b8 467 if (k == NULL) {
468 error("sc_get_pubkey failed");
469 goto send;
470 }
471 success = 1;
472
473 tab = idtab_lookup(1);
a0e0f486 474 k->type = KEY_RSA1;
95f0a918 475 if (lookup_identity(k, 1) == NULL) {
476 Identity *id = xmalloc(sizeof(Identity));
2b5fe3b8 477 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());
95f0a918 481 id->key = n;
482 id->comment = xstrdup("rsa1 smartcard");
483 TAILQ_INSERT_TAIL(&tab->idlist, id, next);
2b5fe3b8 484 tab->nentries++;
485 }
a0e0f486 486 k->type = KEY_RSA;
2b5fe3b8 487 tab = idtab_lookup(2);
95f0a918 488 if (lookup_identity(k, 2) == NULL) {
489 Identity *id = xmalloc(sizeof(Identity));
2b5fe3b8 490 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());
95f0a918 494 id->key = n;
495 id->comment = xstrdup("rsa smartcard");
496 TAILQ_INSERT_TAIL(&tab->idlist, id, next);
2b5fe3b8 497 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{
95f0a918 509 Key *k = NULL;
2b5fe3b8 510 int success = 0;
9ff6f66f 511 char *sc_reader_id = NULL;
2b5fe3b8 512
9ff6f66f 513 sc_reader_id = buffer_get_string(&e->input, NULL);
514 k = sc_get_key(sc_reader_id);
515 xfree(sc_reader_id);
2b5fe3b8 516
9ff6f66f 517 if (k == NULL) {
2b5fe3b8 518 error("sc_get_pubkey failed");
519 } else {
95f0a918 520 Identity *id;
77261db4 521 k->type = KEY_RSA1;
95f0a918 522 id = lookup_identity(k, 1);
523 if (id != NULL) {
2b5fe3b8 524 Idtab *tab = idtab_lookup(1);
95f0a918 525 TAILQ_REMOVE(&tab->idlist, id, next);
526 free_identity(id);
2b5fe3b8 527 tab->nentries--;
528 success = 1;
529 }
77261db4 530 k->type = KEY_RSA;
95f0a918 531 id = lookup_identity(k, 2);
532 if (id != NULL) {
2b5fe3b8 533 Idtab *tab = idtab_lookup(2);
95f0a918 534 TAILQ_REMOVE(&tab->idlist, id, next);
535 free_identity(id);
2b5fe3b8 536 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}
3e984472 546#endif /* SMARTCARD */
2b5fe3b8 547
2e73a022 548/* dispatch incoming messages */
549
396c147e 550static void
8efc0c15 551process_message(SocketEntry *e)
552{
1e3b8b07 553 u_int msg_len;
554 u_int type;
555 u_char *cp;
5260325f 556 if (buffer_len(&e->input) < 5)
557 return; /* Incomplete message. */
20905a8e 558 cp = buffer_ptr(&e->input);
5260325f 559 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);
570
2b5fe3b8 571 debug("type %d", type);
5260325f 572 switch (type) {
2e73a022 573 /* ssh1 */
5260325f 574 case SSH_AGENTC_RSA_CHALLENGE:
2e73a022 575 process_authentication_challenge1(e);
576 break;
577 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
578 process_request_identities(e, 1);
5260325f 579 break;
580 case SSH_AGENTC_ADD_RSA_IDENTITY:
2e73a022 581 process_add_identity(e, 1);
5260325f 582 break;
583 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
2e73a022 584 process_remove_identity(e, 1);
5260325f 585 break;
586 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
2e73a022 587 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);
5260325f 604 break;
2b5fe3b8 605#ifdef SMARTCARD
606 case SSH_AGENTC_ADD_SMARTCARD_KEY:
607 process_add_smartcard_key(e);
184eed6a 608 break;
2b5fe3b8 609 case SSH_AGENTC_REMOVE_SMARTCARD_KEY:
610 process_remove_smartcard_key(e);
184eed6a 611 break;
3e984472 612#endif /* SMARTCARD */
5260325f 613 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 }
8efc0c15 621}
622
396c147e 623static void
17a3011c 624new_socket(sock_type type, int fd)
8efc0c15 625{
1e3b8b07 626 u_int i, old_alloc;
5260325f 627 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
628 error("fcntl O_NONBLOCK: %s", strerror(errno));
629
630 if (fd > max_fd)
631 max_fd = fd;
632
633 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);
8efc0c15 653}
654
396c147e 655static int
e364646f 656prepare_select(fd_set **fdrp, fd_set **fdwp, int *fdl, int *nallocp)
8efc0c15 657{
42f11eb2 658 u_int i, sz;
659 int n = 0;
660
661 for (i = 0; i < sockets_alloc; i++) {
5260325f 662 switch (sockets[i].type) {
663 case AUTH_SOCKET:
664 case AUTH_CONNECTION:
42f11eb2 665 n = MAX(n, sockets[i].fd);
5260325f 666 break;
667 case AUTH_UNUSED:
668 break;
669 default:
670 fatal("Unknown socket type %d", sockets[i].type);
671 break;
672 }
42f11eb2 673 }
674
675 sz = howmany(n+1, NFDBITS) * sizeof(fd_mask);
e364646f 676 if (*fdrp == NULL || sz > *nallocp) {
42f11eb2 677 if (*fdrp)
894c5fa6 678 xfree(*fdrp);
42f11eb2 679 if (*fdwp)
894c5fa6 680 xfree(*fdwp);
42f11eb2 681 *fdrp = xmalloc(sz);
682 *fdwp = xmalloc(sz);
e364646f 683 *nallocp = sz;
42f11eb2 684 }
e364646f 685 if (n < *fdl)
686 debug("XXX shrink: %d < %d", n, *fdl);
687 *fdl = n;
42f11eb2 688 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);
8efc0c15 704}
705
396c147e 706static void
5260325f 707after_select(fd_set *readset, fd_set *writeset)
8efc0c15 708{
1e3b8b07 709 u_int i;
5260325f 710 int len, sock;
610cd5c6 711 socklen_t slen;
5260325f 712 char buf[1024];
713 struct sockaddr_un sunaddr;
714
715 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)) {
610cd5c6 721 slen = sizeof(sunaddr);
42f11eb2 722 sock = accept(sockets[i].fd,
723 (struct sockaddr *) &sunaddr, &slen);
5260325f 724 if (sock < 0) {
fccfbe3b 725 error("accept from AUTH_SOCKET: %s",
726 strerror(errno));
5260325f 727 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)) {
bbc62e59 735 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);
5260325f 744 if (len <= 0) {
745 shutdown(sockets[i].fd, SHUT_RDWR);
746 close(sockets[i].fd);
747 sockets[i].type = AUTH_UNUSED;
0ac7199f 748 buffer_free(&sockets[i].input);
749 buffer_free(&sockets[i].output);
5260325f 750 break;
751 }
752 buffer_consume(&sockets[i].output, len);
753 }
754 if (FD_ISSET(sockets[i].fd, readset)) {
bbc62e59 755 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);
5260325f 762 if (len <= 0) {
763 shutdown(sockets[i].fd, SHUT_RDWR);
764 close(sockets[i].fd);
765 sockets[i].type = AUTH_UNUSED;
0ac7199f 766 buffer_free(&sockets[i].input);
767 buffer_free(&sockets[i].output);
5260325f 768 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 }
8efc0c15 777}
778
396c147e 779static void
fccfbe3b 780cleanup_socket(void *p)
dae3fa13 781{
2f4b2e38 782 if (socket_name[0])
783 unlink(socket_name);
784 if (socket_dir[0])
785 rmdir(socket_dir);
8efc0c15 786}
787
396c147e 788static void
dae3fa13 789cleanup_exit(int i)
790{
fccfbe3b 791 cleanup_socket(NULL);
5260325f 792 exit(i);
dae3fa13 793}
794
396c147e 795static void
2f4b2e38 796cleanup_handler(int sig)
797{
fccfbe3b 798 cleanup_socket(NULL);
2f4b2e38 799 _exit(2);
800}
801
7efa8482 802static void
803check_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
396c147e 816static void
cc8aca8a 817usage(void)
dae3fa13 818{
33e766d2 819 fprintf(stderr, "Usage: %s [options] [command [args ...]]\n",
0e3c1f95 820 __progname);
33e766d2 821 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");
5260325f 826 exit(1);
dae3fa13 827}
828
8efc0c15 829int
830main(int ac, char **av)
831{
e364646f 832 int sock, c_flag = 0, d_flag = 0, k_flag = 0, s_flag = 0, ch, nalloc;
5260325f 833 struct sockaddr_un sunaddr;
b03bd394 834#ifdef HAVE_SETRLIMIT
0b6fbf03 835 struct rlimit rlim;
c7ccfd39 836#endif
837#ifdef HAVE_CYGWIN
838 int prev_mask;
b03bd394 839#endif
5260325f 840 pid_t pid;
841 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
c04f75f1 842 extern int optind;
42f11eb2 843 fd_set *readsetp = NULL, *writesetp = NULL;
fcb975eb 844
457fc0c6 845 SSLeay_add_all_algorithms();
846
260d427b 847 __progname = get_progname(av[0]);
264dce47 848 init_rng();
e339aa53 849 seed_rng();
2b87da3b 850
4e577b89 851#ifdef __GNU_LIBRARY__
ffdb5d70 852 while ((ch = getopt(ac, av, "+cdks")) != -1) {
4e577b89 853#else /* __GNU_LIBRARY__ */
ffdb5d70 854 while ((ch = getopt(ac, av, "cdks")) != -1) {
4e577b89 855#endif /* __GNU_LIBRARY__ */
5260325f 856 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;
ffdb5d70 870 case 'd':
871 if (d_flag)
872 usage();
873 d_flag++;
874 break;
5260325f 875 default:
876 usage();
877 }
dae3fa13 878 }
5260325f 879 ac -= optind;
880 av += optind;
881
ffdb5d70 882 if (ac > 0 && (c_flag || k_flag || s_flag || d_flag))
5260325f 883 usage();
884
ffdb5d70 885 if (ac == 0 && !c_flag && !k_flag && !s_flag && !d_flag) {
5260325f 886 shell = getenv("SHELL");
887 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
888 c_flag = 1;
dae3fa13 889 }
5260325f 890 if (k_flag) {
891 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
892 if (pidstr == NULL) {
893 fprintf(stderr, "%s not set, cannot kill agent\n",
42f11eb2 894 SSH_AGENTPID_ENV_NAME);
5260325f 895 exit(1);
896 }
897 pid = atoi(pidstr);
42f11eb2 898 if (pid < 1) {
5260325f 899 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
42f11eb2 900 SSH_AGENTPID_ENV_NAME, pidstr);
5260325f 901 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);
dae3fa13 912 }
5260325f 913 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);
920 }
921 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
42f11eb2 922 parent_pid);
5260325f 923
aa3378df 924 /*
925 * Create socket early so it will exist before command gets run from
926 * the parent.
927 */
5260325f 928 sock = socket(AF_UNIX, SOCK_STREAM, 0);
929 if (sock < 0) {
930 perror("socket");
931 cleanup_exit(1);
932 }
933 memset(&sunaddr, 0, sizeof(sunaddr));
934 sunaddr.sun_family = AF_UNIX;
935 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
c7ccfd39 936#ifdef HAVE_CYGWIN
937 prev_mask = umask(0177);
938#endif
5260325f 939 if (bind(sock, (struct sockaddr *) & sunaddr, sizeof(sunaddr)) < 0) {
940 perror("bind");
c7ccfd39 941#ifdef HAVE_CYGWIN
942 umask(prev_mask);
943#endif
5260325f 944 cleanup_exit(1);
945 }
c7ccfd39 946#ifdef HAVE_CYGWIN
947 umask(prev_mask);
948#endif
5260325f 949 if (listen(sock, 5) < 0) {
950 perror("listen");
951 cleanup_exit(1);
dae3fa13 952 }
42f11eb2 953
aa3378df 954 /*
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 */
ffdb5d70 958 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 }
5260325f 966 pid = fork();
967 if (pid == -1) {
968 perror("fork");
fccfbe3b 969 cleanup_exit(1);
dae3fa13 970 }
5260325f 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,
42f11eb2 977 SSH_AUTHSOCKET_ENV_NAME);
5260325f 978 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
42f11eb2 979 SSH_AGENTPID_ENV_NAME);
5260325f 980 printf("echo Agent pid %d;\n", pid);
981 exit(0);
982 }
bcbf86ec 983 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 }
5260325f 988 execvp(av[0], av);
989 perror(av[0]);
990 exit(1);
991 }
fccfbe3b 992 /* child */
993 log_init(__progname, SYSLOG_LEVEL_INFO, SYSLOG_FACILITY_AUTH, 0);
ec9f3450 994
995 if (setsid() == -1) {
fccfbe3b 996 error("setsid: %s", strerror(errno));
ec9f3450 997 cleanup_exit(1);
998 }
999
1000 (void)chdir("/");
5260325f 1001 close(0);
1002 close(1);
1003 close(2);
dae3fa13 1004
b03bd394 1005#ifdef HAVE_SETRLIMIT
0b6fbf03 1006 /* deny core dumps, since memory contains unencrypted private keys */
1007 rlim.rlim_cur = rlim.rlim_max = 0;
1008 if (setrlimit(RLIMIT_CORE, &rlim) < 0) {
fccfbe3b 1009 error("setrlimit RLIMIT_CORE: %s", strerror(errno));
0b6fbf03 1010 cleanup_exit(1);
1011 }
b03bd394 1012#endif
ffdb5d70 1013
1014skip:
fccfbe3b 1015 fatal_add_cleanup(cleanup_socket, NULL);
5260325f 1016 new_socket(AUTH_SOCKET, sock);
1017 if (ac > 0) {
1018 signal(SIGALRM, check_parent_exists);
1019 alarm(10);
1020 }
2e73a022 1021 idtab_init();
1ee482c5 1022 if (!d_flag)
ffdb5d70 1023 signal(SIGINT, SIG_IGN);
1ee482c5 1024 signal(SIGPIPE, SIG_IGN);
2f4b2e38 1025 signal(SIGHUP, cleanup_handler);
1026 signal(SIGTERM, cleanup_handler);
e364646f 1027 nalloc = 0;
1028
5260325f 1029 while (1) {
e364646f 1030 prepare_select(&readsetp, &writesetp, &max_fd, &nalloc);
42f11eb2 1031 if (select(max_fd + 1, readsetp, writesetp, NULL, NULL) < 0) {
5260325f 1032 if (errno == EINTR)
1033 continue;
fccfbe3b 1034 fatal("select: %s", strerror(errno));
5260325f 1035 }
42f11eb2 1036 after_select(readsetp, writesetp);
8efc0c15 1037 }
5260325f 1038 /* NOTREACHED */
8efc0c15 1039}
This page took 0.449153 seconds and 5 git commands to generate.