=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/sshd.c,v retrieving revision 1.119 retrieving revision 1.120 diff -u -r1.119 -r1.120 --- src/usr.bin/ssh/sshd.c 2000/06/22 16:32:27 1.119 +++ src/usr.bin/ssh/sshd.c 2000/06/26 21:59:18 1.120 @@ -14,7 +14,7 @@ */ #include "includes.h" -RCSID("$OpenBSD: sshd.c,v 1.119 2000/06/22 16:32:27 markus Exp $"); +RCSID("$OpenBSD: sshd.c,v 1.120 2000/06/26 21:59:18 markus Exp $"); #include "xmalloc.h" #include "rsa.h" @@ -395,6 +395,9 @@ key_free(sensitive_data.dsa_host_key); } +int *startup_pipes = NULL; /* options.max_startup sized array of fd ints */ +int startup_pipe; /* in child */ + /* * Main program for the daemon. */ @@ -403,7 +406,7 @@ { extern char *optarg; extern int optind; - int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, on = 1; + int opt, sock_in = 0, sock_out = 0, newsock, j, i, fdsetsz, on = 1; pid_t pid; socklen_t fromlen; int silent = 0; @@ -416,6 +419,8 @@ struct addrinfo *ai; char ntop[NI_MAXHOST], strport[NI_MAXSERV]; int listen_sock, maxfd; + int startup_p[2]; + int startups = 0; /* Save argv[0]. */ saved_argv = av; @@ -737,6 +742,7 @@ /* Arrange to restart on SIGHUP. The handler needs listen_sock. */ signal(SIGHUP, sighup_handler); + signal(SIGTERM, sigterm_handler); signal(SIGQUIT, sigterm_handler); @@ -744,12 +750,15 @@ signal(SIGCHLD, main_sigchld_handler); /* setup fd set for listen */ + fdset = NULL; maxfd = 0; for (i = 0; i < num_listen_socks; i++) if (listen_socks[i] > maxfd) maxfd = listen_socks[i]; - fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); - fdset = (fd_set *)xmalloc(fdsetsz); + /* pipes connected to unauthenticated childs */ + startup_pipes = xmalloc(options.max_startups * sizeof(int)); + for (i = 0; i < options.max_startups; i++) + startup_pipes[i] = -1; /* * Stay listening for connections until the system crashes or @@ -758,80 +767,128 @@ for (;;) { if (received_sighup) sighup_restart(); - /* Wait in select until there is a connection. */ + if (fdset != NULL) + xfree(fdset); + fdsetsz = howmany(maxfd, NFDBITS) * sizeof(fd_mask); + fdset = (fd_set *)xmalloc(fdsetsz); memset(fdset, 0, fdsetsz); + for (i = 0; i < num_listen_socks; i++) FD_SET(listen_socks[i], fdset); + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1) + FD_SET(startup_pipes[i], fdset); + + /* Wait in select until there is a connection. */ if (select(maxfd + 1, fdset, NULL, NULL, NULL) < 0) { if (errno != EINTR) error("select: %.100s", strerror(errno)); continue; } + for (i = 0; i < options.max_startups; i++) + if (startup_pipes[i] != -1 && + FD_ISSET(startup_pipes[i], fdset)) { + /* + * the read end of the pipe is ready + * if the child has closed the pipe + * after successfull authentication + * or if the child has died + */ + close(startup_pipes[i]); + startup_pipes[i] = -1; + startups--; + } for (i = 0; i < num_listen_socks; i++) { if (!FD_ISSET(listen_socks[i], fdset)) continue; - fromlen = sizeof(from); - newsock = accept(listen_socks[i], (struct sockaddr *)&from, - &fromlen); - if (newsock < 0) { - if (errno != EINTR && errno != EWOULDBLOCK) - error("accept: %.100s", strerror(errno)); - continue; - } - if (fcntl(newsock, F_SETFL, 0) < 0) { - error("newsock del O_NONBLOCK: %s", strerror(errno)); - continue; - } - /* - * Got connection. Fork a child to handle it, unless - * we are in debugging mode. - */ - if (debug_flag) { + fromlen = sizeof(from); + newsock = accept(listen_socks[i], (struct sockaddr *)&from, + &fromlen); + if (newsock < 0) { + if (errno != EINTR && errno != EWOULDBLOCK) + error("accept: %.100s", strerror(errno)); + continue; + } + if (fcntl(newsock, F_SETFL, 0) < 0) { + error("newsock del O_NONBLOCK: %s", strerror(errno)); + continue; + } + if (startups >= options.max_startups) { + close(newsock); + continue; + } + if (pipe(startup_p) == -1) { + close(newsock); + continue; + } + + for (j = 0; j < options.max_startups; j++) + if (startup_pipes[j] == -1) { + startup_pipes[j] = startup_p[0]; + if (maxfd < startup_p[0]) + maxfd = startup_p[0]; + startups++; + break; + } + /* - * In debugging mode. Close the listening - * socket, and start processing the - * connection without forking. + * Got connection. Fork a child to handle it, unless + * we are in debugging mode. */ - debug("Server will not fork when running in debugging mode."); - close_listen_socks(); - sock_in = newsock; - sock_out = newsock; - pid = getpid(); - break; - } else { - /* - * Normal production daemon. Fork, and have - * the child process the connection. The - * parent continues listening. - */ - if ((pid = fork()) == 0) { + if (debug_flag) { /* - * Child. Close the listening socket, and start using the - * accepted socket. Reinitialize logging (since our pid has - * changed). We break out of the loop to handle the connection. + * In debugging mode. Close the listening + * socket, and start processing the + * connection without forking. */ + debug("Server will not fork when running in debugging mode."); close_listen_socks(); sock_in = newsock; sock_out = newsock; - log_init(av0, options.log_level, options.log_facility, log_stderr); + pid = getpid(); break; + } else { + /* + * Normal production daemon. Fork, and have + * the child process the connection. The + * parent continues listening. + */ + if ((pid = fork()) == 0) { + /* + * Child. Close the listening and max_startup + * sockets. Start using the accepted socket. + * Reinitialize logging (since our pid has + * changed). We break out of the loop to handle + * the connection. + */ + startup_pipe = startup_p[1]; + for (j = 0; j < options.max_startups; j++) + if (startup_pipes[j] != -1) + close(startup_pipes[j]); + close_listen_socks(); + sock_in = newsock; + sock_out = newsock; + log_init(av0, options.log_level, options.log_facility, log_stderr); + break; + } } - } - /* Parent. Stay in the loop. */ - if (pid < 0) - error("fork: %.100s", strerror(errno)); - else - debug("Forked child %d.", pid); + /* Parent. Stay in the loop. */ + if (pid < 0) + error("fork: %.100s", strerror(errno)); + else + debug("Forked child %d.", pid); - /* Mark that the key has been used (it was "given" to the child). */ - key_used = 1; + close(startup_p[1]); - arc4random_stir(); + /* Mark that the key has been used (it was "given" to the child). */ + key_used = 1; - /* Close the new socket (the child is now taking care of it). */ - close(newsock); - } /* for (i = 0; i < num_listen_socks; i++) */ + arc4random_stir(); + + /* Close the new socket (the child is now taking care of it). */ + close(newsock); + } /* child process check (or debug mode) */ if (num_listen_socks < 0) break;