diff options
Diffstat (limited to 'sshd.c')
-rw-r--r-- | sshd.c | 114 |
1 files changed, 86 insertions, 28 deletions
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: sshd.c,v 1.532 2019/01/21 10:38:54 djm Exp $ */ | 1 | /* $OpenBSD: sshd.c,v 1.533 2019/03/01 02:32:39 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 |
@@ -213,9 +213,26 @@ u_int session_id2_len = 0; | |||
213 | /* record remote hostname or ip */ | 213 | /* record remote hostname or ip */ |
214 | u_int utmp_len = HOST_NAME_MAX+1; | 214 | u_int utmp_len = HOST_NAME_MAX+1; |
215 | 215 | ||
216 | /* options.max_startup sized array of fd ints */ | 216 | /* |
217 | * startup_pipes/flags are used for tracking children of the listening sshd | ||
218 | * process early in their lifespans. This tracking is needed for three things: | ||
219 | * | ||
220 | * 1) Implementing the MaxStartups limit of concurrent unauthenticated | ||
221 | * connections. | ||
222 | * 2) Avoiding a race condition for SIGHUP processing, where child processes | ||
223 | * may have listen_socks open that could collide with main listener process | ||
224 | * after it restarts. | ||
225 | * 3) Ensuring that rexec'd sshd processes have received their initial state | ||
226 | * from the parent listen process before handling SIGHUP. | ||
227 | * | ||
228 | * Child processes signal that they have completed closure of the listen_socks | ||
229 | * and (if applicable) received their rexec state by sending a char over their | ||
230 | * sock. Child processes signal that authentication has completed by closing | ||
231 | * the sock (or by exiting). | ||
232 | */ | ||
217 | static int *startup_pipes = NULL; | 233 | static int *startup_pipes = NULL; |
218 | static int startup_pipe; /* in child */ | 234 | static int *startup_flags = NULL; /* Indicates child closed listener */ |
235 | static int startup_pipe = -1; /* in child */ | ||
219 | 236 | ||
220 | /* variables used for privilege separation */ | 237 | /* variables used for privilege separation */ |
221 | int use_privsep = -1; | 238 | int use_privsep = -1; |
@@ -901,14 +918,9 @@ server_accept_inetd(int *sock_in, int *sock_out) | |||
901 | { | 918 | { |
902 | int fd; | 919 | int fd; |
903 | 920 | ||
904 | startup_pipe = -1; | ||
905 | if (rexeced_flag) { | 921 | if (rexeced_flag) { |
906 | close(REEXEC_CONFIG_PASS_FD); | 922 | close(REEXEC_CONFIG_PASS_FD); |
907 | *sock_in = *sock_out = dup(STDIN_FILENO); | 923 | *sock_in = *sock_out = dup(STDIN_FILENO); |
908 | if (!debug_flag) { | ||
909 | startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); | ||
910 | close(REEXEC_STARTUP_PIPE_FD); | ||
911 | } | ||
912 | } else { | 924 | } else { |
913 | *sock_in = dup(STDIN_FILENO); | 925 | *sock_in = dup(STDIN_FILENO); |
914 | *sock_out = dup(STDOUT_FILENO); | 926 | *sock_out = dup(STDOUT_FILENO); |
@@ -1033,8 +1045,9 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1033 | { | 1045 | { |
1034 | fd_set *fdset; | 1046 | fd_set *fdset; |
1035 | int i, j, ret, maxfd; | 1047 | int i, j, ret, maxfd; |
1036 | int startups = 0; | 1048 | int startups = 0, listening = 0, lameduck = 0; |
1037 | int startup_p[2] = { -1 , -1 }; | 1049 | int startup_p[2] = { -1 , -1 }; |
1050 | char c = 0; | ||
1038 | struct sockaddr_storage from; | 1051 | struct sockaddr_storage from; |
1039 | socklen_t fromlen; | 1052 | socklen_t fromlen; |
1040 | pid_t pid; | 1053 | pid_t pid; |
@@ -1048,6 +1061,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1048 | maxfd = listen_socks[i]; | 1061 | maxfd = listen_socks[i]; |
1049 | /* pipes connected to unauthenticated childs */ | 1062 | /* pipes connected to unauthenticated childs */ |
1050 | startup_pipes = xcalloc(options.max_startups, sizeof(int)); | 1063 | startup_pipes = xcalloc(options.max_startups, sizeof(int)); |
1064 | startup_flags = xcalloc(options.max_startups, sizeof(int)); | ||
1051 | for (i = 0; i < options.max_startups; i++) | 1065 | for (i = 0; i < options.max_startups; i++) |
1052 | startup_pipes[i] = -1; | 1066 | startup_pipes[i] = -1; |
1053 | 1067 | ||
@@ -1056,8 +1070,15 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1056 | * the daemon is killed with a signal. | 1070 | * the daemon is killed with a signal. |
1057 | */ | 1071 | */ |
1058 | for (;;) { | 1072 | for (;;) { |
1059 | if (received_sighup) | 1073 | if (received_sighup) { |
1060 | sighup_restart(); | 1074 | if (!lameduck) { |
1075 | debug("Received SIGHUP; waiting for children"); | ||
1076 | close_listen_socks(); | ||
1077 | lameduck = 1; | ||
1078 | } | ||
1079 | if (listening <= 0) | ||
1080 | sighup_restart(); | ||
1081 | } | ||
1061 | free(fdset); | 1082 | free(fdset); |
1062 | fdset = xcalloc(howmany(maxfd + 1, NFDBITS), | 1083 | fdset = xcalloc(howmany(maxfd + 1, NFDBITS), |
1063 | sizeof(fd_mask)); | 1084 | sizeof(fd_mask)); |
@@ -1083,19 +1104,37 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1083 | if (ret < 0) | 1104 | if (ret < 0) |
1084 | continue; | 1105 | continue; |
1085 | 1106 | ||
1086 | for (i = 0; i < options.max_startups; i++) | 1107 | for (i = 0; i < options.max_startups; i++) { |
1087 | if (startup_pipes[i] != -1 && | 1108 | if (startup_pipes[i] == -1 || |
1088 | FD_ISSET(startup_pipes[i], fdset)) { | 1109 | !FD_ISSET(startup_pipes[i], fdset)) |
1089 | /* | 1110 | continue; |
1090 | * the read end of the pipe is ready | 1111 | switch (read(startup_pipes[i], &c, sizeof(c))) { |
1091 | * if the child has closed the pipe | 1112 | case -1: |
1092 | * after successful authentication | 1113 | if (errno == EINTR || errno == EAGAIN) |
1093 | * or if the child has died | 1114 | continue; |
1094 | */ | 1115 | if (errno != EPIPE) { |
1116 | error("%s: startup pipe %d (fd=%d): " | ||
1117 | "read %s", __func__, i, | ||
1118 | startup_pipes[i], strerror(errno)); | ||
1119 | } | ||
1120 | /* FALLTHROUGH */ | ||
1121 | case 0: | ||
1122 | /* child exited or completed auth */ | ||
1095 | close(startup_pipes[i]); | 1123 | close(startup_pipes[i]); |
1096 | startup_pipes[i] = -1; | 1124 | startup_pipes[i] = -1; |
1097 | startups--; | 1125 | startups--; |
1126 | if (startup_flags[i]) | ||
1127 | listening--; | ||
1128 | break; | ||
1129 | case 1: | ||
1130 | /* child has finished preliminaries */ | ||
1131 | if (startup_flags[i]) { | ||
1132 | listening--; | ||
1133 | startup_flags[i] = 0; | ||
1134 | } | ||
1135 | break; | ||
1098 | } | 1136 | } |
1137 | } | ||
1099 | for (i = 0; i < num_listen_socks; i++) { | 1138 | for (i = 0; i < num_listen_socks; i++) { |
1100 | if (!FD_ISSET(listen_socks[i], fdset)) | 1139 | if (!FD_ISSET(listen_socks[i], fdset)) |
1101 | continue; | 1140 | continue; |
@@ -1149,6 +1188,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1149 | if (maxfd < startup_p[0]) | 1188 | if (maxfd < startup_p[0]) |
1150 | maxfd = startup_p[0]; | 1189 | maxfd = startup_p[0]; |
1151 | startups++; | 1190 | startups++; |
1191 | startup_flags[j] = 1; | ||
1152 | break; | 1192 | break; |
1153 | } | 1193 | } |
1154 | 1194 | ||
@@ -1174,7 +1214,7 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1174 | send_rexec_state(config_s[0], cfg); | 1214 | send_rexec_state(config_s[0], cfg); |
1175 | close(config_s[0]); | 1215 | close(config_s[0]); |
1176 | } | 1216 | } |
1177 | break; | 1217 | return; |
1178 | } | 1218 | } |
1179 | 1219 | ||
1180 | /* | 1220 | /* |
@@ -1183,13 +1223,14 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1183 | * parent continues listening. | 1223 | * parent continues listening. |
1184 | */ | 1224 | */ |
1185 | platform_pre_fork(); | 1225 | platform_pre_fork(); |
1226 | listening++; | ||
1186 | if ((pid = fork()) == 0) { | 1227 | if ((pid = fork()) == 0) { |
1187 | /* | 1228 | /* |
1188 | * Child. Close the listening and | 1229 | * Child. Close the listening and |
1189 | * max_startup sockets. Start using | 1230 | * max_startup sockets. Start using |
1190 | * the accepted socket. Reinitialize | 1231 | * the accepted socket. Reinitialize |
1191 | * logging (since our pid has changed). | 1232 | * logging (since our pid has changed). |
1192 | * We break out of the loop to handle | 1233 | * We return from this function to handle |
1193 | * the connection. | 1234 | * the connection. |
1194 | */ | 1235 | */ |
1195 | platform_post_fork_child(); | 1236 | platform_post_fork_child(); |
@@ -1204,7 +1245,18 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1204 | log_stderr); | 1245 | log_stderr); |
1205 | if (rexec_flag) | 1246 | if (rexec_flag) |
1206 | close(config_s[0]); | 1247 | close(config_s[0]); |
1207 | break; | 1248 | else { |
1249 | /* | ||
1250 | * Signal parent that the preliminaries | ||
1251 | * for this child are complete. For the | ||
1252 | * re-exec case, this happens after the | ||
1253 | * child has received the rexec state | ||
1254 | * from the server. | ||
1255 | */ | ||
1256 | (void)atomicio(vwrite, startup_pipe, | ||
1257 | "\0", 1); | ||
1258 | } | ||
1259 | return; | ||
1208 | } | 1260 | } |
1209 | 1261 | ||
1210 | /* Parent. Stay in the loop. */ | 1262 | /* Parent. Stay in the loop. */ |
@@ -1236,10 +1288,6 @@ server_accept_loop(int *sock_in, int *sock_out, int *newsock, int *config_s) | |||
1236 | #endif | 1288 | #endif |
1237 | explicit_bzero(rnd, sizeof(rnd)); | 1289 | explicit_bzero(rnd, sizeof(rnd)); |
1238 | } | 1290 | } |
1239 | |||
1240 | /* child process check (or debug mode) */ | ||
1241 | if (num_listen_socks < 0) | ||
1242 | break; | ||
1243 | } | 1291 | } |
1244 | } | 1292 | } |
1245 | 1293 | ||
@@ -1569,8 +1617,18 @@ main(int ac, char **av) | |||
1569 | /* Fetch our configuration */ | 1617 | /* Fetch our configuration */ |
1570 | if ((cfg = sshbuf_new()) == NULL) | 1618 | if ((cfg = sshbuf_new()) == NULL) |
1571 | fatal("%s: sshbuf_new failed", __func__); | 1619 | fatal("%s: sshbuf_new failed", __func__); |
1572 | if (rexeced_flag) | 1620 | if (rexeced_flag) { |
1573 | recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg); | 1621 | recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg); |
1622 | if (!debug_flag) { | ||
1623 | startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); | ||
1624 | close(REEXEC_STARTUP_PIPE_FD); | ||
1625 | /* | ||
1626 | * Signal parent that this child is at a point where | ||
1627 | * they can go away if they have a SIGHUP pending. | ||
1628 | */ | ||
1629 | (void)atomicio(vwrite, startup_pipe, "\0", 1); | ||
1630 | } | ||
1631 | } | ||
1574 | else if (strcasecmp(config_file_name, "none") != 0) | 1632 | else if (strcasecmp(config_file_name, "none") != 0) |
1575 | load_server_config(config_file_name, cfg); | 1633 | load_server_config(config_file_name, cfg); |
1576 | 1634 | ||