changeset 1173:d734fe76b72f coverity

merge
author Matt Johnston <matt@ucc.asn.au>
date Mon, 23 Nov 2015 23:04:48 +0800
parents 624fc24cfae5 (current diff) a44747e5a05a (diff)
children a29559086628
files
diffstat 21 files changed, 302 insertions(+), 226 deletions(-) [+]
line wrap: on
line diff
--- a/.hgsigs	Fri Aug 07 21:26:03 2015 +0800
+++ b/.hgsigs	Mon Nov 23 23:04:48 2015 +0800
@@ -14,3 +14,5 @@
 caac692b366c153cea0e9cd59aa2d79a7d843d4e 0 iEYEABECAAYFAlPk1mcACgkQjPn4sExkf7wLpgCeOqMYqpkf4lYUuyrn9VYThNpc7PkAn3JOSNgIqkKUcmSy6FstrI8jwJzq
 2d421bc0545d1be6d59a4ebfe61606d94b124b0c 0 iEYEABECAAYFAlRJDCQACgkQjPn4sExkf7xUYACcCwVJkYWXJn5x/D5A+qMupy778lEAn0rg1oNiq96YU/4jOPsS5IMItihu
 1d2d81b1b7c1b100e9c369e40b9fa5b2d491eea9 0 iEYEABECAAYFAlTKOKUACgkQjPn4sExkf7xWMACfYFozyHiRk5GaocTa5z6Ws1uyB4kAoLubxoxcnM3E7AA9mHAzc3OB5M0Y
+a687f835236c7025b5cb2968fe9c4ebc4a49f0ea 0 iQIcBAABCgAGBQJVxg62AAoJEPSYMBLCC7qsC+EQAKw8YWogrVHhIFct2fx/nqybSPVrhFyKFKHhq7K/lZeVm0MGIWdSyVcQgP+Hs2jWNBWzG4AJ1BtifHWQH6IDh7W5RuwOXu5KobgPW9BsN3EVE9KIR+xe9jCAmFl9rIw0tNpy1q6R0TpYXx/sWlMilxecyEGyr2Ias2Sm19aY2mOEv8PLfh9BLfrJEKtt2NxL7TX8ScPwJXJMmVIQjN9WK4Ptx3tjcGNRivEVR/dftP5sJx2DBJx9avyDqrfloMW7Q7sPgJ88MPruCDxedOkbzH7JdHe3Humr2G4LsI0KPU7pNN6EBDjhJ+SVXuOyAgu5j/C0R+0ggGfjSrjDu8WjHyclFlwwu2MSGuHf111I1qkLtaRY3H1FZO5Y2gbLwBLQ82svA4klcBIxtP5jKAZDTh1jQMYsfKotvZdawOWrPDkNmKoUg2JXLHAtj9Dd0uGIhqfspZY3qlpzxw9uCkljWclUBD097ygotwAb2XdLoAWZ3KdvoPM+k448vIAQ7Q/aqcnm/dLQJr3Le029gpkOKoWKaQTlk0itrRGpgETHAhE2LnmWxYSKp6NYSKMgEONbfDiVNLyDTOlvpPiEb20RsOP64xA4wVDGmPenCURmMYoepQK6oJdtkNtCdth2S49KxPQAC+Dem4YZ7b+5b+cXrK5Nz7elBxZzRQWdjmZ4JDQK
+ef4b26364b0cdda1084751d7de3d76c589e2d9cb 0 iQIcBAABCgAGBQJVxg7BAAoJEESTFJTynGdz9Q4P/A0Kq4H52rQqxq42PoEMFbVQIUfkFzyWjAz8eEGLmP5x5/sdpyxZDEyBSUG55uyNvOPTHE+Sd3t2h2Iieq749qwYgqggXC0P+C0zGzW3hB5Rv6dTUrKN1yCyaWE2tY488RsyVlcAs4vrp1Cum5Gv8/BUVKjzZmkZ1iq/3RyrvbLEiLoMrcLnQ+sUdaYHvfEwxDbzpOEvepg8iDJBitTrfG9xHp9otX6ucahwn1EumFvC5mvUxbiQ9jv76t4FJztjMoB24hPCH9T1FjB8uNsoM+j2Z67r81eJrGgNpJzjX0S3lY/AADZGhfGnfybTM9gFuQayIJuCJqduQibVwYkAAnPi17NmbdwPu0Rdz55oU+ft09XLVm/qkQcD1EP5bxYWnLIEMkkZQnFx7WdMpjKK9oGxZHeFYAKEgPgePCkk4TQ4PxNa+3854H19AUssQlaueGcbDLyPIRiSyqhleXawGfaJi+1jBt0DM7CNbAHAUWUE07VhQzNGWjabdEk4eXKTmDL+mZJFdHGBhyCve8sPmZBYJvM2PRgcXe8fwFh+R7gVj6kFbZJvgM9kG7EeF+4ZMEXG4yKpV/SKfMMeEPBCZjFxZhlJJ0fsZbB1Y/iLw8LXnJ0fa/5xFYv6k+iytfom/rqS4iUD7NWTjcEYHjd4EO4QlPD2Ef/AWOO8YBUBv8kA
--- a/.hgtags	Fri Aug 07 21:26:03 2015 +0800
+++ b/.hgtags	Mon Nov 23 23:04:48 2015 +0800
@@ -47,3 +47,4 @@
 e9579816f20ea85affc6135e87f8477992808948 DROPBEAR_2014.65
 735511a4c761141416ad0e6728989d2dafa55bc2 DROPBEAR_2014.66
 cbd674d63cd4f3781464a8d4056a5506c8ae926f DROPBEAR_2015.67
