changeset 1641:a2bbc22ea1e6 coverity

merge coverity
author Matt Johnston <matt@ucc.asn.au>
date Thu, 21 Mar 2019 00:14:38 +0800
parents 96e4c9b2cc00 (current diff) 228b086794b7 (diff)
children b59623a64678
files default_options.h
diffstat 32 files changed, 283 insertions(+), 146 deletions(-) [+]
line wrap: on
line diff
--- a/README	Wed Mar 21 00:52:02 2018 +0800
+++ b/README	Thu Mar 21 00:14:38 2019 +0800
@@ -51,7 +51,7 @@
 
 ============================================================================
 
-To run the server, you need to server keys, this is one-off:
+To run the server, you need to generate server keys, this is one-off:
 ./dropbearkey -t rsa -f dropbear_rsa_host_key
 ./dropbearkey -t dss -f dropbear_dss_host_key
 ./dropbearkey -t ecdsa -f dropbear_ecdsa_host_key
--- a/auth.h	Wed Mar 21 00:52:02 2018 +0800
+++ b/auth.h	Thu Mar 21 00:14:38 2019 +0800
@@ -37,9 +37,9 @@
 void send_msg_userauth_failure(int partial, int incrfail);
 void send_msg_userauth_success(void);
 void send_msg_userauth_banner(const buffer *msg);
-void svr_auth_password(void);
-void svr_auth_pubkey(void);
-void svr_auth_pam(void);
+void svr_auth_password(int valid_user);
+void svr_auth_pubkey(int valid_user);
+void svr_auth_pam(int valid_user);
 
 #if DROPBEAR_SVR_PUBKEY_OPTIONS_BUILT
 int svr_pubkey_allows_agentfwd(void);
@@ -78,7 +78,7 @@
 void cli_auth_pubkey_cleanup(void);
 
 
-#define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
+#define MAX_USERNAME_LEN 100 /* arbitrary for the moment */
 
 #define AUTH_TYPE_NONE      1
 #define AUTH_TYPE_PUBKEY    (1 << 1)
@@ -108,11 +108,14 @@
 	unsigned int authdone; /* 0 if we haven't authed, 1 if we have. Applies for
 							  client and server (though has differing 
 							  meanings). */
+
 	unsigned int perm_warn; /* Server only, set if bad permissions on 
 							   ~/.ssh/authorized_keys have already been
 							   logged. */
 	unsigned int checkusername_failed;  /* Server only, set if checkusername
 	                                has already failed */
+	struct timespec auth_starttime; /* Server only, time of receiving current 
+									SSH_MSG_USERAUTH_REQUEST */
 
 	/* These are only used for the server */
 	uid_t pw_uid;
--- a/channel.h	Wed Mar 21 00:52:02 2018 +0800
+++ b/channel.h	Thu Mar 21 00:14:38 2019 +0800
@@ -69,10 +69,6 @@
 	int sent_close, recv_close;
 	int recv_eof, sent_eof;
 
-	/* Set after running the ChanType-specific close hander
-	 * to ensure we don't run it twice (nor type->checkclose()). */
-	int close_handler_done;
-
 	struct dropbear_progress_connection *conn_pending;
 	int initconn; /* used for TCP forwarding, whether the channel has been
 					 fully initialised */
@@ -95,10 +91,17 @@
 
 	int sepfds; /* Whether this channel has separate pipes for in/out or not */
 	const char *name;
+	/* Sets up the channel */
 	int (*inithandler)(struct Channel*);
+	/* Called to check whether a channel should close, separately from the FD being closed.
+	Used for noticing process exiting */
 	int (*check_close)(const struct Channel*);
+	/* Handler for ssh_msg_channel_request */
 	void (*reqhandler)(struct Channel*);
+	/* Called prior to sending ssh_msg_channel_close, used for sending exit status */
 	void (*closehandler)(const struct Channel*);
+	/* Frees resources, called just prior to channel being removed */
+	void (*cleanup)(const struct Channel*);
 };
 
 /* Callback for connect_remote */
--- a/cli-agentfwd.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/cli-agentfwd.c	Thu Mar 21 00:14:38 2019 +0800
@@ -52,6 +52,7 @@
 	new_agent_chan,
 	NULL,
 	NULL,
+	NULL,
 	NULL
 };
 
--- a/cli-chansession.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/cli-chansession.c	Thu Mar 21 00:14:38 2019 +0800
@@ -35,7 +35,7 @@
 #include "chansession.h"
 #include "agentfwd.h"
 
-static void cli_closechansess(const struct Channel *channel);
+static void cli_cleanupchansess(const struct Channel *channel);
 static int cli_initchansess(struct Channel *channel);
 static void cli_chansessreq(struct Channel *channel);
 static void send_chansess_pty_req(const struct Channel *channel);
@@ -51,7 +51,8 @@
 	cli_initchansess, /* inithandler */
 	NULL, /* checkclosehandler */
 	cli_chansessreq, /* reqhandler */
