PSARC/2017/022 OpenSSH 7.4
25295722 upgrade OpenSSH to 7.4p1
25295787 problem in UTILITY/OPENSSH
25295804 problem in UTILITY/OPENSSH
25295822 problem in UTILITY/OPENSSH
25295840 problem in UTILITY/OPENSSH
25809379 Openssh 7.4p1 has 3 regressions, fixed in 7.5
25795760 openssh drops connection when GSSAPIAuthentication set to no
#
# Implements watchdog process, which backs up login_grace_time alarm.
#
# If the main process is hung in a syscall, SIGALRM is queued but not
# delivered and the connection stays unauthenticated for too long.
#
# Function start_grace_watchdog forks of a watchdog process, that sends the
# main process a SIGTERM, if it does neither authenticate nor exit before
# (login_grace_time + GRACE_WATCHDOG_THRESHOLD).
# If the main process does not react to SIGTERM, SIGKILL is sent after
# additional GRACE_WATCHDOG_THRESHOLD seconds.
#
# Patch source: in-house
# Reported to [email protected] as security issue.
#
# Per agreement with upstream developers, filed:
# https://bugzilla.mindrot.org/show_bug.cgi?id=2615
#
diff -pur old/sshd.c new/sshd.c
--- old/sshd.c
+++ new/sshd.c
@@ -236,7 +236,14 @@ Buffer loginmsg;
/* Unprivileged user */
struct passwd *privsep_pw = NULL;
+/* Pid of process backing up login_grace_time alarm. */
+pid_t grace_watchdog_pid = -1;
+
+/* Time in seconds */
+#define GRACE_WATCHDOG_THRESHOLD 10
+
/* Prototypes for various functions defined later in this file. */
+static void stop_grace_watchdog(void);
void destroy_sensitive_data(void);
void demote_sensitive_data(void);
static void do_ssh2_kex(void);
@@ -351,12 +358,101 @@ grace_alarm_handler(int sig)
signal(SIGTERM, SIG_IGN);
kill(0, SIGTERM);
}
+ stop_grace_watchdog();
/* Log error and exit. */
sigdie("Timeout before authentication for %s port %d",
ssh_remote_ipaddr(active_state), ssh_remote_port(active_state));
}
+static inline void
+sleep_reliably(unsigned int seconds)
+{
+ while (seconds > 0)
+ seconds = sleep(seconds);
+}
+
+/*
+ * Implements watchdog process, which backs up login_grace_time alarm.
+ *
+ * If the main process is hung in a syscall, SIGALRM is queued but not
+ * delivered and the connection stays unauthenticated for too long.
+ *
+ * This function forks off a watchdog process, which sends the main process
+ * a SIGTERM, if it does neither authenticate nor exit before
+ * (login_grace_time + GRACE_WATCHDOG_THRESHOLD).
+ * If the main process does not react to SIGTERM, SIGKILL is sent after
+ * additional GRACE_WATCHDOG_THRESHOLD seconds.
+ */
+static void
+start_grace_watchdog(int login_grace_time)
+{
+ pid_t ppid = getpid();
+
+ if (login_grace_time == 0)
+ return;
+
+ if (grace_watchdog_pid != -1) {
+ error("login_grace_time watchdog process already running");
+ return;
+ }
+
+ grace_watchdog_pid = fork();
+ if (grace_watchdog_pid == -1)
+ fatal("fork of login_grace_time watchdog process failed");
+ else if (grace_watchdog_pid > 0)
+ return;
+
+ /* child */
+
+ /* close open fds, including client socket and startup_pipe */
+ closefrom(3);
+
+ /* kill the monitor with SIGTERM after timeout + threshold */
+ sleep_reliably(login_grace_time + GRACE_WATCHDOG_THRESHOLD);
+ if (getppid() != ppid) {
+ debug("login_grace_time watchdog still active, "
+ "but watched process %d already exited.", (int)ppid);
+ exit(0);
+ }
+ error("Timeout before authentication for %s. Killing process %d "
+ "with SIGTERM.", ssh_remote_ipaddr(active_state), (int)ppid);
+ kill(ppid, SIGTERM);
+
+ /* if neccessary, kill it with SIGKILL */
+ sleep_reliably(GRACE_WATCHDOG_THRESHOLD);
+ if (getppid() != ppid)
+ exit(0);
+ error("Watched process %d did not respond to SIGTERM. "
+ "Killing it with SIGKILL.", (int)ppid);
+ kill(ppid, SIGKILL);
+
+ /* give up */
+ sleep_reliably(GRACE_WATCHDOG_THRESHOLD);
+ if (getppid() == ppid) {
+ error("login_grace_time watchdog failed to kill %d", (int)ppid);
+ exit(255);
+ }
+ exit(0);
+}
+
+/* kill grace watchdog process */
+static void
+stop_grace_watchdog()
+{
+ if (grace_watchdog_pid == -1) {
+ debug3("login_grace_time watchdog process not running");
+ return;
+ }
+
+ kill(grace_watchdog_pid, SIGTERM);
+ while (waitpid(grace_watchdog_pid, NULL, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ }
+ grace_watchdog_pid = -1;
+}
+
static void
sshd_exchange_identification(struct ssh *ssh, int sock_in, int sock_out)
{
@@ -623,6 +719,7 @@ privsep_preauth(Authctxt *authctxt)
/* child */
close(pmonitor->m_sendfd);
close(pmonitor->m_log_recvfd);
+ grace_watchdog_pid = -1;
/* Arrange for logging to be sent to the monitor */
set_log_handler(mm_log_handler, pmonitor);
@@ -2006,8 +2103,10 @@ main(int ac, char **av)
* are about to discover the bug.
*/
signal(SIGALRM, grace_alarm_handler);
- if (!debug_flag)
+ if (!debug_flag) {
alarm(options.login_grace_time);
+ start_grace_watchdog(options.login_grace_time);
+ }
sshd_exchange_identification(ssh, sock_in, sock_out);
packet_set_nonblocking();
@@ -2055,6 +2154,7 @@ main(int ac, char **av)
*/
alarm(0);
signal(SIGALRM, SIG_DFL);
+ stop_grace_watchdog();
authctxt->authenticated = 1;
if (startup_pipe != -1) {
close(startup_pipe);