+809feaa9408f036734129c77f2b3c7e779d4f099 DROPBEAR_2015.68
--- a/CHANGES	Fri Aug 07 21:26:03 2015 +0800
+++ b/CHANGES	Mon Nov 23 23:04:48 2015 +0800
@@ -1,23 +1,31 @@
-- Improve efficiency of writing data to local program/pipes, measured 30% increase
+2015.68 - Saturday 8 August 2015
+
+- Reduce local data copying for improved efficiency. Measured 30%
   increase in throughput for connections to localhost
 
-- Use TCP Fast Open on Linux if available. saves a round trip at connection
+- Forwarded TCP ports connect asynchronously and try all available addresses
+  (IPv4, IPv6, round robin DNS)
+
+- Fix all compile warnings, many patches from Gaël Portay
+  Note that configure with -Werror may not be successful on some platforms (OS X)
+  and some configuration options may still result in unused variable
+  warnings.
+
+- Use TCP Fast Open on Linux if available. Saves a round trip at connection
   to hosts that have previously been connected. 
   Needs a recent Linux kernel and possibly "sysctl -w net.ipv4.tcp_fastopen=3"
   Client side is disabled by default pending further compatibility testing
   with networks and systems.
 
-- Forwarded TCP ports connect asynchronously and retry with other available
-  addresses (IPv4 versus IPv6, round robin IPs)
+- Increase maximum command length to 9000 bytes
 
 - Free memory before exiting, patch from Thorsten Horstmann. Useful for
   Dropbear ports to embedded systems and for checking memory leaks
-  with valgrind. Only partially implemented for client side.
+  with valgrind. Only partially implemented for dbclient.
+  This is disabled by default, enable with DROPBEAR_CLEANUP in sysoptions.h
 
-- Fix all compile warnings, patch from Gaël Portay
-  (note that configure with -Werror may not be successful on some platforms
-  such as OS X and some configuration options may result in unused variable
-  warnings)
+- DROPBEAR_DEFAULT_CLI_AUTHKEY setting now always prepends home directory unless
+  there is a leading slash (~ isn't treated specially)
 
 - Fix small ECC memory leaks
 
@@ -29,10 +37,13 @@
 - Fix pre-authentication timeout when waiting for client SSH-2.0 banner, thanks
   to CL Ouyang
 
-- Increase maximum command size to 9000 bytes
+- Fix null pointer crash with restrictions in authorized_keys without a command, patch from
+  Guilhem Moulin
 
-- DROPBEAR_DEFAULT_CLI_AUTHKEY setting now always prepends home directory unless
-  there is a leading slash (~ isn't treated specially)
+- Ensure authentication timeout is handled while reading the initial banner,
+  thanks to CL Ouyang for finding it.
+
+- Fix null pointer crash when handling bad ECC keys. Found by afl-fuzz
 
 2015.67 - Wednesday 28 January 2015
 
--- a/circbuffer.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/circbuffer.c	Mon Nov 23 23:04:48 2015 +0800
@@ -37,9 +37,8 @@
 	}
 
 	cbuf = (circbuffer*)m_malloc(sizeof(circbuffer));
-	if (size > 0) {
-		cbuf->data = (unsigned char*)m_malloc(size);
-	}
+	/* data is malloced on first write */
+	cbuf->data = NULL;
 	cbuf->used = 0;
 	cbuf->readpos = 0;
 	cbuf->writepos = 0;
@@ -50,8 +49,10 @@
 
 void cbuf_free(circbuffer * cbuf) {
 
-	m_burn(cbuf->data, cbuf->size);
-	m_free(cbuf->data);
+	if (cbuf->data) {
+		m_burn(cbuf->data, cbuf->size);
+		m_free(cbuf->data);
+	}
 	m_free(cbuf);
 }
 
@@ -106,6 +107,11 @@
 		dropbear_exit("Bad cbuf write");
 	}
 
+	if (!cbuf->data) {
+		/* lazy allocation */
+		cbuf->data = (unsigned char*)m_malloc(cbuf->size);
+	}
+
 	return &cbuf->data[cbuf->writepos];
 }
 
--- a/cli-auth.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/cli-auth.c	Mon Nov 23 23:04:48 2015 +0800
@@ -324,6 +324,7 @@
 	return DROPBEAR_FAILURE;
 }
 
+#if defined(ENABLE_CLI_PASSWORD_AUTH) || defined(ENABLE_CLI_INTERACT_AUTH)
 /* A helper for getpass() that exits if the user cancels. The returned
  * password is statically allocated by getpass() */
 char* getpass_or_cancel(char* prompt)
@@ -347,3 +348,4 @@
 	}
 	return password;
 }
