components/openssh/patches/047-login_grace_time_watchdog.patch
changeset 7059 f2f4b9922ef9
child 7562 2adf9e2cdc69
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/openssh/patches/047-login_grace_time_watchdog.patch	Thu Oct 06 07:26:21 2016 -0700
@@ -0,0 +1,165 @@
+#
+# 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
+@@ -252,9 +252,16 @@ 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. */
+ void destroy_sensitive_data(void);
+ void demote_sensitive_data(void);
++static void stop_grace_watchdog(void);
+ 
+ #ifdef WITH_SSH1
+ static void do_ssh1_kex(void);
+@@ -369,12 +376,98 @@ 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);
++	grace_watchdog_pid = -1;
++}
++
++
+ /*
+  * Signal handler for the key regeneration alarm.  Note that this
+  * alarm only occurs in the daemon waiting for connections, and it does not
+@@ -723,6 +816,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);
+@@ -2235,8 +2329,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);
+ 
+@@ -2302,6 +2398,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);