-	cli_closechansess, /* closehandler */
+	NULL, /* closehandler */
+	cli_cleanupchansess, /* cleanup */
 };
 
 static void cli_chansessreq(struct Channel *channel) {
@@ -83,7 +84,7 @@
 	
 
 /* If the main session goes, we close it up */
-static void cli_closechansess(const struct Channel *UNUSED(channel)) {
+static void cli_cleanupchansess(const struct Channel *UNUSED(channel)) {
 	cli_tty_cleanup(); /* Restore tty modes etc */
 
 	/* This channel hasn't gone yet, so we have > 1 */
@@ -387,7 +388,8 @@
 	cli_init_netcat, /* inithandler */
 	NULL,
 	NULL,
-	cli_closechansess
+	NULL,
+	cli_cleanupchansess
 };
 
 void cli_send_netcat_request() {
--- a/cli-runopts.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/cli-runopts.c	Thu Mar 21 00:14:38 2019 +0800
@@ -891,6 +891,7 @@
 #ifndef DISABLE_SYSLOG
 			"\tUseSyslog\n"
 #endif
+			"\tPort\n"
 		);
 		exit(EXIT_SUCCESS);
 	}
@@ -909,5 +910,10 @@
 	}
 #endif
 
+	if (match_extendedopt(&optstr, "Port") == DROPBEAR_SUCCESS) {
+		cli_opts.remoteport = optstr;
+		return;
+	}
+
 	dropbear_log(LOG_WARNING, "Ignoring unknown configuration option '%s'", origstr);
 }
--- a/cli-session.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/cli-session.c	Thu Mar 21 00:14:38 2019 +0800
@@ -356,6 +356,7 @@
 }
 
 static void cli_finished() {
+	TRACE(("cli_finised()"))
 
 	session_cleanup();
 	fprintf(stderr, "Connection to %[email protected]%s:%s closed.\n", cli_opts.username,
--- a/cli-tcpfwd.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/cli-tcpfwd.c	Thu Mar 21 00:14:38 2019 +0800
@@ -40,6 +40,7 @@
 	newtcpforwarded,
 	NULL,
 	NULL,
+	NULL,
 	NULL
 };
 #endif
@@ -55,6 +56,7 @@
 	tcp_prio_inithandler,
 	NULL,
 	NULL,
+	NULL,
 	NULL
 };
 #endif
@@ -135,7 +137,7 @@
 	tcpinfo->chantype = &cli_chan_tcplocal;
 	tcpinfo->tcp_type = direct;
 
