components/openssh/patches/047-login_grace_time_watchdog.patch
author David Hollister <david.hollister@oracle.com>
Wed, 12 Oct 2016 14:01:13 -0600
changeset 7094 61352b4e5af5
parent 7059 f2f4b9922ef9
child 7562 2adf9e2cdc69
permissions -rw-r--r--
24797203 OpenStack RBAC profiles allow reading too many files 24797238 keystone RBAC and SMF should point at Apache log files 24797256 cinder RBAC and SMF should point at Apache log files 24830959 horizon RBAC and SMF should point at Apache log files

#
# 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);