+#endif
--- a/cli-runopts.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/cli-runopts.c	Mon Nov 23 23:04:48 2015 +0800
@@ -105,25 +105,30 @@
 void cli_getopts(int argc, char ** argv) {
 	unsigned int i, j;
 	char ** next = 0;
-	unsigned int cmdlen;
+	enum {
 #ifdef ENABLE_CLI_PUBKEY_AUTH
-	int nextiskey = 0; /* A flag if the next argument is a keyfile */
+		OPT_AUTHKEY,
 #endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
-	int nextislocal = 0;
+		OPT_LOCALTCPFWD,
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
-	int nextisremote = 0;
+		OPT_REMOTETCPFWD,
 #endif
 #ifdef ENABLE_CLI_NETCAT
-	int nextisnetcat = 0;
+		OPT_NETCAT,
 #endif
+		/* a flag (no arg) if 'next' is NULL, a string-valued option otherwise */
+		OPT_OTHER
+	} opt;
+	unsigned int cmdlen;
 	char* dummy = NULL; /* Not used for anything real */
 
 	char* recv_window_arg = NULL;
 	char* keepalive_arg = NULL;
 	char* idle_timeout_arg = NULL;
 	char *host_arg = NULL;
+	char c;
 
 	/* see printhelp() for options */
 	cli_opts.progname = argv[0];
@@ -172,54 +177,23 @@
 
 	fill_own_user();
 
-	/* Iterate all the arguments */
 	for (i = 1; i < (unsigned int)argc; i++) {
-#ifdef ENABLE_CLI_PUBKEY_AUTH
-		if (nextiskey) {
-			/* Load a hostkey since the previous argument was "-i" */
-			loadidentityfile(argv[i], 1);
-			nextiskey = 0;
-			continue;
-		}
-#endif
-#ifdef ENABLE_CLI_REMOTETCPFWD
-		if (nextisremote) {
-			TRACE(("nextisremote true"))
-			addforward(argv[i], cli_opts.remotefwds);
-			nextisremote = 0;
-			continue;
-		}
-#endif
-#ifdef ENABLE_CLI_LOCALTCPFWD
-		if (nextislocal) {
-			TRACE(("nextislocal true"))
-			addforward(argv[i], cli_opts.localfwds);
-			nextislocal = 0;
-			continue;
-		}
-#endif
-#ifdef ENABLE_CLI_NETCAT
-		if (nextisnetcat) {
-			TRACE(("nextisnetcat true"))
-			add_netcat(argv[i]);
-			nextisnetcat = 0;
-			continue;
-		}
-#endif
-		if (next) {
-			/* The previous flag set a value to assign */
-			*next = argv[i];
-			if (*next == NULL) {
-				dropbear_exit("Invalid null argument");
+		/* Handle non-flag arguments such as hostname or commands for the remote host */
+		if (argv[i][0] != '-')
+		{
+			if (host_arg == NULL) {
+				host_arg = argv[i];
+				continue;
 			}
-			next = NULL;
-			continue;
+			/* Commands to pass to the remote host. No more flag handling,
+			commands are consumed below */
+			break;
 		}
 
-		if (argv[i][0] == '-') {
-			/* A flag *waves* */
-
-			switch (argv[i][1]) {
+		/* Begins with '-' */
+		opt = OPT_OTHER;
+		for (j = 1; (c = argv[i][j]) != '\0' && !next && opt == OPT_OTHER; j++) {
+			switch (c) {
 				case 'y': /* always accept the remote hostkey */
 					if (cli_opts.always_accept_key) {
 						/* twice means no checking at all */
@@ -232,12 +206,7 @@
 					break;
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 				case 'i': /* an identityfile */
-					/* Keep scp happy when it changes "-i file" to "-ifile" */
-					if (strlen(argv[i]) > 2) {
-						loadidentityfile(&argv[i][2], 1);
-					} else  {
-						nextiskey = 1;
-					}
+					opt = OPT_AUTHKEY;
 					break;
 #endif
 				case 't': /* we want a pty */
@@ -257,7 +226,7 @@
 					break;
 #ifdef ENABLE_CLI_LOCALTCPFWD
 				case 'L':
-					nextislocal = 1;
+					opt = OPT_LOCALTCPFWD;
 					break;
 				case 'g':
 					opts.listen_fwd_all = 1;
@@ -265,12 +234,12 @@
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
 				case 'R':
-					nextisremote = 1;
+					opt = OPT_REMOTETCPFWD;
 					break;
 #endif
 #ifdef ENABLE_CLI_NETCAT
 				case 'B':
-					nextisnetcat = 1;
+					opt = OPT_NETCAT;
 					break;
 #endif
 #ifdef ENABLE_CLI_PROXYCMD
@@ -336,50 +305,85 @@
 				case 'b':
 					next = &dummy;
 				default:
-					fprintf(stderr, 
-						"WARNING: Ignoring unknown argument '%s'\n", argv[i]);
+					fprintf(stderr,
+						"WARNING: Ignoring unknown option -%c\n", c);
 					break;
 			} /* Switch */
-			
-			/* Now we handle args where they might be "-luser" (no spaces)*/
-			if (next && strlen(argv[i]) > 2) {
-				*next = &argv[i][2];
-				next = NULL;
-			}
+		}
 
-			continue; /* next argument */
+		if (!next && opt == OPT_OTHER) /* got a flag */
+			continue;
 
-		} else {
-			TRACE(("non-flag arg: '%s'", argv[i]))
-
-			/* Either the hostname or commands */
-
-			if (host_arg == NULL) {
-				host_arg = argv[i];
-			} else {
+		if (c == '\0') {
+			i++;
+			j = 0;
+			if (!argv[i])
+				dropbear_exit("Missing argument");
+		}
 
-				/* this is part of the commands to send - after this we
-				 * don't parse any more options, and flags are sent as the
-				 * command */
-				cmdlen = 0;
-				for (j = i; j < (unsigned int)argc; j++) {
-					cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */
-				}
-				/* Allocate the space */
-				cli_opts.cmd = (char*)m_malloc(cmdlen);
-				cli_opts.cmd[0] = '\0';
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+		if (opt == OPT_AUTHKEY) {
+			TRACE(("opt authkey"))
+			loadidentityfile(&argv[i][j], 1);
+		}
+		else
+#endif
+#ifdef ENABLE_CLI_REMOTETCPFWD
+		if (opt == OPT_REMOTETCPFWD) {
+			TRACE(("opt remotetcpfwd"))
+			addforward(&argv[i][j], cli_opts.remotefwds);
+		}
+		else
+#endif
+#ifdef ENABLE_CLI_LOCALTCPFWD
+		if (opt == OPT_LOCALTCPFWD) {
+			TRACE(("opt localtcpfwd"))
+			addforward(&argv[i][j], cli_opts.localfwds);
+		}
+		else
+#endif
+#ifdef ENABLE_CLI_NETCAT
+		if (opt == OPT_NETCAT) {
+			TRACE(("opt netcat"))
+			add_netcat(&argv[i][j]);
+		}
+		else
+#endif
+		if (next) {
+			/* The previous flag set a value to assign */
+			*next = &argv[i][j];
+			if (*next == NULL)
+				dropbear_exit("Invalid null argument");
+			next = NULL;
+		}
+	}
 
-				/* Append all the bits */
-				for (j = i; j < (unsigned int)argc; j++) {
-					strlcat(cli_opts.cmd, argv[j], cmdlen);
-					strlcat(cli_opts.cmd, " ", cmdlen);
-				}
-				/* It'll be null-terminated here */
+	/* Done with options/flags; now handle the hostname (which may not
+	 * start with a hyphen) and optional command */
+
+	if (host_arg == NULL) { /* missing hostname */
+		printhelp();
+		exit(EXIT_FAILURE);
+	}
+	TRACE(("host is: %s", host_arg))
 
-				/* We've eaten all the options and flags */
-				break;
-			}
+	if (i < (unsigned int)argc) {
+		/* Build the command to send */
+		cmdlen = 0;
+		for (j = i; j < (unsigned int)argc; j++)
+			cmdlen += strlen(argv[j]) + 1; /* +1 for spaces */
+
+		/* Allocate the space */
+		cli_opts.cmd = (char*)m_malloc(cmdlen);
+		cli_opts.cmd[0] = '\0';
+
+		/* Append all the bits */
+		for (j = i; j < (unsigned int)argc; j++) {
+			strlcat(cli_opts.cmd, argv[j], cmdlen);
+			strlcat(cli_opts.cmd, " ", cmdlen);
 		}
+		/* It'll be null-terminated here */
+		TRACE(("cmd is: %s", cli_opts.cmd))
 	}
 
 	/* And now a few sanity checks and setup */
@@ -388,11 +392,6 @@
 	parse_ciphers_macs();
 #endif
 
-	if (host_arg == NULL) {
-		printhelp();
-		exit(EXIT_FAILURE);
-	}
-
 #ifdef ENABLE_CLI_PROXYCMD                                                                                                                                   
 	if (cli_opts.proxycmd) {
 		/* To match the common path of m_freeing it */
--- a/common-channel.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/common-channel.c	Mon Nov 23 23:04:48 2015 +0800
@@ -42,7 +42,7 @@
 static void send_msg_channel_open_confirmation(struct Channel* channel,
 		unsigned int recvwindow, 
 		unsigned int recvmaxpacket);
-static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
+static int writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
 	const unsigned char *moredata, unsigned int *morelen);
 static void send_msg_channel_window_adjust(struct Channel *channel, 
 		unsigned int incr);
@@ -100,15 +100,6 @@
 	TRACE(("leave chancleanup"))
 }
 
-static void
-chan_initwritebuf(struct Channel *channel)
-{
-	dropbear_assert(channel->writebuf->size == 0 && channel->recvwindow == 0);
-	cbuf_free(channel->writebuf);
-	channel->writebuf = cbuf_new(opts.recv_window);
-	channel->recvwindow = opts.recv_window;
-}
-
 /* Create a new channel entry, send a reply confirm or failure */
 /* If remotechan, transwindow and transmaxpacket are not know (for a new
  * outgoing connection, with them to be filled on confirmation), they should
@@ -167,8 +158,8 @@
 	newchan->await_open = 0;
 	newchan->flushing = 0;
 
-	newchan->writebuf = cbuf_new(0); /* resized later by chan_initwritebuf */
-	newchan->recvwindow = 0;
+	newchan->writebuf = cbuf_new(opts.recv_window);
+	newchan->recvwindow = opts.recv_window;
 
 	newchan->extrabuf = NULL; /* The user code can set it up */
 	newchan->recvdonelen = 0;
@@ -256,7 +247,6 @@
 		if (ses.channel_signal_pending) {
 			/* SIGCHLD can change channel state for server sessions */
 			do_check_close = 1;
-			ses.channel_signal_pending = 0;
 		}
 	
 		/* handle any channel closing etc */
@@ -265,6 +255,8 @@
 		}
 	}
 
+	ses.channel_signal_pending = 0;
+
 #ifdef USING_LISTENERS
 	handle_listeners(readfds);
 #endif
@@ -378,7 +370,6 @@
 	{
 		channel->readfd = channel->writefd = sock;
 		channel->conn_pending = NULL;
-		chan_initwritebuf(channel);
 		send_msg_channel_open_confirmation(channel, channel->recvwindow,
 				channel->recvmaxpacket);
 		TRACE(("leave channel_connect_done: success"))
@@ -435,7 +426,7 @@
 }
 
 #ifndef HAVE_WRITEV
-static void writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf,
+static int writechannel_fallback(struct Channel* channel, int fd, circbuffer *cbuf,
 	const unsigned char *UNUSED(moredata), unsigned int *morelen) {
 
 	unsigned char *circ_p1, *circ_p2;
@@ -454,23 +445,24 @@
 		if (errno != EINTR && errno != EAGAIN) {
 			TRACE(("channel IO write error fd %d %s", fd, strerror(errno)))
 			close_chan_fd(channel, fd, SHUT_WR);
+			return DROPBEAR_FAILURE;
 		}
-	} else {
-		cbuf_incrread(cbuf, written);
-		channel->recvdonelen += written;
 	}
+	cbuf_incrread(cbuf, written);
+	channel->recvdonelen += written;
+	return DROPBEAR_SUCCESS;
 }
 #endif /* !HAVE_WRITEV */
 
 #ifdef HAVE_WRITEV
-static void writechannel_writev(struct Channel* channel, int fd, circbuffer *cbuf,
+static int writechannel_writev(struct Channel* channel, int fd, circbuffer *cbuf,
 	const unsigned char *moredata, unsigned int *morelen) {
 
 	struct iovec iov[3];
 	unsigned char *circ_p1, *circ_p2;
 	unsigned int circ_len1, circ_len2;
 	int io_count = 0;
-
+	int cbuf_written;
 	ssize_t written;
 
 	cbuf_readptrs(cbuf, &circ_p1, &circ_len1, &circ_p2, &circ_len2);
@@ -502,7 +494,7 @@
 		From common_recv_msg_channel_data() then channelio().
 		The second call may not have any data to write, so we just return. */
 		TRACE(("leave writechannel, no data"))
-		return;
+		return DROPBEAR_SUCCESS;
 	}
 
 	if (morelen) {
@@ -516,29 +508,32 @@
 		if (errno != EINTR && errno != EAGAIN) {
 			TRACE(("channel IO write error fd %d %s", fd, strerror(errno)))
 			close_chan_fd(channel, fd, SHUT_WR);
+			return DROPBEAR_FAILURE;
 		}
-	} else {
-		int cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written);
-		cbuf_incrread(cbuf, cbuf_written);
-		if (morelen) {
-			*morelen = written - cbuf_written;
-		}
-		channel->recvdonelen += written;
+	} 
+
+	cbuf_written = MIN(circ_len1+circ_len2, (unsigned int)written);
+	cbuf_incrread(cbuf, cbuf_written);
+	if (morelen) {
+		*morelen = written - cbuf_written;
 	}
-
+	channel->recvdonelen += written;
+	return DROPBEAR_SUCCESS;
 }
 #endif /* HAVE_WRITEV */
 
 /* Called to write data out to the local side of the channel. 
    Writes the circular buffer contents and also the "moredata" buffer
-   if not null. Will ignore EAGAIN */
-static void writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
+   if not null. Will ignore EAGAIN.
+   Returns DROPBEAR_FAILURE if writing to fd had an error and the channel is being closed, DROPBEAR_SUCCESS otherwise */
+static int writechannel(struct Channel* channel, int fd, circbuffer *cbuf,
 	const unsigned char *moredata, unsigned int *morelen) {
+	int ret = DROPBEAR_SUCCESS;
 	TRACE(("enter writechannel fd %d", fd))
 #ifdef HAVE_WRITEV
-	writechannel_writev(channel, fd, cbuf, moredata, morelen);
+	ret = writechannel_writev(channel, fd, cbuf, moredata, morelen);
 #else
-	writechannel_fallback(channel, fd, cbuf, moredata, morelen);
+	ret = writechannel_fallback(channel, fd, cbuf, moredata, morelen);
 #endif
 
 	/* Window adjust handling */
@@ -554,6 +549,7 @@
 			channel->recvwindow <= cbuf_getavail(channel->extrabuf));
 	
 	TRACE(("leave writechannel"))
+	return ret;
 }
 
 
