version 1.532, 2019/01/21 10:38:54 |
version 1.533, 2019/03/01 02:32:39 |
|
|
/* record remote hostname or ip */ |
/* record remote hostname or ip */ |
u_int utmp_len = HOST_NAME_MAX+1; |
u_int utmp_len = HOST_NAME_MAX+1; |
|
|
/* options.max_startup sized array of fd ints */ |
/* |
|
* startup_pipes/flags are used for tracking children of the listening sshd |
|
* process early in their lifespans. This tracking is needed for three things: |
|
* |
|
* 1) Implementing the MaxStartups limit of concurrent unauthenticated |
|
* connections. |
|
* 2) Avoiding a race condition for SIGHUP processing, where child processes |
|
* may have listen_socks open that could collide with main listener process |
|
* after it restarts. |
|
* 3) Ensuring that rexec'd sshd processes have received their initial state |
|
* from the parent listen process before handling SIGHUP. |
|
* |
|
* Child processes signal that they have completed closure of the listen_socks |
|
* and (if applicable) received their rexec state by sending a char over their |
|
* sock. Child processes signal that authentication has completed by closing |
|
* the sock (or by exiting). |
|
*/ |
static int *startup_pipes = NULL; |
static int *startup_pipes = NULL; |
static int startup_pipe; /* in child */ |
static int *startup_flags = NULL; /* Indicates child closed listener */ |
|
static int startup_pipe = -1; /* in child */ |
|
|
/* variables used for privilege separation */ |
/* variables used for privilege separation */ |
int use_privsep = -1; |
int use_privsep = -1; |
|
|
{ |
{ |
int fd; |
int fd; |
|
|
startup_pipe = -1; |
|
if (rexeced_flag) { |
if (rexeced_flag) { |
close(REEXEC_CONFIG_PASS_FD); |
close(REEXEC_CONFIG_PASS_FD); |
*sock_in = *sock_out = dup(STDIN_FILENO); |
*sock_in = *sock_out = dup(STDIN_FILENO); |
if (!debug_flag) { |
|
startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); |
|
close(REEXEC_STARTUP_PIPE_FD); |
|
} |
|
} else { |
} else { |
*sock_in = dup(STDIN_FILENO); |
*sock_in = dup(STDIN_FILENO); |
*sock_out = dup(STDOUT_FILENO); |
*sock_out = dup(STDOUT_FILENO); |
|
|
{ |
{ |
fd_set *fdset; |
fd_set *fdset; |
int i, j, ret, maxfd; |
int i, j, ret, maxfd; |
int startups = 0; |
int startups = 0, listening = 0, lameduck = 0; |
int startup_p[2] = { -1 , -1 }; |
int startup_p[2] = { -1 , -1 }; |
|
char c = 0; |
struct sockaddr_storage from; |
struct sockaddr_storage from; |
socklen_t fromlen; |
socklen_t fromlen; |
pid_t pid; |
pid_t pid; |
|
|
maxfd = listen_socks[i]; |
maxfd = listen_socks[i]; |
/* pipes connected to unauthenticated childs */ |
/* pipes connected to unauthenticated childs */ |
startup_pipes = xcalloc(options.max_startups, sizeof(int)); |
startup_pipes = xcalloc(options.max_startups, sizeof(int)); |
|
startup_flags = xcalloc(options.max_startups, sizeof(int)); |
for (i = 0; i < options.max_startups; i++) |
for (i = 0; i < options.max_startups; i++) |
startup_pipes[i] = -1; |
startup_pipes[i] = -1; |
|
|
|
|
* the daemon is killed with a signal. |
* the daemon is killed with a signal. |
*/ |
*/ |
for (;;) { |
for (;;) { |
if (received_sighup) |
if (received_sighup) { |
sighup_restart(); |
if (!lameduck) { |
|
debug("Received SIGHUP; waiting for children"); |
|
close_listen_socks(); |
|
lameduck = 1; |
|
} |
|
if (listening <= 0) |
|
sighup_restart(); |
|
} |
free(fdset); |
free(fdset); |
fdset = xcalloc(howmany(maxfd + 1, NFDBITS), |
fdset = xcalloc(howmany(maxfd + 1, NFDBITS), |
sizeof(fd_mask)); |
sizeof(fd_mask)); |
|
|
if (ret < 0) |
if (ret < 0) |
continue; |
continue; |
|
|
for (i = 0; i < options.max_startups; i++) |
for (i = 0; i < options.max_startups; i++) { |
if (startup_pipes[i] != -1 && |
if (startup_pipes[i] == -1 || |
FD_ISSET(startup_pipes[i], fdset)) { |
!FD_ISSET(startup_pipes[i], fdset)) |
/* |
continue; |
* the read end of the pipe is ready |
switch (read(startup_pipes[i], &c, sizeof(c))) { |
* if the child has closed the pipe |
case -1: |
* after successful authentication |
if (errno == EINTR || errno == EAGAIN) |
* or if the child has died |
continue; |
*/ |
if (errno != EPIPE) { |
|
error("%s: startup pipe %d (fd=%d): " |
|
"read %s", __func__, i, |
|
startup_pipes[i], strerror(errno)); |
|
} |
|
/* FALLTHROUGH */ |
|
case 0: |
|
/* child exited or completed auth */ |
close(startup_pipes[i]); |
close(startup_pipes[i]); |
startup_pipes[i] = -1; |
startup_pipes[i] = -1; |
startups--; |
startups--; |
|
if (startup_flags[i]) |
|
listening--; |
|
break; |
|
case 1: |
|
/* child has finished preliminaries */ |
|
if (startup_flags[i]) { |
|
listening--; |
|
startup_flags[i] = 0; |
|
} |
|
break; |
} |
} |
|
} |
for (i = 0; i < num_listen_socks; i++) { |
for (i = 0; i < num_listen_socks; i++) { |
if (!FD_ISSET(listen_socks[i], fdset)) |
if (!FD_ISSET(listen_socks[i], fdset)) |
continue; |
continue; |
|
|
if (maxfd < startup_p[0]) |
if (maxfd < startup_p[0]) |
maxfd = startup_p[0]; |
maxfd = startup_p[0]; |
startups++; |
startups++; |
|
startup_flags[j] = 1; |
break; |
break; |
} |
} |
|
|
|
|
send_rexec_state(config_s[0], cfg); |
send_rexec_state(config_s[0], cfg); |
close(config_s[0]); |
close(config_s[0]); |
} |
} |
break; |
return; |
} |
} |
|
|
/* |
/* |
|
|
* the child process the connection. The |
* the child process the connection. The |
* parent continues listening. |
* parent continues listening. |
*/ |
*/ |
|
listening++; |
if ((pid = fork()) == 0) { |
if ((pid = fork()) == 0) { |
/* |
/* |
* Child. Close the listening and |
* Child. Close the listening and |
* max_startup sockets. Start using |
* max_startup sockets. Start using |
* the accepted socket. Reinitialize |
* the accepted socket. Reinitialize |
* logging (since our pid has changed). |
* logging (since our pid has changed). |
* We break out of the loop to handle |
* We return from this function to handle |
* the connection. |
* the connection. |
*/ |
*/ |
startup_pipe = startup_p[1]; |
startup_pipe = startup_p[1]; |
|
|
log_stderr); |
log_stderr); |
if (rexec_flag) |
if (rexec_flag) |
close(config_s[0]); |
close(config_s[0]); |
break; |
else { |
|
/* |
|
* Signal parent that the preliminaries |
|
* for this child are complete. For the |
|
* re-exec case, this happens after the |
|
* child has received the rexec state |
|
* from the server. |
|
*/ |
|
(void)atomicio(vwrite, startup_pipe, |
|
"\0", 1); |
|
} |
|
return; |
} |
} |
|
|
/* Parent. Stay in the loop. */ |
/* Parent. Stay in the loop. */ |
|
|
} |
} |
close(*newsock); |
close(*newsock); |
} |
} |
|
|
/* child process check (or debug mode) */ |
|
if (num_listen_socks < 0) |
|
break; |
|
} |
} |
} |
} |
|
|
|
|
/* Fetch our configuration */ |
/* Fetch our configuration */ |
if ((cfg = sshbuf_new()) == NULL) |
if ((cfg = sshbuf_new()) == NULL) |
fatal("%s: sshbuf_new failed", __func__); |
fatal("%s: sshbuf_new failed", __func__); |
if (rexeced_flag) |
if (rexeced_flag) { |
recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg); |
recv_rexec_state(REEXEC_CONFIG_PASS_FD, cfg); |
|
if (!debug_flag) { |
|
startup_pipe = dup(REEXEC_STARTUP_PIPE_FD); |
|
close(REEXEC_STARTUP_PIPE_FD); |
|
/* |
|
* Signal parent that this child is at a point where |
|
* they can go away if they have a SIGHUP pending. |
|
*/ |
|
(void)atomicio(vwrite, startup_pipe, "\0", 1); |
|
} |
|
} |
else if (strcasecmp(config_file_name, "none") != 0) |
else if (strcasecmp(config_file_name, "none") != 0) |
load_server_config(config_file_name, cfg); |
load_server_config(config_file_name, cfg); |
|
|