1 /* $OpenBSD: ssh-agent.c,v 1.16 1999/10/28 20:41:23 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.17 1999/11/02 19:42:36 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 /* Check if we have the key. */
200 for (i = 0; i < num_identities; i++)
201 if (BN_cmp(identities[i].key->n, n) == 0)
203 /* We have this key. Free the old key. Since we don\'t want to leave
204 empty slots in the middle of the array, we actually free the
205 key there and copy data from the last entry. */
206 RSA_free(identities[i].key);
207 xfree(identities[i].comment);
208 if (i < num_identities - 1)
209 identities[i] = identities[num_identities - 1];
211 BN_clear_free(dummy);
215 buffer_put_int(&e->output, 1);
216 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
219 /* We did not have the key. */
224 buffer_put_int(&e->output, 1);
225 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
228 /* Removes all identities from the agent. */
231 process_remove_all_identities(SocketEntry *e)
235 /* Loop over all identities and clear the keys. */
236 for (i = 0; i < num_identities; i++)
238 RSA_free(identities[i].key);
239 xfree(identities[i].comment);
242 /* Mark that there are no identities. */
246 buffer_put_int(&e->output, 1);
247 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
251 /* Adds an identity to the agent. */
254 process_add_identity(SocketEntry *e)
261 if (num_identities == 0)
262 identities = xmalloc(sizeof(Identity));
264 identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
266 identities[num_identities].key = RSA_new();
267 k = identities[num_identities].key;
268 buffer_get_int(&e->input); /* bits */
270 buffer_get_bignum(&e->input, k->n);
272 buffer_get_bignum(&e->input, k->e);
274 buffer_get_bignum(&e->input, k->d);
276 buffer_get_bignum(&e->input, k->iqmp);
277 /* SSH and SSL have p and q swapped */
279 buffer_get_bignum(&e->input, k->q); /* p */
281 buffer_get_bignum(&e->input, k->p); /* q */
283 /* Generate additional parameters */
287 BN_sub(aux, k->q, BN_value_one());
289 BN_mod(k->dmq1, k->d, aux, ctx);
291 BN_sub(aux, k->p, BN_value_one());
293 BN_mod(k->dmp1, k->d, aux, ctx);
298 identities[num_identities].comment = buffer_get_string(&e->input, NULL);
300 /* Check if we already have the key. */
301 for (i = 0; i < num_identities; i++)
302 if (BN_cmp(identities[i].key->n, k->n) == 0)
304 /* We already have this key. Clear and free the new data and
307 xfree(identities[num_identities].comment);
310 buffer_put_int(&e->output, 1);
311 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
315 /* Increment the number of identities. */
318 /* Send a success message. */
319 buffer_put_int(&e->output, 1);
320 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
324 process_message(SocketEntry *e)
326 unsigned int msg_len;
329 if (buffer_len(&e->input) < 5)
330 return; /* Incomplete message. */
331 cp = (unsigned char *)buffer_ptr(&e->input);
332 msg_len = GET_32BIT(cp);
333 if (msg_len > 256 * 1024)
335 shutdown(e->fd, SHUT_RDWR);
337 e->type = AUTH_UNUSED;
340 if (buffer_len(&e->input) < msg_len + 4)
342 buffer_consume(&e->input, 4);
343 type = buffer_get_char(&e->input);
347 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
348 process_request_identity(e);
350 case SSH_AGENTC_RSA_CHALLENGE:
351 process_authentication_challenge(e);
353 case SSH_AGENTC_ADD_RSA_IDENTITY:
354 process_add_identity(e);
356 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
357 process_remove_identity(e);
359 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
360 process_remove_all_identities(e);
363 /* Unknown message. Respond with failure. */
364 error("Unknown message %d", type);
365 buffer_clear(&e->input);
366 buffer_put_int(&e->output, 1);
367 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
373 new_socket(int type, int fd)
375 unsigned int i, old_alloc;
376 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
377 error("fcntl O_NONBLOCK: %s", strerror(errno));
382 for (i = 0; i < sockets_alloc; i++)
383 if (sockets[i].type == AUTH_UNUSED)
386 sockets[i].type = type;
387 buffer_init(&sockets[i].input);
388 buffer_init(&sockets[i].output);
391 old_alloc = sockets_alloc;
394 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
396 sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
397 for (i = old_alloc; i < sockets_alloc; i++)
398 sockets[i].type = AUTH_UNUSED;
399 sockets[old_alloc].type = type;
400 sockets[old_alloc].fd = fd;
401 buffer_init(&sockets[old_alloc].input);
402 buffer_init(&sockets[old_alloc].output);
406 prepare_select(fd_set *readset, fd_set *writeset)
409 for (i = 0; i < sockets_alloc; i++)
410 switch (sockets[i].type)
413 case AUTH_CONNECTION:
414 FD_SET(sockets[i].fd, readset);
415 if (buffer_len(&sockets[i].output) > 0)
416 FD_SET(sockets[i].fd, writeset);
421 fatal("Unknown socket type %d", sockets[i].type);
426 void after_select(fd_set *readset, fd_set *writeset)
431 struct sockaddr_un sunaddr;
433 for (i = 0; i < sockets_alloc; i++)
434 switch (sockets[i].type)
439 if (FD_ISSET(sockets[i].fd, readset))
441 len = sizeof(sunaddr);
442 sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
445 perror("accept from AUTH_SOCKET");
448 new_socket(AUTH_CONNECTION, sock);
451 case AUTH_CONNECTION:
452 if (buffer_len(&sockets[i].output) > 0 &&
453 FD_ISSET(sockets[i].fd, writeset))
455 len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
456 buffer_len(&sockets[i].output));
459 shutdown(sockets[i].fd, SHUT_RDWR);
460 close(sockets[i].fd);
461 sockets[i].type = AUTH_UNUSED;
464 buffer_consume(&sockets[i].output, len);
466 if (FD_ISSET(sockets[i].fd, readset))
468 len = read(sockets[i].fd, buf, sizeof(buf));
471 shutdown(sockets[i].fd, SHUT_RDWR);
472 close(sockets[i].fd);
473 sockets[i].type = AUTH_UNUSED;
476 buffer_append(&sockets[i].input, buf, len);
477 process_message(&sockets[i]);
481 fatal("Unknown type %d", sockets[i].type);
486 check_parent_exists(int sig)
488 if (kill(parent_pid, 0) < 0)
490 /* printf("Parent has died - Authentication agent exiting.\n"); */
493 signal(SIGALRM, check_parent_exists);
514 fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
515 fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
521 main(int ac, char **av)
523 fd_set readset, writeset;
524 int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
525 struct sockaddr_un sunaddr;
527 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
529 /* check if RSA support exists */
530 if (rsa_alive() == 0) {
532 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
537 #if defined(__GNU_LIBRARY__)
538 while ((ch = getopt(ac, av, "+cks")) != -1)
540 while ((ch = getopt(ac, av, "cks")) != -1)
541 #endif /* defined(__GNU_LIBRARY__) */
565 if (ac > 0 && (c_flag || k_flag || s_flag))
568 if (ac == 0 && !c_flag && !k_flag && !s_flag)
570 shell = getenv("SHELL");
571 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
577 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
580 fprintf(stderr, "%s not set, cannot kill agent\n",
581 SSH_AGENTPID_ENV_NAME);
585 if (pid < 1) /* XXX PID_MAX check too */
587 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
588 SSH_AGENTPID_ENV_NAME, pidstr);
591 if (kill(pid, SIGTERM) == -1)
596 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
597 printf(format, SSH_AUTHSOCKET_ENV_NAME);
598 printf(format, SSH_AGENTPID_ENV_NAME);
599 printf("echo Agent pid %d killed;\n", pid);
603 parent_pid = getpid();
605 /* Create private directory for agent socket */
606 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
607 if (mkdtemp(socket_dir) == NULL) {
608 perror("mkdtemp: private socket dir");
611 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
614 /* Create socket early so it will exist before command gets run from
616 sock = socket(AF_UNIX, SOCK_STREAM, 0);
622 memset(&sunaddr, 0, sizeof(sunaddr));
623 sunaddr.sun_family = AF_UNIX;
624 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
625 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
630 if (listen(sock, 5) < 0)
636 /* Fork, and have the parent execute the command, if any, or present the
637 socket data. The child continues as the authentication agent. */
645 { /* Parent - execute the given command. */
647 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
650 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
651 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
652 SSH_AUTHSOCKET_ENV_NAME);
653 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
654 SSH_AGENTPID_ENV_NAME);
655 printf("echo Agent pid %d;\n", pid);
659 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
660 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
676 if (atexit(cleanup_socket) < 0)
682 new_socket(AUTH_SOCKET, sock);
685 signal(SIGALRM, check_parent_exists);
689 signal(SIGINT, SIG_IGN);
690 signal(SIGPIPE, SIG_IGN);
695 prepare_select(&readset, &writeset);
696 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
702 after_select(&readset, &writeset);