diff --git a/ssh-agent.c b/ssh-agent.c index cd028913e..d8a8260f9 100644 --- a/ssh-agent.c +++ b/ssh-agent.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ssh-agent.c,v 1.230 2018/04/10 00:10:49 djm Exp $ */ +/* $OpenBSD: ssh-agent.c,v 1.231 2018/05/11 03:38:51 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -886,10 +886,10 @@ handle_conn_write(u_int socknum) } static void -after_poll(struct pollfd *pfd, size_t npfd) +after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds) { size_t i; - u_int socknum; + u_int socknum, activefds = npfd; for (i = 0; i < npfd; i++) { if (pfd[i].revents == 0) @@ -909,18 +909,30 @@ after_poll(struct pollfd *pfd, size_t npfd) /* Process events */ switch (sockets[socknum].type) { case AUTH_SOCKET: - if ((pfd[i].revents & (POLLIN|POLLERR)) != 0) - handle_socket_read(socknum); + if ((pfd[i].revents & (POLLIN|POLLERR)) == 0) + break; + if (npfd > maxfds) { + debug3("out of fds (active %u >= limit %u); " + "skipping accept", activefds, maxfds); + break; + } + if (handle_socket_read(socknum) == 0) + activefds++; break; case AUTH_CONNECTION: if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 && handle_conn_read(socknum) != 0) { - close_socket(&sockets[socknum]); - break; + goto close_sock; } if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 && - handle_conn_write(socknum) != 0) + handle_conn_write(socknum) != 0) { + close_sock: + if (activefds == 0) + fatal("activefds == 0 at close_sock"); close_socket(&sockets[socknum]); + activefds--; + break; + } break; default: break; @@ -929,7 +941,7 @@ after_poll(struct pollfd *pfd, size_t npfd) } static int -prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp) +prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds) { struct pollfd *pfd = *pfdp; size_t i, j, npfd = 0; @@ -958,6 +970,16 @@ prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp) for (i = j = 0; i < sockets_alloc; i++) { switch (sockets[i].type) { case AUTH_SOCKET: + if (npfd > maxfds) { + debug3("out of fds (active %zu >= limit %u); " + "skipping arming listener", npfd, maxfds); + break; + } + pfd[j].fd = sockets[i].fd; + pfd[j].revents = 0; + pfd[j].events = POLLIN; + j++; + break; case AUTH_CONNECTION: pfd[j].fd = sockets[i].fd; pfd[j].revents = 0; @@ -1058,6 +1080,7 @@ main(int ac, char **av) int timeout = -1; /* INFTIM */ struct pollfd *pfd = NULL; size_t npfd = 0; + u_int maxfds; ssh_malloc_init(); /* must be called before any mallocs */ /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ @@ -1069,6 +1092,9 @@ main(int ac, char **av) platform_disable_tracing(0); /* strict=no */ + if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) + fatal("%s: getrlimit: %s", __progname, strerror(errno)); + #ifdef WITH_OPENSSL OpenSSL_add_all_algorithms(); #endif @@ -1165,6 +1191,18 @@ main(int ac, char **av) printf("echo Agent pid %ld killed;\n", (long)pid); exit(0); } + + /* + * Minimum file descriptors: + * stdio (3) + listener (1) + syslog (1 maybe) + connection (1) + + * a few spare for libc / stack protectors / sanitisers, etc. + */ +#define SSH_AGENT_MIN_FDS (3+1+1+1+4) + if (rlim.rlim_cur < SSH_AGENT_MIN_FDS) + fatal("%s: file descriptior rlimit %lld too low (minimum %u)", + __progname, (long long)rlim.rlim_cur, SSH_AGENT_MIN_FDS); + maxfds = rlim.rlim_cur - SSH_AGENT_MIN_FDS; + parent_pid = getpid(); if (agentsocket == NULL) { @@ -1284,7 +1322,7 @@ skip: platform_pledge_agent(); while (1) { - prepare_poll(&pfd, &npfd, &timeout); + prepare_poll(&pfd, &npfd, &timeout, maxfds); result = poll(pfd, npfd, timeout); saved_errno = errno; if (parent_alive_interval != 0) @@ -1295,7 +1333,7 @@ skip: continue; fatal("poll: %s", strerror(saved_errno)); } else if (result > 0) - after_poll(pfd, npfd); + after_poll(pfd, npfd, maxfds); } /* NOTREACHED */ }