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.16 1999/10/28 20:41:23 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);
139 assert(len <= 32 && len);
141 BN_bn2bin(challenge, buf + 32 - len);
143 MD5_Update(&md, buf, 32);
144 MD5_Update(&md, session_id, 16);
145 MD5_Final(mdbuf, &md);
149 fatal("process_authentication_challenge: bad response_type %d",
154 /* Send the response. */
155 buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
156 for (i = 0; i < 16; i++)
157 buffer_put_char(&msg, mdbuf[i]);
161 /* Unknown identity. Send failure. */
162 buffer_put_char(&msg, SSH_AGENT_FAILURE);
164 buffer_put_int(&e->output, buffer_len(&msg));
165 buffer_append(&e->output, buffer_ptr(&msg),
168 BN_clear_free(pub_e);
169 BN_clear_free(pub_n);
170 BN_clear_free(challenge);
174 process_remove_identity(SocketEntry *e)
183 /* Get the key from the packet. */
184 bits = buffer_get_int(&e->input);
185 buffer_get_bignum(&e->input, dummy);
186 buffer_get_bignum(&e->input, n);
188 /* Check if we have the key. */
189 for (i = 0; i < num_identities; i++)
190 if (BN_cmp(identities[i].key->n, n) == 0)
192 /* We have this key. Free the old key. Since we don\'t want to leave
193 empty slots in the middle of the array, we actually free the
194 key there and copy data from the last entry. */
195 RSA_free(identities[i].key);
196 xfree(identities[i].comment);
197 if (i < num_identities - 1)
198 identities[i] = identities[num_identities - 1];
200 BN_clear_free(dummy);
204 buffer_put_int(&e->output, 1);
205 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
208 /* We did not have the key. */
213 buffer_put_int(&e->output, 1);
214 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
217 /* Removes all identities from the agent. */
220 process_remove_all_identities(SocketEntry *e)
224 /* Loop over all identities and clear the keys. */
225 for (i = 0; i < num_identities; i++)
227 RSA_free(identities[i].key);
228 xfree(identities[i].comment);
231 /* Mark that there are no identities. */
235 buffer_put_int(&e->output, 1);
236 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
240 /* Adds an identity to the agent. */
243 process_add_identity(SocketEntry *e)
250 if (num_identities == 0)
251 identities = xmalloc(sizeof(Identity));
253 identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
255 identities[num_identities].key = RSA_new();
256 k = identities[num_identities].key;
257 buffer_get_int(&e->input); /* bits */
259 buffer_get_bignum(&e->input, k->n);
261 buffer_get_bignum(&e->input, k->e);
263 buffer_get_bignum(&e->input, k->d);
265 buffer_get_bignum(&e->input, k->iqmp);
266 /* SSH and SSL have p and q swapped */
268 buffer_get_bignum(&e->input, k->q); /* p */
270 buffer_get_bignum(&e->input, k->p); /* q */
272 /* Generate additional parameters */
276 BN_sub(aux, k->q, BN_value_one());
278 BN_mod(k->dmq1, k->d, aux, ctx);
280 BN_sub(aux, k->p, BN_value_one());
282 BN_mod(k->dmp1, k->d, aux, ctx);
287 identities[num_identities].comment = buffer_get_string(&e->input, NULL);
289 /* Check if we already have the key. */
290 for (i = 0; i < num_identities; i++)
291 if (BN_cmp(identities[i].key->n, k->n) == 0)
293 /* We already have this key. Clear and free the new data and
296 xfree(identities[num_identities].comment);
299 buffer_put_int(&e->output, 1);
300 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
304 /* Increment the number of identities. */
307 /* Send a success message. */
308 buffer_put_int(&e->output, 1);
309 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
313 process_message(SocketEntry *e)
315 unsigned int msg_len;
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)
324 shutdown(e->fd, SHUT_RDWR);
326 e->type = AUTH_UNUSED;
329 if (buffer_len(&e->input) < msg_len + 4)
331 buffer_consume(&e->input, 4);
332 type = buffer_get_char(&e->input);
336 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
337 process_request_identity(e);
339 case SSH_AGENTC_RSA_CHALLENGE:
340 process_authentication_challenge(e);
342 case SSH_AGENTC_ADD_RSA_IDENTITY:
343 process_add_identity(e);
345 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
346 process_remove_identity(e);
348 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
349 process_remove_all_identities(e);
352 /* Unknown message. Respond with failure. */
353 error("Unknown message %d", type);
354 buffer_clear(&e->input);
355 buffer_put_int(&e->output, 1);
356 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
362 new_socket(int type, int fd)
364 unsigned int i, old_alloc;
365 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
366 error("fcntl O_NONBLOCK: %s", strerror(errno));
371 for (i = 0; i < sockets_alloc; i++)
372 if (sockets[i].type == AUTH_UNUSED)
375 sockets[i].type = type;
376 buffer_init(&sockets[i].input);
377 buffer_init(&sockets[i].output);
380 old_alloc = sockets_alloc;
383 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
385 sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
386 for (i = old_alloc; i < sockets_alloc; i++)
387 sockets[i].type = AUTH_UNUSED;
388 sockets[old_alloc].type = type;
389 sockets[old_alloc].fd = fd;
390 buffer_init(&sockets[old_alloc].input);
391 buffer_init(&sockets[old_alloc].output);
395 prepare_select(fd_set *readset, fd_set *writeset)
398 for (i = 0; i < sockets_alloc; i++)
399 switch (sockets[i].type)
402 case AUTH_CONNECTION:
403 FD_SET(sockets[i].fd, readset);
404 if (buffer_len(&sockets[i].output) > 0)
405 FD_SET(sockets[i].fd, writeset);
410 fatal("Unknown socket type %d", sockets[i].type);
415 void after_select(fd_set *readset, fd_set *writeset)
420 struct sockaddr_un sunaddr;
422 for (i = 0; i < sockets_alloc; i++)
423 switch (sockets[i].type)
428 if (FD_ISSET(sockets[i].fd, readset))
430 len = sizeof(sunaddr);
431 sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
434 perror("accept from AUTH_SOCKET");
437 new_socket(AUTH_CONNECTION, sock);
440 case AUTH_CONNECTION:
441 if (buffer_len(&sockets[i].output) > 0 &&
442 FD_ISSET(sockets[i].fd, writeset))
444 len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
445 buffer_len(&sockets[i].output));
448 shutdown(sockets[i].fd, SHUT_RDWR);
449 close(sockets[i].fd);
450 sockets[i].type = AUTH_UNUSED;
453 buffer_consume(&sockets[i].output, len);
455 if (FD_ISSET(sockets[i].fd, readset))
457 len = read(sockets[i].fd, buf, sizeof(buf));
460 shutdown(sockets[i].fd, SHUT_RDWR);
461 close(sockets[i].fd);
462 sockets[i].type = AUTH_UNUSED;
465 buffer_append(&sockets[i].input, buf, len);
466 process_message(&sockets[i]);
470 fatal("Unknown type %d", sockets[i].type);
475 check_parent_exists(int sig)
477 if (kill(parent_pid, 0) < 0)
479 /* printf("Parent has died - Authentication agent exiting.\n"); */
482 signal(SIGALRM, check_parent_exists);
503 extern char *__progname;
505 fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
506 fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
512 main(int ac, char **av)
514 fd_set readset, writeset;
515 int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
516 struct sockaddr_un sunaddr;
518 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
520 /* check if RSA support exists */
521 if (rsa_alive() == 0) {
522 extern char *__progname;
524 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
529 while ((ch = getopt(ac, av, "cks")) != -1)
553 if (ac > 0 && (c_flag || k_flag || s_flag))
556 if (ac == 0 && !c_flag && !k_flag && !s_flag)
558 shell = getenv("SHELL");
559 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
565 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
568 fprintf(stderr, "%s not set, cannot kill agent\n",
569 SSH_AGENTPID_ENV_NAME);
573 if (pid < 1) /* XXX PID_MAX check too */
575 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
576 SSH_AGENTPID_ENV_NAME, pidstr);
579 if (kill(pid, SIGTERM) == -1)
584 format = c_flag ? "unsetenv %s;\n" : "unset %s;\n";
585 printf(format, SSH_AUTHSOCKET_ENV_NAME);
586 printf(format, SSH_AGENTPID_ENV_NAME);
587 printf("echo Agent pid %d killed;\n", pid);
591 parent_pid = getpid();
593 /* Create private directory for agent socket */
594 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
595 if (mkdtemp(socket_dir) == NULL) {
596 perror("mkdtemp: private socket dir");
599 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
602 /* Create socket early so it will exist before command gets run from
604 sock = socket(AF_UNIX, SOCK_STREAM, 0);
610 memset(&sunaddr, 0, sizeof(sunaddr));
611 sunaddr.sun_family = AF_UNIX;
612 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
613 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
618 if (listen(sock, 5) < 0)
624 /* Fork, and have the parent execute the command, if any, or present the
625 socket data. The child continues as the authentication agent. */
633 { /* Parent - execute the given command. */
635 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
638 format = c_flag ? "setenv %s %s;\n" : "%s=%s; export %s;\n";
639 printf(format, SSH_AUTHSOCKET_ENV_NAME, socket_name,
640 SSH_AUTHSOCKET_ENV_NAME);
641 printf(format, SSH_AGENTPID_ENV_NAME, pidstrbuf,
642 SSH_AGENTPID_ENV_NAME);
643 printf("echo Agent pid %d;\n", pid);
647 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
648 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
664 if (atexit(cleanup_socket) < 0)
670 new_socket(AUTH_SOCKET, sock);
673 signal(SIGALRM, check_parent_exists);
677 signal(SIGINT, SIG_IGN);
678 signal(SIGPIPE, SIG_IGN);
683 prepare_select(&readset, &writeset);
684 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
690 after_select(&readset, &writeset);