@@ -828,6 +824,7 @@
 	unsigned int buflen;
 	unsigned int len;
 	unsigned int consumed;
+	int res;
 
 	TRACE(("enter recv_msg_channel_data"))
 
@@ -860,7 +857,7 @@
 
 	/* Attempt to write the data immediately without having to put it in the circular buffer */
 	consumed = datalen;
-	writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed);
+	res = writechannel(channel, fd, cbuf, buf_getptr(ses.payload, datalen), &consumed);
 
 	datalen -= consumed;
 	buf_incrpos(ses.payload, consumed);
@@ -868,17 +865,20 @@
 
 	/* We may have to run throught twice, if the buffer wraps around. Can't
 	 * just "leave it for next time" like with writechannel, since this
-	 * is payload data */
-	len = datalen;
-	while (len > 0) {
-		buflen = cbuf_writelen(cbuf);
-		buflen = MIN(buflen, len);
+	 * is payload data.
+	 * If the writechannel() failed then remaining data is discarded */
+	if (res == DROPBEAR_SUCCESS) {
+		len = datalen;
+		while (len > 0) {
+			buflen = cbuf_writelen(cbuf);
+			buflen = MIN(buflen, len);
 
-		memcpy(cbuf_writeptr(cbuf, buflen), 
-				buf_getptr(ses.payload, buflen), buflen);
-		cbuf_incrwrite(cbuf, buflen);
-		buf_incrpos(ses.payload, buflen);
-		len -= buflen;
+			memcpy(cbuf_writeptr(cbuf, buflen), 
+					buf_getptr(ses.payload, buflen), buflen);
+			cbuf_incrwrite(cbuf, buflen);
+			buf_incrpos(ses.payload, buflen);
+			len -= buflen;
+		}
 	}
 
 	TRACE(("leave recv_msg_channel_data"))
@@ -970,6 +970,7 @@
 
 	if (channel == NULL) {
 		TRACE(("newchannel returned NULL"))
+		errtype = SSH_OPEN_RESOURCE_SHORTAGE;
 		goto failure;
 	}
 
@@ -991,8 +992,6 @@
 		channel->prio = DROPBEAR_CHANNEL_PRIO_BULK;
 	}
 
