blob: 27e064d64e541f718d4029230044441d30640c93 [file] [log] [blame]
Damien Miller1e4772c1999-10-30 11:39:56 +10001/* $OpenBSD: ssh-agent.c,v 1.16 1999/10/28 20:41:23 markus Exp $ */
Damien Miller792c5111999-10-29 09:47:09 +10002
Damien Millerd4a8b7e1999-10-27 13:42:43 +10003/*
4
5ssh-agent.c
6
7Author: Tatu Ylonen <ylo@cs.hut.fi>
8
9Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
10 All rights reserved
11
12Created: Wed Mar 29 03:46:59 1995 ylo
13
14The authentication agent program.
15
16*/
17
18#include "includes.h"
Damien Millerfd7c9111999-11-08 16:15:55 +110019RCSID("$OpenBSD: ssh-agent.c,v 1.17 1999/11/02 19:42:36 markus Exp $");
Damien Millerd4a8b7e1999-10-27 13:42:43 +100020
21#include "ssh.h"
22#include "rsa.h"
23#include "authfd.h"
24#include "buffer.h"
25#include "bufaux.h"
26#include "xmalloc.h"
27#include "packet.h"
28#include "getput.h"
29#include "mpaux.h"
30
Damien Miller7f6ea021999-10-28 13:25:17 +100031#ifdef HAVE_OPENSSL
Damien Millerd4a8b7e1999-10-27 13:42:43 +100032#include <openssl/md5.h>
Damien Miller7f6ea021999-10-28 13:25:17 +100033#endif
34#ifdef HAVE_SSL
35#include <ssl/md5.h>
36#endif
Damien Millerd4a8b7e1999-10-27 13:42:43 +100037
Damien Miller3f905871999-11-15 17:10:57 +110038#ifdef HAVE___PROGNAME
39extern char *__progname;
40#else /* HAVE___PROGNAME */
41const char *__progname = "ssh-agent";
42#endif /* HAVE___PROGNAME */
43
Damien Millerd4a8b7e1999-10-27 13:42:43 +100044typedef struct
45{
46 int fd;
47 enum { AUTH_UNUSED, AUTH_SOCKET, AUTH_CONNECTION } type;
48 Buffer input;
49 Buffer output;
50} SocketEntry;
51
52unsigned int sockets_alloc = 0;
53SocketEntry *sockets = NULL;
54
55typedef struct
56{
57 RSA *key;
58 char *comment;
59} Identity;
60
61unsigned int num_identities = 0;
62Identity *identities = NULL;
63
64int max_fd = 0;
65
66/* pid of shell == parent of agent */
67int parent_pid = -1;
68
69/* pathname and directory for AUTH_SOCKET */
70char socket_name[1024];
71char socket_dir[1024];
72
73void
74process_request_identity(SocketEntry *e)
75{
76 Buffer msg;
77 int i;
78
79 buffer_init(&msg);
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++)
83 {
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));
89 }
90 buffer_put_int(&e->output, buffer_len(&msg));
91 buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
92 buffer_free(&msg);
93}
94
95void
96process_authentication_challenge(SocketEntry *e)
97{
98 int i, pub_bits, len;
99 BIGNUM *pub_e, *pub_n, *challenge;
100 Buffer msg;
101 MD5_CTX md;
102 unsigned char buf[32], mdbuf[16], session_id[16];
103 unsigned int response_type;
104
105 buffer_init(&msg);
106 pub_e = BN_new();
107 pub_n = BN_new();
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)
114 {
115 /* Compatibility code for old servers. */
116 memset(session_id, 0, 16);
117 response_type = 0;
118 }
119 else
120 {
121 /* New code. */
122 buffer_get(&e->input, (char *)session_id, 16);
123 response_type = buffer_get_int(&e->input);
124 }
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)
129 {
130 /* Decrypt the challenge using the private key. */
131 rsa_private_decrypt(challenge, challenge, identities[i].key);
132
133 /* Compute the desired response. */
134 switch (response_type)
135 {
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);
140 goto send;
141
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);
Damien Millerfd7c9111999-11-08 16:15:55 +1100145
146 if (len <= 0 || len > 32) {
147 fatal("process_authentication_challenge: "
148 "bad challenge length %d", len);
149 }
150
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000151 memset(buf, 0, 32);
152 BN_bn2bin(challenge, buf + 32 - len);
153 MD5_Init(&md);
154 MD5_Update(&md, buf, 32);
155 MD5_Update(&md, session_id, 16);
156 MD5_Final(mdbuf, &md);
157 break;
158
159 default:
160 fatal("process_authentication_challenge: bad response_type %d",
161 response_type);
162 break;
163 }
164
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]);
169
170 goto send;
171 }
172 /* Unknown identity. Send failure. */
173 buffer_put_char(&msg, SSH_AGENT_FAILURE);
174 send:
175 buffer_put_int(&e->output, buffer_len(&msg));
176 buffer_append(&e->output, buffer_ptr(&msg),
177 buffer_len(&msg));
178 buffer_free(&msg);
179 BN_clear_free(pub_e);
180 BN_clear_free(pub_n);
181 BN_clear_free(challenge);
182}
183
184void
185process_remove_identity(SocketEntry *e)
186{
187 unsigned int bits;
188 unsigned int i;
189 BIGNUM *dummy, *n;
190
191 dummy = BN_new();
192 n = BN_new();
193
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);
198
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)
202 {
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];
210 num_identities--;
211 BN_clear_free(dummy);
212 BN_clear_free(n);
213
214 /* Send success. */
215 buffer_put_int(&e->output, 1);
216 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
217 return;
218 }
219 /* We did not have the key. */
220 BN_clear(dummy);
221 BN_clear(n);
222
223 /* Send failure. */
224 buffer_put_int(&e->output, 1);
225 buffer_put_char(&e->output, SSH_AGENT_FAILURE);
226}
227
228/* Removes all identities from the agent. */
229
230void
231process_remove_all_identities(SocketEntry *e)
232{
233 unsigned int i;
234
235 /* Loop over all identities and clear the keys. */
236 for (i = 0; i < num_identities; i++)
237 {
238 RSA_free(identities[i].key);
239 xfree(identities[i].comment);
240 }
241
242 /* Mark that there are no identities. */
243 num_identities = 0;
244
245 /* Send success. */
246 buffer_put_int(&e->output, 1);
247 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
248 return;
249}
250
251/* Adds an identity to the agent. */
252
253void
254process_add_identity(SocketEntry *e)
255{
256 RSA *k;
257 int i;
258 BIGNUM *aux;
259 BN_CTX *ctx;
260
261 if (num_identities == 0)
262 identities = xmalloc(sizeof(Identity));
263 else
264 identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
265
266 identities[num_identities].key = RSA_new();
267 k = identities[num_identities].key;
268 buffer_get_int(&e->input); /* bits */
269 k->n = BN_new();
270 buffer_get_bignum(&e->input, k->n);
271 k->e = BN_new();
272 buffer_get_bignum(&e->input, k->e);
273 k->d = BN_new();
274 buffer_get_bignum(&e->input, k->d);
275 k->iqmp = BN_new();
276 buffer_get_bignum(&e->input, k->iqmp);
277 /* SSH and SSL have p and q swapped */
278 k->q = BN_new();
279 buffer_get_bignum(&e->input, k->q); /* p */
280 k->p = BN_new();
281 buffer_get_bignum(&e->input, k->p); /* q */
282
283 /* Generate additional parameters */
284 aux = BN_new();
285 ctx = BN_CTX_new();
286
287 BN_sub(aux, k->q, BN_value_one());
288 k->dmq1 = BN_new();
289 BN_mod(k->dmq1, k->d, aux, ctx);
290
291 BN_sub(aux, k->p, BN_value_one());
292 k->dmp1 = BN_new();
293 BN_mod(k->dmp1, k->d, aux, ctx);
294
295 BN_clear_free(aux);
296 BN_CTX_free(ctx);
297
298 identities[num_identities].comment = buffer_get_string(&e->input, NULL);
299
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)
303 {
304 /* We already have this key. Clear and free the new data and
305 return success. */
306 RSA_free(k);
307 xfree(identities[num_identities].comment);
308
309 /* Send success. */
310 buffer_put_int(&e->output, 1);
311 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
312 return;
313 }
314
315 /* Increment the number of identities. */
316 num_identities++;
317
318 /* Send a success message. */
319 buffer_put_int(&e->output, 1);
320 buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
321}
322
323void
324process_message(SocketEntry *e)
325{
326 unsigned int msg_len;
327 unsigned int type;
328 unsigned char *cp;
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)
334 {
335 shutdown(e->fd, SHUT_RDWR);
336 close(e->fd);
337 e->type = AUTH_UNUSED;
338 return;
339 }
340 if (buffer_len(&e->input) < msg_len + 4)
341 return;
342 buffer_consume(&e->input, 4);
343 type = buffer_get_char(&e->input);
344
345 switch (type)
346 {
347 case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
348 process_request_identity(e);
349 break;
350 case SSH_AGENTC_RSA_CHALLENGE:
351 process_authentication_challenge(e);
352 break;
353 case SSH_AGENTC_ADD_RSA_IDENTITY:
354 process_add_identity(e);
355 break;
356 case SSH_AGENTC_REMOVE_RSA_IDENTITY:
357 process_remove_identity(e);
358 break;
359 case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
360 process_remove_all_identities(e);
361 break;
362 default:
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);
368 break;
369 }
370}
371
372void
373new_socket(int type, int fd)
374{
375 unsigned int i, old_alloc;
376 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
377 error("fcntl O_NONBLOCK: %s", strerror(errno));
378
379 if (fd > max_fd)
380 max_fd = fd;
381
382 for (i = 0; i < sockets_alloc; i++)
383 if (sockets[i].type == AUTH_UNUSED)
384 {
385 sockets[i].fd = fd;
386 sockets[i].type = type;
387 buffer_init(&sockets[i].input);
388 buffer_init(&sockets[i].output);
389 return;
390 }
391 old_alloc = sockets_alloc;
392 sockets_alloc += 10;
393 if (sockets)
394 sockets = xrealloc(sockets, sockets_alloc * sizeof(sockets[0]));
395 else
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);
403}
404
405void
406prepare_select(fd_set *readset, fd_set *writeset)
407{
408 unsigned int i;
409 for (i = 0; i < sockets_alloc; i++)
410 switch (sockets[i].type)
411 {
412 case AUTH_SOCKET:
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);
417 break;
418 case AUTH_UNUSED:
419 break;
420 default:
421 fatal("Unknown socket type %d", sockets[i].type);
422 break;
423 }
424}
425
426void after_select(fd_set *readset, fd_set *writeset)
427{
428 unsigned int i;
429 int len, sock;
430 char buf[1024];
431 struct sockaddr_un sunaddr;
432
433 for (i = 0; i < sockets_alloc; i++)
434 switch (sockets[i].type)
435 {
436 case AUTH_UNUSED:
437 break;
438 case AUTH_SOCKET:
439 if (FD_ISSET(sockets[i].fd, readset))
440 {
441 len = sizeof(sunaddr);
442 sock = accept(sockets[i].fd, (struct sockaddr *)&sunaddr, &len);
443 if (sock < 0)
444 {
445 perror("accept from AUTH_SOCKET");
446 break;
447 }
448 new_socket(AUTH_CONNECTION, sock);
449 }
450 break;
451 case AUTH_CONNECTION:
452 if (buffer_len(&sockets[i].output) > 0 &&
453 FD_ISSET(sockets[i].fd, writeset))
454 {
455 len = write(sockets[i].fd, buffer_ptr(&sockets[i].output),
456 buffer_len(&sockets[i].output));
457 if (len <= 0)
458 {
459 shutdown(sockets[i].fd, SHUT_RDWR);
460 close(sockets[i].fd);
461 sockets[i].type = AUTH_UNUSED;
462 break;
463 }
464 buffer_consume(&sockets[i].output, len);
465 }
466 if (FD_ISSET(sockets[i].fd, readset))
467 {
468 len = read(sockets[i].fd, buf, sizeof(buf));
469 if (len <= 0)
470 {
471 shutdown(sockets[i].fd, SHUT_RDWR);
472 close(sockets[i].fd);
473 sockets[i].type = AUTH_UNUSED;
474 break;
475 }
476 buffer_append(&sockets[i].input, buf, len);
477 process_message(&sockets[i]);
478 }
479 break;
480 default:
481 fatal("Unknown type %d", sockets[i].type);
482 }
483}
484
485void
486check_parent_exists(int sig)
487{
488 if (kill(parent_pid, 0) < 0)
489 {
490 /* printf("Parent has died - Authentication agent exiting.\n"); */
491 exit(1);
492 }
493 signal(SIGALRM, check_parent_exists);
494 alarm(10);
495}
496
Damien Miller792c5111999-10-29 09:47:09 +1000497void
498cleanup_socket(void)
499{
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000500 remove(socket_name);
501 rmdir(socket_dir);
502}
503
Damien Miller792c5111999-10-29 09:47:09 +1000504void
505cleanup_exit(int i)
506{
507 cleanup_socket();
508 exit(i);
509}
510
511void
512usage()
513{
Damien Miller792c5111999-10-29 09:47:09 +1000514 fprintf(stderr, "ssh-agent version %s\n", SSH_VERSION);
515 fprintf(stderr, "Usage: %s [-c | -s] [-k] [command {args...]]\n",
516 __progname);
517 exit(1);
518}
519
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000520int
521main(int ac, char **av)
522{
523 fd_set readset, writeset;
Damien Miller792c5111999-10-29 09:47:09 +1000524 int sock, c_flag = 0, k_flag = 0, s_flag = 0, ch;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000525 struct sockaddr_un sunaddr;
Damien Miller792c5111999-10-29 09:47:09 +1000526 pid_t pid;
527 char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000528
529 /* check if RSA support exists */
530 if (rsa_alive() == 0) {
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000531 fprintf(stderr,
532 "%s: no RSA support in libssl and libcrypto. See ssl(8).\n",
533 __progname);
534 exit(1);
535 }
536
Damien Millerb77870f1999-11-10 12:48:08 +1100537#if defined(__GNU_LIBRARY__)
538 while ((ch = getopt(ac, av, "+cks")) != -1)
539#else
Damien Miller792c5111999-10-29 09:47:09 +1000540 while ((ch = getopt(ac, av, "cks")) != -1)
Damien Millerb77870f1999-11-10 12:48:08 +1100541#endif /* defined(__GNU_LIBRARY__) */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000542 {
Damien Miller792c5111999-10-29 09:47:09 +1000543 switch (ch)
544 {
545 case 'c':
546 if (s_flag)
547 usage();
548 c_flag++;
549 break;
550 case 'k':
551 k_flag++;
552 break;
553 case 's':
554 if (c_flag)
555 usage();
556 s_flag++;
557 break;
558 default:
559 usage();
560 }
561 }
562 ac -= optind;
563 av += optind;
564
565 if (ac > 0 && (c_flag || k_flag || s_flag))
566 usage();
567
568 if (ac == 0 && !c_flag && !k_flag && !s_flag)
569 {
570 shell = getenv("SHELL");
571 if (shell != NULL && strncmp(shell + strlen(shell) - 3, "csh", 3) == 0)
572 c_flag = 1;
573 }
574
575 if (k_flag)
576 {
577 pidstr = getenv(SSH_AGENTPID_ENV_NAME);
578 if (pidstr == NULL)
579 {
580 fprintf(stderr, "%s not set, cannot kill agent\n",
581 SSH_AGENTPID_ENV_NAME);
582 exit(1);
583 }
584 pid = atoi(pidstr);
585 if (pid < 1) /* XXX PID_MAX check too */
586 {
587 fprintf(stderr, "%s=\"%s\", which is not a good PID\n",
588 SSH_AGENTPID_ENV_NAME, pidstr);
589 exit(1);
590 }
591 if (kill(pid, SIGTERM) == -1)
592 {
593 perror("kill");
594 exit(1);
595 }
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);
600 exit(0);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000601 }
602
603 parent_pid = getpid();
604
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");
609 exit(1);
610 }
Damien Miller792c5111999-10-29 09:47:09 +1000611 snprintf(socket_name, sizeof socket_name, "%s/agent.%d", socket_dir,
612 parent_pid);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000613
Damien Miller792c5111999-10-29 09:47:09 +1000614 /* Create socket early so it will exist before command gets run from
615 the parent. */
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000616 sock = socket(AF_UNIX, SOCK_STREAM, 0);
617 if (sock < 0)
618 {
619 perror("socket");
Damien Miller792c5111999-10-29 09:47:09 +1000620 cleanup_exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000621 }
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)
626 {
627 perror("bind");
Damien Miller792c5111999-10-29 09:47:09 +1000628 cleanup_exit(1);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000629 }
630 if (listen(sock, 5) < 0)
631 {
632 perror("listen");
Damien Miller792c5111999-10-29 09:47:09 +1000633 cleanup_exit(1);
634 }
635
636 /* Fork, and have the parent execute the command, if any, or present the
637 socket data. The child continues as the authentication agent. */
638 pid = fork();
639 if (pid == -1)
640 {
641 perror("fork");
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000642 exit(1);
643 }
Damien Miller792c5111999-10-29 09:47:09 +1000644 if (pid != 0)
645 { /* Parent - execute the given command. */
646 close(sock);
647 snprintf(pidstrbuf, sizeof pidstrbuf, "%d", pid);
648 if (ac == 0)
649 {
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);
656 exit(0);
657 }
658
659 setenv(SSH_AUTHSOCKET_ENV_NAME, socket_name, 1);
660 setenv(SSH_AGENTPID_ENV_NAME, pidstrbuf, 1);
661 execvp(av[0], av);
662 perror(av[0]);
663 exit(1);
664 }
665
666 close(0);
667 close(1);
668 close(2);
669
Damien Miller1e4772c1999-10-30 11:39:56 +1000670 if (setsid() == -1)
671 {
672 perror("setsid");
673 cleanup_exit(1);
674 }
Damien Miller792c5111999-10-29 09:47:09 +1000675
676 if (atexit(cleanup_socket) < 0)
Damien Miller1e4772c1999-10-30 11:39:56 +1000677 {
678 perror("atexit");
679 cleanup_exit(1);
680 }
Damien Miller792c5111999-10-29 09:47:09 +1000681
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000682 new_socket(AUTH_SOCKET, sock);
Damien Miller792c5111999-10-29 09:47:09 +1000683 if (ac > 0)
684 {
685 signal(SIGALRM, check_parent_exists);
686 alarm(10);
687 }
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000688
689 signal(SIGINT, SIG_IGN);
Damien Miller792c5111999-10-29 09:47:09 +1000690 signal(SIGPIPE, SIG_IGN);
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000691 while (1)
692 {
693 FD_ZERO(&readset);
694 FD_ZERO(&writeset);
695 prepare_select(&readset, &writeset);
696 if (select(max_fd + 1, &readset, &writeset, NULL, NULL) < 0)
697 {
698 if (errno == EINTR)
699 continue;
Damien Millerd4a8b7e1999-10-27 13:42:43 +1000700 exit(1);
701 }
702 after_select(&readset, &writeset);
703 }
704 /*NOTREACHED*/
705}