-	ret = listen_tcpfwd(tcpinfo);
+	ret = listen_tcpfwd(tcpinfo, NULL);
 
 	if (ret == DROPBEAR_FAILURE) {
 		m_free(tcpinfo);
--- a/common-channel.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/common-channel.c	Thu Mar 21 00:14:38 2019 +0800
@@ -144,7 +144,6 @@
 	newchan->index = i;
 	newchan->sent_close = newchan->recv_close = 0;
 	newchan->sent_eof = newchan->recv_eof = 0;
-	newchan->close_handler_done = 0;
 
 	newchan->remotechan = remotechan;
 	newchan->transwindow = transwindow;
@@ -286,7 +285,7 @@
 				channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0))
 
 	if (!channel->flushing 
-		&& !channel->close_handler_done
+		&& !channel->sent_close
 		&& channel->type->check_close
 		&& channel->type->check_close(channel))
 	{
@@ -298,7 +297,7 @@
 	   channel, to ensure that the shell has exited (and the exit status 
 	   retrieved) before we close things up. */
 	if (!channel->type->check_close	
-		|| channel->close_handler_done
+		|| channel->sent_close
 		|| channel->type->check_close(channel)) {
 		close_allowed = 1;
 	}
@@ -385,10 +384,8 @@
 static void send_msg_channel_close(struct Channel *channel) {
 
 	TRACE(("enter send_msg_channel_close %p", (void*)channel))
-	if (channel->type->closehandler 
-			&& !channel->close_handler_done) {
+	if (channel->type->closehandler) {
 		channel->type->closehandler(channel);
-		channel->close_handler_done = 1;
 	}
 	
 	CHECKCLEARTOWRITE();
@@ -661,10 +658,8 @@
 		m_close(channel->errfd);
 	}
 
-	if (!channel->close_handler_done
-		&& channel->type->closehandler) {
-		channel->type->closehandler(channel);
-		channel->close_handler_done = 1;
+	if (channel->type->cleanup) {
+		channel->type->cleanup(channel);
 	}
 
 	if (channel->conn_pending) {
@@ -690,13 +685,7 @@
 
 	TRACE(("enter recv_msg_channel_request %p", (void*)channel))
 
-	if (channel->sent_close) {
-		TRACE(("leave recv_msg_channel_request: already closed channel"))
-		return;
-	}
-
-	if (channel->type->reqhandler 
-			&& !channel->close_handler_done) {
+	if (channel->type->reqhandler) {
 		channel->type->reqhandler(channel);
 	} else {
 		int wantreply;
@@ -1011,6 +1000,11 @@
 void send_msg_channel_failure(const struct Channel *channel) {
 
 	TRACE(("enter send_msg_channel_failure"))
+
+	if (channel->sent_close) {
+		TRACE(("Skipping sending msg_channel_failure for closed channel"))
+		return;
+	}
 	CHECKCLEARTOWRITE();
 
 	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_FAILURE);
@@ -1024,6 +1018,10 @@
 void send_msg_channel_success(const struct Channel *channel) {
 
 	TRACE(("enter send_msg_channel_success"))
+	if (channel->sent_close) {
+		TRACE(("Skipping sending msg_channel_success for closed channel"))
+		return;
+	}
 	CHECKCLEARTOWRITE();
 
 	buf_putbyte(ses.writepayload, SSH_MSG_CHANNEL_SUCCESS);
--- a/common-session.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/common-session.c	Thu Mar 21 00:14:38 2019 +0800
@@ -68,6 +68,16 @@
 	/* Sets it to lowdelay */
 	update_channel_prio();
 
+#if !DROPBEAR_SVR_MULTIUSER
+	/* A sanity check to prevent an accidental configuration option
+	   leaving multiuser systems exposed */
+	errno = 0;
+	getuid();
+	if (errno != ENOSYS) {
+		dropbear_exit("Non-multiuser Dropbear requires a non-multiuser kernel");
+	}
+#endif
+
 	now = monotonic_now();
 	ses.connect_time = now;
 	ses.last_packet_time_keepalive_recv = now;
--- a/configure.ac	Wed Mar 21 00:52:02 2018 +0800
+++ b/configure.ac	Thu Mar 21 00:14:38 2019 +0800
@@ -497,6 +497,9 @@
 AC_CHECK_FUNCS(setutxent utmpxname)
 AC_CHECK_FUNCS(logout updwtmp logwtmp)
 
+# POSIX monotonic time
+AC_CHECK_FUNCS(clock_gettime)
+
 # OS X monotonic time
 AC_CHECK_HEADERS([mach/mach_time.h])
 AC_CHECK_FUNCS(mach_absolute_time)
--- a/dbutil.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/dbutil.c	Thu Mar 21 00:14:38 2019 +0800
@@ -605,71 +605,67 @@
 	return c;
 }
 
-#if defined(__linux__) && defined(SYS_clock_gettime)
-/* CLOCK_MONOTONIC_COARSE was added in Linux 2.6.32 but took a while to
-reach userspace include headers */
-#ifndef CLOCK_MONOTONIC_COARSE
-#define CLOCK_MONOTONIC_COARSE 6
-#endif
-/* Some old toolchains know SYS_clock_gettime but not CLOCK_MONOTONIC */
-#ifndef CLOCK_MONOTONIC
-#define CLOCK_MONOTONIC 1
-#endif
-static clockid_t get_linux_clock_source() {
-	struct timespec ts;
-	if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC_COARSE, &ts) == 0) {
-		return CLOCK_MONOTONIC_COARSE;
-	}
-
-	if (syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &ts) == 0) {
-		return CLOCK_MONOTONIC;
-	}
-	return -1;
-}
-#endif 
-
-time_t monotonic_now() {
+/* higher-resolution monotonic timestamp, falls back to gettimeofday */
+void gettime_wrapper(struct timespec *now) {
+	struct timeval tv;
 #if DROPBEAR_FUZZ
 	if (fuzz.fuzzing) {
 		/* time stands still when fuzzing */
-		return 5;
+		now->tv_sec = 5;
+		now->tv_nsec = 0;
 	}
 #endif
+
+#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
+	/* POSIX monotonic clock. Newer Linux, BSD, MacOSX >10.12 */
+	if (clock_gettime(CLOCK_MONOTONIC, now) == 0) {
+		return;
+	}
+#endif
+
 #if defined(__linux__) && defined(SYS_clock_gettime)
 	{
-	static clockid_t clock_source = -2;
-
-	if (clock_source == -2) {
-		/* First run, find out which one works. 
-		-1 will fall back to time() */
-		clock_source = get_linux_clock_source();
-	}
-
-	if (clock_source >= 0) {
-		struct timespec ts;
-		if (syscall(SYS_clock_gettime, clock_source, &ts) != 0) {
-			/* Intermittent clock failures should not happen */
-			dropbear_exit("Clock broke");
+	/* Old linux toolchain - kernel might support it but not the build headers */
+	/* Also glibc <2.17 requires -lrt which we neglect to add */
+	static int linux_monotonic_failed = 0;
+	if (!linux_monotonic_failed) {
+		/* CLOCK_MONOTONIC isn't in some headers */
+		int clock_source_monotonic = 1; 
+		if (syscall(SYS_clock_gettime, clock_source_monotonic, now) == 0) {
+			return;
+		} else {
+			/* Don't try again */
+			linux_monotonic_failed = 1;
 		}
-		return ts.tv_sec;
 	}
 	}
-#endif /* linux clock_gettime */
+#endif /* linux fallback clock_gettime */
 
 #if defined(HAVE_MACH_ABSOLUTE_TIME)
 	{
-	/* OS X, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
+	/* OS X pre 10.12, see https://developer.apple.com/library/mac/qa/qa1398/_index.html */
 	static mach_timebase_info_data_t timebase_info;
+	uint64_t scaled_time;
 	if (timebase_info.denom == 0) {
 		mach_timebase_info(&timebase_info);
 	}
-	return mach_absolute_time() * timebase_info.numer / timebase_info.denom
-		/ 1e9;
+	scaled_time = mach_absolute_time() * timebase_info.numer / timebase_info.denom;
+	now->tv_sec = scaled_time / 1000000000;
+	now->tv_nsec = scaled_time % 1000000000;
 	}
 #endif /* osx mach_absolute_time */
 
 	/* Fallback for everything else - this will sometimes go backwards */
-	return time(NULL);
+	gettimeofday(&tv, NULL);
+	now->tv_sec = tv.tv_sec;
+	now->tv_nsec = 1000*tv.tv_usec;
+}
+
+/* second-resolution monotonic timestamp */
+time_t monotonic_now() {
+	struct timespec ts;
+	gettime_wrapper(&ts);
+	return ts.tv_sec;
 }
 
 void fsync_parent_dir(const char* fn) {
--- a/dbutil.h	Wed Mar 21 00:52:02 2018 +0800
+++ b/dbutil.h	Thu Mar 21 00:14:38 2019 +0800
@@ -83,6 +83,8 @@
 /* Returns a time in seconds that doesn't go backwards - does not correspond to
 a real-world clock */
 time_t monotonic_now(void);
+/* Higher resolution clock_gettime(CLOCK_MONOTONIC) wrapper */
+void gettime_wrapper(struct timespec *now);
 
 char * expand_homedir_path(const char *inpath);
 
--- a/default_options.h	Wed Mar 21 00:52:02 2018 +0800
+++ b/default_options.h	Thu Mar 21 00:14:38 2019 +0800
@@ -196,6 +196,11 @@
  * authorized_keys file into account */
 #define DROPBEAR_SVR_PUBKEY_OPTIONS 1
 
+/* Set this to 0 if your system does not have multiple user support.
+   (Linux kernel CONFIG_MULTIUSER option)
+   The resulting binary will not run on a normal system. */
+#define DROPBEAR_SVR_MULTIUSER 1
+
 /* Client authentication options */
 #define DROPBEAR_CLI_PASSWORD_AUTH 1
 #define DROPBEAR_CLI_PUBKEY_AUTH 1
--- a/ifndef_wrapper.sh	Wed Mar 21 00:52:02 2018 +0800
+++ b/ifndef_wrapper.sh	Thu Mar 21 00:14:38 2019 +0800
@@ -2,6 +2,6 @@
 
 # Wrap all "#define X Y" with a #ifndef X...#endif"
 
-sed -E 's/^( *#define ([^ ]+) .*)/#ifndef \2\
+sed 's/^\( *#define \([^ ][^ ]*\) .*\)/#ifndef \2\
 \1\
 #endif/' 
--- a/kex.h	Wed Mar 21 00:52:02 2018 +0800
+++ b/kex.h	Thu Mar 21 00:14:38 2019 +0800
@@ -106,7 +106,4 @@
 int curve25519_donna(unsigned char *out, const unsigned char *secret, const unsigned char *other);
 #endif
 
-
-#define MAX_KEXHASHBUF 2000
-
 #endif /* DROPBEAR_KEX_H_ */
--- a/keyimport.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/keyimport.c	Thu Mar 21 00:14:38 2019 +0800
@@ -1097,7 +1097,9 @@
 		buf_putbytes(seq_buf, curve_oid, curve_oid_len);
 
 		buf_incrwritepos(seq_buf,
-			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 1, 2+1+pubkey_size, 0xa0));
+			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 1,
+			(pubkey_size +1 < 128 ? 2 : 3 ) +1 +pubkey_size, 0xa0));
+
 		buf_incrwritepos(seq_buf,
 			ber_write_id_len(buf_getwriteptr(seq_buf, 10), 3, 1+pubkey_size, 0));
 		buf_putbyte(seq_buf, 0);
--- a/netio.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/netio.c	Thu Mar 21 00:14:38 2019 +0800
@@ -224,7 +224,6 @@
 
 void set_connect_fds(fd_set *writefd) {
 	m_list_elem *iter;
-	TRACE(("enter set_connect_fds"))
 	iter = ses.conn_pending.first;
 	while (iter) {
 		m_list_elem *next_iter = iter->next;
@@ -245,12 +244,10 @@
 		}
 		iter = next_iter;
 	}
-	TRACE(("leave set_connect_fds"))
 }
 
 void handle_connect_fds(const fd_set *writefd) {
 	m_list_elem *iter;
-	TRACE(("enter handle_connect_fds"))
 	for (iter = ses.conn_pending.first; iter; iter = iter->next) {
 		int val;
 		socklen_t vallen = sizeof(val);
@@ -284,7 +281,6 @@
 			return; 
 		}
 	}
-	TRACE(("leave handle_connect_fds - end iter"))
 }
 
 void connect_set_writequeue(struct dropbear_progress_connection *c, struct Queue *writequeue) {
@@ -298,7 +294,11 @@
 	buffer *writebuf;
 
 	#ifndef IOV_MAX
-	#define IOV_MAX UIO_MAXIOV
+		#if defined(__CYGWIN__) && !defined(UIO_MAXIOV)
+		#define IOV_MAX 1024
+		#else
+		#define IOV_MAX UIO_MAXIOV
+		#endif
 	#endif
 
 	*iov_count = MIN(MIN(queue->count, IOV_MAX), *iov_count);
--- a/packet.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/packet.c	Thu Mar 21 00:14:38 2019 +0800
@@ -58,7 +58,7 @@
 void write_packet() {
 
 	ssize_t written;
-#ifdef HAVE_WRITEV
+#if defined(HAVE_WRITEV) && (defined(IOV_MAX) || defined(UIO_MAXIOV))
 	/* 50 is somewhat arbitrary */
 	unsigned int iov_count = 50;
 	struct iovec iov[50];
@@ -110,8 +110,6 @@
 	/* Get the next buffer in the queue of encrypted packets to write*/
 	writebuf = (buffer*)examine(&ses.writequeue);
 
-	/* The last byte of the buffer is not to be transmitted, but is 
-	 * a cleartext packet_type indicator */
 	len = writebuf->len - writebuf->pos;
 	dropbear_assert(len > 0);
 	/* Try to write as much as possible */
--- a/scpmisc.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/scpmisc.c	Thu Mar 21 00:14:38 2019 +0800
@@ -102,7 +102,7 @@
 
 	len = strlen(str) + 1;
 	cp = xmalloc(len);
-	strncpy(cp, str, len);
+	strlcpy(cp, str, len);
 	return cp;
 }
 
--- a/scpmisc.h	Wed Mar 21 00:52:02 2018 +0800
+++ b/scpmisc.h	Thu Mar 21 00:14:38 2019 +0800
@@ -27,8 +27,8 @@
 typedef struct arglist arglist;
 struct arglist {
 	char    **list;
-	int     num;
-	int     nalloc;
+	u_int   num;
+	u_int   nalloc;
 };
 void	 addargs(arglist *, char *, ...);
 void	 replacearg(arglist *, u_int, char *, ...);
--- a/svr-agentfwd.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/svr-agentfwd.c	Thu Mar 21 00:14:38 2019 +0800
@@ -151,6 +151,7 @@
 
 	if (chansess->agentfile != NULL && chansess->agentdir != NULL) {
 
+#if DROPBEAR_SVR_MULTIUSER
 		/* Remove the dir as the user. That way they can't cause problems except
 		 * for themselves */
 		uid = getuid();
@@ -159,6 +160,7 @@
 			(seteuid(ses.authstate.pw_uid)) < 0) {
 			dropbear_exit("Failed to set euid");
 		}
+#endif
 
 		/* 2 for "/" and "\0" */
 		len = strlen(chansess->agentdir) + strlen(chansess->agentfile) + 2;
@@ -170,10 +172,12 @@
 
 		rmdir(chansess->agentdir);
 
+#if DROPBEAR_SVR_MULTIUSER
 		if ((seteuid(uid)) < 0 ||
 			(setegid(gid)) < 0) {
 			dropbear_exit("Failed to revert euid");
 		}
+#endif
 
 		m_free(chansess->agentfile);
 		m_free(chansess->agentdir);
@@ -187,6 +191,7 @@
 	NULL,
 	NULL,
 	NULL,
+	NULL,
 	NULL
 };
 
@@ -208,13 +213,14 @@
 
 	struct sockaddr_un addr;
 	unsigned int prefix;
-	char path[sizeof(addr.sun_path)], sockfile[sizeof(addr.sun_path)];
+	char path[(sizeof(addr.sun_path)-1)/2], sockfile[(sizeof(addr.sun_path)-1)/2];
 	mode_t mode;
 	int i;
 	uid_t uid;
 	gid_t gid;
 	int ret = DROPBEAR_FAILURE;
 
+#if DROPBEAR_SVR_MULTIUSER
 	/* drop to user privs to make the dir/file */
 	uid = getuid();
 	gid = getgid();
@@ -222,6 +228,7 @@
 		(seteuid(ses.authstate.pw_uid)) < 0) {
 		dropbear_exit("Failed to set euid");
 	}
+#endif
 
 	memset((void*)&addr, 0x0, sizeof(addr));
 	addr.sun_family = AF_UNIX;
@@ -261,10 +268,12 @@
 
 
 out:
+#if DROPBEAR_SVR_MULTIUSER
 	if ((seteuid(uid)) < 0 ||
 		(setegid(gid)) < 0) {
 		dropbear_exit("Failed to revert euid");
 	}
+#endif
 	return ret;
 }
 
--- a/svr-auth.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/svr-auth.c	Thu Mar 21 00:14:38 2019 +0800
@@ -79,6 +79,9 @@
 
 	TRACE(("enter recv_msg_userauth_request"))
 
+	/* for compensating failure delay */
+	gettime_wrapper(&ses.authstate.auth_starttime);
+
 	/* ignore packets if auth is already done */
 	if (ses.authstate.authdone == 1) {
 		TRACE(("leave recv_msg_userauth_request: authdone already"))
@@ -149,10 +152,8 @@
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
 					AUTH_METHOD_PASSWORD_LEN) == 0) {
-			if (valid_user) {
-				svr_auth_password();
-				goto out;
-			}
+			svr_auth_password(valid_user);
+			goto out;
 		}
 	}
 #endif