-	chan_initwritebuf(channel);
-
 	/* success */
 	send_msg_channel_open_confirmation(channel, channel->recvwindow,
 			channel->recvmaxpacket);
@@ -1135,7 +1134,6 @@
 
 	/* Outbound opened channels don't make use of in-progress connections,
 	 * we can set it up straight away */
-	chan_initwritebuf(chan);
 
 	/* set fd non-blocking */
 	setnonblocking(fd);
--- a/common-session.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/common-session.c	Mon Nov 23 23:04:48 2015 +0800
@@ -159,6 +159,16 @@
 		FD_ZERO(&readfd);
 		dropbear_assert(ses.payload == NULL);
 
+		/* We get woken up when signal handlers write to this pipe.
+		   SIGCHLD in svr-chansession is the only one currently. */
+		FD_SET(ses.signal_pipe[0], &readfd);
+
+		/* set up for channels which can be read/written */
+		setchannelfds(&readfd, &writefd, writequeue_has_space);
+
+		/* Pending connections to test */
+		set_connect_fds(&writefd);
+
 		/* We delay reading from the input socket during initial setup until
 		after we have written out our initial KEXINIT packet (empty writequeue). 
 		This means our initial packet can be in-flight while we're doing a blocking
@@ -170,19 +180,12 @@
 			&& writequeue_has_space) {
 			FD_SET(ses.sock_in, &readfd);
 		}
+
+		/* Ordering is important, this test must occur after any other function
+		might have queued packets (such as connection handlers) */
 		if (ses.sock_out != -1 && !isempty(&ses.writequeue)) {
 			FD_SET(ses.sock_out, &writefd);
 		}
