upstream: implement EMFILE mitigation for ssh-agent: remember the

fd rlimit and stop accepting new connections when it is exceeded (with some
grace). Accept is resumed when enough connections are closed.

bz#2576. feedback deraadt; ok dtucker@

OpenBSD-Commit-ID: 6a85d9cec7b85741961e7116a49f8dae777911ea
This commit is contained in:
djm@openbsd.org 2018-05-11 03:38:51 +00:00 committed by Damien Miller
parent fdba503fdf
commit b2140a739b

View File

@ -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 <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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 */
}