1 /* $OpenBSD: ssh-agent.c,v 1.15 1999/10/28 08:43:10 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.
20 RCSID("$OpenBSD: ssh-agent.c,v 1.15 1999/10/28 08:43:10 markus Exp $");
33 #include <openssl/md5.h>
42 enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type;
47 unsigned int sockets_alloc = 0;
48 SocketEntry *sockets = NULL;
56 unsigned int num_identities = 0;
57 Identity *identities = NULL;
61 /* pid of shell == parent of agent */
64 /* pathname and directory for AUTH_SOCKET */
65 char socket_name[1024];
66 char socket_dir[1024];
69 process_request_identity(SocketEntry *e)
75 buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
76 buffer_put_int(&msg, num_identities);
77 for (i = 0; i < num_identities; i++)
79 buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
80 buffer_put_bignum(&msg, identities[i].key->e);
81 buffer_put_bignum(&msg, identities[i].key->n);
82 buffer_put_string(&msg, identities[i].comment,
83 strlen(identities[i].comment));
85 buffer_put_int(&e->output, buffer_len(&msg));
86 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
91 process_authentication_challenge(SocketEntry *e)
94 BIGNUM *pub_e, *pub_n, *challenge;
97 unsigned char buf[32], mdbuf[16], session_id[16];
98 unsigned int response_type;
103 challenge = BN_new();
104 pub_bits = buffer_get_int(&e->input);
105 buffer_get_bignum(&e->input, pub_e);
106 buffer_get_bignum(&e->input, pub_n);
107 buffer_get_bignum(&e->input, challenge);
108 if (buffer_len(&e->input) == 0)
110 /* Compatibility code for old servers. */
111 memset(session_id, 0, 16);
117 buffer_get(&e->input, (char *)session_id, 16);
118 response_type = buffer_get_int(&e->input);
120 for (i = 0; i < num_identities; i++)
121 if (pub_bits == BN_num_bits(identities[i].key->n) &&
122 BN_cmp(pub_e, identities[i].key->e) == 0 &&
123 BN_cmp(pub_n, identities[i].key->n) == 0)
125 /* Decrypt the challenge using the private key. */
126 rsa_private_decrypt(challenge, challenge, identities[i].key);
128 /* Compute the desired response. */
129 switch (response_type)
131 case 0: /* As of protocol 1.0 */
132 /* This response type is no longer supported. */
133 log("Compatibility with ssh protocol 1.0 no longer supported.");
134 buffer_put_char(&msg, SSH_AGENT_FAILURE);
137 case 1: /* As of protocol 1.1 */
138 /* The response is MD5 of decrypted challenge plus session id. */
139 len = BN_num_bytes(challenge);
140 assert(len <= 32 && len);
142 BN_bn2bin(challenge, buf + 32 - len);
144 MD5_Update(&md, buf, 32);
145 MD5_Update(&md, session_id, 16);
146 MD5_Final(mdbuf, &md);
150 fatal("process_authentication_challenge: bad response_type %d",
155 /* Send the response. */
156 buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
157 for (i = 0; i < 16; i++)
158 buffer_put_char(&msg, mdbuf[i]);
162 /* Unknown identity. Send failure. */
163 buffer_put_char(&msg, SSH_AGENT_FAILURE);
165 buffer_put_int(&e->output, buffer_len(&msg));
166 buffer_append(&e->output, buffer_ptr(&msg),
169 BN_clear_free(pub_e);
170 BN_clear_free(pub_n);
171 BN_clear_free(challenge);
175 process_remove_identity(SocketEntry *e)
184 /* Get the key from the packet. */
185 bits = buffer_get_int(&e->input);
186 buffer_get_bignum(&e->input, dummy);
187 buffer_get_bignum(&e->input, n);
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)
193 /* We have this key. Free the old key. Since we don\'t want to leave
194 empty slots in the middle of the array, we actually free the
195 key there 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];
201 BN_clear_free(dummy);
205 buffer_put_int(&e->output, 1);
206 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
209 /* We did not have the key. */
214 buffer_put_int(&e->output, 1);
215 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
218 /* Removes all identities from the agent. */
221 process_remove_all_identities(SocketEntry *e)
225 /* Loop over all identities and clear the keys. */
226 for (i = 0; i < num_identities; i++)
228 RSA_free(identities[i].key);
229 xfree(identities[i].comment);
232 /* Mark that there are no identities. */
236 buffer_put_int(&e->output, 1);
237 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
241 /* Adds an identity to the agent. */
244 process_add_identity(SocketEntry *e)
251 if (num_identities == 0)
252 identities = xmalloc(sizeof(Identity));
254 identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
256 identities[num_identities].key = RSA_new();
257 k = identities[num_identities].key;
258 buffer_get_int(&e->input); /* bits */
260 buffer_get_bignum(&e->input, k->n);
262 buffer_get_bignum(&e->input, k->e);
264 buffer_get_bignum(&e->input, k->d);
266 buffer_get_bignum(&e->input, k->iqmp);
267 /* SSH and SSL have p and q swapped */
269 buffer_get_bignum(&e->input, k->q); /* p */
271 buffer_get_bignum(&e->input, k->p); /* q */
273 /* Generate additional parameters */
277 BN_sub(aux, k->q, BN_value_one());
279 BN_mod(k->dmq1, k->d, aux, ctx);
281 BN_sub(aux, k->p, BN_value_one());
283 BN_mod(k->dmp1, k->d, aux, ctx);
288 identities[num_identities].comment = buffer_get_string(&e->input, NULL);
290 /* Check if we already have the key. */
291 for (i = 0; i < num_identities; i++)
292 if (BN_cmp(identities[i].key->n, k->n) == 0)
294 /* We already have this key. Clear and free the new data and
297 xfree(identities[num_identities].comment);
300 buffer_put_int(&e->output, 1);
301 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
305 /* Increment the number of identities. */
308 /* Send a success message. */
309 buffer_put_int(&e->output, 1);
310 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
314 process_message(SocketEntry *e)
316 unsigned int msg_len;
319 if (buffer_len(&e->input) < 5)
320 return; /* Incomplete message. */
321 cp = (unsigned char *)buffer_ptr(&e->input);
322 msg_len = GET_32BIT(cp);
323 if (msg_len > 256 * 1024)
325 shutdown(e->fd, SHUT_RDWR);
327 e->type = AUTH_UNUSED;
330 if (buffer_len(&e->input) < msg_len + 4)
332 buffer_consume(&e->input, 4);
333 type = buffer_get_char(&e->input);
337 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
338 process_request_identity(e);
340 case SSH_AGENTC_RSA_CHALLENGE:
341 process_authentication_challenge(e);
343 case SSH_AGENTC_ADD_RSA_IDENTITY:
344 process_add_identity(e);
346 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
347 process_remove_identity(e);
349 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
350 process_remove_all_identities(e);
353 /* Unknown message. Respond with failure. */
354 error("Unknown message %d", type);
355 buffer_clear(&e->input);
356 buffer_put_int(&e->output, 1);
357 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
363 new_socket(int type, int fd)
365 unsigned int i, old_alloc;
366 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
367 error("fcntl O_NONBLOCK: %s", strerror(errno));
372 for (i = 0; i < sockets_alloc; i++)
373 if (sockets[i].type == AUTH_UNUSED)
376 sockets[i].type = type;
377 buffer_init(&sockets[i].input);
378 buffer_init(&sockets[i].output);
381 old_alloc = sockets_alloc;
384 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
386 sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
387 for (i = old_alloc; i < sockets_alloc; i++)
388 sockets[i].type = AUTH_UNUSED;
389 sockets[old_alloc].type = type;
390 sockets[old_alloc].fd = fd;
391 buffer_init(&sockets[old_alloc].input);
392 buffer_init(&sockets[old_alloc].output);
396 prepare_select(fd_set *readset, fd_set *writeset)
399 for (i = 0; i < sockets_alloc; i++)
400 switch (sockets[i].type)
403 case AUTH_CONNECTION:
404 FD_SET(sockets[i].fd, readset);
405 if (buffer_len(&sockets[i].output) > 0)
406 FD_SET(sockets[i].fd, writeset);
411 fatal("Unknown socket type %d", sockets[i].type);
416 void after_select(fd_set *readset, fd_set *writeset)
421 struct sockaddr_un sunaddr;
423 for (i = 0; i < sockets_alloc; i++)
424 switch (sockets[i].type)
429 if (FD_ISSET(sockets[i].fd, readset))
431 len = sizeof(sunaddr);
432 sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
435 perror("accept from AUTH_SOCKET");
438 new_socket(AUTH_CONNECTION, sock);
441 case AUTH_CONNECTION:
442 if (buffer_len(&sockets[i].output) > 0 &&
443 FD_ISSET(sockets[i].fd, writeset))
445 len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
446 buffer_len(&sockets[i].output));
449 shutdown(sockets[i].fd, SHUT_RDWR);
450 close(sockets[i].fd);
451 sockets[i].type = AUTH_UNUSED;
454 buffer_consume(&sockets[i].output, len);
456 if (FD_ISSET(sockets[i].fd, readset))
458 len = read(sockets[i].fd, buf, sizeof(buf));
461 shutdown(sockets[i].fd, SHUT_RDWR);
462 close(sockets[i].fd);
463 sockets[i].type = AUTH_UNUSED;
466 buffer_append(&sockets[i].input, buf, len);
467 process_message(&sockets[i]);
471 fatal("Unknown type %d", sockets[i].type);
476 check_parent_exists(int sig)
478 if (kill(parent_pid, 0) < 0)
480 /* printf("Parent has died - Authentication agent exiting.\n"); */
483 signal(SIGALRM, check_parent_exists);
504 extern char *__progname;
506 fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
507 fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
513 main(int ac, char **av)
515 fd_set readset, writeset;
516 int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
517 struct sockaddr_un sunaddr;
519 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
521 /* check if RSA support exists */
522 if (rsa_alive() == 0) {
523 extern char *__progname;
525 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
530 while ((ch = getopt(ac, av, "cks")) != -1)
554 if (ac > 0 && (c_flag || k_flag || s_flag))
557 if (ac == 0 && !c_flag && !k_flag && !s_flag)
559 shell = getenv("SHELL");
560 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
566 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
569 fprintf(stderr, "%s not set, cannot kill agent\n",
570 SSH_AGENTPID_ENV_NAME);
574 if (pid < 1) /* XXX PID_MAX check too */
576 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
577 SSH_AGENTPID_ENV_NAME, pidstr);
580 if (kill(pid, SIGTERM) == -1)
585 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
586 printf(format, SSH_AUTHSOCKET_ENV_NAME);
587 printf(format, SSH_AGENTPID_ENV_NAME);
588 printf("echo Agent pid %d killed;\n", pid);
592 parent_pid = getpid();
594 /* Create private directory for agent socket */
595 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
596 if (mkdtemp(socket_dir) == NULL) {
597 perror("mkdtemp: private socket dir");
600 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
603 /* Create socket early so it will exist before command gets run from
605 sock = socket(AF_UNIX, SOCK_STREAM, 0);
611 memset(&sunaddr, 0, sizeof(sunaddr));
612 sunaddr.sun_family = AF_UNIX;
613 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
614 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
619 if (listen(sock, 5) < 0)
625 /* Fork, and have the parent execute the command, if any, or present the
626 socket data. The child continues as the authentication agent. */
634 { /* Parent - execute the given command. */
636 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
639 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
640 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
641 SSH_AUTHSOCKET_ENV_NAME);
642 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
643 SSH_AGENTPID_ENV_NAME);
644 printf("echo Agent pid %d;\n", pid);
648 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
649 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
659 if (ac == 0 && setsid() == -1)
662 if (atexit(cleanup_socket) < 0)
665 new_socket(AUTH_SOCKET, sock);
668 signal(SIGALRM, check_parent_exists);
672 signal(SIGINT, SIG_IGN);
673 signal(SIGPIPE, SIG_IGN);
678 prepare_select(&readset, &writeset);
679 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
685 after_select(&readset, &writeset);