]> andersk Git - openssh.git/blame - ssh-agent.c
- Merged very large OpenBSD source code reformat
[openssh.git] / ssh-agent.c
CommitLineData
5260325f 1/* $OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt 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
7 * Created: Wed Mar 29 03:46:59 1995 ylo
8 * The authentication agent program.
9 */
8efc0c15 10
11#include "includes.h"
5260325f 12RCSID("$OpenBSD: ssh-agent.c,v 1.22 1999/11/24 00:26:03 deraadt Exp $");
8efc0c15 13
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
5881cd60 24#ifdef HAVE_OPENSSL
8efc0c15 25#include <openssl/md5.h>
5881cd60 26#endif
27#ifdef HAVE_SSL
28#include <ssl/md5.h>
29#endif
8efc0c15 30
5260325f 31typedef struct {
32 int fd;
33 enum {
34 AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION
35 } type;
36 Buffer input;
37 Buffer output;
8efc0c15 38} SocketEntry;
39
40unsigned int sockets_alloc = 0;
41SocketEntry *sockets = NULL;
42
5260325f 43typedef struct {
44 RSA *key;
45 char *comment;
8efc0c15 46} 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
5260325f 60#ifdef HAVE___PROGNAME
61extern char *__progname;
62#else /* HAVE___PROGNAME */
63const char *__progname = "ssh-agent";
64#endif /* HAVE___PROGNAME */
65
8efc0c15 66void
67process_request_identity(SocketEntry *e)
68{
5260325f 69 Buffer msg;
70 int i;
71
72 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);
8efc0c15 85}
86
87void
88process_authentication_challenge(SocketEntry *e)
89{
5260325f 90 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;
96
97 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);
120
121 /* 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;
128
129 case 1:/* As of protocol 1.1 */
130 /* The response is MD5 of decrypted challenge plus session id. */
131 len = BN_num_bytes(challenge);
132
133 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;
144
145 default:
146 fatal("process_authentication_challenge: bad response_type %d",
147 response_type);
148 break;
149 }
150
151 /* 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]);
155
156 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);
8efc0c15 168}
169
170void
171process_remove_identity(SocketEntry *e)
172{
5260325f 173 unsigned int bits;
174 unsigned int i;
175 BIGNUM *dummy, *n;
176
177 dummy = BN_new();
178 n = BN_new();
179
180 /* 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. */
8efc0c15 214 buffer_put_int(&e->output, 1);
5260325f 215 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
8efc0c15 216}
217
5260325f 218/*
219 * Removes all identities from the agent.
220 */
8efc0c15 221void
222process_remove_all_identities(SocketEntry *e)
223{
5260325f 224 unsigned int i;
8efc0c15 225
5260325f 226 /* 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 }
8efc0c15 231
5260325f 232 /* Mark that there are no identities. */
233 num_identities = 0;
8efc0c15 234
235 /* Send success. */
236 buffer_put_int(&e->output, 1);
237 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
238 return;
5260325f 239}
240
241/*
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);
8efc0c15 310}
311
312void
313process_message(SocketEntry *e)
314{
5260325f 315 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);
332
333 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 }
8efc0c15 357}
358
359void
360new_socket(int type, int fd)
361{
5260325f 362 unsigned int i, old_alloc;
363 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
364 error("fcntl O_NONBLOCK: %s", strerror(errno));
365
366 if (fd > max_fd)
367 max_fd = fd;
368
369 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);
8efc0c15 389}
390
391void
392prepare_select(fd_set *readset, fd_set *writeset)
393{
5260325f 394 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 }
8efc0c15 409}
410
5260325f 411void
412after_select(fd_set *readset, fd_set *writeset)
8efc0c15 413{
5260325f 414 unsigned int i;
415 int len, sock;
416 char buf[1024];
417 struct sockaddr_un sunaddr;
418
419 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 }
8efc0c15 462}
463
464void
465check_parent_exists(int sig)
466{
5260325f 467 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);
8efc0c15 473}
474
dae3fa13 475void
476cleanup_socket(void)
477{
5260325f 478 remove(socket_name);
479 rmdir(socket_dir);
8efc0c15 480}
481
dae3fa13 482void
483cleanup_exit(int i)
484{
5260325f 485 cleanup_socket();
486 exit(i);
dae3fa13 487}
488
489void
490usage()
491{
5260325f 492 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);
dae3fa13 496}
497
8efc0c15 498int
499main(int ac, char **av)
500{
5260325f 501 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];
506
507 /* 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 }
f370266e 514#if defined(__GNU_LIBRARY__)
5260325f 515 while ((ch = getopt(ac, av, "+cks")) != -1) {
f370266e 516#else
5260325f 517 while ((ch = getopt(ac, av, "cks")) != -1) {
f370266e 518#endif /* defined(__GNU_LIBRARY__) */
5260325f 519 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 }
dae3fa13 536 }
5260325f 537 ac -= optind;
538 av += optind;
539
540 if (ac > 0 && (c_flag || k_flag || s_flag))
541 usage();
542
543 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;
dae3fa13 547 }
5260325f 548 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);
dae3fa13 570 }
5260325f 571 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);
578 }
579 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
580 parent_pid);
581
582 /* 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);
588 }
589 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);
595 }
596 if (listen(sock, 5) < 0) {
597 perror("listen");
598 cleanup_exit(1);
dae3fa13 599 }
5260325f 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);
dae3fa13 607 }
5260325f 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);
dae3fa13 629
5260325f 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);
8efc0c15 655 }
5260325f 656 /* NOTREACHED */
8efc0c15 657}
This page took 0.355209 seconds and 5 git commands to generate.