-		
-		/* We get woken up when signal handlers write to this pipe.
-		   SIGCHLD in svr-chansession is the only one currently. */
-		FD_SET(ses.signal_pipe[0], &readfd);
-
-		/* set up for channels which can be read/written */
-		setchannelfds(&readfd, &writefd, writequeue_has_space);
-
-		/* Pending connections to test */
-		set_connect_fds(&writefd);
 
 		val = select(ses.maxfd+1, &readfd, &writefd, NULL, &timeout);
 
--- a/configure.ac	Fri Aug 07 21:26:03 2015 +0800
+++ b/configure.ac	Mon Nov 23 23:04:48 2015 +0800
@@ -632,7 +632,7 @@
 AC_PROG_GCC_TRADITIONAL
 AC_FUNC_MEMCMP
 AC_FUNC_SELECT_ARGTYPES
-AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork writev])
+AC_CHECK_FUNCS([dup2 getpass getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork writev crypt])
 
 AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME))
 
@@ -719,10 +719,21 @@
 
 AC_MSG_NOTICE()
 if test $BUNDLED_LIBTOM = 1 ; then
-AC_MSG_NOTICE(Using bundled libtomcrypt and libtommath)
+AC_MSG_NOTICE([Using bundled libtomcrypt and libtommath])
 else
-AC_MSG_NOTICE(Using system libtomcrypt and libtommath)
+AC_MSG_NOTICE([Using system libtomcrypt and libtommath])
+fi
+
+
+if test "x$ac_cv_func_getpass" != xyes; then
+AC_MSG_NOTICE()
+AC_MSG_NOTICE([getpass() not available, dbclient will only have public-key authentication])
+fi
+
+if test "x$ac_cv_func_crypt" != xyes; then
+AC_MSG_NOTICE()
+AC_MSG_NOTICE([crypt() not available, dropbear server will not have password authentication])
 fi
 
 AC_MSG_NOTICE()
-AC_MSG_NOTICE(Now edit options.h to choose features.)
+AC_MSG_NOTICE([Now edit options.h to choose features.])
--- a/dbclient.1	Fri Aug 07 21:26:03 2015 +0800
+++ b/dbclient.1	Mon Nov 23 23:04:48 2015 +0800
@@ -3,13 +3,14 @@
 dbclient \- lightweight SSH client
 .SH SYNOPSIS
 .B dbclient
