1 /* $OpenBSD: ssh-agent.c,v 1.18 1999/11/15 20:53:24 markus Exp $ */
7 Author: Tatu Ylonen <ylo@cs.hut.fi>
9 Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
12 Created: Wed Mar 29 03:46:59 1995 ylo
14 The authentication agent program.
19 RCSID("$OpenBSD: ssh-agent.c,v 1.18 1999/11/15 20:53:24 markus Exp $");
32 #include <openssl/md5.h>
38 #ifdef HAVE___PROGNAME
39 extern char *__progname;
40 #else /* HAVE___PROGNAME */
41 const char *__progname = "ssh-agent";
42 #endif /* HAVE___PROGNAME */
47 enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type;
52 unsigned int sockets_alloc = 0;
53 SocketEntry *sockets = NULL;
61 unsigned int num_identities = 0;
62 Identity *identities = NULL;
66 /* pid of shell == parent of agent */
69 /* pathname and directory for AUTH_SOCKET */
70 char socket_name[1024];
71 char socket_dir[1024];
74 process_request_identity(SocketEntry *e)
80 buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
81 buffer_put_int(&msg, num_identities);
82 for (i = 0; i < num_identities; i++)
84 buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
85 buffer_put_bignum(&msg, identities[i].key->e);
86 buffer_put_bignum(&msg, identities[i].key->n);
87 buffer_put_string(&msg, identities[i].comment,
88 strlen(identities[i].comment));
90 buffer_put_int(&e->output, buffer_len(&msg));
91 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
96 process_authentication_challenge(SocketEntry *e)
99 BIGNUM *pub_e, *pub_n, *challenge;
102 unsigned char buf[32], mdbuf[16], session_id[16];
103 unsigned int response_type;
108 challenge = BN_new();
109 pub_bits = buffer_get_int(&e->input);
110 buffer_get_bignum(&e->input, pub_e);
111 buffer_get_bignum(&e->input, pub_n);
112 buffer_get_bignum(&e->input, challenge);
113 if (buffer_len(&e->input) == 0)
115 /* Compatibility code for old servers. */
116 memset(session_id, 0, 16);
122 buffer_get(&e->input, (char *)session_id, 16);
123 response_type = buffer_get_int(&e->input);
125 for (i = 0; i < num_identities; i++)
126 if (pub_bits == BN_num_bits(identities[i].key->n) &&
127 BN_cmp(pub_e, identities[i].key->e) == 0 &&
128 BN_cmp(pub_n, identities[i].key->n) == 0)
130 /* Decrypt the challenge using the private key. */
131 rsa_private_decrypt(challenge, challenge, identities[i].key);
133 /* Compute the desired response. */
134 switch (response_type)
136 case 0: /* As of protocol 1.0 */
137 /* This response type is no longer supported. */
138 log("Compatibility with ssh protocol 1.0 no longer supported.");
139 buffer_put_char(&msg, SSH_AGENT_FAILURE);
142 case 1: /* As of protocol 1.1 */
143 /* The response is MD5 of decrypted challenge plus session id. */
144 len = BN_num_bytes(challenge);
146 if (len <= 0 || len > 32) {
147 fatal("process_authentication_challenge: "
148 "bad challenge length %d", len);
152 BN_bn2bin(challenge, buf + 32 - len);
154 MD5_Update(&md, buf, 32);
155 MD5_Update(&md, session_id, 16);
156 MD5_Final(mdbuf, &md);
160 fatal("process_authentication_challenge: bad response_type %d",
165 /* Send the response. */
166 buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
167 for (i = 0; i < 16; i++)
168 buffer_put_char(&msg, mdbuf[i]);
172 /* Unknown identity. Send failure. */
173 buffer_put_char(&msg, SSH_AGENT_FAILURE);
175 buffer_put_int(&e->output, buffer_len(&msg));
176 buffer_append(&e->output, buffer_ptr(&msg),
179 BN_clear_free(pub_e);
180 BN_clear_free(pub_n);
181 BN_clear_free(challenge);
185 process_remove_identity(SocketEntry *e)
194 /* Get the key from the packet. */
195 bits = buffer_get_int(&e->input);
196 buffer_get_bignum(&e->input, dummy);
197 buffer_get_bignum(&e->input, n);
199 if (bits != BN_num_bits(n))
200 error("Warning: keysize mismatch: actual %d, announced %s",
201 BN_num_bits(n), bits);
203 /* Check if we have the key. */
204 for (i = 0; i < num_identities; i++)
205 if (BN_cmp(identities[i].key->n, n) == 0)
207 /* We have this key. Free the old key. Since we don\'t want to leave
208 empty slots in the middle of the array, we actually free the
209 key there and copy data from the last entry. */
210 RSA_free(identities[i].key);
211 xfree(identities[i].comment);
212 if (i < num_identities - 1)
213 identities[i] = identities[num_identities - 1];
215 BN_clear_free(dummy);
219 buffer_put_int(&e->output, 1);
220 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
223 /* We did not have the key. */
228 buffer_put_int(&e->output, 1);
229 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
232 /* Removes all identities from the agent. */
235 process_remove_all_identities(SocketEntry *e)
239 /* Loop over all identities and clear the keys. */
240 for (i = 0; i < num_identities; i++)
242 RSA_free(identities[i].key);
243 xfree(identities[i].comment);
246 /* Mark that there are no identities. */
250 buffer_put_int(&e->output, 1);
251 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
255 /* Adds an identity to the agent. */
258 process_add_identity(SocketEntry *e)
265 if (num_identities == 0)
266 identities = xmalloc(sizeof(Identity));
268 identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
270 identities[num_identities].key = RSA_new();
271 k = identities[num_identities].key;
272 buffer_get_int(&e->input); /* bits */
274 buffer_get_bignum(&e->input, k->n);
276 buffer_get_bignum(&e->input, k->e);
278 buffer_get_bignum(&e->input, k->d);
280 buffer_get_bignum(&e->input, k->iqmp);
281 /* SSH and SSL have p and q swapped */
283 buffer_get_bignum(&e->input, k->q); /* p */
285 buffer_get_bignum(&e->input, k->p); /* q */
287 /* Generate additional parameters */
291 BN_sub(aux, k->q, BN_value_one());
293 BN_mod(k->dmq1, k->d, aux, ctx);
295 BN_sub(aux, k->p, BN_value_one());
297 BN_mod(k->dmp1, k->d, aux, ctx);
302 identities[num_identities].comment = buffer_get_string(&e->input, NULL);
304 /* Check if we already have the key. */
305 for (i = 0; i < num_identities; i++)
306 if (BN_cmp(identities[i].key->n, k->n) == 0)
308 /* We already have this key. Clear and free the new data and
311 xfree(identities[num_identities].comment);
314 buffer_put_int(&e->output, 1);
315 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
319 /* Increment the number of identities. */
322 /* Send a success message. */
323 buffer_put_int(&e->output, 1);
324 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
328 process_message(SocketEntry *e)
330 unsigned int msg_len;
333 if (buffer_len(&e->input) < 5)
334 return; /* Incomplete message. */
335 cp = (unsigned char *)buffer_ptr(&e->input);
336 msg_len = GET_32BIT(cp);
337 if (msg_len > 256 * 1024)
339 shutdown(e->fd, SHUT_RDWR);
341 e->type = AUTH_UNUSED;
344 if (buffer_len(&e->input) < msg_len + 4)
346 buffer_consume(&e->input, 4);
347 type = buffer_get_char(&e->input);
351 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
352 process_request_identity(e);
354 case SSH_AGENTC_RSA_CHALLENGE:
355 process_authentication_challenge(e);
357 case SSH_AGENTC_ADD_RSA_IDENTITY:
358 process_add_identity(e);
360 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
361 process_remove_identity(e);
363 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
364 process_remove_all_identities(e);
367 /* Unknown message. Respond with failure. */
368 error("Unknown message %d", type);
369 buffer_clear(&e->input);
370 buffer_put_int(&e->output, 1);
371 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
377 new_socket(int type, int fd)
379 unsigned int i, old_alloc;
380 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
381 error("fcntl O_NONBLOCK: %s", strerror(errno));
386 for (i = 0; i < sockets_alloc; i++)
387 if (sockets[i].type == AUTH_UNUSED)
390 sockets[i].type = type;
391 buffer_init(&sockets[i].input);
392 buffer_init(&sockets[i].output);
395 old_alloc = sockets_alloc;
398 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
400 sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
401 for (i = old_alloc; i < sockets_alloc; i++)
402 sockets[i].type = AUTH_UNUSED;
403 sockets[old_alloc].type = type;
404 sockets[old_alloc].fd = fd;
405 buffer_init(&sockets[old_alloc].input);
406 buffer_init(&sockets[old_alloc].output);
410 prepare_select(fd_set *readset, fd_set *writeset)
413 for (i = 0; i < sockets_alloc; i++)
414 switch (sockets[i].type)
417 case AUTH_CONNECTION:
418 FD_SET(sockets[i].fd, readset);
419 if (buffer_len(&sockets[i].output) > 0)
420 FD_SET(sockets[i].fd, writeset);
425 fatal("Unknown socket type %d", sockets[i].type);
430 void after_select(fd_set *readset, fd_set *writeset)
435 struct sockaddr_un sunaddr;
437 for (i = 0; i < sockets_alloc; i++)
438 switch (sockets[i].type)
443 if (FD_ISSET(sockets[i].fd, readset))
445 len = sizeof(sunaddr);
446 sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
449 perror("accept from AUTH_SOCKET");
452 new_socket(AUTH_CONNECTION, sock);
455 case AUTH_CONNECTION:
456 if (buffer_len(&sockets[i].output) > 0 &&
457 FD_ISSET(sockets[i].fd, writeset))
459 len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
460 buffer_len(&sockets[i].output));
463 shutdown(sockets[i].fd, SHUT_RDWR);
464 close(sockets[i].fd);
465 sockets[i].type = AUTH_UNUSED;
468 buffer_consume(&sockets[i].output, len);
470 if (FD_ISSET(sockets[i].fd, readset))
472 len = read(sockets[i].fd, buf, sizeof(buf));
475 shutdown(sockets[i].fd, SHUT_RDWR);
476 close(sockets[i].fd);
477 sockets[i].type = AUTH_UNUSED;
480 buffer_append(&sockets[i].input, buf, len);
481 process_message(&sockets[i]);
485 fatal("Unknown type %d", sockets[i].type);
490 check_parent_exists(int sig)
492 if (kill(parent_pid, 0) < 0)
494 /* printf("Parent has died - Authentication agent exiting.\n"); */
497 signal(SIGALRM, check_parent_exists);
518 fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
519 fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
525 main(int ac, char **av)
527 fd_set readset, writeset;
528 int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
529 struct sockaddr_un sunaddr;
531 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
533 /* check if RSA support exists */
534 if (rsa_alive() == 0) {
536 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
541 #if defined(__GNU_LIBRARY__)
542 while ((ch = getopt(ac, av, "+cks")) != -1)
544 while ((ch = getopt(ac, av, "cks")) != -1)
545 #endif /* defined(__GNU_LIBRARY__) */
569 if (ac > 0 && (c_flag || k_flag || s_flag))
572 if (ac == 0 && !c_flag && !k_flag && !s_flag)
574 shell = getenv("SHELL");
575 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
581 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
584 fprintf(stderr, "%s not set, cannot kill agent\n",
585 SSH_AGENTPID_ENV_NAME);
589 if (pid < 1) /* XXX PID_MAX check too */
591 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
592 SSH_AGENTPID_ENV_NAME, pidstr);
595 if (kill(pid, SIGTERM) == -1)
600 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
601 printf(format, SSH_AUTHSOCKET_ENV_NAME);
602 printf(format, SSH_AGENTPID_ENV_NAME);
603 printf("echo Agent pid %d killed;\n", pid);
607 parent_pid = getpid();
609 /* Create private directory for agent socket */
610 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
611 if (mkdtemp(socket_dir) == NULL) {
612 perror("mkdtemp: private socket dir");
615 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
618 /* Create socket early so it will exist before command gets run from
620 sock = socket(AF_UNIX, SOCK_STREAM, 0);
626 memset(&sunaddr, 0, sizeof(sunaddr));
627 sunaddr.sun_family = AF_UNIX;
628 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
629 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
634 if (listen(sock, 5) < 0)
640 /* Fork, and have the parent execute the command, if any, or present the
641 socket data. The child continues as the authentication agent. */
649 { /* Parent - execute the given command. */
651 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
654 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
655 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
656 SSH_AUTHSOCKET_ENV_NAME);
657 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
658 SSH_AGENTPID_ENV_NAME);
659 printf("echo Agent pid %d;\n", pid);
663 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
664 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
680 if (atexit(cleanup_socket) < 0)
686 new_socket(AUTH_SOCKET, sock);
689 signal(SIGALRM, check_parent_exists);
693 signal(SIGINT, SIG_IGN);
694 signal(SIGPIPE, SIG_IGN);
699 prepare_select(&readset, &writeset);
700 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
706 after_select(&readset, &writeset);