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>
41 enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type;
46 unsigned int sockets_alloc = 0;
47 SocketEntry *sockets = NULL;
55 unsigned int num_identities = 0;
56 Identity *identities = NULL;
60 /* pid of shell == parent of agent */
63 /* pathname and directory for AUTH_SOCKET */
64 char socket_name[1024];
65 char socket_dir[1024];
68 process_request_identity(SocketEntry *e)
74 buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
75 buffer_put_int(&msg, num_identities);
76 for (i = 0; i < num_identities; i++)
78 buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
79 buffer_put_bignum(&msg, identities[i].key->e);
80 buffer_put_bignum(&msg, identities[i].key->n);
81 buffer_put_string(&msg, identities[i].comment,
82 strlen(identities[i].comment));
84 buffer_put_int(&e->output, buffer_len(&msg));
85 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
90 process_authentication_challenge(SocketEntry *e)
93 BIGNUM *pub_e, *pub_n, *challenge;
96 unsigned char buf[32], mdbuf[16], session_id[16];
97 unsigned int response_type;
102 challenge = BN_new();
103 pub_bits = buffer_get_int(&e->input);
104 buffer_get_bignum(&e->input, pub_e);
105 buffer_get_bignum(&e->input, pub_n);
106 buffer_get_bignum(&e->input, challenge);
107 if (buffer_len(&e->input) == 0)
109 /* Compatibility code for old servers. */
110 memset(session_id, 0, 16);
116 buffer_get(&e->input, (char *)session_id, 16);
117 response_type = buffer_get_int(&e->input);
119 for (i = 0; i < num_identities; i++)
120 if (pub_bits == BN_num_bits(identities[i].key->n) &&
121 BN_cmp(pub_e, identities[i].key->e) == 0 &&
122 BN_cmp(pub_n, identities[i].key->n) == 0)
124 /* Decrypt the challenge using the private key. */
125 rsa_private_decrypt(challenge, challenge, identities[i].key);
127 /* Compute the desired response. */
128 switch (response_type)
130 case 0: /* As of protocol 1.0 */
131 /* This response type is no longer supported. */
132 log("Compatibility with ssh protocol 1.0 no longer supported.");
133 buffer_put_char(&msg, SSH_AGENT_FAILURE);
136 case 1: /* As of protocol 1.1 */
137 /* The response is MD5 of decrypted challenge plus session id. */
138 len = BN_num_bytes(challenge);
140 if (len <= 0 || len > 32) {
141 fatal("process_authentication_challenge: "
142 "bad challenge length %d", len);
146 BN_bn2bin(challenge, buf + 32 - len);
148 MD5_Update(&md, buf, 32);
149 MD5_Update(&md, session_id, 16);
150 MD5_Final(mdbuf, &md);
154 fatal("process_authentication_challenge: bad response_type %d",
159 /* Send the response. */
160 buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
161 for (i = 0; i < 16; i++)
162 buffer_put_char(&msg, mdbuf[i]);
166 /* Unknown identity. Send failure. */
167 buffer_put_char(&msg, SSH_AGENT_FAILURE);
169 buffer_put_int(&e->output, buffer_len(&msg));
170 buffer_append(&e->output, buffer_ptr(&msg),
173 BN_clear_free(pub_e);
174 BN_clear_free(pub_n);
175 BN_clear_free(challenge);
179 process_remove_identity(SocketEntry *e)
188 /* Get the key from the packet. */
189 bits = buffer_get_int(&e->input);
190 buffer_get_bignum(&e->input, dummy);
191 buffer_get_bignum(&e->input, n);
193 /* Check if we have the key. */
194 for (i = 0; i < num_identities; i++)
195 if (BN_cmp(identities[i].key->n, n) == 0)
197 /* We have this key. Free the old key. Since we don\'t want to leave
198 empty slots in the middle of the array, we actually free the
199 key there and copy data from the last entry. */
200 RSA_free(identities[i].key);
201 xfree(identities[i].comment);
202 if (i < num_identities - 1)
203 identities[i] = identities[num_identities - 1];
205 BN_clear_free(dummy);
209 buffer_put_int(&e->output, 1);
210 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
213 /* We did not have the key. */
218 buffer_put_int(&e->output, 1);
219 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
222 /* Removes all identities from the agent. */
225 process_remove_all_identities(SocketEntry *e)
229 /* Loop over all identities and clear the keys. */
230 for (i = 0; i < num_identities; i++)
232 RSA_free(identities[i].key);
233 xfree(identities[i].comment);
236 /* Mark that there are no identities. */
240 buffer_put_int(&e->output, 1);
241 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
245 /* Adds an identity to the agent. */
248 process_add_identity(SocketEntry *e)
255 if (num_identities == 0)
256 identities = xmalloc(sizeof(Identity));
258 identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
260 identities[num_identities].key = RSA_new();
261 k = identities[num_identities].key;
262 buffer_get_int(&e->input); /* bits */
264 buffer_get_bignum(&e->input, k->n);
266 buffer_get_bignum(&e->input, k->e);
268 buffer_get_bignum(&e->input, k->d);
270 buffer_get_bignum(&e->input, k->iqmp);
271 /* SSH and SSL have p and q swapped */
273 buffer_get_bignum(&e->input, k->q); /* p */
275 buffer_get_bignum(&e->input, k->p); /* q */
277 /* Generate additional parameters */
281 BN_sub(aux, k->q, BN_value_one());
283 BN_mod(k->dmq1, k->d, aux, ctx);
285 BN_sub(aux, k->p, BN_value_one());
287 BN_mod(k->dmp1, k->d, aux, ctx);
292 identities[num_identities].comment = buffer_get_string(&e->input, NULL);
294 /* Check if we already have the key. */
295 for (i = 0; i < num_identities; i++)
296 if (BN_cmp(identities[i].key->n, k->n) == 0)
298 /* We already have this key. Clear and free the new data and
301 xfree(identities[num_identities].comment);
304 buffer_put_int(&e->output, 1);
305 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
309 /* Increment the number of identities. */
312 /* Send a success message. */
313 buffer_put_int(&e->output, 1);
314 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
318 process_message(SocketEntry *e)
320 unsigned int msg_len;
323 if (buffer_len(&e->input) < 5)
324 return; /* Incomplete message. */
325 cp = (unsigned char *)buffer_ptr(&e->input);
326 msg_len = GET_32BIT(cp);
327 if (msg_len > 256 * 1024)
329 shutdown(e->fd, SHUT_RDWR);
331 e->type = AUTH_UNUSED;
334 if (buffer_len(&e->input) < msg_len + 4)
336 buffer_consume(&e->input, 4);
337 type = buffer_get_char(&e->input);
341 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
342 process_request_identity(e);
344 case SSH_AGENTC_RSA_CHALLENGE:
345 process_authentication_challenge(e);
347 case SSH_AGENTC_ADD_RSA_IDENTITY:
348 process_add_identity(e);
350 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
351 process_remove_identity(e);
353 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
354 process_remove_all_identities(e);
357 /* Unknown message. Respond with failure. */
358 error("Unknown message %d", type);
359 buffer_clear(&e->input);
360 buffer_put_int(&e->output, 1);
361 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
367 new_socket(int type, int fd)
369 unsigned int i, old_alloc;
370 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
371 error("fcntl O_NONBLOCK: %s", strerror(errno));
376 for (i = 0; i < sockets_alloc; i++)
377 if (sockets[i].type == AUTH_UNUSED)
380 sockets[i].type = type;
381 buffer_init(&sockets[i].input);
382 buffer_init(&sockets[i].output);
385 old_alloc = sockets_alloc;
388 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
390 sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
391 for (i = old_alloc; i < sockets_alloc; i++)
392 sockets[i].type = AUTH_UNUSED;
393 sockets[old_alloc].type = type;
394 sockets[old_alloc].fd = fd;
395 buffer_init(&sockets[old_alloc].input);
396 buffer_init(&sockets[old_alloc].output);
400 prepare_select(fd_set *readset, fd_set *writeset)
403 for (i = 0; i < sockets_alloc; i++)
404 switch (sockets[i].type)
407 case AUTH_CONNECTION:
408 FD_SET(sockets[i].fd, readset);
409 if (buffer_len(&sockets[i].output) > 0)
410 FD_SET(sockets[i].fd, writeset);
415 fatal("Unknown socket type %d", sockets[i].type);
420 void after_select(fd_set *readset, fd_set *writeset)
425 struct sockaddr_un sunaddr;
427 for (i = 0; i < sockets_alloc; i++)
428 switch (sockets[i].type)
433 if (FD_ISSET(sockets[i].fd, readset))
435 len = sizeof(sunaddr);
436 sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
439 perror("accept from AUTH_SOCKET");
442 new_socket(AUTH_CONNECTION, sock);
445 case AUTH_CONNECTION:
446 if (buffer_len(&sockets[i].output) > 0 &&
447 FD_ISSET(sockets[i].fd, writeset))
449 len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
450 buffer_len(&sockets[i].output));
453 shutdown(sockets[i].fd, SHUT_RDWR);
454 close(sockets[i].fd);
455 sockets[i].type = AUTH_UNUSED;
458 buffer_consume(&sockets[i].output, len);
460 if (FD_ISSET(sockets[i].fd, readset))
462 len = read(sockets[i].fd, buf, sizeof(buf));
465 shutdown(sockets[i].fd, SHUT_RDWR);
466 close(sockets[i].fd);
467 sockets[i].type = AUTH_UNUSED;
470 buffer_append(&sockets[i].input, buf, len);
471 process_message(&sockets[i]);
475 fatal("Unknown type %d", sockets[i].type);
480 check_parent_exists(int sig)
482 if (kill(parent_pid, 0) < 0)
484 /* printf("Parent has died - Authentication agent exiting.\n"); */
487 signal(SIGALRM, check_parent_exists);
508 extern char *__progname;
510 fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
511 fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
517 main(int ac, char **av)
519 fd_set readset, writeset;
520 int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
521 struct sockaddr_un sunaddr;
523 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
525 /* check if RSA support exists */
526 if (rsa_alive() == 0) {
527 extern char *__progname;
529 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
534 while ((ch = getopt(ac, av, "cks")) != -1)
558 if (ac > 0 && (c_flag || k_flag || s_flag))
561 if (ac == 0 && !c_flag && !k_flag && !s_flag)
563 shell = getenv("SHELL");
564 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
570 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
573 fprintf(stderr, "%s not set, cannot kill agent\n",
574 SSH_AGENTPID_ENV_NAME);
578 if (pid < 1) /* XXX PID_MAX check too */
580 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
581 SSH_AGENTPID_ENV_NAME, pidstr);
584 if (kill(pid, SIGTERM) == -1)
589 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
590 printf(format, SSH_AUTHSOCKET_ENV_NAME);
591 printf(format, SSH_AGENTPID_ENV_NAME);
592 printf("echo Agent pid %d killed;\n", pid);
596 parent_pid = getpid();
598 /* Create private directory for agent socket */
599 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
600 if (mkdtemp(socket_dir) == NULL) {
601 perror("mkdtemp: private socket dir");
604 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
607 /* Create socket early so it will exist before command gets run from
609 sock = socket(AF_UNIX, SOCK_STREAM, 0);
615 memset(&sunaddr, 0, sizeof(sunaddr));
616 sunaddr.sun_family = AF_UNIX;
617 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
618 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
623 if (listen(sock, 5) < 0)
629 /* Fork, and have the parent execute the command, if any, or present the
630 socket data. The child continues as the authentication agent. */
638 { /* Parent - execute the given command. */
640 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
643 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
644 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
645 SSH_AUTHSOCKET_ENV_NAME);
646 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
647 SSH_AGENTPID_ENV_NAME);
648 printf("echo Agent pid %d;\n", pid);
652 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
653 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
669 if (atexit(cleanup_socket) < 0)
675 new_socket(AUTH_SOCKET, sock);
678 signal(SIGALRM, check_parent_exists);
682 signal(SIGINT, SIG_IGN);
683 signal(SIGPIPE, SIG_IGN);
688 prepare_select(&readset, &writeset);
689 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
695 after_select(&readset, &writeset);