]> andersk Git - openssh.git/blame - ssh-agent.c
Initial revision
[openssh.git] / ssh-agent.c
CommitLineData
8efc0c15 1/*
2
3ssh-agent.c
4
5Author: Tatu Ylonen <ylo@cs.hut.fi>
6
7Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
8 All rights reserved
9
10Created: Wed Mar 29 03:46:59 1995 ylo
11
12The authentication agent program.
13
14*/
15
16#include "includes.h"
17RCSID("$Id$");
18
19#include "ssh.h"
20#include "rsa.h"
21#include "authfd.h"
22#include "buffer.h"
23#include "bufaux.h"
24#include "xmalloc.h"
25#include "packet.h"
26#include "getput.h"
27#include "mpaux.h"
28
29#include <openssl/md5.h>
30
31typedef struct
32{
33 int fd;
34 enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type;
35 Buffer input;
36 Buffer output;
37} SocketEntry;
38
39unsigned int sockets_alloc = 0;
40SocketEntry *sockets = NULL;
41
42typedef struct
43{
44 RSA *key;
45 char *comment;
46} Identity;
47
48unsigned int num_identities = 0;
49Identity *identities = NULL;
50
51int max_fd = 0;
52
53/* pid of shell == parent of agent */
54int parent_pid = -1;
55
56/* pathname and directory for AUTH_SOCKET */
57char socket_name[1024];
58char socket_dir[1024];
59
60void
61process_request_identity(SocketEntry *e)
62{
63 Buffer msg;
64 int i;
65
66 buffer_init(&msg);
67 buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
68 buffer_put_int(&msg, num_identities);
69 for (i = 0; i < num_identities; i++)
70 {
71 buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
72 buffer_put_bignum(&msg, identities[i].key->e);
73 buffer_put_bignum(&msg, identities[i].key->n);
74 buffer_put_string(&msg, identities[i].comment,
75 strlen(identities[i].comment));
76 }
77 buffer_put_int(&e->output, buffer_len(&msg));
78 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
79 buffer_free(&msg);
80}
81
82void
83process_authentication_challenge(SocketEntry *e)
84{
85 int i, pub_bits, len;
86 BIGNUM *pub_e, *pub_n, *challenge;
87 Buffer msg;
88 MD5_CTX md;
89 unsigned char buf[32], mdbuf[16], session_id[16];
90 unsigned int response_type;
91
92 buffer_init(&msg);
93 pub_e = BN_new();
94 pub_n = BN_new();
95 challenge = BN_new();
96 pub_bits = buffer_get_int(&e->input);
97 buffer_get_bignum(&e->input, pub_e);
98 buffer_get_bignum(&e->input, pub_n);
99 buffer_get_bignum(&e->input, challenge);
100 if (buffer_len(&e->input) == 0)
101 {
102 /* Compatibility code for old servers. */
103 memset(session_id, 0, 16);
104 response_type = 0;
105 }
106 else
107 {
108 /* New code. */
109 buffer_get(&e->input, (char *)session_id, 16);
110 response_type = buffer_get_int(&e->input);
111 }
112 for (i = 0; i < num_identities; i++)
113 if (pub_bits == BN_num_bits(identities[i].key->n) &&
114 BN_cmp(pub_e, identities[i].key->e) == 0 &&
115 BN_cmp(pub_n, identities[i].key->n) == 0)
116 {
117 /* Decrypt the challenge using the private key. */
118 rsa_private_decrypt(challenge, challenge, identities[i].key);
119
120 /* Compute the desired response. */
121 switch (response_type)
122 {
123 case 0: /* As of protocol 1.0 */
124 /* This response type is no longer supported. */
125 log("Compatibility with ssh protocol 1.0 no longer supported.");
126 buffer_put_char(&msg, SSH_AGENT_FAILURE);
127 goto send;
128
129 case 1: /* As of protocol 1.1 */
130 /* The response is MD5 of decrypted challenge plus session id. */
131 len = BN_num_bytes(challenge);
132 assert(len <= 32 && len);
133 memset(buf, 0, 32);
134 BN_bn2bin(challenge, buf + 32 - len);
135 MD5_Init(&md);
136 MD5_Update(&md, buf, 32);
137 MD5_Update(&md, session_id, 16);
138 MD5_Final(mdbuf, &md);
139 break;
140
141 default:
142 fatal("process_authentication_challenge: bad response_type %d",
143 response_type);
144 break;
145 }
146
147 /* Send the response. */
148 buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
149 for (i = 0; i < 16; i++)
150 buffer_put_char(&msg, mdbuf[i]);
151
152 goto send;
153 }
154 /* Unknown identity. Send failure. */
155 buffer_put_char(&msg, SSH_AGENT_FAILURE);
156 send:
157 buffer_put_int(&e->output, buffer_len(&msg));
158 buffer_append(&e->output, buffer_ptr(&msg),
159 buffer_len(&msg));
160 buffer_free(&msg);
161 BN_clear_free(pub_e);
162 BN_clear_free(pub_n);
163 BN_clear_free(challenge);
164}
165
166void
167process_remove_identity(SocketEntry *e)
168{
169 unsigned int bits;
170 unsigned int i;
171 BIGNUM *dummy, *n;
172
173 dummy = BN_new();
174 n = BN_new();
175
176 /* Get the key from the packet. */
177 bits = buffer_get_int(&e->input);
178 buffer_get_bignum(&e->input, dummy);
179 buffer_get_bignum(&e->input, n);
180
181 /* Check if we have the key. */
182 for (i = 0; i < num_identities; i++)
183 if (BN_cmp(identities[i].key->n, n) == 0)
184 {
185 /* We have this key. Free the old key. Since we don\'t want to leave
186 empty slots in the middle of the array, we actually free the
187 key there and copy data from the last entry. */
188 RSA_free(identities[i].key);
189 xfree(identities[i].comment);
190 if (i < num_identities - 1)
191 identities[i] = identities[num_identities - 1];
192 num_identities--;
193 BN_clear_free(dummy);
194 BN_clear_free(n);
195
196 /* Send success. */
197 buffer_put_int(&e->output, 1);
198 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
199 return;
200 }
201 /* We did not have the key. */
202 BN_clear(dummy);
203 BN_clear(n);
204
205 /* Send failure. */
206 buffer_put_int(&e->output, 1);
207 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
208}
209
210/* Removes all identities from the agent. */
211
212void
213process_remove_all_identities(SocketEntry *e)
214{
215 unsigned int i;
216
217 /* Loop over all identities and clear the keys. */
218 for (i = 0; i < num_identities; i++)
219 {
220 RSA_free(identities[i].key);
221 xfree(identities[i].comment);
222 }
223
224 /* Mark that there are no identities. */
225 num_identities = 0;
226
227 /* Send success. */
228 buffer_put_int(&e->output, 1);
229 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
230 return;
231}
232
233/* Adds an identity to the agent. */
234
235void
236process_add_identity(SocketEntry *e)
237{
238 RSA *k;
239 int i;
240 BIGNUM *aux;
241 BN_CTX *ctx;
242
243 if (num_identities == 0)
244 identities = xmalloc(sizeof(Identity));
245 else
246 identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
247
248 identities[num_identities].key = RSA_new();
249 k = identities[num_identities].key;
250 buffer_get_int(&e->input); /* bits */
251 k->n = BN_new();
252 buffer_get_bignum(&e->input, k->n);
253 k->e = BN_new();
254 buffer_get_bignum(&e->input, k->e);
255 k->d = BN_new();
256 buffer_get_bignum(&e->input, k->d);
257 k->iqmp = BN_new();
258 buffer_get_bignum(&e->input, k->iqmp);
259 /* SSH and SSL have p and q swapped */
260 k->q = BN_new();
261 buffer_get_bignum(&e->input, k->q); /* p */
262 k->p = BN_new();
263 buffer_get_bignum(&e->input, k->p); /* q */
264
265 /* Generate additional parameters */
266 aux = BN_new();
267 ctx = BN_CTX_new();
268
269 BN_sub(aux, k->q, BN_value_one());
270 k->dmq1 = BN_new();
271 BN_mod(k->dmq1, k->d, aux, ctx);
272
273 BN_sub(aux, k->p, BN_value_one());
274 k->dmp1 = BN_new();
275 BN_mod(k->dmp1, k->d, aux, ctx);
276
277 BN_clear_free(aux);
278 BN_CTX_free(ctx);
279
280 identities[num_identities].comment = buffer_get_string(&e->input, NULL);
281
282 /* Check if we already have the key. */
283 for (i = 0; i < num_identities; i++)
284 if (BN_cmp(identities[i].key->n, k->n) == 0)
285 {
286 /* We already have this key. Clear and free the new data and
287 return success. */
288 RSA_free(k);
289 xfree(identities[num_identities].comment);
290
291 /* Send success. */
292 buffer_put_int(&e->output, 1);
293 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
294 return;
295 }
296
297 /* Increment the number of identities. */
298 num_identities++;
299
300 /* Send a success message. */
301 buffer_put_int(&e->output, 1);
302 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
303}
304
305void
306process_message(SocketEntry *e)
307{
308 unsigned int msg_len;
309 unsigned int type;
310 unsigned char *cp;
311 if (buffer_len(&e->input) < 5)
312 return; /* Incomplete message. */
313 cp = (unsigned char *)buffer_ptr(&e->input);
314 msg_len = GET_32BIT(cp);
315 if (msg_len > 256 * 1024)
316 {
317 shutdown(e->fd, SHUT_RDWR);
318 close(e->fd);
319 e->type = AUTH_UNUSED;
320 return;
321 }
322 if (buffer_len(&e->input) < msg_len + 4)
323 return;
324 buffer_consume(&e->input, 4);
325 type = buffer_get_char(&e->input);
326
327 switch (type)
328 {
329 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
330 process_request_identity(e);
331 break;
332 case SSH_AGENTC_RSA_CHALLENGE:
333 process_authentication_challenge(e);
334 break;
335 case SSH_AGENTC_ADD_RSA_IDENTITY:
336 process_add_identity(e);
337 break;
338 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
339 process_remove_identity(e);
340 break;
341 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
342 process_remove_all_identities(e);
343 break;
344 default:
345 /* Unknown message. Respond with failure. */
346 error("Unknown message %d", type);
347 buffer_clear(&e->input);
348 buffer_put_int(&e->output, 1);
349 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
350 break;
351 }
352}
353
354void
355new_socket(int type, int fd)
356{
357 unsigned int i, old_alloc;
358 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
359 error("fcntl O_NONBLOCK: %s", strerror(errno));
360
361 if (fd > max_fd)
362 max_fd = fd;
363
364 for (i = 0; i < sockets_alloc; i++)
365 if (sockets[i].type == AUTH_UNUSED)
366 {
367 sockets[i].fd = fd;
368 sockets[i].type = type;
369 buffer_init(&sockets[i].input);
370 buffer_init(&sockets[i].output);
371 return;
372 }
373 old_alloc = sockets_alloc;
374 sockets_alloc += 10;
375 if (sockets)
376 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
377 else
378 sockets = xmalloc(sockets_alloc * sizeof(sockets[0]));
379 for (i = old_alloc; i < sockets_alloc; i++)
380 sockets[i].type = AUTH_UNUSED;
381 sockets[old_alloc].type = type;
382 sockets[old_alloc].fd = fd;
383 buffer_init(&sockets[old_alloc].input);
384 buffer_init(&sockets[old_alloc].output);
385}
386
387void
388prepare_select(fd_set *readset, fd_set *writeset)
389{
390 unsigned int i;
391 for (i = 0; i < sockets_alloc; i++)
392 switch (sockets[i].type)
393 {
394 case AUTH_SOCKET:
395 case AUTH_CONNECTION:
396 FD_SET(sockets[i].fd, readset);
397 if (buffer_len(&sockets[i].output) > 0)
398 FD_SET(sockets[i].fd, writeset);
399 break;
400 case AUTH_UNUSED:
401 break;
402 default:
403 fatal("Unknown socket type %d", sockets[i].type);
404 break;
405 }
406}
407
408void after_select(fd_set *readset, fd_set *writeset)
409{
410 unsigned int i;
411 int len, sock;
412 char buf[1024];
413 struct sockaddr_un sunaddr;
414
415 for (i = 0; i < sockets_alloc; i++)
416 switch (sockets[i].type)
417 {
418 case AUTH_UNUSED:
419 break;
420 case AUTH_SOCKET:
421 if (FD_ISSET(sockets[i].fd, readset))
422 {
423 len = sizeof(sunaddr);
424 sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
425 if (sock < 0)
426 {
427 perror("accept from AUTH_SOCKET");
428 break;
429 }
430 new_socket(AUTH_CONNECTION, sock);
431 }
432 break;
433 case AUTH_CONNECTION:
434 if (buffer_len(&sockets[i].output) > 0 &&
435 FD_ISSET(sockets[i].fd, writeset))
436 {
437 len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
438 buffer_len(&sockets[i].output));
439 if (len <= 0)
440 {
441 shutdown(sockets[i].fd, SHUT_RDWR);
442 close(sockets[i].fd);
443 sockets[i].type = AUTH_UNUSED;
444 break;
445 }
446 buffer_consume(&sockets[i].output, len);
447 }
448 if (FD_ISSET(sockets[i].fd, readset))
449 {
450 len = read(sockets[i].fd, buf, sizeof(buf));
451 if (len <= 0)
452 {
453 shutdown(sockets[i].fd, SHUT_RDWR);
454 close(sockets[i].fd);
455 sockets[i].type = AUTH_UNUSED;
456 break;
457 }
458 buffer_append(&sockets[i].input, buf, len);
459 process_message(&sockets[i]);
460 }
461 break;
462 default:
463 fatal("Unknown type %d", sockets[i].type);
464 }
465}
466
467void
468check_parent_exists(int sig)
469{
470 if (kill(parent_pid, 0) < 0)
471 {
472 /* printf("Parent has died - Authentication agent exiting.\n"); */
473 exit(1);
474 }
475 signal(SIGALRM, check_parent_exists);
476 alarm(10);
477}
478
479void cleanup_socket(void) {
480 remove(socket_name);
481 rmdir(socket_dir);
482}
483
484int
485main(int ac, char **av)
486{
487 fd_set readset, writeset;
488 int sock;
489 struct sockaddr_un sunaddr;
490
491 /* check if RSA support exists */
492 if (rsa_alive() == 0) {
493 extern char *__progname;
494 fprintf(stderr,
495 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
496 __progname);
497 exit(1);
498 }
499
500 if (ac < 2)
501 {
502 fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
503 fprintf(stderr, "Usage: %s command\n", av[0]);
504 exit(1);
505 }
506
507 parent_pid = getpid();
508
509 /* Create private directory for agent socket */
510 strlcpy(socket_dir, "/tmp/ssh-XXXXXXXX", sizeof socket_dir);
511 if (mkdtemp(socket_dir) == NULL) {
512 perror("mkdtemp: private socket dir");
513 exit(1);
514 }
515 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir, parent_pid);
516
517 /* Fork, and have the parent execute the command. The child continues as
518 the authentication agent. */
519 if (fork() != 0)
520 { /* Parent - execute the given command. */
521 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
522 execvp(av[1], av + 1);
523 perror(av[1]);
524 exit(1);
525 }
526
527 if (atexit(cleanup_socket) < 0) {
528 perror("atexit");
529 cleanup_socket();
530 exit(1);
531 }
532
533 sock = socket(AF_UNIX, SOCK_STREAM, 0);
534 if (sock < 0)
535 {
536 perror("socket");
537 exit(1);
538 }
539 memset(&sunaddr, 0, sizeof(sunaddr));
540 sunaddr.sun_family = AF_UNIX;
541 strlcpy(sunaddr.sun_path, socket_name, sizeof(sunaddr.sun_path));
542 if (bind(sock, (struct sockaddr *)&sunaddr, sizeof(sunaddr)) < 0)
543 {
544 perror("bind");
545 exit(1);
546 }
547 if (listen(sock, 5) < 0)
548 {
549 perror("listen");
550 exit(1);
551 }
552 new_socket(AUTH_SOCKET, sock);
553 signal(SIGALRM, check_parent_exists);
554 alarm(10);
555
556 signal(SIGINT, SIG_IGN);
557 while (1)
558 {
559 FD_ZERO(&readset);
560 FD_ZERO(&writeset);
561 prepare_select(&readset, &writeset);
562 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
563 {
564 if (errno == EINTR)
565 continue;
566 perror("select");
567 exit(1);
568 }
569 after_select(&readset, &writeset);
570 }
571 /*NOTREACHED*/
572}
This page took 0.244737 seconds and 5 git commands to generate.