diff options
author | djm@openbsd.org <djm@openbsd.org> | 2018-05-11 03:38:51 +0000 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2018-05-11 13:56:42 +1000 |
commit | b2140a739be4c3b43cc1dc08322dca39a1e39d20 (patch) | |
tree | 156f0f28048aef460860c93bb908005e4e06c008 /ssh-agent.c | |
parent | fdba503fdfc647ee8a244002f1581e869c1f3d90 (diff) |
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
Diffstat (limited to 'ssh-agent.c')
-rw-r--r-- | ssh-agent.c | 60 |
1 files changed, 49 insertions, 11 deletions
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 @@ | |||
1 | /* $OpenBSD: ssh-agent.c,v 1.230 2018/04/10 00:10:49 djm Exp $ */ | 1 | /* $OpenBSD: ssh-agent.c,v 1.231 2018/05/11 03:38:51 djm Exp $ */ |
2 | /* | 2 | /* |
3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> | 3 | * Author: Tatu Ylonen <ylo@cs.hut.fi> |
4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland | 4 | * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland |
@@ -886,10 +886,10 @@ handle_conn_write(u_int socknum) | |||
886 | } | 886 | } |
887 | 887 | ||
888 | static void | 888 | static void |
889 | after_poll(struct pollfd *pfd, size_t npfd) | 889 | after_poll(struct pollfd *pfd, size_t npfd, u_int maxfds) |
890 | { | 890 | { |
891 | size_t i; | 891 | size_t i; |
892 | u_int socknum; | 892 | u_int socknum, activefds = npfd; |
893 | 893 | ||
894 | for (i = 0; i < npfd; i++) { | 894 | for (i = 0; i < npfd; i++) { |
895 | if (pfd[i].revents == 0) | 895 | if (pfd[i].revents == 0) |
@@ -909,18 +909,30 @@ after_poll(struct pollfd *pfd, size_t npfd) | |||
909 | /* Process events */ | 909 | /* Process events */ |
910 | switch (sockets[socknum].type) { | 910 | switch (sockets[socknum].type) { |
911 | case AUTH_SOCKET: | 911 | case AUTH_SOCKET: |
912 | if ((pfd[i].revents & (POLLIN|POLLERR)) != 0) | 912 | if ((pfd[i].revents & (POLLIN|POLLERR)) == 0) |
913 | handle_socket_read(socknum); | 913 | break; |
914 | if (npfd > maxfds) { | ||
915 | debug3("out of fds (active %u >= limit %u); " | ||
916 | "skipping accept", activefds, maxfds); | ||
917 | break; | ||
918 | } | ||
919 | if (handle_socket_read(socknum) == 0) | ||
920 | activefds++; | ||
914 | break; | 921 | break; |
915 | case AUTH_CONNECTION: | 922 | case AUTH_CONNECTION: |
916 | if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 && | 923 | if ((pfd[i].revents & (POLLIN|POLLERR)) != 0 && |
917 | handle_conn_read(socknum) != 0) { | 924 | handle_conn_read(socknum) != 0) { |
918 | close_socket(&sockets[socknum]); | 925 | goto close_sock; |
919 | break; | ||
920 | } | 926 | } |
921 | if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 && | 927 | if ((pfd[i].revents & (POLLOUT|POLLHUP)) != 0 && |
922 | handle_conn_write(socknum) != 0) | 928 | handle_conn_write(socknum) != 0) { |
929 | close_sock: | ||
930 | if (activefds == 0) | ||
931 | fatal("activefds == 0 at close_sock"); | ||
923 | close_socket(&sockets[socknum]); | 932 | close_socket(&sockets[socknum]); |
933 | activefds--; | ||
934 | break; | ||
935 | } | ||
924 | break; | 936 | break; |
925 | default: | 937 | default: |
926 | break; | 938 | break; |
@@ -929,7 +941,7 @@ after_poll(struct pollfd *pfd, size_t npfd) | |||
929 | } | 941 | } |
930 | 942 | ||
931 | static int | 943 | static int |
932 | prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp) | 944 | prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp, u_int maxfds) |
933 | { | 945 | { |
934 | struct pollfd *pfd = *pfdp; | 946 | struct pollfd *pfd = *pfdp; |
935 | size_t i, j, npfd = 0; | 947 | size_t i, j, npfd = 0; |
@@ -958,6 +970,16 @@ prepare_poll(struct pollfd **pfdp, size_t *npfdp, int *timeoutp) | |||
958 | for (i = j = 0; i < sockets_alloc; i++) { | 970 | for (i = j = 0; i < sockets_alloc; i++) { |
959 | switch (sockets[i].type) { | 971 | switch (sockets[i].type) { |
960 | case AUTH_SOCKET: | 972 | case AUTH_SOCKET: |
973 | if (npfd > maxfds) { | ||
974 | debug3("out of fds (active %zu >= limit %u); " | ||
975 | "skipping arming listener", npfd, maxfds); | ||
976 | break; | ||
977 | } | ||
978 | pfd[j].fd = sockets[i].fd; | ||
979 | pfd[j].revents = 0; | ||
980 | pfd[j].events = POLLIN; | ||
981 | j++; | ||
982 | break; | ||
961 | case AUTH_CONNECTION: | 983 | case AUTH_CONNECTION: |
962 | pfd[j].fd = sockets[i].fd; | 984 | pfd[j].fd = sockets[i].fd; |
963 | pfd[j].revents = 0; | 985 | pfd[j].revents = 0; |
@@ -1058,6 +1080,7 @@ main(int ac, char **av) | |||
1058 | int timeout = -1; /* INFTIM */ | 1080 | int timeout = -1; /* INFTIM */ |
1059 | struct pollfd *pfd = NULL; | 1081 | struct pollfd *pfd = NULL; |
1060 | size_t npfd = 0; | 1082 | size_t npfd = 0; |
1083 | u_int maxfds; | ||
1061 | 1084 | ||
1062 | ssh_malloc_init(); /* must be called before any mallocs */ | 1085 | ssh_malloc_init(); /* must be called before any mallocs */ |
1063 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ | 1086 | /* Ensure that fds 0, 1 and 2 are open or directed to /dev/null */ |
@@ -1069,6 +1092,9 @@ main(int ac, char **av) | |||
1069 | 1092 | ||
1070 | platform_disable_tracing(0); /* strict=no */ | 1093 | platform_disable_tracing(0); /* strict=no */ |
1071 | 1094 | ||
1095 | if (getrlimit(RLIMIT_NOFILE, &rlim) == -1) | ||
1096 | fatal("%s: getrlimit: %s", __progname, strerror(errno)); | ||
1097 | |||
1072 | #ifdef WITH_OPENSSL | 1098 | #ifdef WITH_OPENSSL |
1073 | OpenSSL_add_all_algorithms(); | 1099 | OpenSSL_add_all_algorithms(); |
1074 | #endif | 1100 | #endif |
@@ -1165,6 +1191,18 @@ main(int ac, char **av) | |||
1165 | printf("echo Agent pid %ld killed;\n", (long)pid); | 1191 | printf("echo Agent pid %ld killed;\n", (long)pid); |
1166 | exit(0); | 1192 | exit(0); |
1167 | } | 1193 | } |
1194 | |||
1195 | /* | ||
1196 | * Minimum file descriptors: | ||
1197 | * stdio (3) + listener (1) + syslog (1 maybe) + connection (1) + | ||
1198 | * a few spare for libc / stack protectors / sanitisers, etc. | ||
1199 | */ | ||
1200 | #define SSH_AGENT_MIN_FDS (3+1+1+1+4) | ||
1201 | if (rlim.rlim_cur < SSH_AGENT_MIN_FDS) | ||
1202 | fatal("%s: file descriptior rlimit %lld too low (minimum %u)", | ||
1203 | __progname, (long long)rlim.rlim_cur, SSH_AGENT_MIN_FDS); | ||
1204 | maxfds = rlim.rlim_cur - SSH_AGENT_MIN_FDS; | ||
1205 | |||
1168 | parent_pid = getpid(); | 1206 | parent_pid = getpid(); |
1169 | 1207 | ||
1170 | if (agentsocket == NULL) { | 1208 | if (agentsocket == NULL) { |
@@ -1284,7 +1322,7 @@ skip: | |||
1284 | platform_pledge_agent(); | 1322 | platform_pledge_agent(); |
1285 | 1323 | ||
1286 | while (1) { | 1324 | while (1) { |
1287 | prepare_poll(&pfd, &npfd, &timeout); | 1325 | prepare_poll(&pfd, &npfd, &timeout, maxfds); |
1288 | result = poll(pfd, npfd, timeout); | 1326 | result = poll(pfd, npfd, timeout); |
1289 | saved_errno = errno; | 1327 | saved_errno = errno; |
1290 | if (parent_alive_interval != 0) | 1328 | if (parent_alive_interval != 0) |
@@ -1295,7 +1333,7 @@ skip: | |||
1295 | continue; | 1333 | continue; |
1296 | fatal("poll: %s", strerror(saved_errno)); | 1334 | fatal("poll: %s", strerror(saved_errno)); |
1297 | } else if (result > 0) | 1335 | } else if (result > 0) |
1298 | after_poll(pfd, npfd); | 1336 | after_poll(pfd, npfd, maxfds); |
1299 | } | 1337 | } |
1300 | /* NOTREACHED */ | 1338 | /* NOTREACHED */ |
1301 | } | 1339 | } |