-[\-Tt] [\-p
+[flag arguments] [\-p
 .I port\fR] [\-i
 .I id\fR] [\-L
-.I l\fR:\fIh\fR:\fIr\fR] [\-R
-.I l\fR:\fIh\fR:\fIr\fR] [\-l
+.I l\fR:\fIh\fR:\fIp\fR] [\-R
+.I l\fR:\fIh\fR:\fIp\fR] [\-l
 .IR user ]
 .I host
+.RI [ more\ flags ]
 .RI [ command ]
 
 .B dbclient
@@ -22,6 +23,13 @@
 is a small SSH client 
 .SH OPTIONS
 .TP
+.TP
+.B command
+A command to run on the remote host. This will normally be run by the remote host
+using the user's shell. The command begins at the first hyphen argument after the 
+host argument. If no command is specified an interactive terminal will be opened
+(see -t and -T).
+.TP
 .B \-p \fIport
 Connect to 
 .I port
@@ -60,10 +68,12 @@
 on the remote host.
 .TP
 .B \-t
-Allocate a PTY.
+Allocate a PTY. This is the default when no command is given, it gives a full
+interactive remote session. The main effect is that keystrokes are sent remotely 
+immediately as opposed to local line-based editing.
 .TP
 .B \-T
-Don't allocate a PTY.
+Don't allocate a PTY. This is the default a command is given. See -t.
 .TP
 .B \-N
 Don't request a remote shell or run any commands. Any command arguments are ignored.
@@ -129,7 +139,7 @@
 this case a connection will be made to the first host, then a TCP forwarded 
 connection will be made through that to the second host, and so on. Hosts other than
 the final destination will not see anything other than the encrypted SSH stream. 
-A port for a host can be specified with a hash (eg matt@martello^44 ).
+A port for a host can be specified with a caret (eg matt@martello^44 ).
 This syntax can also be used with scp or rsync (specifying dbclient as the 
 ssh/rsh command). A file can be "bounced" through multiple SSH hops, eg
 
@@ -157,6 +167,10 @@
 on standard output. This program will only be used if either DISPLAY is set and
 standard input is not a TTY, or the environment variable SSH_ASKPASS_ALWAYS is
 set.
+.SH NOTES
+If compiled with zlib support and if the server supports it, dbclient will
+always use compression.
+
 .SH AUTHOR
 Matt Johnston ([email protected]).
 .br
--- a/debian/changelog	Fri Aug 07 21:26:03 2015 +0800
+++ b/debian/changelog	Mon Nov 23 23:04:48 2015 +0800
@@ -1,3 +1,9 @@
+dropbear (2015.68-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Sat, 8 Aug 2015 22:52:58 +0800
+
 dropbear (2015.67-0.1) unstable; urgency=low
 
   * New upstream release.
--- a/dropbear.8	Fri Aug 07 21:26:03 2015 +0800
+++ b/dropbear.8	Mon Nov 23 23:04:48 2015 +0800
@@ -3,7 +3,7 @@
 dropbear \- lightweight SSH server
 .SH SYNOPSIS
 .B dropbear
-[\-RFEmwsgjki] [\-b
+[flag arguments] [\-b
 .I banner\fR] 
 [\-r
 .I hostkeyfile\fR] [\-p
@@ -100,7 +100,8 @@
 .TP
 Authorized Keys
 
-~/.ssh/authorized_keys can be set up to allow remote login with a RSA or DSS
+~/.ssh/authorized_keys can be set up to allow remote login with a RSA,
+ECDSA, or DSS
 key. Each line is of the form
 .TP
 [restrictions] ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAIgAsp... [comment]
@@ -139,7 +140,7 @@
 
 Host key files are read at startup from a standard location, by default
 /etc/dropbear/dropbear_dss_host_key, /etc/dropbear/dropbear_rsa_host_key, and 
-/etc/dropbear/dropbear-ecdsa_host_key
+/etc/dropbear/dropbear_ecdsa_host_key
 or specified on the commandline with -r. These are of the form generated
 by dropbearkey. The -R option can be used to automatically generate keys
 in the default location - keys will be generated after startup when the first
--- a/dropbearkey.1	Fri Aug 07 21:26:03 2015 +0800
+++ b/dropbearkey.1	Mon Nov 23 23:04:48 2015 +0800
@@ -9,6 +9,7 @@
 .I file
 [\-s
 .IR bits ]
+[\-y]
 .SH DESCRIPTION
 .B dropbearkey
 generates a
@@ -39,12 +40,19 @@
 Set the key size to
 .I bits
 bits, should be multiple of 8 (optional). 
+.TP
+.B \-y
+Just print the publickey and fingerprint for the private key in \fIfile\fR.
 .SH NOTES
 The program dropbearconvert(1) can be used to convert between Dropbear and OpenSSH key formats.
 .P
 Dropbear does not support encrypted keys. 
 .SH EXAMPLE
+generate a host-key:
  # dropbearkey -t rsa -f /etc/dropbear/dropbear_rsa_host_key
+
+extract a public key suitable for authorized_keys from private key:
+ # dropbearkey -y -f id_rsa | grep "^ssh-rsa " >> authorized_keys
 .SH AUTHOR
 Matt Johnston ([email protected]).
 .br
--- a/netio.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/netio.c	Mon Nov 23 23:04:48 2015 +0800
@@ -174,28 +174,26 @@
 
 void set_connect_fds(fd_set *writefd) {
 	m_list_elem *iter;
-	TRACE(("enter handle_connect_fds"))
-	for (iter = ses.conn_pending.first; iter; iter = iter->next) {
+	TRACE(("enter set_connect_fds"))
+	iter = ses.conn_pending.first;
+	while (iter) {
+		m_list_elem *next_iter = iter->next;
 		struct dropbear_progress_connection *c = iter->item;
 		/* Set one going */
-		while (c->res_iter && c->sock < 0)
-		{
+		while (c->res_iter && c->sock < 0) {
 			connect_try_next(c);
 		}
 		if (c->sock >= 0) {
 			FD_SET(c->sock, writefd);
 		} else {
-			m_list_elem *remove_iter;
 			/* Final failure */
 			if (!c->errstring) {
 				c->errstring = m_strdup("unexpected failure");
 			}
 			c->cb(DROPBEAR_FAILURE, -1, c->cb_data, c->errstring);
-			/* Safely remove without invalidating iter */
-			remove_iter = iter;
-			iter = iter->prev;
-			remove_connect(c, remove_iter);
+			remove_connect(c, iter);
 		}
+		iter = next_iter;
 	}
 }
 
--- a/options.h	Fri Aug 07 21:26:03 2015 +0800
+++ b/options.h	Mon Nov 23 23:04:48 2015 +0800
@@ -206,7 +206,10 @@
  * PAM challenge/response.
  * You can't enable both PASSWORD and PAM. */
 
+/* This requires crypt() */
+#ifdef HAVE_CRYPT
 #define ENABLE_SVR_PASSWORD_AUTH
+#endif
 /* PAM requires ./configure --enable-pam */
 /*#define ENABLE_SVR_PAM_AUTH */
 #define ENABLE_SVR_PUBKEY_AUTH
@@ -217,9 +220,12 @@
 #define ENABLE_SVR_PUBKEY_OPTIONS
 #endif
 
+/* This requires getpass. */
+#ifdef HAVE_GETPASS
 #define ENABLE_CLI_PASSWORD_AUTH
+#define ENABLE_CLI_INTERACT_AUTH
+#endif
 #define ENABLE_CLI_PUBKEY_AUTH
-#define ENABLE_CLI_INTERACT_AUTH
 
 /* A default argument for dbclient -i <privatekey>. 
 Homedir is prepended unless path begins with / */
--- a/scp.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/scp.c	Mon Nov 23 23:04:48 2015 +0800
@@ -992,7 +992,7 @@
 			continue;
 		}
 		omode = mode;
-		mode |= S_IWRITE;
+		mode |= S_IWUSR;
 		if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) {
 bad:			run_err("%s: %s", np, strerror(errno));
 			continue;
--- a/session.h	Fri Aug 07 21:26:03 2015 +0800
+++ b/session.h	Mon Nov 23 23:04:48 2015 +0800
@@ -293,10 +293,9 @@
 	int interact_request_received; /* flag whether we've received an 
 									  info request from the server for
 									  interactive auth.*/
-
+#endif
 	int cipher_none_after_auth; /* Set to 1 if the user requested "none"
 								   auth */
-#endif
 	sign_key *lastprivkey;
 
 	int retval; /* What the command exit status was - we emulate it */
--- a/svr-authpubkeyoptions.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/svr-authpubkeyoptions.c	Mon Nov 23 23:04:48 2015 +0800
@@ -91,7 +91,7 @@
 /* Set chansession command to the one forced 
  * by any 'command' public key option. */
 void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
-	if (ses.authstate.pubkey_options) {
+	if (ses.authstate.pubkey_options && ses.authstate.pubkey_options->forced_command) {
 		if (chansess->cmd) {
 			/* original_command takes ownership */
 			chansess->original_command = chansess->cmd;
--- a/svr-chansession.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/svr-chansession.c	Mon Nov 23 23:04:48 2015 +0800
@@ -814,7 +814,7 @@
 		login_free_entry(li);
 
 #ifdef DO_MOTD
-		if (svr_opts.domotd) {
+		if (svr_opts.domotd && !chansess->cmd) {
 			/* don't show the motd if ~/.hushlogin exists */
 
 			/* 12 == strlen("/.hushlogin\0") */
--- a/svr-runopts.c	Fri Aug 07 21:26:03 2015 +0800
+++ b/svr-runopts.c	Mon Nov 23 23:04:48 2015 +0800
@@ -112,13 +112,14 @@
 
 void svr_getopts(int argc, char ** argv) {
 
-	unsigned int i;
+	unsigned int i, j;
 	char ** next = 0;
 	int nextisport = 0;
 	char* recv_window_arg = NULL;
 	char* keepalive_arg = NULL;
 	char* idle_timeout_arg = NULL;
 	char* keyfile = NULL;
+	char c;
 
 
 	/* see printhelp() for options */
@@ -168,28 +169,11 @@
 #endif
 
 	for (i = 1; i < (unsigned int)argc; i++) {
-		if (nextisport) {
-			addportandaddress(argv[i]);
-			nextisport = 0;
-			continue;
-		}
-	  
-		if (next) {
-			*next = argv[i];
-			if (*next == NULL) {
-				dropbear_exit("Invalid null argument");
-			}
-			next = 0x00;
+		if (argv[i][0] != '-' || argv[i][1] == '\0')
+			dropbear_exit("Invalid argument: %s", argv[i]);
 
-			if (keyfile) {
-				addhostkey(keyfile);
-				keyfile = NULL;
-			}
-			continue;
-		}
-
-		if (argv[i][0] == '-') {
-			switch (argv[i][1]) {
+		for (j = 1; (c = argv[i][j]) != '\0' && !next && !nextisport; j++) {
+			switch (c) {
 				case 'b':
 					next = &svr_opts.bannerfile;
 					break;
@@ -278,12 +262,39 @@
 					exit(EXIT_SUCCESS);
 					break;
 				default:
-					fprintf(stderr, "Unknown argument %s\n", argv[i]);
+					fprintf(stderr, "Invalid option -%c\n", c);
 					printhelp(argv[0]);
 					exit(EXIT_FAILURE);
 					break;
 			}
 		}
+
+		if (!next && !nextisport)
+			continue;
+
+		if (c == '\0') {
+			i++;
+			j = 0;
+			if (!argv[i]) {
+				dropbear_exit("Missing argument");
+			}
+		}
+
+		if (nextisport) {
+			addportandaddress(&argv[i][j]);
+			nextisport = 0;
+		} else if (next) {
+			*next = &argv[i][j];
+			if (*next == NULL) {
+				dropbear_exit("Invalid null argument");
+			}
+			next = 0x00;
+
+			if (keyfile) {
+				addhostkey(keyfile);
+				keyfile = NULL;
+			}
+		}
 	}
 
 	/* Set up listening ports */
--- a/sysoptions.h	Fri Aug 07 21:26:03 2015 +0800
+++ b/sysoptions.h	Mon Nov 23 23:04:48 2015 +0800
@@ -4,7 +4,7 @@
  *******************************************************************/
 
 #ifndef DROPBEAR_VERSION
-#define DROPBEAR_VERSION "2015.67"
+#define DROPBEAR_VERSION "2015.68"
 #endif
 
 #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
@@ -150,7 +150,7 @@
 								RECV_WINDOWEXTEND bytes */
 #define MAX_RECV_WINDOW (1024*1024) /* 1 MB should be enough */
 
-#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11
+#define MAX_CHANNELS 1000 /* simple mem restriction, includes each tcp/x11
 							connection, so can't be _too_ small */
 
 #define MAX_STRING_LEN (MAX(MAX_CMD_LEN, 2400)) /* Sun SSH needs 2400 for algos,