The unified diff between revisions [f452aa06..] and [a4cab2b4..] is displayed below. It can also be downloaded as a raw diff.

#
#
# add_file "cli-authinteract.c"
#  content [001567a2e62e0f5f87d73463ab4f6e1f85415f35]
#
# patch "INSTALL"
#  from [9efe85a4525184ca98a93c02802a1d42a24f2bcc]
#    to [52b3298eab1a71cead9d4b3364a2ec9090262bf2]
#
# patch "Makefile.in"
#  from [4be8bcfc6c1a68ca515d55ad95fa09cb6cdae56f]
#    to [18432081a78218026941c91a605bf3101dda141a]
#
# patch "auth.h"
#  from [aac1a55d01be93e35ab5b0281b9760f8c8ec79d1]
#    to [84e61bd4fb2b09bcc8e5bef98e4d5c71fde907a2]
#
# patch "buffer.c"
#  from [2fe54eba701ee5c354f265c40914f2733519af16]
#    to [8818b6d266850c114615e8780d216c588f89d3c1]
#
# patch "channel.h"
#  from [f0285633bae6fc082951524deaa3ada0061ebff4]
#    to [b72f20eddf4824bdd1280ee7dbe855301813610c]
#
# patch "circbuffer.c"
#  from [b56cdc09c658edcbf0fdc60a86c561026e97dd69]
#    to [93e513140e4cc4cbde3d3ece44364ea0715e2a4d]
#
# patch "cli-auth.c"
#  from [e4689b84510bdf41e8aa2279bfdb8fb31879c0a7]
#    to [eb6d2144c4d4b27aaa50b7b1a1f2b879a9c0a06f]
#
# patch "cli-authpasswd.c"
#  from [cf039b904bb3b86945491680557f2d3847d5740c]
#    to [ac2864b6001ecb7272b258aeee75ed1287d745e2]
#
# patch "cli-chansession.c"
#  from [e830bb4828a09f4f5c2094ae43bbc77b3d8a8970]
#    to [8250f8508ce081c3facf84f9a8b5cd5caab220e5]
#
# patch "cli-runopts.c"
#  from [79955079fc49e1b252055d125874535d52b5914b]
#    to [5e702c8f3984796ffec8a22f7032ab2ad64140cd]
#
# patch "cli-session.c"
#  from [e088b599dbdbd93930ff21e6bebe7dcbfbf66ed3]
#    to [49416025c7fc3883ad9b8975474ad68307b24717]
#
# patch "cli-tcpfwd.c"
#  from [d8d52da1d269d157d91e9731508a3b8beba444c2]
#    to [3c2b139864eb87c901783d9d9398f3f584674533]
#
# patch "common-algo.c"
#  from [37d9d05401ab9d81872628b05045b6437296e78b]
#    to [a54999708e3805153b7f6857ce09f86ea3ad41e8]
#
# patch "common-channel.c"
#  from [522b3539ac8ac8feb32efd8c3e7a0490e51b29c3]
#    to [97ae918fb65d16853d0994b99fa1961d1c47fd32]
#
# patch "common-kex.c"
#  from [960bfda8deaa204b5866f14647f3cd86d262d93f]
#    to [b06f424fe466a6d3ecfb7072d59457ebb39c8780]
#
# patch "common-session.c"
#  from [c141f714affb71abd896785b986c44fdfe98a357]
#    to [1264a49e05001b96e621efa4abf6f9e77402ed71]
#
# patch "configure.in"
#  from [2b2625caa07530f2fa625a0eee5d6e654f167dba]
#    to [efc775ba6fb049596194158e06479c2a29a0e165]
#
# patch "dbclient.1"
#  from [aed09b6b3d241203fd11a777e5fae635066d2dfc]
#    to [2ae47b2e32dbafef9abf583844711c265e418619]
#
# patch "dbutil.c"
#  from [9440c0ae5ca45076f63b0f295585bfaeb82dd57c]
#    to [bcd256f4bdc8183dfd6b813375c39af6513b183d]
#
# patch "dbutil.h"
#  from [3806a1758a9a15ee7a1e3b75e3a49c0d27a7343a]
#    to [76eb992fc016821fd1584982e59abe7ac66c5671]
#
# patch "dropbear.8"
#  from [8d1707f168fb2870937ffc24cb85e328328225ce]
#    to [01a2e001c0e1dcbaa0105b5914c737744ae5c802]
#
# patch "dss.c"
#  from [e72e0ab248024c0d858b55fdd382a414aeb31317]
#    to [d58b7df483ca328e4102dbc59fab1cf85494263e]
#
# patch "includes.h"
#  from [cb0e389740b51fc33f98b8c29c9f2ffe7cd11bc2]
#    to [9baa00e2da012281cecc528405cf06e48b53fda8]
#
# patch "kex.h"
#  from [4aedb4c80abfbc1df32da43a4903b34e8cddc56a]
#    to [596a833742cc5ab88d3930e46214f19e129347f2]
#
# patch "keyimport.c"
#  from [38a61bdea3196db5ff2a57fd389236948142b4ef]
#    to [4d6aa56819151b18d6828b1a9b1db0b9864e40b3]
#
# patch "options.h"
#  from [22166eca17d24203e474f1d8b26ce95ea45e0166]
#    to [562d600e35b6a1ef1ab8d8df1dd61654056bd697]
#
# patch "packet.c"
#  from [93ffb969c3097b6fe2c9f50cf80716ca0f209295]
#    to [01803cc5860fe470ae70a883d807579b3bd0fc68]
#
# patch "queue.c"
#  from [ccb24cf04c2fae9ff0008f7181239fff1e93f82b]
#    to [0b2bee6c831b234a3fba77c4ae6d598126a3c07c]
#
# patch "rsa.c"
#  from [a5bd0348ccf9497600f633057ec6028e8cf3c329]
#    to [af6a357404e3480445b0b2835065a3e4666ee812]
#
# patch "runopts.h"
#  from [866a99e2213c6a2602a649a2915471ef4b39770a]
#    to [36768f6323c18233529bb202e8876a3dfbc5d5a5]
#
# patch "session.h"
#  from [0e62967a248214d43a774911bfa4a0c503f0da17]
#    to [029633beee5cd56701332bc0fa563ae201223469]
#
# patch "signkey.c"
#  from [0b42ca44158bc9c2057064ddf12aa3ab11fb76b4]
#    to [c0ce28d518a22769fdee4e64e6fcd77a9100e61f]
#
# patch "ssh.h"
#  from [b63c0c60a29e5dec39b53218d20fe30cfd00a60e]
#    to [190af1a6af0c6977129303befae06af9c58e6e07]
#
# patch "svr-authpam.c"
#  from [d2e49b9902777338e827e1935b24457b86fce347]
#    to [aa255168b81cef1ec80b008a7e98975cfa9600a3]
#
# patch "svr-authpubkey.c"
#  from [a659d961ca849841adc5ac41a68f360a135763d5]
#    to [6a8b306f446ff4aede63a6d5a72de0b8a15129e4]
#
# patch "svr-chansession.c"
#  from [14640c883f2d3f5a07cb5614102eeb7762aa9b79]
#    to [8b52f1a622321b7cf4ed3ac6f69e7241d14d324d]
#
# patch "svr-main.c"
#  from [956679879949d5e02205ac66872e03986c76fc61]
#    to [5c0e59de51df9c03a47947b7dcd100053623bfd3]
#
# patch "svr-runopts.c"
#  from [6d28fe6609d584d48c70f1707e7d4f8460513aea]
#    to [f68d09f9fe84a764badcdd18a0f69b14e44cb0e8]
#
# patch "svr-tcpfwd.c"
#  from [9c0362f8d1c15ce9d702873cc1a2064754d7919b]
#    to [bfd47e1ced10186c0d9ce06dca0f0d3694e9c3df]
#
# patch "tcp-accept.c"
#  from [85218bbb73f0a9394f0f100aa416a917978318b7]
#    to [413de2351e02bf67ef6e78238c449c6a480a2f75]
#
# patch "tcpfwd.h"
#  from [e093ea296308000bdf7c4c54f49caf7bf353a47e]
#    to [16c1fbd5251921ecc87e704bc2afe2076dd55a4e]
#
============================================================
--- cli-authinteract.c	001567a2e62e0f5f87d73463ab4f6e1f85415f35
+++ cli-authinteract.c	001567a2e62e0f5f87d73463ab4f6e1f85415f35
@@ -0,0 +1,168 @@
+/*
+ * Dropbear SSH
+ *
+ * Copyright (c) 2005 Matt Johnston
+ * All rights reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#include "includes.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "session.h"
+#include "ssh.h"
+#include "runopts.h"
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+
+static unsigned char* get_response(unsigned char* prompt)
+{
+	FILE* tty = NULL;
+	unsigned char* response = NULL;
+	/* not a password, but a reasonable limit */
+	char buf[DROPBEAR_MAX_CLI_PASS];
+	char* ret = NULL;
+
+	fprintf(stderr, "%s", prompt);
+
+	tty = fopen(_PATH_TTY, "r");
+	if (tty) {
+		ret = fgets(buf, sizeof(buf), tty);
+		fclose(tty);
+	} else {
+		ret = fgets(buf, sizeof(buf), stdin);
+	}
+
+	if (ret == NULL) {
+		response = (unsigned char*)m_strdup("");
+	} else {
+		unsigned int buflen = strlen(buf);
+		/* fgets includes newlines */
+		if (buflen > 0 && buf[buflen-1] == '\n')
+			buf[buflen-1] = '\0';
+		response = (unsigned char*)m_strdup(buf);
+	}
+
+	m_burn(buf, sizeof(buf));
+
+	return response;
+}
+
+void recv_msg_userauth_info_request() {
+
+	unsigned char *name = NULL;
+	unsigned char *instruction = NULL;
+	unsigned int num_prompts = 0;
+	unsigned int i;
+
+	unsigned char *prompt = NULL;
+	unsigned int echo = 0;
+	unsigned char *response = NULL;
+
+	TRACE(("enter recv_msg_recv_userauth_info_request"))
+
+	cli_ses.interact_request_received = 1;
+
+	name = buf_getstring(ses.payload, NULL);
+	instruction = buf_getstring(ses.payload, NULL);
+
+	/* language tag */
+	buf_eatstring(ses.payload);
+
+	num_prompts = buf_getint(ses.payload);
+
+	if (num_prompts >= DROPBEAR_MAX_CLI_INTERACT_PROMPTS) {
+		dropbear_exit("Too many prompts received for keyboard-interactive");
+	}
+
+	/* we'll build the response as we go */
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_INFO_RESPONSE);
+	buf_putint(ses.writepayload, num_prompts);
+
+	if (strlen(name) > 0) {
+		cleantext(name);
+		fprintf(stderr, "%s", name);
+		m_free(name);
+	}
+	if (strlen(instruction) > 0) {
+		cleantext(instruction);
+		fprintf(stderr, "%s", instruction);
+		m_free(instruction);
+	}
+
+	for (i = 0; i < num_prompts; i++) {
+		unsigned int response_len = 0;
+		prompt = buf_getstring(ses.payload, NULL);
+		cleantext(prompt);
+
+		echo = buf_getbool(ses.payload);
+
+		if (!echo) {
+			unsigned char* p = getpass(prompt);
+			response = m_strdup(p);
+			m_burn(p, strlen(p));
+		} else {
+			response = get_response(prompt);
+		}
+
+		response_len = strlen(response);
+		buf_putstring(ses.writepayload, response, response_len);
+		m_burn(response, response_len);
+		m_free(response);
+	}
+
+	encrypt_packet();
+
+
+	TRACE(("leave recv_msg_recv_userauth_info_request"))
+}
+
+void cli_auth_interactive() {
+
+	TRACE(("enter cli_auth_interactive"))
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+
+	/* username */
+	buf_putstring(ses.writepayload, cli_opts.username,
+			strlen(cli_opts.username));
+
+	/* service name */
+	buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION,
+			SSH_SERVICE_CONNECTION_LEN);
+
+	/* method */
+	buf_putstring(ses.writepayload, AUTH_METHOD_INTERACT,
+			AUTH_METHOD_INTERACT_LEN);
+
+	/* empty language tag */
+	buf_putstring(ses.writepayload, "", 0);
+
+	/* empty submethods */
+	buf_putstring(ses.writepayload, "", 0);
+
+	encrypt_packet();
+	cli_ses.interact_request_received = 0;
+
+	TRACE(("leave cli_auth_interactive"))
+
+}
+#endif	/* ENABLE_CLI_INTERACT_AUTH */
============================================================
--- INSTALL	9efe85a4525184ca98a93c02802a1d42a24f2bcc
+++ INSTALL	52b3298eab1a71cead9d4b3364a2ec9090262bf2
@@ -28,6 +28,11 @@ Binaries can be strippd with "make strip

 ============================================================================

+If you're compiling for a 386-class CPU, you will probably need to add
+CFLAGS=-DLTC_NO_BSWAP so that libtomcrypt doesn't use 486+ instructions.
+
+============================================================================
+
 Compiling with uClibc:

 Firstly, make sure you have at least uclibc 0.9.17, as getusershell() in prior
============================================================
--- Makefile.in	4be8bcfc6c1a68ca515d55ad95fa09cb6cdae56f
+++ Makefile.in	18432081a78218026941c91a605bf3101dda141a
@@ -29,7 +29,7 @@ CLIOBJS=cli-algo.o cli-main.o cli-auth.o

 CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
 		cli-session.o cli-service.o cli-runopts.o cli-chansession.o \
-		cli-authpubkey.o cli-tcpfwd.o cli-channel.o
+		cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o

 CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
 			common-channel.o common-chansession.o termcodes.o loginrec.o \
============================================================
--- auth.h	aac1a55d01be93e35ab5b0281b9760f8c8ec79d1
+++ auth.h	84e61bd4fb2b09bcc8e5bef98e4d5c71fde907a2
@@ -41,29 +41,37 @@ void recv_msg_userauth_success();
 /* Client functions */
 void recv_msg_userauth_failure();
 void recv_msg_userauth_success();
+void recv_msg_userauth_specific_60();
 void recv_msg_userauth_pk_ok();
+void recv_msg_userauth_info_request();
 void cli_get_user();
 void cli_auth_getmethods();
 void cli_auth_try();
 void recv_msg_userauth_banner();
 void cli_pubkeyfail();
-int cli_auth_password();
+void cli_auth_password();
 int cli_auth_pubkey();
+void cli_auth_interactive();


 #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */

-#define AUTH_TYPE_PUBKEY	1 << 0
-#define AUTH_TYPE_PASSWORD	1 << 1
+#define AUTH_TYPE_NONE      1
+#define AUTH_TYPE_PUBKEY    1 << 1
+#define AUTH_TYPE_PASSWORD  1 << 2
+#define AUTH_TYPE_INTERACT  1 << 3

-/* auth types, "none" means we should return list of acceptable types */
-#define AUTH_METHOD_NONE	"none"
+#define AUTH_METHOD_NONE "none"
 #define AUTH_METHOD_NONE_LEN 4
 #define AUTH_METHOD_PUBKEY "publickey"
 #define AUTH_METHOD_PUBKEY_LEN 9
 #define AUTH_METHOD_PASSWORD "password"
 #define AUTH_METHOD_PASSWORD_LEN 8
+#define AUTH_METHOD_INTERACT "keyboard-interactive"
+#define AUTH_METHOD_INTERACT_LEN 20

+
+
 /* This structure is shared between server and client - it contains
  * relatively little extraneous bits when used for the client rather than the
  * server */
@@ -77,6 +85,9 @@ struct AuthState {
 	unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for
 							  client and server (though has differing [obvious]
 							  meanings). */
+	unsigned perm_warn : 1; /* Server only, set if bad permissions on
+							   ~/.ssh/authorized_keys have already been
+							   logged. */

 	/* These are only used for the server */
 	char *printableuser; /* stripped of control chars, used for logs etc */
============================================================
--- buffer.c	2fe54eba701ee5c354f265c40914f2733519af16
+++ buffer.c	8818b6d266850c114615e8780d216c588f89d3c1
@@ -153,7 +153,7 @@ unsigned char buf_getbyte(buffer* buf) {
 unsigned char buf_getbyte(buffer* buf) {

 	/* This check is really just ==, but the >= allows us to check for the
-	 * assert()able case of pos > len, which should _never_ happen. */
+	 * bad case of pos > len, which should _never_ happen. */
 	if (buf->pos >= buf->len) {
 		dropbear_exit("bad buf_getbyte");
 	}
@@ -270,7 +270,7 @@ void buf_putmpint(buffer* buf, mp_int *
 	unsigned int len, pad = 0;
 	TRACE(("enter buf_putmpint"))

-	assert(mp != NULL);
+	dropbear_assert(mp != NULL);

 	if (SIGN(mp) == MP_NEG) {
 		dropbear_exit("negative bignum");
============================================================
--- channel.h	f0285633bae6fc082951524deaa3ada0061ebff4
+++ channel.h	b72f20eddf4824bdd1280ee7dbe855301813610c
@@ -65,9 +65,9 @@ struct Channel {
 	unsigned int recvdonelen;
 	unsigned int recvmaxpacket, transmaxpacket;
 	void* typedata; /* a pointer to type specific data */
-	int infd; /* data to send over the wire */
-	int outfd; /* data for consumption, what was in writebuf */
-	int errfd; /* used like infd or errfd, depending if it's client or server.
+	int writefd; /* read from wire, written to insecure side */
+	int readfd; /* read from insecure size, written to wire */
+	int errfd; /* used like writefd or readfd, depending if it's client or server.
 				  Doesn't exactly belong here, but is cleaner here */
 	circbuffer *writebuf; /* data from the wire, for local consumption */
 	circbuffer *extrabuf; /* extended-data for the program - used like writebuf
@@ -81,6 +81,10 @@ struct Channel {
 	int initconn; /* used for TCP forwarding, whether the channel has been
 					 fully initialised */

+	int await_open; /* flag indicating whether we've sent an open request
+					   for this channel (and are awaiting a confirmation
+					   or failure). */
+
 	const struct ChanType* type;

 };
@@ -96,7 +100,7 @@ struct ChanType {

 };

-void chaninitialise();
+void chaninitialise(const struct ChanType *chantypes[]);
 void chancleanup();
 void setchannelfds(fd_set *readfd, fd_set *writefd);
 void channelio(fd_set *readfd, fd_set *writefd);
============================================================
--- circbuffer.c	b56cdc09c658edcbf0fdc60a86c561026e97dd69
+++ circbuffer.c	93e513140e4cc4cbde3d3ece44364ea0715e2a4d
@@ -66,8 +66,8 @@ unsigned int cbuf_readlen(circbuffer *cb

 unsigned int cbuf_readlen(circbuffer *cbuf) {

-	assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
-	assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);
+	dropbear_assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
+	dropbear_assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);

 	if (cbuf->used == 0) {
 		TRACE(("cbuf_readlen: unused buffer"))
@@ -83,9 +83,9 @@ unsigned int cbuf_writelen(circbuffer *c

 unsigned int cbuf_writelen(circbuffer *cbuf) {

-	assert(cbuf->used <= cbuf->size);
-	assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
-	assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);
+	dropbear_assert(cbuf->used <= cbuf->size);
+	dropbear_assert(((2*cbuf->size)+cbuf->writepos-cbuf->readpos)%cbuf->size == cbuf->used%cbuf->size);
+	dropbear_assert(((2*cbuf->size)+cbuf->readpos-cbuf->writepos)%cbuf->size == (cbuf->size-cbuf->used)%cbuf->size);

 	if (cbuf->used == cbuf->size) {
 		TRACE(("cbuf_writelen: full buffer"))
@@ -122,7 +122,7 @@ void cbuf_incrwrite(circbuffer *cbuf, un
 	}

 	cbuf->used += len;
-	assert(cbuf->used <= cbuf->size);
+	dropbear_assert(cbuf->used <= cbuf->size);
 	cbuf->writepos = (cbuf->writepos + len) % cbuf->size;
 }

@@ -132,7 +132,7 @@ void cbuf_incrread(circbuffer *cbuf, uns
 		dropbear_exit("bad cbuf read");
 	}

-	assert(cbuf->used >= len);
+	dropbear_assert(cbuf->used >= len);
 	cbuf->used -= len;
 	cbuf->readpos = (cbuf->readpos + len) % cbuf->size;
 }
============================================================
--- cli-auth.c	e4689b84510bdf41e8aa2279bfdb8fb31879c0a7
+++ cli-auth.c	eb6d2144c4d4b27aaa50b7b1a1f2b879a9c0a06f
@@ -32,7 +32,6 @@
 #include "packet.h"
 #include "runopts.h"

-
 void cli_authinitialise() {

 	memset(&ses.authstate, 0, sizeof(ses.authstate));
@@ -99,7 +98,41 @@ out:
 	TRACE(("leave recv_msg_userauth_banner"))
 }

+/* This handles the message-specific types which
+ * all have a value of 60. These are
+ * SSH_MSG_USERAUTH_PASSWD_CHANGEREQ,
+ * SSH_MSG_USERAUTH_PK_OK, &
+ * SSH_MSG_USERAUTH_INFO_REQUEST. */
+void recv_msg_userauth_specific_60() {

+#ifdef ENABLE_CLI_PUBKEY_AUTH
+	if (cli_ses.lastauthtype == AUTH_TYPE_PUBKEY) {
+		recv_msg_userauth_pk_ok();
+		return;
+	}
+#endif
+
+#ifdef ENABLE_CLI_INTERACT_AUTH
+	if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT) {
+		recv_msg_userauth_info_request();
+		return;
+	}
+#endif
+
+#ifdef ENABLE_CLI_PASSWORD_AUTH
+	if (cli_ses.lastauthtype == AUTH_TYPE_PASSWORD) {
+		/* Eventually there could be proper password-changing
+		 * support. However currently few servers seem to
+		 * implement it, and password auth is last-resort
+		 * regardless - keyboard-interactive is more likely
+		 * to be used anyway. */
+		dropbear_close("Your password has expired.");
+	}
+#endif
+
+	dropbear_exit("Unexpected userauth packet");
+}
+
 void recv_msg_userauth_failure() {

 	unsigned char * methods = NULL;
@@ -113,8 +146,7 @@ void recv_msg_userauth_failure() {

 	if (cli_ses.state != USERAUTH_REQ_SENT) {
 		/* Perhaps we should be more fatal? */
-		TRACE(("But we didn't send a userauth request!!!!!!"))
-		return;
+		dropbear_exit("Unexpected userauth failure");
 	}

 #ifdef ENABLE_CLI_PUBKEY_AUTH
@@ -125,6 +157,19 @@ void recv_msg_userauth_failure() {
 	}
 #endif

+#ifdef ENABLE_CLI_INTERACT_AUTH
+	/* If we get a failure message for keyboard interactive without
+	 * receiving any request info packet, then we don't bother trying
+	 * keyboard interactive again */
+	if (cli_ses.lastauthtype == AUTH_TYPE_INTERACT
+			&& !cli_ses.interact_request_received) {
+		TRACE(("setting auth_interact_failed = 1"))
+		cli_ses.auth_interact_failed = 1;
+	}
+#endif
+
+	cli_ses.lastauthtype = AUTH_TYPE_NONE;
+
 	methods = buf_getstring(ses.payload, &methlen);

 	partial = buf_getbool(ses.payload);
@@ -157,6 +202,12 @@ void recv_msg_userauth_failure() {
 				ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
 			}
 #endif
+#ifdef ENABLE_CLI_INTERACT_AUTH
+			if (strncmp(AUTH_METHOD_INTERACT, tok,
+				AUTH_METHOD_INTERACT_LEN) == 0) {
+				ses.authstate.authtypes |= AUTH_TYPE_INTERACT;
+			}
+#endif
 #ifdef ENABLE_CLI_PASSWORD_AUTH
 			if (strncmp(AUTH_METHOD_PASSWORD, tok,
 				AUTH_METHOD_PASSWORD_LEN) == 0) {
@@ -180,6 +231,7 @@ void recv_msg_userauth_success() {
 	TRACE(("received msg_userauth_success"))
 	ses.authstate.authdone = 1;
 	cli_ses.state = USERAUTH_SUCCESS_RCVD;
+	cli_ses.lastauthtype = AUTH_TYPE_NONE;
 }

 void cli_auth_try() {
@@ -189,7 +241,8 @@ void cli_auth_try() {

 	CHECKCLEARTOWRITE();

-	/* XXX We hardcode that we try a pubkey first */
+	/* Order to try is pubkey, interactive, password.
+	 * As soon as "finished" is set for one, we don't do any more. */
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 	if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
 		finished = cli_auth_pubkey();
@@ -197,13 +250,28 @@ void cli_auth_try() {
 	}
 #endif

+#ifdef ENABLE_CLI_INTERACT_AUTH
+	if (!finished && ses.authstate.authtypes & AUTH_TYPE_INTERACT) {
+		if (cli_ses.auth_interact_failed) {
+			finished = 0;
+		} else {
+			cli_auth_interactive();
+			cli_ses.lastauthtype = AUTH_TYPE_INTERACT;
+			finished = 1;
+		}
+	}
+#endif
+
 #ifdef ENABLE_CLI_PASSWORD_AUTH
 	if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
-		finished = cli_auth_password();
+		cli_auth_password();
+		finished = 1;
 		cli_ses.lastauthtype = AUTH_TYPE_PASSWORD;
 	}
 #endif

+	TRACE(("cli_auth_try lastauthtype %d", cli_ses.lastauthtype))
+
 	if (!finished) {
 		dropbear_exit("No auth methods could be used.");
 	}
============================================================
--- cli-authpasswd.c	cf039b904bb3b86945491680557f2d3847d5740c
+++ cli-authpasswd.c	ac2864b6001ecb7272b258aeee75ed1287d745e2
@@ -113,7 +113,7 @@ static char *gui_getpass(const char *pro
 }
 #endif /* ENABLE_CLI_ASKPASS_HELPER */

-int cli_auth_password() {
+void cli_auth_password() {

 	char* password = NULL;

@@ -149,7 +149,5 @@ int cli_auth_password() {
 	m_burn(password, strlen(password));

 	TRACE(("leave cli_auth_password"))
-	return 1; /* Password auth can always be tried */
-
 }
 #endif	/* ENABLE_CLI_PASSWORD_AUTH */
============================================================
--- cli-chansession.c	e830bb4828a09f4f5c2094ae43bbc77b3d8a8970
+++ cli-chansession.c	8250f8508ce081c3facf84f9a8b5cd5caab220e5
@@ -340,10 +340,10 @@ static int cli_initchansess(struct Chann
 static int cli_initchansess(struct Channel *channel) {


-	channel->infd = STDOUT_FILENO;
+	channel->writefd = STDOUT_FILENO;
 	setnonblocking(STDOUT_FILENO);

-	channel->outfd = STDIN_FILENO;
+	channel->readfd = STDIN_FILENO;
 	setnonblocking(STDIN_FILENO);

 	channel->errfd = STDERR_FILENO;
============================================================
--- cli-runopts.c	79955079fc49e1b252055d125874535d52b5914b
+++ cli-runopts.c	5e702c8f3984796ffec8a22f7032ab2ad64140cd
@@ -47,6 +47,7 @@ static void printhelp() {
 					"Usage: %s [options] [user@]host\n"
 					"Options are:\n"
 					"-p <remoteport>\n"
+					"-l <username>\n"
 					"-t    Allocate a pty\n"
 					"-T    Don't allocate a pty\n"
 #ifdef ENABLE_CLI_PUBKEY_AUTH
@@ -54,11 +55,11 @@ static void printhelp() {
 #endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
 					"-L <listenport:remotehost:remoteport> Local port forwarding\n"
+					"-g    Allow remote hosts to connect to forwarded ports\n"
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
 					"-R <listenport:remotehost:remoteport> Remote port forwarding\n"
 #endif
-					"-l <username>\n"
 #ifdef DEBUG_TRACE
 					"-v    verbose\n"
 #endif
@@ -93,12 +94,11 @@ void cli_getopts(int argc, char ** argv)
 #endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
 	cli_opts.localfwds = NULL;
+	opts.listen_fwd_all = 0;
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
 	cli_opts.remotefwds = NULL;
 #endif
-	opts.nolocaltcp = 0;
-	opts.noremotetcp = 0;
 	/* not yet
 	opts.ipv4 = 1;
 	opts.ipv6 = 1;
@@ -167,6 +167,9 @@ void cli_getopts(int argc, char ** argv)
 				case 'L':
 					nextislocal = 1;
 					break;
+				case 'g':
+					opts.listen_fwd_all = 1;
+					break;
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
 				case 'R':
============================================================
--- cli-session.c	e088b599dbdbd93930ff21e6bebe7dcbfbf66ed3
+++ cli-session.c	49416025c7fc3883ad9b8975474ad68307b24717
@@ -63,9 +63,7 @@ static const packettype cli_packettypes[
 	{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
 	{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
 	{SSH_MSG_USERAUTH_BANNER, recv_msg_userauth_banner}, /* client */
-#ifdef ENABLE_CLI_PUBKEY_AUTH
-	{SSH_MSG_USERAUTH_PK_OK, recv_msg_userauth_pk_ok}, /* client */
-#endif
+	{SSH_MSG_USERAUTH_SPECIFIC_60, recv_msg_userauth_specific_60}, /* client */
 	{0, 0} /* End */
 };

@@ -285,7 +283,8 @@ static void cli_remoteclosed() {
 }

 /* Operates in-place turning dirty (untrusted potentially containing control
- * characters) text into clean text. */
+ * characters) text into clean text.
+ * Note: this is safe only with ascii - other charsets could have problems. */
 void cleantext(unsigned char* dirtytext) {

 	unsigned int i, j;
============================================================
--- cli-tcpfwd.c	d8d52da1d269d157d91e9731508a3b8beba444c2
+++ cli-tcpfwd.c	3c2b139864eb87c901783d9d9398f3f584674533
@@ -95,10 +95,19 @@ static int cli_localtcp(unsigned int lis
 				remoteport));

 	tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
+
 	tcpinfo->sendaddr = m_strdup(remoteaddr);
 	tcpinfo->sendport = remoteport;
+
+	if (opts.listen_fwd_all) {
+		tcpinfo->listenaddr = m_strdup("");
+	} else {
+		tcpinfo->listenaddr = m_strdup("localhost");
+	}
 	tcpinfo->listenport = listenport;
+
 	tcpinfo->chantype = &cli_chan_tcplocal;
+	tcpinfo->tcp_type = direct;

 	ret = listen_tcpfwd(tcpinfo);

@@ -113,13 +122,20 @@ static void send_msg_global_request_remo
 #ifdef  ENABLE_CLI_REMOTETCPFWD
 static void send_msg_global_request_remotetcp(int port) {

+	char* listenspec = NULL;
 	TRACE(("enter send_msg_global_request_remotetcp"))

 	CHECKCLEARTOWRITE();
 	buf_putbyte(ses.writepayload, SSH_MSG_GLOBAL_REQUEST);
 	buf_putstring(ses.writepayload, "tcpip-forward", 13);
 	buf_putbyte(ses.writepayload, 0);
-	buf_putstring(ses.writepayload, "0.0.0.0", 7); /* TODO: IPv6? */
+	if (opts.listen_fwd_all) {
+		listenspec = "";
+	} else {
+		listenspec = "localhost";
+	}
+	/* TODO: IPv6? */;
+	buf_putstring(ses.writepayload, listenspec, strlen(listenspec));
 	buf_putint(ses.writepayload, port);

 	encrypt_packet();
@@ -186,11 +202,9 @@ static int newtcpforwarded(struct Channe

 	ses.maxfd = MAX(ses.maxfd, sock);

-	/* Note that infd is actually the "outgoing" direction on the
-	 * tcp connection, vice versa for outfd.
-	 * We don't set outfd, that will get set after the connection's
+	/* We don't set readfd, that will get set after the connection's
 	 * progress succeeds */
-	channel->infd = sock;
+	channel->writefd = sock;
 	channel->initconn = 1;

 	err = SSH_OPEN_IN_PROGRESS;
============================================================
--- common-algo.c	37d9d05401ab9d81872628b05045b6437296e78b
+++ common-algo.c	a54999708e3805153b7f6857ce09f86ea3ad41e8
@@ -32,20 +32,28 @@
 /* Mappings for ciphers, parameters are
    {&cipher_desc, keysize, blocksize} */

+#ifdef DROPBEAR_AES256_CBC
+static const struct dropbear_cipher dropbear_aes256 =
+	{&aes_desc, 32, 16};
+#endif
 #ifdef DROPBEAR_AES128_CBC
-const struct dropbear_cipher dropbear_aes128 =
+static const struct dropbear_cipher dropbear_aes128 =
 	{&aes_desc, 16, 16};
 #endif
 #ifdef DROPBEAR_BLOWFISH_CBC
-const struct dropbear_cipher dropbear_blowfish =
+static const struct dropbear_cipher dropbear_blowfish =
 	{&blowfish_desc, 16, 8};
 #endif
+#ifdef DROPBEAR_TWOFISH256_CBC
+static const struct dropbear_cipher dropbear_twofish256 =
+	{&twofish_desc, 32, 16};
+#endif
 #ifdef DROPBEAR_TWOFISH128_CBC
-const struct dropbear_cipher dropbear_twofish128 =
+static const struct dropbear_cipher dropbear_twofish128 =
 	{&twofish_desc, 16, 16};
 #endif
 #ifdef DROPBEAR_3DES_CBC
-const struct dropbear_cipher dropbear_3des =
+static const struct dropbear_cipher dropbear_3des =
 	{&des3_desc, 24, 8};
 #endif

@@ -57,11 +65,15 @@ const struct dropbear_cipher dropbear_no
    {&hash_desc, keysize, hashsize} */

 #ifdef DROPBEAR_SHA1_HMAC
-const struct dropbear_hash dropbear_sha1 =
+static const struct dropbear_hash dropbear_sha1 =
 	{&sha1_desc, 20, 20};
 #endif
+#ifdef DROPBEAR_SHA1_96_HMAC
+static const struct dropbear_hash dropbear_sha1_96 =
+	{&sha1_desc, 20, 12};
+#endif
 #ifdef DROPBEAR_MD5_HMAC
-const struct dropbear_hash dropbear_md5 =
+static const struct dropbear_hash dropbear_md5 =
 	{&md5_desc, 16, 16};
 #endif

@@ -75,19 +87,29 @@ algo_type sshciphers[] = {
 #ifdef DROPBEAR_AES128_CBC
 	{"aes128-cbc", 0, (void*)&dropbear_aes128, 1},
 #endif
-#ifdef DROPBEAR_BLOWFISH_CBC
-	{"blowfish-cbc", 0, (void*)&dropbear_blowfish, 1},
+#ifdef DROPBEAR_3DES_CBC
+	{"3des-cbc", 0, (void*)&dropbear_3des, 1},
 #endif
+#ifdef DROPBEAR_AES256_CBC
+	{"aes256-cbc", 0, (void*)&dropbear_aes256, 1},
+#endif
+#ifdef DROPBEAR_TWOFISH256_CBC
+	{"twofish256-cbc", 0, (void*)&dropbear_twofish256, 1},
+	{"twofish-cbc", 0, (void*)&dropbear_twofish256, 1},
+#endif
 #ifdef DROPBEAR_TWOFISH128_CBC
-	{"twofish-cbc", 0, (void*)&dropbear_twofish128, 1},
+	{"twofish128-cbc", 0, (void*)&dropbear_twofish128, 1},
 #endif
-#ifdef DROPBEAR_3DES_CBC
-	{"3des-cbc", 0, (void*)&dropbear_3des, 1},
+#ifdef DROPBEAR_BLOWFISH_CBC
+	{"blowfish-cbc", 0, (void*)&dropbear_blowfish, 1},
 #endif
 	{NULL, 0, NULL, 0}
 };

 algo_type sshhashes[] = {
+#ifdef DROPBEAR_SHA1_96_HMAC
+	{"hmac-sha1-96", 0, (void*)&dropbear_sha1_96, 1},
+#endif
 #ifdef DROPBEAR_SHA1_HMAC
 	{"hmac-sha1", 0, (void*)&dropbear_sha1, 1},
 #endif
@@ -98,10 +120,10 @@ algo_type sshcompress[] = {
 };

 algo_type sshcompress[] = {
-	{"none", DROPBEAR_COMP_NONE, NULL, 1},
 #ifndef DISABLE_ZLIB
 	{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1},
 #endif
+	{"none", DROPBEAR_COMP_NONE, NULL, 1},
 	{NULL, 0, NULL, 0}
 };

@@ -126,13 +148,13 @@ void crypto_init() {
 void crypto_init() {

 	const struct ltc_cipher_descriptor *regciphers[] = {
-#ifdef DROPBEAR_AES128_CBC
+#ifdef DROPBEAR_AES_CBC
 		&aes_desc,
 #endif
 #ifdef DROPBEAR_BLOWFISH_CBC
 		&blowfish_desc,
 #endif
-#ifdef DROPBEAR_TWOFISH128_CBC
+#ifdef DROPBEAR_TWOFISH_CBC
 		&twofish_desc,
 #endif
 #ifdef DROPBEAR_3DES_CBC
@@ -187,21 +209,20 @@ void buf_put_algolist(buffer * buf, algo
 /* Output a comma separated list of algorithms to a buffer */
 void buf_put_algolist(buffer * buf, algo_type localalgos[]) {

-	unsigned int pos = 0, i, len;
-	char str[50]; /* enough for local algo storage */
+	unsigned int i, len;
+	unsigned int donefirst = 0;
+	buffer *algolist = NULL;

+	algolist = buf_new(100);
 	for (i = 0; localalgos[i].name != NULL; i++) {
 		if (localalgos[i].usable) {
-			/* Avoid generating a trailing comma */
-			if (pos)
-			    str[pos++] = ',';
+			if (donefirst)
+				buf_putbyte(algolist, ',');
+			donefirst = 1;
 			len = strlen(localalgos[i].name);
-			memcpy(&str[pos], localalgos[i].name, len);
-			pos += len;
+			buf_putbytes(algolist, localalgos[i].name, len);
 		}
 	}
-	str[pos]=0;
-	/* Debug this */
-	TRACE(("buf_put_algolist: %s", str))
-	buf_putstring(buf, str, pos);
+	buf_putstring(buf, algolist->data, algolist->len);
+	buf_free(algolist);
 }
============================================================
--- common-channel.c	522b3539ac8ac8feb32efd8c3e7a0490e51b29c3
+++ common-channel.c	97ae918fb65d16853d0994b99fa1961d1c47fd32
@@ -52,8 +52,8 @@ static void checkclose(struct Channel *c
 static void checkinitdone(struct Channel *channel);
 static void checkclose(struct Channel *channel);

-static void closeinfd(struct Channel * channel);
-static void closeoutfd(struct Channel * channel, int fd);
+static void closewritefd(struct Channel * channel);
+static void closereadfd(struct Channel * channel, int fd);
 static void closechanfd(struct Channel *channel, int fd, int how);

 #define FD_UNINIT (-2)
@@ -143,10 +143,11 @@ struct Channel* newchannel(unsigned int
 	newchan->transmaxpacket = transmaxpacket;

 	newchan->typedata = NULL;
-	newchan->infd = FD_UNINIT;
-	newchan->outfd = FD_UNINIT;
+	newchan->writefd = FD_UNINIT;
+	newchan->readfd = FD_UNINIT;
 	newchan->errfd = FD_CLOSED; /* this isn't always set to start with */
 	newchan->initconn = 0;
+	newchan->await_open = 0;

 	newchan->writebuf = cbuf_new(RECV_MAXWINDOW);
 	newchan->extrabuf = NULL; /* The user code can set it up */
@@ -176,7 +177,7 @@ struct Channel* getchannel() {
 }

 /* Iterate through the channels, performing IO if available */
-void channelio(fd_set *readfd, fd_set *writefd) {
+void channelio(fd_set *readfds, fd_set *writefds) {

 	struct Channel *channel;
 	unsigned int i;
@@ -191,21 +192,21 @@ void channelio(fd_set *readfd, fd_set *w
 			continue;
 		}

-		/* read from program/pipe stdout */
-		if (channel->outfd >= 0 && FD_ISSET(channel->outfd, readfd)) {
+		/* read data and send it over the wire */
+		if (channel->readfd >= 0 && FD_ISSET(channel->readfd, readfds)) {
 			send_msg_channel_data(channel, 0, 0);
 		}

-		/* read from program/pipe stderr */
+		/* read stderr data and send it over the wire */
 		if (channel->extrabuf == NULL &&
-				channel->errfd >= 0 && FD_ISSET(channel->errfd, readfd)) {
+				channel->errfd >= 0 && FD_ISSET(channel->errfd, readfds)) {
 				send_msg_channel_data(channel, 1, SSH_EXTENDED_DATA_STDERR);
 		}

-		/* if we can read from the infd, it might be closed, so we try to
+		/* if we can read from the writefd, it might be closed, so we try to
 		 * see if it has errors */
-		if (channel->infd >= 0 && channel->infd != channel->outfd
-				&& FD_ISSET(channel->infd, readfd)) {
+		if (channel->writefd >= 0 && channel->writefd != channel->readfd
+				&& FD_ISSET(channel->writefd, readfds)) {
 			if (channel->initconn) {
 				/* Handling for "in progress" connection - this is needed
 				 * to avoid spinning 100% CPU when we connect to a server
@@ -214,25 +215,25 @@ void channelio(fd_set *readfd, fd_set *w
 				continue; /* Important not to use the channel after
 							 checkinitdone(), as it may be NULL */
 			}
-			ret = write(channel->infd, NULL, 0); /* Fake write */
+			ret = write(channel->writefd, NULL, 0); /* Fake write */
 			if (ret < 0 && errno != EINTR && errno != EAGAIN) {
-				closeinfd(channel);
+				closewritefd(channel);
 			}
 		}

 		/* write to program/pipe stdin */
-		if (channel->infd >= 0 && FD_ISSET(channel->infd, writefd)) {
+		if (channel->writefd >= 0 && FD_ISSET(channel->writefd, writefds)) {
 			if (channel->initconn) {
 				checkinitdone(channel);
 				continue; /* Important not to use the channel after
 							 checkinitdone(), as it may be NULL */
 			}
-			writechannel(channel, channel->infd, channel->writebuf);
+			writechannel(channel, channel->writefd, channel->writebuf);
 		}

 		/* stderr for client mode */
 		if (channel->extrabuf != NULL
-				&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefd)) {
+				&& channel->errfd >= 0 && FD_ISSET(channel->errfd, writefds)) {
 			writechannel(channel, channel->errfd, channel->extrabuf);
 		}

@@ -243,7 +244,7 @@ void channelio(fd_set *readfd, fd_set *w

 	/* Listeners such as TCP, X11, agent-auth */
 #ifdef USING_LISTENERS
-	handle_listeners(readfd);
+	handle_listeners(readfds);
 #endif
 }

@@ -251,8 +252,8 @@ static void checkclose(struct Channel *c
 /* do all the EOF/close type stuff checking for a channel */
 static void checkclose(struct Channel *channel) {

-	TRACE(("checkclose: infd %d, outfd %d, errfd %d, sentclosed %d, recvclosed %d",
-				channel->infd, channel->outfd,
+	TRACE(("checkclose: writefd %d, readfd %d, errfd %d, sentclosed %d, recvclosed %d",
+				channel->writefd, channel->readfd,
 				channel->errfd, channel->sentclosed, channel->recvclosed))
 	TRACE(("writebuf %d extrabuf %s extrabuf %d",
 				cbuf_getused(channel->writebuf),
@@ -265,18 +266,18 @@ static void checkclose(struct Channel *c
 		 * if the shell has exited etc */
 		if (channel->type->checkclose) {
 			if (channel->type->checkclose(channel)) {
-				closeinfd(channel);
+				closewritefd(channel);
 			}
 		}

 		if (!channel->senteof
-			&& channel->outfd == FD_CLOSED
+			&& channel->readfd == FD_CLOSED
 			&& (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
 			send_msg_channel_eof(channel);
 		}

-		if (channel->infd == FD_CLOSED
-			&& channel->outfd == FD_CLOSED
+		if (channel->writefd == FD_CLOSED
+			&& channel->readfd == FD_CLOSED
 			&& (channel->extrabuf != NULL || channel->errfd == FD_CLOSED)) {
 			send_msg_channel_close(channel);
 		}
@@ -313,17 +314,17 @@ static void checkinitdone(struct Channel

 	TRACE(("enter checkinitdone"))

-	if (getsockopt(channel->infd, SOL_SOCKET, SO_ERROR, &val, &vallen)
+	if (getsockopt(channel->writefd, SOL_SOCKET, SO_ERROR, &val, &vallen)
 			|| val != 0) {
 		send_msg_channel_open_failure(channel->remotechan,
 				SSH_OPEN_CONNECT_FAILED, "", "");
-		close(channel->infd);
+		close(channel->writefd);
 		deletechannel(channel);
 		TRACE(("leave checkinitdone: fail"))
 	} else {
 		send_msg_channel_open_confirmation(channel, channel->recvwindow,
 				channel->recvmaxpacket);
-		channel->outfd = channel->infd;
+		channel->readfd = channel->writefd;
 		channel->initconn = 0;
 		TRACE(("leave checkinitdone: success"))
 	}
@@ -385,7 +386,7 @@ static void writechannel(struct Channel*
 		if (len < 0 && errno != EINTR) {
 			/* no more to write - we close it even if the fd was stderr, since
 			 * that's a nasty failure too */
-			closeinfd(channel);
+			closewritefd(channel);
 		}
 		TRACE(("leave writechannel: len <= 0"))
 		return;
@@ -394,9 +395,9 @@ static void writechannel(struct Channel*
 	cbuf_incrread(cbuf, len);
 	channel->recvdonelen += len;

-	if (fd == channel->infd && len == maxlen && channel->recveof) {
+	if (fd == channel->writefd && len == maxlen && channel->recveof) {
 		/* Check if we're closing up */
-		closeinfd(channel);
+		closewritefd(channel);
 		TRACE(("leave writechannel: recveof set"))
 		return;
 	}
@@ -409,9 +410,9 @@ static void writechannel(struct Channel*
 		channel->recvdonelen = 0;
 	}

-	assert(channel->recvwindow <= RECV_MAXWINDOW);
-	assert(channel->recvwindow <= cbuf_getavail(channel->writebuf));
-	assert(channel->extrabuf == NULL ||
+	dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW);
+	dropbear_assert(channel->recvwindow <= cbuf_getavail(channel->writebuf));
+	dropbear_assert(channel->extrabuf == NULL ||
 			channel->recvwindow <= cbuf_getavail(channel->extrabuf));


@@ -420,7 +421,7 @@ static void writechannel(struct Channel*

 /* Set the file descriptors for the main select in session.c
  * This avoid channels which don't have any window available, are closed, etc*/
-void setchannelfds(fd_set *readfd, fd_set *writefd) {
+void setchannelfds(fd_set *readfds, fd_set *writefds) {

 	unsigned int i;
 	struct Channel * channel;
@@ -435,41 +436,41 @@ void setchannelfds(fd_set *readfd, fd_se
 		/* Stuff to put over the wire */
 		if (channel->transwindow > 0) {

-			if (channel->outfd >= 0) {
-				FD_SET(channel->outfd, readfd);
+			if (channel->readfd >= 0) {
+				FD_SET(channel->readfd, readfds);
 			}

 			if (channel->extrabuf == NULL && channel->errfd >= 0) {
-					FD_SET(channel->errfd, readfd);
+					FD_SET(channel->errfd, readfds);
 			}
 		}

 		/* For checking FD status (ie closure etc) - we don't actually
-		 * read data from infd */
-		TRACE(("infd = %d, outfd %d, errfd %d, bufused %d",
-					channel->infd, channel->outfd,
+		 * read data from writefd */
+		TRACE(("writefd = %d, readfd %d, errfd %d, bufused %d",
+					channel->writefd, channel->readfd,
 					channel->errfd,
 					cbuf_getused(channel->writebuf) ))
-		if (channel->infd >= 0 && channel->infd != channel->outfd) {
-			FD_SET(channel->infd, readfd);
+		if (channel->writefd >= 0 && channel->writefd != channel->readfd) {
+			FD_SET(channel->writefd, readfds);
 		}

 		/* Stuff from the wire, to local program/shell/user etc */
-		if ((channel->infd >= 0 && cbuf_getused(channel->writebuf) > 0 )
+		if ((channel->writefd >= 0 && cbuf_getused(channel->writebuf) > 0 )
 				|| channel->initconn) {

-				FD_SET(channel->infd, writefd);
+				FD_SET(channel->writefd, writefds);
 		}

 		if (channel->extrabuf != NULL && channel->errfd >= 0
 				&& cbuf_getused(channel->extrabuf) > 0 ) {
-				FD_SET(channel->errfd, writefd);
+				FD_SET(channel->errfd, writefds);
 		}

 	} /* foreach channel */

 #ifdef USING_LISTENERS
-	set_listener_fds(readfd);
+	set_listener_fds(readfds);
 #endif

 }
@@ -492,7 +493,7 @@ void recv_msg_channel_eof() {
 	if (cbuf_getused(channel->writebuf) == 0
 			&& (channel->extrabuf == NULL
 					|| cbuf_getused(channel->extrabuf) == 0)) {
-		closeinfd(channel);
+		closewritefd(channel);
 	}

 	TRACE(("leave recv_msg_channel_eof"))
@@ -540,8 +541,8 @@ static void removechannel(struct Channel

 	/* close the FDs in case they haven't been done
 	 * yet (ie they were shutdown etc */
-	close(channel->infd);
-	close(channel->outfd);
+	close(channel->writefd);
+	close(channel->readfd);
 	close(channel->errfd);

 	channel->typedata = NULL;
@@ -603,14 +604,14 @@ static void send_msg_channel_data(struct

 	CHECKCLEARTOWRITE();

-	assert(!channel->sentclosed);
+	dropbear_assert(!channel->sentclosed);

 	if (isextended) {
 		fd = channel->errfd;
 	} else {
-		fd = channel->outfd;
+		fd = channel->readfd;
 	}
-	assert(fd >= 0);
+	dropbear_assert(fd >= 0);

 	maxlen = MIN(channel->transwindow, channel->transmaxpacket);
 	/* -(1+4+4) is SSH_MSG_CHANNEL_DATA, channel number, string length, and
@@ -630,7 +631,7 @@ static void send_msg_channel_data(struct
 	if (len <= 0) {
 		/* on error/eof, send eof */
 		if (len == 0 || errno != EINTR) {
-			closeoutfd(channel, fd);
+			closereadfd(channel, fd);
 		}
 		buf_free(buf);
 		buf = NULL;
@@ -668,7 +669,7 @@ void recv_msg_channel_data() {
 		dropbear_exit("Unknown channel");
 	}

-	common_recv_msg_channel_data(channel, channel->infd, channel->writebuf);
+	common_recv_msg_channel_data(channel, channel->writefd, channel->writebuf);
 }

 /* Shared for data and stderr data - when we receive data, put it in a buffer
@@ -688,7 +689,7 @@ void common_recv_msg_channel_data(struct
 	}

  	if (fd < 0) {
-		dropbear_exit("received data with bad infd");
+		dropbear_exit("received data with bad writefd");
 	}

 	datalen = buf_getint(ses.payload);
@@ -718,9 +719,9 @@ void common_recv_msg_channel_data(struct
 		len -= buflen;
 	}

-	assert(channel->recvwindow >= datalen);
+	dropbear_assert(channel->recvwindow >= datalen);
 	channel->recvwindow -= datalen;
-	assert(channel->recvwindow <= RECV_MAXWINDOW);
+	dropbear_assert(channel->recvwindow <= RECV_MAXWINDOW);

 	TRACE(("leave recv_msg_channel_data"))
 }
@@ -930,9 +931,11 @@ int send_msg_channel_open_init(int fd, c
 	/* set fd non-blocking */
 	setnonblocking(fd);

-	chan->infd = chan->outfd = fd;
+	chan->writefd = chan->readfd = fd;
 	ses.maxfd = MAX(ses.maxfd, fd);

+	chan->await_open = 1;
+
 	/* now open the channel connection */
 	CHECKCLEARTOWRITE();

@@ -960,6 +963,11 @@ void recv_msg_channel_open_confirmation(
 		dropbear_exit("Unknown channel");
 	}

+	if (!channel->await_open) {
+		dropbear_exit("unexpected channel reply");
+	}
+	channel->await_open = 0;
+
 	channel->remotechan =  buf_getint(ses.payload);
 	channel->transwindow = buf_getint(ses.payload);
 	channel->transmaxpacket = buf_getint(ses.payload);
@@ -990,26 +998,31 @@ void recv_msg_channel_open_failure() {
 		dropbear_exit("Unknown channel");
 	}

+	if (!channel->await_open) {
+		dropbear_exit("unexpected channel reply");
+	}
+	channel->await_open = 0;
+
 	removechannel(channel);
 }
 #endif /* USING_LISTENERS */

 /* close a stdout/stderr fd */
-static void closeoutfd(struct Channel * channel, int fd) {
+static void closereadfd(struct Channel * channel, int fd) {

-	/* don't close it if it is the same as infd,
-	 * unless infd is already set -1 */
-	TRACE(("enter closeoutfd"))
+	/* don't close it if it is the same as writefd,
+	 * unless writefd is already set -1 */
+	TRACE(("enter closereadfd"))
 	closechanfd(channel, fd, 0);
-	TRACE(("leave closeoutfd"))
+	TRACE(("leave closereadfd"))
 }

 /* close a stdin fd */
-static void closeinfd(struct Channel * channel) {
+static void closewritefd(struct Channel * channel) {

-	TRACE(("enter closeinfd"))
-	closechanfd(channel, channel->infd, 1);
-	TRACE(("leave closeinfd"))
+	TRACE(("enter closewritefd"))
+	closechanfd(channel, channel->writefd, 1);
+	TRACE(("leave closewritefd"))
 }

 /* close a fd, how is 0 for stdout/stderr, 1 for stdin */
@@ -1031,15 +1044,15 @@ static void closechanfd(struct Channel *
 		closein = closeout = 1;
 	}

-	if (closeout && fd == channel->outfd) {
-		channel->outfd = FD_CLOSED;
+	if (closeout && fd == channel->readfd) {
+		channel->readfd = FD_CLOSED;
 	}
 	if (closeout && (channel->extrabuf == NULL) && (fd == channel->errfd)) {
 		channel->errfd = FD_CLOSED;
 	}

-	if (closein && fd == channel->infd) {
-		channel->infd = FD_CLOSED;
+	if (closein && fd == channel->writefd) {
+		channel->writefd = FD_CLOSED;
 	}
 	if (closein && (channel->extrabuf != NULL) && (fd == channel->errfd)) {
 		channel->errfd = FD_CLOSED;
@@ -1047,8 +1060,8 @@ static void closechanfd(struct Channel *

 	/* if we called shutdown on it and all references are gone, then we
 	 * need to close() it to stop it lingering */
-	if (channel->type->sepfds && channel->outfd == FD_CLOSED
-		&& channel->infd == FD_CLOSED && channel->errfd == FD_CLOSED) {
+	if (channel->type->sepfds && channel->readfd == FD_CLOSED
+		&& channel->writefd == FD_CLOSED && channel->errfd == FD_CLOSED) {
 		close(fd);
 	}
 }
============================================================
--- common-kex.c	960bfda8deaa204b5866f14647f3cd86d262d93f
+++ common-kex.c	b06f424fe466a6d3ecfb7072d59457ebb39c8780
@@ -35,7 +35,7 @@
 #include "random.h"

 /* diffie-hellman-group1-sha1 value for p */
-const unsigned char dh_p_val[] = {
+static const unsigned char dh_p_val[] = {
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,
     0x21, 0x68, 0xC2, 0x34, 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
 	0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, 0x02, 0x0B, 0xBE, 0xA6,
@@ -47,8 +47,9 @@ const unsigned char dh_p_val[] = {
 	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
 	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
 	0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
+#define DH_P_LEN sizeof(dh_p_val)

-const int DH_G_VAL = 2;
+static const int DH_G_VAL = 2;

 static void kexinitialise();
 void gen_new_keys();
@@ -393,19 +394,29 @@ void recv_msg_kexinit() {
 /* Belongs in common_kex.c where it should be moved after review */
 void recv_msg_kexinit() {

+	unsigned int kexhashbuf_len = 0;
+	unsigned int remote_ident_len = 0;
+	unsigned int local_ident_len = 0;
+
 	TRACE(("<- KEXINIT"))
 	TRACE(("enter recv_msg_kexinit"))

-	/* start the kex hash */
-	ses.kexhashbuf = buf_new(MAX_KEXHASHBUF);
-
 	if (!ses.kexstate.sentkexinit) {
 		/* we need to send a kex packet */
 		send_msg_kexinit();
 		TRACE(("continue recv_msg_kexinit: sent kexinit"))
 	}

+	/* start the kex hash */
+	local_ident_len = strlen(LOCAL_IDENT);
+	remote_ident_len = strlen((char*)ses.remoteident);

+	kexhashbuf_len = local_ident_len + remote_ident_len
+		+ ses.transkexinit->len + ses.payload->len
+		+ KEXHASHBUF_MAX_INTS;
+
+	ses.kexhashbuf = buf_new(kexhashbuf_len);
+
 	if (IS_DROPBEAR_CLIENT) {

 		/* read the peer's choice of algos */
@@ -413,20 +424,16 @@ void recv_msg_kexinit() {

 		/* V_C, the client's version string (CR and NL excluded) */
 	    buf_putstring(ses.kexhashbuf,
-			(unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT));
+			(unsigned char*)LOCAL_IDENT, local_ident_len);
 		/* V_S, the server's version string (CR and NL excluded) */
-	    buf_putstring(ses.kexhashbuf,
-			ses.remoteident, strlen((char*)ses.remoteident));
+	    buf_putstring(ses.kexhashbuf, ses.remoteident, remote_ident_len);

 		/* I_C, the payload of the client's SSH_MSG_KEXINIT */
 	    buf_putstring(ses.kexhashbuf,
-			buf_getptr(ses.transkexinit, ses.transkexinit->len),
-			ses.transkexinit->len);
+			ses.transkexinit->data, ses.transkexinit->len);
 		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
 	    buf_setpos(ses.payload, 0);
-	    buf_putstring(ses.kexhashbuf,
-			buf_getptr(ses.payload, ses.payload->len),
-			ses.payload->len);
+	    buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);

 	} else {
 		/* SERVER */
@@ -434,21 +441,19 @@ void recv_msg_kexinit() {
 		/* read the peer's choice of algos */
 		read_kex_algos();
 		/* V_C, the client's version string (CR and NL excluded) */
-	    buf_putstring(ses.kexhashbuf,
-			ses.remoteident, strlen((char*)ses.remoteident));
+	    buf_putstring(ses.kexhashbuf, ses.remoteident, remote_ident_len);
 		/* V_S, the server's version string (CR and NL excluded) */
-	    buf_putstring(ses.kexhashbuf,
-			(unsigned char*)LOCAL_IDENT, strlen(LOCAL_IDENT));
+	    buf_putstring(ses.kexhashbuf,
+				(unsigned char*)LOCAL_IDENT, local_ident_len);

 		/* I_C, the payload of the client's SSH_MSG_KEXINIT */
 	    buf_setpos(ses.payload, 0);
-	    buf_putstring(ses.kexhashbuf,
-			buf_getptr(ses.payload, ses.payload->len),
-			ses.payload->len);
+	    buf_putstring(ses.kexhashbuf, ses.payload->data, ses.payload->len);
+
 		/* I_S, the payload of the server's SSH_MSG_KEXINIT */
 	    buf_putstring(ses.kexhashbuf,
-			buf_getptr(ses.transkexinit, ses.transkexinit->len),
-			ses.transkexinit->len);
+			ses.transkexinit->data, ses.transkexinit->len);
+
 		ses.requirenext = SSH_MSG_KEXDH_INIT;
 	}

@@ -621,7 +626,7 @@ static void read_kex_algos() {
 		erralgo = "enc c->s";
 		goto error;
 	}
-	TRACE(("c2s is  %s", c2s_cipher_algo->name))
+	TRACE(("enc c2s is  %s", c2s_cipher_algo->name))

 	/* encryption_algorithms_server_to_client */
 	s2c_cipher_algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
@@ -629,7 +634,7 @@ static void read_kex_algos() {
 		erralgo = "enc s->c";
 		goto error;
 	}
-	TRACE(("s2c is  %s", s2c_cipher_algo->name))
+	TRACE(("enc s2c is  %s", s2c_cipher_algo->name))

 	/* mac_algorithms_client_to_server */
 	c2s_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
@@ -637,6 +642,7 @@ static void read_kex_algos() {
 		erralgo = "mac c->s";
 		goto error;
 	}
+	TRACE(("hash c2s is  %s", c2s_hash_algo->name))

 	/* mac_algorithms_server_to_client */
 	s2c_hash_algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
@@ -644,6 +650,7 @@ static void read_kex_algos() {
 		erralgo = "mac s->c";
 		goto error;
 	}
+	TRACE(("hash s2c is  %s", s2c_hash_algo->name))

 	/* compression_algorithms_client_to_server */
 	c2s_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
@@ -651,6 +658,7 @@ static void read_kex_algos() {
 		erralgo = "comp c->s";
 		goto error;
 	}
+	TRACE(("hash c2s is  %s", c2s_comp_algo->name))

 	/* compression_algorithms_server_to_client */
 	s2c_comp_algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
@@ -658,6 +666,7 @@ static void read_kex_algos() {
 		erralgo = "comp s->c";
 		goto error;
 	}
+	TRACE(("hash s2c is  %s", s2c_comp_algo->name))

 	/* languages_client_to_server */
 	buf_eatstring(ses.payload);
@@ -700,13 +709,6 @@ static void read_kex_algos() {
 		ses.newkeys->trans_algo_comp = s2c_comp_algo->val;
 	}

-	TRACE(("enc algo recv %s", algo->name))
-	TRACE(("enc algo trans %s", algo->name))
-	TRACE(("mac algo recv %s", algo->name))
-	TRACE(("mac algo trans %s", algo->name))
-	TRACE(("comp algo recv %s", algo->name))
-	TRACE(("comp algo trans %s", algo->name))
-
 	/* reserved for future extensions */
 	buf_getint(ses.payload);
 	return;
============================================================
--- common-session.c	c141f714affb71abd896785b986c44fdfe98a357
+++ common-session.c	1264a49e05001b96e621efa4abf6f9e77402ed71
@@ -63,7 +63,6 @@ void common_session_init(int sock, char*
 	ses.connecttimeout = 0;

 	kexfirstinitialise(); /* initialise the kex state */
-	chaninitialise(); /* initialise the channel state */

 	ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
 	ses.transseq = 0;
@@ -131,7 +130,7 @@ void session_loop(void(*loophandler)())
 		timeout.tv_usec = 0;
 		FD_ZERO(&writefd);
 		FD_ZERO(&readfd);
-		assert(ses.payload == NULL);
+		dropbear_assert(ses.payload == NULL);
 		if (ses.sock != -1) {
 			FD_SET(ses.sock, &readfd);
 			if (!isempty(&ses.writequeue)) {
============================================================
--- configure.in	2b2625caa07530f2fa625a0eee5d6e654f167dba
+++ configure.in	efc775ba6fb049596194158e06479c2a29a0e165
@@ -206,7 +206,7 @@ AC_HEADER_SYS_WAIT
 # Checks for header files.
 AC_HEADER_STDC
 AC_HEADER_SYS_WAIT
-AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h netinet/tcp.h stdlib.h string.h sys/socket.h sys/time.h termios.h unistd.h crypt.h pty.h ioctl.h libutil.h libgen.h inttypes.h stropts.h utmp.h utmpx.h lastlog.h paths.h util.h netdb.h security/pam_appl.h pam/pam_appl.h])
+AC_CHECK_HEADERS([fcntl.h limits.h netinet/in.h netinet/tcp.h stdlib.h string.h sys/socket.h sys/time.h termios.h unistd.h crypt.h pty.h ioctl.h libutil.h libgen.h inttypes.h stropts.h utmp.h utmpx.h lastlog.h paths.h util.h netdb.h security/pam_appl.h pam/pam_appl.h netinet/in_systm.h])

 # Checks for typedefs, structures, and compiler characteristics.
 AC_C_CONST
============================================================
--- dbclient.1	aed09b6b3d241203fd11a777e5fae635066d2dfc
+++ dbclient.1	2ae47b2e32dbafef9abf583844711c265e418619
@@ -59,6 +59,11 @@ Don't allocate a pty.
 .TP
 .B \-T
 Don't allocate a pty.
+.TP
+.B \-g
+Allow non-local hosts to connect to forwarded ports. Applies to -L and -R
+forwarded ports, though remote connections to -R forwarded ports may be limited
+by the ssh server.
 .SH AUTHOR
 Matt Johnston (matt@ucc.asn.au).
 .br
============================================================
--- dbutil.c	9440c0ae5ca45076f63b0f295585bfaeb82dd57c
+++ dbutil.c	bcd256f4bdc8183dfd6b813375c39af6513b183d
@@ -110,6 +110,10 @@ static void generic_dropbear_exit(int ex
 	exit(exitcode);
 }

+void fail_assert(const char* expr, const char* file, int line) {
+	dropbear_exit("failed assertion (%s:%d): `%s'", file, line, expr);
+}
+
 static void generic_dropbear_log(int UNUSED(priority), const char* format,
 		va_list param) {

@@ -149,8 +153,33 @@ void dropbear_trace(const char* format,
 }
 #endif /* DEBUG_TRACE */

-/* Listen on address:port. Unless address is NULL, in which case listen on
- * everything. If called with address == "", we'll listen on localhost/loopback.
+static void set_sock_priority(int sock) {
+
+	int val;
+
+	/* disable nagle */
+	val = 1;
+	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
+
+	/* set the TOS bit. note that this will fail for ipv6, I can't find any
+	 * equivalent. */
+#ifdef IPTOS_LOWDELAY
+	val = IPTOS_LOWDELAY;
+	setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&val, sizeof(val));
+#endif
+
+#ifdef SO_PRIORITY
+	/* linux specific, sets QoS class.
+	 * 6 looks to be optimal for interactive traffic (see tc-prio(8) ). */
+	val = 6;
+	setsockopt(sock, SOL_SOCKET, SO_PRIORITY, (void*) &val, sizeof(val));
+#endif
+
+}
+
+/* Listen on address:port.
+ * Special cases are address of "" listening on everything,
+ * and address of NULL listening on localhost only.
  * Returns the number of sockets bound on success, or -1 on failure. On
  * failure, if errstring wasn't NULL, it'll be a newly malloced error
  * string.*/
@@ -170,11 +199,17 @@ int dropbear_listen(const char* address,
 	hints.ai_family = AF_UNSPEC; /* TODO: let them flag v4 only etc */
 	hints.ai_socktype = SOCK_STREAM;

-	if (address && address[0] == '\0') {
+	// for calling getaddrinfo:
+	// address == NULL and !AI_PASSIVE: local loopback
+	// address == NULL and AI_PASSIVE: all interfaces
+	// address != NULL: whatever the address says
+	if (!address) {
 		TRACE(("dropbear_listen: local loopback"))
-		address = NULL;
 	} else {
-		TRACE(("dropbear_listen: not local loopback"))
+		if (address[0] == '\0') {
+			TRACE(("dropbear_listen: all interfaces"))
+			address = NULL;
+		}
 		hints.ai_flags = AI_PASSIVE;
 	}
 	err = getaddrinfo(address, port, &hints, &res0);
@@ -186,6 +221,10 @@ int dropbear_listen(const char* address,
 			*errstring = (char*)m_malloc(len);
 			snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
 		}
+		if (res0) {
+			freeaddrinfo(res0);
+			res0 = NULL;
+		}
 		TRACE(("leave dropbear_listen: failed resolving"))
 		return -1;
 	}
@@ -215,8 +254,7 @@ int dropbear_listen(const char* address,
 		linger.l_linger = 5;
 		setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger));

-		/* disable nagle */
-		setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&val, sizeof(val));
+		set_sock_priority(sock);

 		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
 			err = errno;
@@ -237,6 +275,11 @@ int dropbear_listen(const char* address,
 		nsock++;
 	}

+	if (res0) {
+		freeaddrinfo(res0);
+		res0 = NULL;
+	}
+
 	if (nsock == 0) {
 		if (errstring != NULL && *errstring == NULL) {
 			int len;
@@ -334,8 +377,7 @@ int connect_remote(const char* remotehos
 		TRACE(("Error connecting: %s", strerror(err)))
 	} else {
 		/* Success */
-		/* (err is used as a dummy var here) */
-		setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (void*)&err, sizeof(err));
+		set_sock_priority(sock);
 	}

 	freeaddrinfo(res0);
============================================================
--- dbutil.h	3806a1758a9a15ee7a1e3b75e3a49c0d27a7343a
+++ dbutil.h	76eb992fc016821fd1584982e59abe7ac66c5671
@@ -39,6 +39,7 @@ void dropbear_log(int priority, const ch
 void dropbear_exit(const char* format, ...);
 void dropbear_close(const char* format, ...);
 void dropbear_log(int priority, const char* format, ...);
+void fail_assert(const char* expr, const char* file, int line);
 #ifdef DEBUG_TRACE
 void dropbear_trace(const char* format, ...);
 void printhex(const char * label, const unsigned char * buf, int len);
@@ -66,4 +67,7 @@ void setnonblocking(int fd);
 /* Used to force mp_ints to be initialised */
 #define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL}

+/* Dropbear assertion */
+#define dropbear_assert(X) do { if (!(X)) { fail_assert(#X, __FILE__, __LINE__); } } while (0)
+
 #endif /* _DBUTIL_H_ */
============================================================
--- dropbear.8	8d1707f168fb2870937ffc24cb85e328328225ce
+++ dropbear.8	01a2e001c0e1dcbaa0105b5914c737744ae5c802
@@ -71,6 +71,9 @@ In program mode the \-F option is implie
 .B dropbear
 under TCP/IP servers like inetd, tcpsvd, or tcpserver.
 In program mode the \-F option is implied, and \-p options are ignored.
+.TP
+.B \-a
+Allow remote hosts to connect to forwarded ports.
 .SH AUTHOR
 Matt Johnston (matt@ucc.asn.au).
 .br
============================================================
--- dss.c	e72e0ab248024c0d858b55fdd382a414aeb31317
+++ dss.c	d58b7df483ca328e4102dbc59fab1cf85494263e
@@ -46,7 +46,7 @@ int buf_get_dss_pub_key(buffer* buf, dss
 int buf_get_dss_pub_key(buffer* buf, dss_key *key) {

 	TRACE(("enter buf_get_dss_pub_key"))
-	assert(key != NULL);
+	dropbear_assert(key != NULL);
 	key->p = m_malloc(sizeof(mp_int));
 	key->q = m_malloc(sizeof(mp_int));
 	key->g = m_malloc(sizeof(mp_int));
@@ -80,7 +80,7 @@ int buf_get_dss_priv_key(buffer* buf, ds

 	int ret = DROPBEAR_FAILURE;

-	assert(key != NULL);
+	dropbear_assert(key != NULL);

 	ret = buf_get_dss_pub_key(buf, key);
 	if (ret == DROPBEAR_FAILURE) {
@@ -137,7 +137,7 @@ void buf_put_dss_pub_key(buffer* buf, ds
  */
 void buf_put_dss_pub_key(buffer* buf, dss_key *key) {

-	assert(key != NULL);
+	dropbear_assert(key != NULL);
 	buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
 	buf_putmpint(buf, key->p);
 	buf_putmpint(buf, key->q);
@@ -149,7 +149,7 @@ void buf_put_dss_priv_key(buffer* buf, d
 /* Same as buf_put_dss_pub_key, but with the private "x" key appended */
 void buf_put_dss_priv_key(buffer* buf, dss_key *key) {

-	assert(key != NULL);
+	dropbear_assert(key != NULL);
 	buf_put_dss_pub_key(buf, key);
 	buf_putmpint(buf, key->x);

@@ -172,7 +172,7 @@ int buf_dss_verify(buffer* buf, dss_key
 	int stringlen;

 	TRACE(("enter buf_dss_verify"))
-	assert(key != NULL);
+	dropbear_assert(key != NULL);

 	m_mp_init_multi(&val1, &val2, &val3, &val4, NULL);

@@ -310,7 +310,7 @@ void buf_put_dss_sign(buffer* buf, dss_k
 	hash_state hs;

 	TRACE(("enter buf_put_dss_sign"))
-	assert(key != NULL);
+	dropbear_assert(key != NULL);

 	/* hash the data */
 	sha1_init(&hs);
@@ -380,7 +380,7 @@ void buf_put_dss_sign(buffer* buf, dss_k
 	buf_putint(buf, 2*SHA1_HASH_SIZE);

 	writelen = mp_unsigned_bin_size(&dss_r);
-	assert(writelen <= SHA1_HASH_SIZE);
+	dropbear_assert(writelen <= SHA1_HASH_SIZE);
 	/* need to pad to 160 bits with leading zeros */
 	for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) {
 		buf_putbyte(buf, 0);
@@ -393,7 +393,7 @@ void buf_put_dss_sign(buffer* buf, dss_k
 	buf_incrwritepos(buf, writelen);

 	writelen = mp_unsigned_bin_size(&dss_s);
-	assert(writelen <= SHA1_HASH_SIZE);
+	dropbear_assert(writelen <= SHA1_HASH_SIZE);
 	/* need to pad to 160 bits with leading zeros */
 	for (i = 0; i < SHA1_HASH_SIZE - writelen; i++) {
 		buf_putbyte(buf, 0);
============================================================
--- includes.h	cb0e389740b51fc33f98b8c29c9f2ffe7cd11bc2
+++ includes.h	9baa00e2da012281cecc528405cf06e48b53fda8
@@ -44,7 +44,6 @@
 #include <fcntl.h>
 #include <grp.h>
 #include <limits.h>
-#include <netinet/in.h>
 #include <pwd.h>
 #include <signal.h>
 #include <stdlib.h>
@@ -57,8 +56,6 @@
 #include <stdarg.h>
 #include <dirent.h>

-#include <arpa/inet.h>
-
 #ifdef HAVE_UTMP_H
 #include <utmp.h>
 #endif
@@ -75,10 +72,20 @@
 #include <lastlog.h>
 #endif

+#include <arpa/inet.h>
+
 #ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
 #endif

+/* netbsd 1.6 needs this to be included before netinet/ip.h for some
+ * undocumented reason */
+#ifdef HAVE_NETINET_IN_SYSTM_H
+#include <netinet/in_systm.h>
+#endif
+
+#include <netinet/ip.h>
+
 #ifdef HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>
 #endif
============================================================
--- kex.h	4aedb4c80abfbc1df32da43a4903b34e8cddc56a
+++ kex.h	596a833742cc5ab88d3930e46214f19e129347f2
@@ -42,11 +42,6 @@ void recv_msg_kexdh_reply(); /* client *
 void send_msg_kexdh_init(); /* client */
 void recv_msg_kexdh_reply(); /* client */

-extern const unsigned char dh_p_val[];
-#define DH_P_LEN 128 /* The length of the dh_p_val array */
-
-extern const int DH_G_VAL; /* == 2 */
-
 struct KEXState {

 	unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
============================================================
--- keyimport.c	38a61bdea3196db5ff2a57fd389236948142b4ef
+++ keyimport.c	4d6aa56819151b18d6828b1a9b1db0b9864e40b3
@@ -173,6 +173,8 @@ static int dropbear_write(const char*fil
 		buf_incrpos(buf, len);
 	} while (len > 0 && buf->len != buf->pos);

+	fclose(fp);
+
 	if (buf->pos != buf->len) {
 		ret = 0;
 	} else {
@@ -203,7 +205,7 @@ static void base64_encode_fp(FILE * fp,
 	unsigned long outlen;
 	int rawcpl;
 	rawcpl = cpl * 3 / 4;
-	assert((unsigned int)cpl < sizeof(out));
+	dropbear_assert((unsigned int)cpl < sizeof(out));

     while (datalen > 0) {
 		n = (datalen < rawcpl ? datalen : rawcpl);
@@ -714,7 +716,7 @@ static int openssh_write(const char *fil
 	}
 #endif

-	assert(keytype != -1);
+	dropbear_assert(keytype != -1);

 	/*
 	 * Fetch the key blobs.
@@ -913,7 +915,7 @@ static int openssh_write(const char *fil
 	 * with the same value. Those are all removed and the rest is
 	 * returned.
 	 */
-	assert(pos == len);
+	dropbear_assert(pos == len);
 	while (pos < outlen) {
 		outblob[pos++] = outlen - len;
 	}
@@ -1491,7 +1493,7 @@ sign_key *sshcom_read(const char *filena
 		privlen = pos - publen;
 	}

-	assert(privlen > 0);			   /* should have bombed by now if not */
+	dropbear_assert(privlen > 0);			   /* should have bombed by now if not */

 	retkey = snew(struct ssh2_userkey);
 	retkey->alg = alg;
@@ -1557,7 +1559,7 @@ int sshcom_write(const char *filename, s
 		pos += ssh2_read_mpint(privblob+pos, privlen-pos, &q);
 		pos += ssh2_read_mpint(privblob+pos, privlen-pos, &iqmp);

-		assert(e.start && iqmp.start); /* can't go wrong */
+		dropbear_assert(e.start && iqmp.start); /* can't go wrong */

 		numbers[0] = e;
 		numbers[1] = d;
@@ -1581,7 +1583,7 @@ int sshcom_write(const char *filename, s
 		pos = 0;
 		pos += ssh2_read_mpint(privblob+pos, privlen-pos, &x);

-		assert(y.start && x.start); /* can't go wrong */
+		dropbear_assert(y.start && x.start); /* can't go wrong */

 		numbers[0] = p;
 		numbers[1] = g;
@@ -1593,7 +1595,7 @@ int sshcom_write(const char *filename, s
 		initial_zero = 1;
 		type = "dl-modp{sign{dsa-nist-sha1},dh{plain}}";
 	} else {
-		assert(0);					 /* zoinks! */
+		dropbear_assert(0);					 /* zoinks! */
 	}

 	/*
@@ -1637,13 +1639,13 @@ int sshcom_write(const char *filename, s
 	}
 	ciphertext = (char *)outblob+lenpos+4;
 	cipherlen = pos - (lenpos+4);
-	assert(!passphrase || cipherlen % 8 == 0);
+	dropbear_assert(!passphrase || cipherlen % 8 == 0);
 	/* Wrap up the encrypted blob string. */
 	PUT_32BIT(outblob+lenpos, cipherlen);
 	/* And finally fill in the total length field. */
 	PUT_32BIT(outblob+4, pos);

-	assert(pos < outlen);
+	dropbear_assert(pos < outlen);

 	/*
 	 * Encrypt the key.
============================================================
--- options.h	22166eca17d24203e474f1d8b26ce95ea45e0166
+++ options.h	562d600e35b6a1ef1ab8d8df1dd61654056bd697
@@ -62,25 +62,30 @@ etc) slower (perhaps by 50%). Recommende
 #define ENABLE_AGENTFWD

 /* Encryption - at least one required.
- * RFC Draft requires 3DES, and recommends Blowfish, AES128 & Twofish128 */
+ * RFC Draft requires 3DES and recommends AES128 for interoperability.
+ * Including multiple keysize variants the same cipher
+ * (eg AES256 as well as AES128) will result in a minimal size increase.*/
 #define DROPBEAR_AES128_CBC
+#define DROPBEAR_3DES_CBC
+#define DROPBEAR_AES256_CBC
 #define DROPBEAR_BLOWFISH_CBC
+#define DROPBEAR_TWOFISH256_CBC
 #define DROPBEAR_TWOFISH128_CBC
-#define DROPBEAR_3DES_CBC

-/* Integrity - at least one required.
- * RFC Draft requires sha1-hmac, and recommends md5-hmac.
+/* Message Integrity - at least one required.
+ * RFC Draft requires sha1 and recommends sha1-96.
+ * sha1-96 may be of use for slow links, as it has a smaller overhead.
  *
- * Note: there's no point disabling sha1 to save space, since it's used in the
+ * Note: there's no point disabling sha1 to save space, since it's used
  * for the random number generator and public-key cryptography anyway.
  * Disabling it here will just stop it from being used as the integrity portion
  * of the ssh protocol.
  *
- * These are also used for key fingerprints in logs (when pubkey auth is used),
- * MD5 fingerprints are printed if available, however SHA1 fingerprints will be
- * generated otherwise. This isn't exactly optimal, although SHA1 fingerprints
- * are not too hard to create from pubkeys if required. */
+ * These hashes are also used for public key fingerprints in logs.
+ * If you disable MD5, Dropbear will fall back to SHA1 fingerprints,
+ * which are not the standard form. */
 #define DROPBEAR_SHA1_HMAC
+#define DROPBEAR_SHA1_96_HMAC
 #define DROPBEAR_MD5_HMAC

 /* Hostkey/public key algorithms - at least one required, these are used
@@ -128,6 +133,7 @@ etc) slower (perhaps by 50%). Recommende

 #define ENABLE_CLI_PASSWORD_AUTH
 #define ENABLE_CLI_PUBKEY_AUTH
+#define ENABLE_CLI_INTERACT_AUTH

 /* Define this (as well as ENABLE_CLI_PASSWORD_AUTH) to allow the use of
  * a helper program for the ssh client. The helper program should be
@@ -273,7 +279,7 @@ etc) slower (perhaps by 50%). Recommende
 #define MAX_MAC_LEN SHA1_HASH_SIZE


-#define MAX_KEY_LEN 24 /* 3DES requires a 24 byte key */
+#define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */
 #define MAX_IV_LEN 20 /* must be same as max blocksize,
 						 and >= SHA1_HASH_SIZE */
 #define MAX_MAC_KEY 20
@@ -300,16 +306,32 @@ etc) slower (perhaps by 50%). Recommende
 #define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
 							   is the max length for a password etc */

-/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */
-#define MAX_PUBKEY_SIZE 1600
-/* For a 4096 bit DSS key, empirically determined to be 1590 bytes */
-#define MAX_PRIVKEY_SIZE 1600
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PUBKEY_SIZE 1700
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PRIVKEY_SIZE 1700

+/* The maximum size of the bignum portion of the kexhash buffer */
+/* Sect. 8 of the transport draft, K_S + e + f + K */
+#define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130)
+
 #define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit
 								in a few years time.... */

 #define DROPBEAR_MAX_CLI_PASS 1024

+#define DROPBEAR_MAX_CLI_INTERACT_PROMPTS 80 /* The number of prompts we'll
+												accept for keyb-interactive
+												auth */
+
+#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
+#define DROPBEAR_AES_CBC
+#endif
+
+#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
+#define DROPBEAR_TWOFISH_CBC
+#endif
+
 #ifndef ENABLE_X11FWD
 #define DISABLE_X11FWD
 #endif
============================================================
--- packet.c	93ffb969c3097b6fe2c9f50cf80716ca0f209295
+++ packet.c	01803cc5860fe470ae70a883d807579b3bd0fc68
@@ -54,13 +54,13 @@ void write_packet() {
 	buffer * writebuf = NULL;

 	TRACE(("enter write_packet"))
-	assert(!isempty(&ses.writequeue));
+	dropbear_assert(!isempty(&ses.writequeue));

 	/* Get the next buffer in the queue of encrypted packets to write*/
 	writebuf = (buffer*)examine(&ses.writequeue);

 	len = writebuf->len - writebuf->pos;
-	assert(len > 0);
+	dropbear_assert(len > 0);
 	/* Try to write as much as possible */
 	written = write(ses.sock, buf_getptr(writebuf, len), len);

@@ -119,7 +119,7 @@ void read_packet() {

 	/* Attempt to read the remainder of the packet, note that there
 	 * mightn't be any available (EAGAIN) */
-	assert(ses.readbuf != NULL);
+	dropbear_assert(ses.readbuf != NULL);
 	maxlen = ses.readbuf->len - ses.readbuf->pos;
 	len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);

@@ -167,7 +167,7 @@ static void read_packet_init() {
 	if (ses.readbuf == NULL) {
 		/* start of a new packet */
 		ses.readbuf = buf_new(INIT_READBUF);
-		assert(ses.decryptreadbuf == NULL);
+		dropbear_assert(ses.decryptreadbuf == NULL);
 		ses.decryptreadbuf = buf_new(blocksize);
 	}

@@ -220,7 +220,7 @@ static void read_packet_init() {
 	if ((len > MAX_PACKET_LEN) ||
 		(len < MIN_PACKET_LEN + macsize) ||
 		((len - macsize) % blocksize != 0)) {
-		dropbear_exit("bad packet size");
+		dropbear_exit("bad packet size %d", len);
 	}

 	buf_resize(ses.readbuf, len);
@@ -319,14 +319,13 @@ static int checkmac(buffer* macbuf, buff
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
 static int checkmac(buffer* macbuf, buffer* sourcebuf) {

-	unsigned char macsize;
+	unsigned int macsize;
 	hmac_state hmac;
 	unsigned char tempbuf[MAX_MAC_LEN];
-	unsigned long hashsize;
-	int len;
+	unsigned long bufsize;
+	unsigned int len;

 	macsize = ses.keys->recv_algo_mac->hashsize;
-
 	if (macsize == 0) {
 		return DROPBEAR_SUCCESS;
 	}
@@ -352,8 +351,8 @@ static int checkmac(buffer* macbuf, buff
 		dropbear_exit("HMAC error");
 	}

-	hashsize = sizeof(tempbuf);
-	if (hmac_done(&hmac, tempbuf, &hashsize) != CRYPT_OK) {
+	bufsize = sizeof(tempbuf);
+	if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) {
 		dropbear_exit("HMAC error");
 	}

@@ -529,15 +528,15 @@ static void writemac(buffer * outputbuff
 /* Create the packet mac, and append H(seqno|clearbuf) to the output */
 static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {

-	int macsize;
+	unsigned int macsize;
 	unsigned char seqbuf[4];
-	unsigned long hashsize;
+	unsigned char tempbuf[MAX_MAC_LEN];
+	unsigned long bufsize;
 	hmac_state hmac;

 	TRACE(("enter writemac"))

 	macsize = ses.keys->trans_algo_mac->hashsize;
-
 	if (macsize > 0) {
 		/* calculate the mac */
 		if (hmac_init(&hmac,
@@ -562,12 +561,12 @@ static void writemac(buffer * outputbuff
 			dropbear_exit("HMAC error");
 		}

-		hashsize = macsize;
-		if (hmac_done(&hmac, buf_getwriteptr(outputbuffer, macsize), &hashsize)
+		bufsize = sizeof(tempbuf);
+		if (hmac_done(&hmac, tempbuf, &bufsize)
 				!= CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
-		buf_incrwritepos(outputbuffer, macsize);
+		buf_putbytes(outputbuffer, tempbuf, macsize);
 	}
 	TRACE(("leave writemac"))
 }
@@ -606,7 +605,7 @@ static void buf_compress(buffer * dest,
 			break;
 		}

-		assert(ses.keys->trans_zstream->avail_out == 0);
+		dropbear_assert(ses.keys->trans_zstream->avail_out == 0);

 		/* the buffer has been filled, we must extend. This only happens in
 		 * unusual circumstances where the data grows in size after deflate(),
============================================================
--- queue.c	ccb24cf04c2fae9ff0008f7181239fff1e93f82b
+++ queue.c	0b2bee6c831b234a3fba77c4ae6d598126a3c07c
@@ -42,7 +42,7 @@ void* dequeue(struct Queue* queue) {

 	void* ret;
 	struct Link* oldhead;
-	assert(!isempty(queue));
+	dropbear_assert(!isempty(queue));

 	ret = queue->head->item;
 	oldhead = queue->head;
@@ -62,7 +62,7 @@ void *examine(struct Queue* queue) {

 void *examine(struct Queue* queue) {

-	assert(!isempty(queue));
+	dropbear_assert(!isempty(queue));
 	return queue->head->item;
 }

============================================================
--- rsa.c	a5bd0348ccf9497600f633057ec6028e8cf3c329
+++ rsa.c	af6a357404e3480445b0b2835065a3e4666ee812
@@ -49,7 +49,7 @@ int buf_get_rsa_pub_key(buffer* buf, rsa
 int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) {

 	TRACE(("enter buf_get_rsa_pub_key"))
-	assert(key != NULL);
+	dropbear_assert(key != NULL);
 	key->e = m_malloc(sizeof(mp_int));
 	key->n = m_malloc(sizeof(mp_int));
 	m_mp_init_multi(key->e, key->n, NULL);
@@ -80,7 +80,7 @@ int buf_get_rsa_priv_key(buffer* buf, rs
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
 int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) {

-	assert(key != NULL);
+	dropbear_assert(key != NULL);

 	TRACE(("enter buf_get_rsa_priv_key"))

@@ -163,7 +163,7 @@ void buf_put_rsa_pub_key(buffer* buf, rs
 void buf_put_rsa_pub_key(buffer* buf, rsa_key *key) {

 	TRACE(("enter buf_put_rsa_pub_key"))
-	assert(key != NULL);
+	dropbear_assert(key != NULL);

 	buf_putstring(buf, SSH_SIGNKEY_RSA, SSH_SIGNKEY_RSA_LEN);
 	buf_putmpint(buf, key->e);
@@ -178,7 +178,7 @@ void buf_put_rsa_priv_key(buffer* buf, r

 	TRACE(("enter buf_put_rsa_priv_key"))

-	assert(key != NULL);
+	dropbear_assert(key != NULL);
 	buf_put_rsa_pub_key(buf, key);
 	buf_putmpint(buf, key->d);

@@ -209,7 +209,7 @@ int buf_rsa_verify(buffer * buf, rsa_key

 	TRACE(("enter buf_rsa_verify"))

-	assert(key != NULL);
+	dropbear_assert(key != NULL);

 	m_mp_init_multi(&rsa_mdash, &rsa_s, &rsa_em, NULL);

@@ -267,7 +267,7 @@ void buf_put_rsa_sign(buffer* buf, rsa_k
 	unsigned char *tmpbuf;

 	TRACE(("enter buf_put_rsa_sign"))
-	assert(key != NULL);
+	dropbear_assert(key != NULL);

 	m_mp_init_multi(&rsa_s, &rsa_tmp1, &rsa_tmp2, &rsa_tmp3, NULL);

@@ -320,7 +320,7 @@ void buf_put_rsa_sign(buffer* buf, rsa_k
 	buf_putint(buf, nsize);
 	/* pad out s to same length as n */
 	ssize = mp_unsigned_bin_size(&rsa_s);
-	assert(ssize <= nsize);
+	dropbear_assert(ssize <= nsize);
 	for (i = 0; i < nsize-ssize; i++) {
 		buf_putbyte(buf, 0x00);
 	}
@@ -365,8 +365,8 @@ static void rsa_pad_em(rsa_key * key,
 	hash_state hs;
 	unsigned int nsize;

-	assert(key != NULL);
-	assert(data != NULL);
+	dropbear_assert(key != NULL);
+	dropbear_assert(data != NULL);
 	nsize = mp_unsigned_bin_size(key->n);

 	rsa_EM = buf_new(nsize-1);
@@ -387,7 +387,7 @@ static void rsa_pad_em(rsa_key * key,
 	sha1_done(&hs, buf_getwriteptr(rsa_EM, SHA1_HASH_SIZE));
 	buf_incrwritepos(rsa_EM, SHA1_HASH_SIZE);

-	assert(rsa_EM->pos == rsa_EM->size);
+	dropbear_assert(rsa_EM->pos == rsa_EM->size);

 	/* Create the mp_int from the encoded bytes */
 	buf_setpos(rsa_EM, 0);
============================================================
--- runopts.h	866a99e2213c6a2602a649a2915471ef4b39770a
+++ runopts.h	36768f6323c18233529bb202e8876a3dfbc5d5a5
@@ -33,8 +33,9 @@ typedef struct runopts {

 typedef struct runopts {

-	int nolocaltcp;
-	int noremotetcp;
+#if defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
+	int listen_fwd_all;
+#endif

 } runopts;

@@ -73,6 +74,13 @@ typedef struct svr_runopts {
 	int noauthpass;
 	int norootpass;

+#ifdef ENABLE_SVR_REMOTETCPFWD
+	int noremotetcp;
+#endif
+#ifdef ENABLE_SVR_LOCALTCPFWD
+	int nolocaltcp;
+#endif
+
 	sign_key *hostkey;
 	buffer * banner;

@@ -83,7 +91,6 @@ void loadhostkeys();
 void svr_getopts(int argc, char ** argv);
 void loadhostkeys();

-/* Uncompleted XXX matt */
 typedef struct cli_runopts {

 	char *progname;
@@ -103,7 +110,6 @@ typedef struct cli_runopts {
 #ifdef ENABLE_CLI_LOCALTCPFWD
 	struct TCPFwdList * localfwds;
 #endif
-	/* XXX TODO */

 } cli_runopts;

============================================================
--- session.h	0e62967a248214d43a774911bfa4a0c503f0da17
+++ session.h	029633beee5cd56701332bc0fa563ae201223469
@@ -226,6 +226,13 @@ struct clientsession {

 	int lastauthtype; /* either AUTH_TYPE_PUBKEY or AUTH_TYPE_PASSWORD,
 						 for the last type of auth we tried */
+#ifdef ENABLE_CLI_INTERACT_AUTH
+	int auth_interact_failed; /* flag whether interactive auth can still
+								 be used */
+	int interact_request_received; /* flag whether we've received an
+									  info request from the server for
+									  interactive auth.*/
+#endif
 	struct SignKeyList *lastprivkey;

 	int retval; /* What the command exit status was - we emulate it */
============================================================
--- signkey.c	0b42ca44158bc9c2057064ddf12aa3ab11fb76b4
+++ signkey.c	c0ce28d518a22769fdee4e64e6fcd77a9100e61f
@@ -404,6 +404,9 @@ int buf_verify(buffer * buf, sign_key *k
 	if (bloblen == DSS_SIGNATURE_SIZE &&
 			memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) {
 		m_free(ident);
+		if (key->dsskey == NULL) {
+			dropbear_exit("no dss key to verify signature");
+		}
 		return buf_dss_verify(buf, key->dsskey, data, len);
 	}
 #endif
@@ -411,6 +414,9 @@ int buf_verify(buffer * buf, sign_key *k
 #ifdef DROPBEAR_RSA
 	if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) {
 		m_free(ident);
+		if (key->rsakey == NULL) {
+			dropbear_exit("no rsa key to verify signature");
+		}
 		return buf_rsa_verify(buf, key->rsakey, data, len);
 	}
 #endif
============================================================
--- ssh.h	b63c0c60a29e5dec39b53218d20fe30cfd00a60e
+++ ssh.h	190af1a6af0c6977129303befae06af9c58e6e07
@@ -42,8 +42,19 @@
 #define SSH_MSG_USERAUTH_FAILURE            51
 #define SSH_MSG_USERAUTH_SUCCESS            52
 #define SSH_MSG_USERAUTH_BANNER             53
+
+/* packets 60-79 are method-specific, aren't one-one mapping */
+#define SSH_MSG_USERAUTH_SPECIFIC_60   60
+
+#define SSH_MSG_USERAUTH_PASSWD_CHANGEREQ   60
+
 #define SSH_MSG_USERAUTH_PK_OK				60

+/* keyboard interactive auth */
+#define SSH_MSG_USERAUTH_INFO_REQUEST           60
+#define SSH_MSG_USERAUTH_INFO_RESPONSE          61
+
+
 /* If adding numbers here, check MAX_UNAUTH_PACKET_TYPE in process-packet.c
  * is still valid */

============================================================
--- svr-authpam.c	d2e49b9902777338e827e1935b24457b86fce347
+++ svr-authpam.c	aa255168b81cef1ec80b008a7e98975cfa9600a3
@@ -59,7 +59,7 @@ pamConvFunc(int num_msg,

 	const char* message = (*msg)->msg;

-	// make a copy we can strip
+	/* make a copy we can strip */
 	char * compare_message = m_strdup(message);

 	TRACE(("enter pamConvFunc"))
@@ -80,14 +80,14 @@ pamConvFunc(int num_msg,
 	}


-	// Make the string lowercase.
+	/* Make the string lowercase. */
 	msg_len = strlen(compare_message);
 	for (i = 0; i < msg_len; i++) {
 		compare_message[i] = tolower(compare_message[i]);
 	}

-	// If the string ends with ": ", remove the space.
-	// ie "login: " vs "login:"
+	/* If the string ends with ": ", remove the space.
+	   ie "login: " vs "login:" */
 	if (msg_len > 2
 			&& compare_message[msg_len-2] == ':'
 			&& compare_message[msg_len-1] == ' ') {
@@ -99,9 +99,9 @@ pamConvFunc(int num_msg,
 		case PAM_PROMPT_ECHO_OFF:

 			if (!(strcmp(compare_message, "password:") == 0)) {
-				// We don't recognise the prompt as asking for a password,
-				// so can't handle it. Add more above as required for
-				// different pam modules/implementations
+				/* We don't recognise the prompt as asking for a password,
+				   so can't handle it. Add more above as required for
+				   different pam modules/implementations */
 				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (no echo)",
 						compare_message);
 				rc = PAM_CONV_ERR;
@@ -125,9 +125,9 @@ pamConvFunc(int num_msg,

 			if (!((strcmp(compare_message, "login:" ) == 0)
 				|| (strcmp(compare_message, "please enter username:") == 0))) {
-				// We don't recognise the prompt as asking for a username,
-				// so can't handle it. Add more above as required for
-				// different pam modules/implementations
+				/* We don't recognise the prompt as asking for a username,
+				   so can't handle it. Add more above as required for
+				   different pam modules/implementations */
 				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (with echo)",
 						compare_message);
 				rc = PAM_CONV_ERR;
============================================================
--- svr-authpubkey.c	a659d961ca849841adc5ac41a68f360a135763d5
+++ svr-authpubkey.c	6a8b306f446ff4aede63a6d5a72de0b8a15129e4
@@ -266,7 +266,6 @@ static int checkpubkeyperms() {

 	TRACE(("enter checkpubkeyperms"))

-	assert(ses.authstate.pw);
 	if (ses.authstate.pw->pw_dir == NULL) {
 		goto out;
 	}
@@ -312,6 +311,7 @@ static int checkfileperm(char * filename
 /* returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
 static int checkfileperm(char * filename) {
 	struct stat filestat;
+	int badperm = 0;

 	TRACE(("enter checkfileperm(%s)", filename))

@@ -322,14 +322,23 @@ static int checkfileperm(char * filename
 	/* check ownership - user or root only*/
 	if (filestat.st_uid != ses.authstate.pw->pw_uid
 			&& filestat.st_uid != 0) {
-		TRACE(("leave checkfileperm: wrong ownership"))
-		return DROPBEAR_FAILURE;
+		badperm = 1;
+		TRACE(("wrong ownership"))
 	}
 	/* check permissions - don't want group or others +w */
 	if (filestat.st_mode & (S_IWGRP | S_IWOTH)) {
-		TRACE(("leave checkfileperm: wrong perms"))
+		badperm = 1;
+		TRACE(("wrong perms"))
+	}
+	if (badperm) {
+		if (!ses.authstate.perm_warn) {
+			ses.authstate.perm_warn = 1;
+			dropbear_log(LOG_INFO, "%s must be owned by user or root, and not writable by others", filename);
+		}
+		TRACE(("leave checkfileperm: failure perms/owner"))
 		return DROPBEAR_FAILURE;
 	}
+
 	TRACE(("leave checkfileperm: success"))
 	return DROPBEAR_SUCCESS;
 }
============================================================
--- svr-chansession.c	14640c883f2d3f5a07cb5614102eeb7762aa9b79
+++ svr-chansession.c	8b52f1a622321b7cf4ed3ac6f69e7241d14d324d
@@ -148,8 +148,8 @@ static void send_msg_chansess_exitstatus
 static void send_msg_chansess_exitstatus(struct Channel * channel,
 		struct ChanSess * chansess) {

-	assert(chansess->exit.exitpid != -1);
-	assert(chansess->exit.exitsignal == -1);
+	dropbear_assert(chansess->exit.exitpid != -1);
+	dropbear_assert(chansess->exit.exitsignal == -1);

 	CHECKCLEARTOWRITE();

@@ -170,8 +170,8 @@ static void send_msg_chansess_exitsignal
 	int i;
 	char* signame = NULL;

-	assert(chansess->exit.exitpid != -1);
-	assert(chansess->exit.exitsignal > 0);
+	dropbear_assert(chansess->exit.exitpid != -1);
+	dropbear_assert(chansess->exit.exitsignal > 0);

 	CHECKCLEARTOWRITE();

@@ -205,7 +205,7 @@ static int newchansess(struct Channel *c

 	struct ChanSess *chansess;

-	assert(channel->typedata == NULL);
+	dropbear_assert(channel->typedata == NULL);

 	chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
 	chansess->cmd = NULL;
@@ -279,7 +279,7 @@ static void closechansess(struct Channel
 	/* clear child pid entries */
 	for (i = 0; i < svr_ses.childpidsize; i++) {
 		if (svr_ses.childpids[i].chansess == chansess) {
-			assert(svr_ses.childpids[i].pid > 0);
+			dropbear_assert(svr_ses.childpids[i].pid > 0);
 			TRACE(("closing pid %d", svr_ses.childpids[i].pid))
 			TRACE(("exitpid = %d", chansess->exit.exitpid))
 			svr_ses.childpids[i].pid = -1;
@@ -313,7 +313,7 @@ static void chansessionrequest(struct Ch
 	}

 	chansess = (struct ChanSess*)channel->typedata;
-	assert(chansess != NULL);
+	dropbear_assert(chansess != NULL);
 	TRACE(("type is %s", type))

 	if (strcmp(type, "window-change") == 0) {
@@ -673,15 +673,15 @@ static int noptycommand(struct Channel *
 		close(infds[FDIN]);
 		close(outfds[FDOUT]);
 		close(errfds[FDOUT]);
-		channel->infd = infds[FDOUT];
-		channel->outfd = outfds[FDIN];
+		channel->writefd = infds[FDOUT];
+		channel->readfd = outfds[FDIN];
 		channel->errfd = errfds[FDIN];
-		ses.maxfd = MAX(ses.maxfd, channel->infd);
-		ses.maxfd = MAX(ses.maxfd, channel->outfd);
+		ses.maxfd = MAX(ses.maxfd, channel->writefd);
+		ses.maxfd = MAX(ses.maxfd, channel->readfd);
 		ses.maxfd = MAX(ses.maxfd, channel->errfd);

-		setnonblocking(channel->outfd);
-		setnonblocking(channel->infd);
+		setnonblocking(channel->readfd);
+		setnonblocking(channel->writefd);
 		setnonblocking(channel->errfd);

 	}
@@ -784,8 +784,8 @@ static int ptycommand(struct Channel *ch
 		addchildpid(chansess, pid);

 		close(chansess->slave);
-		channel->infd = chansess->master;
-		channel->outfd = chansess->master;
+		channel->writefd = chansess->master;
+		channel->readfd = chansess->master;
 		/* don't need to set stderr here */
 		ses.maxfd = MAX(ses.maxfd, chansess->master);

============================================================
--- svr-main.c	956679879949d5e02205ac66872e03986c76fc61
+++ svr-main.c	5c0e59de51df9c03a47947b7dcd100053623bfd3
@@ -284,7 +284,7 @@ void main_noinetd() {
 								getaddrhostname(&remoteaddr),
 								addrstring);
 				/* don't return */
-				assert(0);
+				dropbear_assert(0);
 			}

 			/* parent */
@@ -375,7 +375,7 @@ static int listensockets(int *sock, int

 		TRACE(("listening on '%s'", svr_opts.ports[i]))

-		nsock = dropbear_listen(NULL, svr_opts.ports[i], &sock[sockpos],
+		nsock = dropbear_listen("", svr_opts.ports[i], &sock[sockpos],
 				sockcount - sockpos,
 				&errstring, maxfd);

============================================================
--- svr-runopts.c	6d28fe6609d584d48c70f1707e7d4f8460513aea
+++ svr-runopts.c	f68d09f9fe84a764badcdd18a0f69b14e44cb0e8
@@ -63,13 +63,14 @@ static void printhelp(const char * progn
 					"-s		Disable password logins\n"
 					"-g		Disable password logins for root\n"
 #endif
-#ifndef DISABLE_LOCALTCPFWD
+#ifdef ENABLE_SVR_LOCALTCPFWD
 					"-j		Disable local port forwarding\n"
 #endif
-#ifndef DISABLE_REMOTETCPFWD
+#ifdef ENABLE_SVR_REMOTETCPFWD
 					"-k		Disable remote port forwarding\n"
+					"-a		Allow connections to forwarded ports from any host\n"
 #endif
-					"-p port	Listen on specified tcp port, up to %d can be specified\n"
+					"-p port		Listen on specified tcp port, up to %d can be specified\n"
 					"		(default %s if none specified)\n"
 #ifdef INETD_MODE
 					"-i		Start for inetd\n"
@@ -104,8 +105,8 @@ void svr_getopts(int argc, char ** argv)
 	svr_opts.inetdmode = 0;
 	svr_opts.portcount = 0;
 	svr_opts.hostkey = NULL;
-	opts.nolocaltcp = 0;
-	opts.noremotetcp = 0;
+	svr_opts.nolocaltcp = 0;
+	svr_opts.noremotetcp = 0;
 	/* not yet
 	opts.ipv4 = 1;
 	opts.ipv6 = 1;
@@ -116,6 +117,9 @@ void svr_getopts(int argc, char ** argv)
 #ifndef DISABLE_SYSLOG
 	svr_opts.usingsyslog = 1;
 #endif
+#ifdef ENABLE_SVR_REMOTETCPFWD
+	opts.listen_fwd_all = 0;
+#endif

 	for (i = 1; i < (unsigned int)argc; i++) {
 		if (next) {
@@ -152,13 +156,16 @@ void svr_getopts(int argc, char ** argv)
 #endif
 #ifndef DISABLE_LOCALTCPFWD
 				case 'j':
-					opts.nolocaltcp = 1;
+					svr_opts.nolocaltcp = 1;
 					break;
 #endif
 #ifndef DISABLE_REMOTETCPFWD
 				case 'k':
-					opts.noremotetcp = 1;
+					svr_opts.noremotetcp = 1;
 					break;
+				case 'a':
+					opts.listen_fwd_all = 1;
+					break;
 #endif
 #ifdef INETD_MODE
 				case 'i':
============================================================
--- svr-tcpfwd.c	9c0362f8d1c15ce9d702873cc1a2064754d7919b
+++ svr-tcpfwd.c	bfd47e1ced10186c0d9ce06dca0f0d3694e9c3df
@@ -72,7 +72,7 @@ void recv_msg_global_request_remotetcp()

 	TRACE(("enter recv_msg_global_request_remotetcp"))

-	if (opts.noremotetcp) {
+	if (svr_opts.noremotetcp) {
 		TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"))
 		goto out;
 	}
@@ -129,9 +129,9 @@ static int matchtcp(void* typedata1, voi
 	const struct TCPListener *info1 = (struct TCPListener*)typedata1;
 	const struct TCPListener *info2 = (struct TCPListener*)typedata2;

-	return (info1->sendport == info2->sendport)
+	return (info1->listenport == info2->listenport)
 			&& (info1->chantype == info2->chantype)
-			&& (strcmp(info1->sendaddr, info2->sendaddr) == 0);
+			&& (strcmp(info1->listenaddr, info2->listenaddr) == 0);
 }

 static int svr_cancelremotetcp() {
@@ -153,8 +153,10 @@ static int svr_cancelremotetcp() {

 	port = buf_getint(ses.payload);

-	tcpinfo.sendaddr = bindaddr;
-	tcpinfo.sendport = port;
+	tcpinfo.sendaddr = NULL;
+	tcpinfo.sendport = 0;
+	tcpinfo.listenaddr = bindaddr;
+	tcpinfo.listenport = port;
 	listener = get_listener(CHANNEL_ID_TCPFORWARDED, &tcpinfo, matchtcp);
 	if (listener) {
 		remove_listener( listener );
@@ -177,7 +179,6 @@ static int svr_remotetcpreq() {

 	TRACE(("enter remotetcpreq"))

-	/* NOTE: at this stage, we ignore bindaddr. see below and listen_tcpfwd */
 	bindaddr = buf_getstring(ses.payload, &addrlen);
 	if (addrlen > MAX_IP_LEN) {
 		TRACE(("addr len too long: %d", addrlen))
@@ -202,20 +203,20 @@ static int svr_remotetcpreq() {
 	}

 	tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
-	tcpinfo->sendaddr = bindaddr;
-	tcpinfo->sendport = port;
+	tcpinfo->sendaddr = NULL;
+	tcpinfo->sendport = 0;
+	tcpinfo->listenaddr = bindaddr;
 	tcpinfo->listenport = port;
 	tcpinfo->chantype = &svr_chan_tcpremote;
+	tcpinfo->tcp_type = forwarded;

-	/* Note: bindaddr is actually ignored by listen_tcpfwd, since
-	 * we only want to bind to localhost */
 	ret = listen_tcpfwd(tcpinfo);

 out:
 	if (ret == DROPBEAR_FAILURE) {
 		/* we only free it if a listener wasn't created, since the listener
 		 * has to remember it if it's to be cancelled */
-		m_free(tcpinfo->sendaddr);
+		m_free(tcpinfo->listenaddr);
 		m_free(tcpinfo);
 	}
 	TRACE(("leave remotetcpreq"))
@@ -235,7 +236,7 @@ static int newtcpdirect(struct Channel *
 	int len;
 	int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;

-	if (opts.nolocaltcp) {
+	if (svr_opts.nolocaltcp) {
 		TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
 		goto out;
 	}
@@ -272,11 +273,9 @@ static int newtcpdirect(struct Channel *

 	ses.maxfd = MAX(ses.maxfd, sock);

-	/* Note that infd is actually the "outgoing" direction on the
-	 * tcp connection, vice versa for outfd.
-	 * We don't set outfd, that will get set after the connection's
+	 /* We don't set readfd, that will get set after the connection's
 	 * progress succeeds */
-	channel->infd = sock;
+	channel->writefd = sock;
 	channel->initconn = 1;

 	err = SSH_OPEN_IN_PROGRESS;
============================================================
--- tcp-accept.c	85218bbb73f0a9394f0f100aa416a917978318b7
+++ tcp-accept.c	413de2351e02bf67ef6e78238c449c6a480a2f75
@@ -39,6 +39,7 @@ static void cleanup_tcp(struct Listener
 	struct TCPListener *tcpinfo = (struct TCPListener*)(listener->typedata);

 	m_free(tcpinfo->sendaddr);
+	m_free(tcpinfo->listenaddr);
 	m_free(tcpinfo);
 }

@@ -64,11 +65,28 @@ static void tcp_acceptor(struct Listener
 	}

 	if (send_msg_channel_open_init(fd, tcpinfo->chantype) == DROPBEAR_SUCCESS) {
+		unsigned char* addr = NULL;
+		unsigned int port = 0;

-		buf_putstring(ses.writepayload, tcpinfo->sendaddr,
-				strlen(tcpinfo->sendaddr));
-		buf_putint(ses.writepayload, tcpinfo->sendport);
+		if (tcpinfo->tcp_type == direct) {
+			/* "direct-tcpip" */
+			/* host to connect, port to connect */
+			addr = tcpinfo->sendaddr;
+			port = tcpinfo->sendport;
+		} else {
+			dropbear_assert(tcpinfo->tcp_type == forwarded);
+			/* "forwarded-tcpip" */
+			/* address that was connected, port that was connected */
+			addr = tcpinfo->listenaddr;
+			port = tcpinfo->listenport;
+		}
+
+		buf_putstring(ses.writepayload, addr, strlen(addr));
+		buf_putint(ses.writepayload, port);
+
+		/* originator ip */
 		buf_putstring(ses.writepayload, ipstring, strlen(ipstring));
+		/* originator port */
 		buf_putint(ses.writepayload, atol(portstring));

 		encrypt_packet();
@@ -86,16 +104,21 @@ int listen_tcpfwd(struct TCPListener* tc
 	struct Listener *listener = NULL;
 	int nsocks;
 	char* errstring = NULL;
+	// listen_spec = NULL indicates localhost
+	const char* listen_spec = NULL;

 	TRACE(("enter listen_tcpfwd"))

 	/* first we try to bind, so don't need to do so much cleanup on failure */
 	snprintf(portstring, sizeof(portstring), "%d", tcpinfo->listenport);

-	/* XXX Note: we're just listening on localhost, no matter what they tell
-	 * us. If someone wants to make it listen otherways, then change
-	 * the "" argument. but that requires UI changes too */
-	nsocks = dropbear_listen("", portstring, socks,
+	/* a listenaddr of "" will indicate all interfaces */
+	if (opts.listen_fwd_all
+			&& (strcmp(tcpinfo->listenaddr, "localhost") != 0) ) {
+		listen_spec = tcpinfo->listenaddr;
+	}
+
+	nsocks = dropbear_listen(listen_spec, portstring, socks,
 			DROPBEAR_MAX_SOCKS, &errstring, &ses.maxfd);
 	if (nsocks < 0) {
 		dropbear_log(LOG_INFO, "TCP forward failed: %s", errstring);
============================================================
--- tcpfwd.h	e093ea296308000bdf7c4c54f49caf7bf353a47e
+++ tcpfwd.h	16c1fbd5251921ecc87e704bc2afe2076dd55a4e
@@ -28,20 +28,19 @@ struct TCPListener {

 struct TCPListener {

-	/* sendaddr/sendport are what we send in the channel init request. For a
-	 * forwarded-tcpip request, it's the addr/port we were binding to.
-	 * For a direct-tcpip request, it's the addr/port we want the other
+	/* For a direct-tcpip request, it's the addr/port we want the other
 	 * end to connect to */
-
 	unsigned char *sendaddr;
 	unsigned int sendport;

-	/* This is for direct-tcpip (ie the client listening), and specifies the
-	 * port to listen on. Is unspecified for the server */
+	/* This is the address/port that we listen on. The address has special
+	 * meanings as per the rfc, "" for all interfaces, "localhost" for
+	 * localhost, or a normal interface name. */
+	unsigned char *listenaddr;
 	unsigned int listenport;

 	const struct ChanType *chantype;
-
+	enum {direct, forwarded} tcp_type;
 };

 /* A link in a list of forwards */