@@ -164,10 +165,8 @@
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
 					AUTH_METHOD_PASSWORD_LEN) == 0) {
-			if (valid_user) {
-				svr_auth_pam();
-				goto out;
-			}
+			svr_auth_pam(valid_user);
+			goto out;
 		}
 	}
 #endif
@@ -177,12 +176,7 @@
 	if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
 			strncmp(methodname, AUTH_METHOD_PUBKEY,
 				AUTH_METHOD_PUBKEY_LEN) == 0) {
-		if (valid_user) {
-			svr_auth_pubkey();
-		} else {
-			/* pubkey has no failure delay */
-			send_msg_userauth_failure(0, 0);
-		}
+		svr_auth_pubkey(valid_user);
 		goto out;
 	}
 #endif
@@ -282,7 +276,7 @@
 
 	/* check if we are running as non-root, and login user is different from the server */
 	uid = geteuid();
-	if (uid != 0 && uid != ses.authstate.pw_uid) {
+	if (!(DROPBEAR_SVR_MULTIUSER && uid == 0) && uid != ses.authstate.pw_uid) {
 		TRACE(("running as nonroot, only server uid is allowed"))
 		dropbear_log(LOG_WARNING,
 				"Login attempt with wrong user %s from %s",
@@ -391,16 +385,48 @@
 	encrypt_packet();
 
 	if (incrfail) {
-		unsigned int delay;
-		genrandom((unsigned char*)&delay, sizeof(delay));
-		/* We delay for 300ms +- 50ms */
-		delay = 250000 + (delay % 100000);
+		/* The SSH_MSG_AUTH_FAILURE response is delayed to attempt to
+		avoid user enumeration and slow brute force attempts.
+		The delay is adjusted by the time already spent in processing
+		authentication (ses.authstate.auth_starttime timestamp). */
+
+		/* Desired total delay 300ms +-50ms (in nanoseconds).
+		Beware of integer overflow if increasing these values */
+		const unsigned int mindelay = 250000000;
+		const unsigned int vardelay = 100000000;
+		unsigned int rand_delay;
+		struct timespec delay;
+
+		gettime_wrapper(&delay);
+		delay.tv_sec -= ses.authstate.auth_starttime.tv_sec;
+		delay.tv_nsec -= ses.authstate.auth_starttime.tv_nsec;
+
+		/* carry */
+		if (delay.tv_nsec < 0) {
+			delay.tv_nsec += 1000000000;
+			delay.tv_sec -= 1;
+		}
+
+		genrandom((unsigned char*)&rand_delay, sizeof(rand_delay));
+		rand_delay = mindelay + (rand_delay % vardelay);
+
+		if (delay.tv_sec == 0 && delay.tv_nsec <= mindelay) {
+			/* Compensate for elapsed time */
+			delay.tv_nsec = rand_delay - delay.tv_nsec;
+		} else {
+			/* No time left or time went backwards, just delay anyway */
+			delay.tv_sec = 0;
+			delay.tv_nsec = rand_delay;
+		}
+
+
 #if DROPBEAR_FUZZ
 		if (!fuzz.fuzzing)
 #endif
 		{
-		usleep(delay);
+			while (nanosleep(&delay, &delay) == -1 && errno == EINTR) { /* Go back to sleep */ }
 		}
+
 		ses.authstate.failcount++;
 	}
 
--- a/svr-authpam.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/svr-authpam.c	Thu Mar 21 00:14:38 2019 +0800
@@ -178,13 +178,14 @@
  * Keyboard interactive would be a lot nicer, but since PAM is synchronous, it
  * gets very messy trying to send the interactive challenges, and read the
  * interactive responses, over the network. */
-void svr_auth_pam() {
+void svr_auth_pam(int valid_user) {
 
 	struct UserDataS userData = {NULL, NULL};
 	struct pam_conv pamConv = {
 		pamConvFunc,
 		&userData /* submitted to pamvConvFunc as appdata_ptr */ 
 	};
+	const char* printable_user = NULL;
 
 	pam_handle_t* pamHandlep = NULL;
 
@@ -204,12 +205,23 @@
 
 	password = buf_getstring(ses.payload, &passwordlen);
 
+	/* We run the PAM conversation regardless of whether the username is valid
+	in case the conversation function has an inherent delay.
+	Use ses.authstate.username rather than ses.authstate.pw_name.
+	After PAM succeeds we then check the valid_user flag too */
+
 	/* used to pass data to the PAM conversation function - don't bother with
 	 * strdup() etc since these are touched only by our own conversation
 	 * function (above) which takes care of it */
-	userData.user = ses.authstate.pw_name;
+	userData.user = ses.authstate.username;
 	userData.passwd = password;
 
+	if (ses.authstate.pw_name) {
+		printable_user = ses.authstate.pw_name;
+	} else {
+		printable_user = "<invalid username>";
+	}
+
 	/* Init pam */
 	if ((rc = pam_start("sshd", NULL, &pamConv, &pamHandlep)) != PAM_SUCCESS) {
 		dropbear_log(LOG_WARNING, "pam_start() failed, rc=%d, %s", 
@@ -242,7 +254,7 @@
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
 				"Bad PAM password attempt for '%s' from %s",
-				ses.authstate.pw_name,
+				printable_user,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		goto cleanup;
@@ -253,12 +265,19 @@
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
 				"Bad PAM password attempt for '%s' from %s",
-				ses.authstate.pw_name,
+				printable_user,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		goto cleanup;
 	}
 
+	if (!valid_user) {
+		/* PAM auth succeeded but the username isn't allowed in for another reason
+		(checkusername() failed) */
+		send_msg_userauth_failure(0, 1);
+		goto cleanup;
+	}
+
 	/* successful authentication */
 	dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
 			ses.authstate.pw_name,
--- a/svr-authpasswd.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/svr-authpasswd.c	Thu Mar 21 00:14:38 2019 +0800
@@ -48,22 +48,14 @@
 
 /* Process a password auth request, sending success or failure messages as
  * appropriate */
-void svr_auth_password() {
+void svr_auth_password(int valid_user) {
 	
 	char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */
 	char * testcrypt = NULL; /* crypt generated from the user's password sent */
-	char * password;
+	char * password = NULL;
 	unsigned int passwordlen;
-
 	unsigned int changepw;
 
-	passwdcrypt = ses.authstate.pw_passwd;
-
-#ifdef DEBUG_HACKCRYPT
-	/* debugging crypt for non-root testing with shadows */
-	passwdcrypt = DEBUG_HACKCRYPT;
-#endif
-
 	/* check if client wants to change password */
 	changepw = buf_getbool(ses.payload);
 	if (changepw) {
@@ -73,12 +65,30 @@
 	}
 
 	password = buf_getstring(ses.payload, &passwordlen);
-
-	/* the first bytes of passwdcrypt are the salt */
-	testcrypt = crypt(password, passwdcrypt);
+	if (valid_user && passwordlen <= DROPBEAR_MAX_PASSWORD_LEN) {
+		/* the first bytes of passwdcrypt are the salt */
+		passwdcrypt = ses.authstate.pw_passwd;
+		testcrypt = crypt(password, passwdcrypt);
+	}
 	m_burn(password, passwordlen);
 	m_free(password);
 
+	/* After we have got the payload contents we can exit if the username
+	is invalid. Invalid users have already been logged. */
+	if (!valid_user) {
+		send_msg_userauth_failure(0, 1);
+		return;
+	}
+
+	if (passwordlen > DROPBEAR_MAX_PASSWORD_LEN) {
+		dropbear_log(LOG_WARNING,
+				"Too-long password attempt for '%s' from %s",
+				ses.authstate.pw_name,
+				svr_ses.addrstring);
+		send_msg_userauth_failure(0, 1);
+		return;
+	}
+
 	if (testcrypt == NULL) {
 		/* crypt() with an invalid salt like "!!" */
 		dropbear_log(LOG_WARNING, "User account '%s' is locked",
--- a/svr-authpubkey.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/svr-authpubkey.c	Thu Mar 21 00:14:38 2019 +0800
@@ -79,7 +79,7 @@
 
 /* process a pubkey auth request, sending success or failure message as
  * appropriate */
-void svr_auth_pubkey() {
+void svr_auth_pubkey(int valid_user) {
 
 	unsigned char testkey; /* whether we're just checking if a key is usable */
 	char* algo = NULL; /* pubkey algo */
@@ -102,6 +102,15 @@
 	keybloblen = buf_getint(ses.payload);
 	keyblob = buf_getptr(ses.payload, keybloblen);
 
+	if (!valid_user) {
+		/* Return failure once we have read the contents of the packet
+		required to validate a public key. 
+		Avoids blind user enumeration though it isn't possible to prevent
+		testing for user existence if the public key is known */
+		send_msg_userauth_failure(0, 0);
+		goto out;
+	}
+
 	/* check if the key is valid */
 	if (checkpubkey(algo, algolen, keyblob, keybloblen) == DROPBEAR_FAILURE) {
 		send_msg_userauth_failure(0, 0);
@@ -338,6 +347,7 @@
 	snprintf(filename, len + 22, "%s/.ssh/authorized_keys", 
 				ses.authstate.pw_dir);
 
+#if DROPBEAR_SVR_MULTIUSER
 	/* open the file as the authenticating user. */
 	origuid = getuid();
 	origgid = getgid();
@@ -345,13 +355,16 @@
 		(seteuid(ses.authstate.pw_uid)) < 0) {
 		dropbear_exit("Failed to set euid");
 	}
+#endif
 
 	authfile = fopen(filename, "r");
 
+#if DROPBEAR_SVR_MULTIUSER
 	if ((seteuid(origuid)) < 0 ||
 		(setegid(origgid)) < 0) {
 		dropbear_exit("Failed to revert euid");
 	}
+#endif
 
 	if (authfile == NULL) {
 		goto out;
@@ -415,8 +428,9 @@
 
 	/* allocate max required pathname storage,
 	 * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
-	filename = m_malloc(len + 22);
-	strncpy(filename, ses.authstate.pw_dir, len+1);
+	len += 22;
+	filename = m_malloc(len);
+	strlcpy(filename, ses.authstate.pw_dir, len);
 
 	/* check ~ */
 	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
@@ -424,13 +438,13 @@
 	}
 
 	/* check ~/.ssh */
-	strncat(filename, "/.ssh", 5); /* strlen("/.ssh") == 5 */
+	strlcat(filename, "/.ssh", len);
 	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
 		goto out;
 	}
 
 	/* now check ~/.ssh/authorized_keys */
-	strncat(filename, "/authorized_keys", 16);
+	strlcat(filename, "/authorized_keys", len);
 	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
 		goto out;
 	}
--- a/svr-chansession.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/svr-chansession.c	Thu Mar 21 00:14:38 2019 +0800
@@ -51,6 +51,7 @@
 static void addchildpid(struct ChanSess *chansess, pid_t pid);
 static void sesssigchild_handler(int val);
 static void closechansess(const struct Channel *channel);
+static void cleanupchansess(const struct Channel *channel);
 static int newchansess(struct Channel *channel);
 static void chansessionrequest(struct Channel *channel);
 static int sesscheckclose(const struct Channel *channel);
@@ -69,6 +70,7 @@
 	sesscheckclose, /* checkclosehandler */
 	chansessionrequest, /* reqhandler */
 	closechansess, /* closehandler */
+	cleanupchansess /* cleanup */
 };
 
 /* required to clear environment */
@@ -91,7 +93,7 @@
 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
 		unsigned int i;
 		struct exitinfo *ex = NULL;
-		TRACE(("sigchld handler: pid %d", pid))
+		TRACE(("svr_chansess_checksignal : pid %d", pid))
 
 		ex = NULL;
 		/* find the corresponding chansess */
@@ -285,8 +287,25 @@
 	return li;
 }
 
+/* send exit status message before the channel is closed */
+static void closechansess(const struct Channel *channel) {
+	struct ChanSess *chansess;
+
+	TRACE(("enter closechansess"))
+
+	chansess = (struct ChanSess*)channel->typedata;
+
+	if (chansess == NULL) {
+		TRACE(("leave closechansess: chansess == NULL"))
+		return;
+	}
+
+	send_exitsignalstatus(channel);
+	TRACE(("leave closechansess"))
+}
+
 /* clean a session channel */
-static void closechansess(const struct Channel *channel) {
+static void cleanupchansess(const struct Channel *channel) {
 
 	struct ChanSess *chansess;
 	unsigned int i;
@@ -301,8 +320,6 @@
 		return;
 	}
 
-	send_exitsignalstatus(channel);
-
 	m_free(chansess->cmd);
 	m_free(chansess->term);
 
@@ -932,6 +949,7 @@
 #endif /* HAVE_CLEARENV */
 #endif /* DEBUG_VALGRIND */
 
+#if DROPBEAR_SVR_MULTIUSER
 	/* We can only change uid/gid as root ... */
 	if (getuid() == 0) {
 
@@ -955,6 +973,7 @@
 			dropbear_exit("Couldn't	change user as non-root");
 		}
 	}
+#endif
 
 	/* set env vars */
 	addnewvar("USER", ses.authstate.pw_name);
--- a/svr-tcpfwd.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/svr-tcpfwd.c	Thu Mar 21 00:14:38 2019 +0800
@@ -57,6 +57,7 @@
 	tcp_prio_inithandler,
 	NULL,
 	NULL,
+	NULL,
 	NULL
 };
 
@@ -168,6 +169,7 @@
 	unsigned int addrlen;
 	struct TCPListener *tcpinfo = NULL;
 	unsigned int port;
+	struct Listener *listener = NULL;
 
 	TRACE(("enter remotetcpreq"))
 
@@ -208,9 +210,9 @@
 		tcpinfo->listenaddr = m_strdup(request_addr);
 	}
 
-	ret = listen_tcpfwd(tcpinfo);
+	ret = listen_tcpfwd(tcpinfo, &listener);
 	if (DROPBEAR_SUCCESS == ret) {
-		tcpinfo->listenport = get_sock_port(ses.listeners[0]->socks[0]);
+		tcpinfo->listenport = get_sock_port(listener->socks[0]);
 		*allocated_listen_port = tcpinfo->listenport;
 	}
 
@@ -237,7 +239,8 @@
 	newtcpdirect, /* init */
 	NULL, /* checkclose */
 	NULL, /* reqhandler */
-	NULL /* closehandler */
+	NULL, /* closehandler */
+	NULL /* cleanup */
 };
 
 /* Called upon creating a new direct tcp channel (ie we connect out to an
--- a/svr-x11fwd.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/svr-x11fwd.c	Thu Mar 21 00:14:38 2019 +0800
@@ -216,7 +216,8 @@
 	x11_inithandler, /* inithandler */
 	NULL, /* checkclose */
 	NULL, /* reqhandler */
-	NULL /* closehandler */
+	NULL, /* closehandler */
+	NULL /* cleanup */
 };
 
 
--- a/sysoptions.h	Wed Mar 21 00:52:02 2018 +0800
+++ b/sysoptions.h	Thu Mar 21 00:14:38 2019 +0800
@@ -86,6 +86,8 @@
 /* Required for pubkey auth */
 #define DROPBEAR_SIGNKEY_VERIFY ((DROPBEAR_SVR_PUBKEY_AUTH) || (DROPBEAR_CLIENT))
 
+#define DROPBEAR_MAX_PASSWORD_LEN 100
+
 #define SHA1_HASH_SIZE 20
 #define MD5_HASH_SIZE 16
 #define MAX_HASH_SIZE 64 /* sha512 */
@@ -225,7 +227,7 @@
 #define DROPBEAR_ZLIB_MEM_LEVEL 8
 
 #if (DROPBEAR_SVR_PASSWORD_AUTH) && (DROPBEAR_SVR_PAM_AUTH)
-#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
+#error "You can't turn on PASSWORD and PAM auth both at once. Fix it in localoptions.h"
 #endif
 
 /* PAM requires ./configure --enable-pam */
--- a/tcp-accept.c	Wed Mar 21 00:52:02 2018 +0800
+++ b/tcp-accept.c	Thu Mar 21 00:14:38 2019 +0800
@@ -110,12 +110,12 @@
 	}
 }
 
-int listen_tcpfwd(struct TCPListener* tcpinfo) {
+int listen_tcpfwd(struct TCPListener* tcpinfo, struct Listener **ret_listener) {
 
 	char portstring[NI_MAXSERV];
 	int socks[DROPBEAR_MAX_SOCKS];
-	struct Listener *listener = NULL;
 	int nsocks;
+	struct Listener *listener;
 	char* errstring = NULL;
 
 	TRACE(("enter listen_tcpfwd"))
@@ -142,6 +142,10 @@
 		return DROPBEAR_FAILURE;
 	}
 
+	if (ret_listener) {
+		*ret_listener = listener;
+	}
+
 	TRACE(("leave listen_tcpfwd: success"))
 	return DROPBEAR_SUCCESS;
 }
--- a/tcpfwd.h	Wed Mar 21 00:52:02 2018 +0800
+++ b/tcpfwd.h	Thu Mar 21 00:14:38 2019 +0800
@@ -26,6 +26,7 @@
 
 #include "channel.h"
 #include "list.h"
+#include "listener.h"
 
 struct TCPListener {
 
@@ -69,7 +70,7 @@
 void cli_recv_msg_request_failure(void);
 
 /* Common */
-int listen_tcpfwd(struct TCPListener* tcpinfo);
+int listen_tcpfwd(struct TCPListener* tcpinfo, struct Listener **ret_listener);
 int tcp_prio_inithandler(struct Channel* chan);
 
 /* A random identifier */