changeset 681:a4b7627b3157 insecure-nocrypto

Update insecure-nocrypto to current head
author Matt Johnston <matt@ucc.asn.au>
date Wed, 16 May 2012 22:54:51 +0800
parents bd4b5d7886e5 (diff) 0129fd8ccc71 (current diff)
children c37857676924
files cli-auth.c common-algo.c common-kex.c options.h
diffstat 90 files changed, 2418 insertions(+), 1197 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgsigs	Wed May 16 22:54:51 2012 +0800
@@ -0,0 +1,3 @@
+aa2f51a6b81d33de5e9898a7f27c792a173d9b26 0 iD8DBQBOuADmjPn4sExkf7wRAv/fAJ9FJFvjDoF+wd1ipDx1wkzdeBQNqgCgykUrSbXv76FBbxKntVbk9oS3GjI=
+3f12086c2ef2b9ffe36a822fdb3ff647fcec1831 0 iD8DBQBOuSlQjPn4sExkf7wRAvkbAKCgE1e8xEMQ16CGeoywhIQ0QR4eNgCfZdYYlzjb/+521Uvh5/7FRYEmrho=
+85f835f2fe0ac2c503c50a414de127222fb0a57c 0 iD8DBQBPRkMUjPn4sExkf7wRAvM4AJ9mw2OAkyjhSbamM1MizlEJUX18HACgoFKQkYf6BnYxN34Nv2HhM0cmzUc=
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.hgtags	Wed May 16 22:54:51 2012 +0800
@@ -0,0 +1,33 @@
+03f65e461915a940939e4cc689fc89721ffc40de DROPBEAR_0.48.1
+0f967bfef5cd0056b7ec60e2305d917e51cbf30d DROPBEAR_0.44
+170329dc8ce5dfcf6298e1ad6699f109bf78e73d DROPBEAR_0.51
+1dbd2473482f320ea59f76ce961385cb3a0150a9 DROPBEAR_0.46
+2098857ab826dd42ae05a9a22c3ce2cc835b9844 DROPBEAR_0.45
+36160290a1b27451178be36752ed038840f59cdd LTC_DB_0.46
+39d5d58461d6e93337636e69d4cdf184a09c8d24 LTC_1.05
+55a99934db873be2e63b5968fb6532e5d9bd02e4 DROPBEAR_0.48
+59400faa4b44708c5d0b595e81193bc621e752d3 libtomcrypt-1.05
+66087d87c3555c78b47cf01f32bb5a32054c3ceb DROPBEAR_0.44test4
+677843bfa734238a67636b461a02c110c462ffaf DROPBEAR_0.44test1
+7faae8f46238e23975430876547b8950b4e75481 t:ltc-0.95-orig
+8220862baae829ebc762587b99c662480d57bb23 DROPBEAR_0.53
+86e0b50a9b588239c3fc9cc9cfe255ef586df17b ltm-0.30-orig
+88e0a1ad951add46b795511dc2698e36b4aee922 DROPBEAR_0.44test3
+8e94663164c6e106ccc5c9e997dedf6e04d77dd2 LTM_DB_0.44
+91fbc376f01084037cd5f6a5bf2e2db4903e8e99 libtommath-0.35
+97db060d0ef5f8cf8e67eb602ef037055a185ca9 libtommath-0.40
+aa2f51a6b81d33de5e9898a7f27c792a173d9b26 DROPBEAR_0.53.1
+ab370c629d363f8c9a3eca512bfa86e362034654 DROPBEAR_0.49
+c2ac796b130eeb6fa840873d8c230544c8ec7e4b DROPBEAR_0.44test2
+cd1143579f00b0248c79f63ca70efee4a35a57e8 LTC_DB_0.44
+ce104c8b0be1ff3f2c2590b7cdc3fd6870c865cd DROPBEAR_0.52
+d5faf4814ddbc5abd9e209409bb9e7a4686c8cd7 libtomcrypt-1.16
+d7da3b1e15401eb234ec866d5eac992fc4cd5878 t:ltc-0.95-db-merge1
+d8254fc979e99560c93ca2cece77a6df31927ea5 LTM_0.35
+e109027b9edfb02f0bdf96ec45bb1cd9ad41e7da LTM_DB_0.46
+e109027b9edfb02f0bdf96ec45bb1cd9ad41e7da LTM_DB_0.47
+e37b160c414cab6466622f63b0c4dcbf6ebc47a9 DROPBEAR_0.47
+e430a26064ee86ab79aef372118d6d03b2441996 DROPBEAR_0.50
+e5d119ea4c63656bc54ecfd865d04591ac2ed225 LTC_DB_0.47
+3f12086c2ef2b9ffe36a822fdb3ff647fcec1831 DROPBEAR_2011.54
+d354464b2aa6f6ba0bf44d43bcae5aa798435393 DROPBEAR_2012.55
--- a/CHANGES	Thu Nov 06 13:33:06 2008 +0000
+++ b/CHANGES	Wed May 16 22:54:51 2012 +0800
@@ -1,4 +1,114 @@
-0.52
+2012.55 - Wednesday 22 February 2012
+
+- Security: Fix use-after-free bug that could be triggered if command="..."
+  authorized_keys restrictions are used.  Could allow arbitrary code execution
+  or bypass of the command="..." restriction to an authenticated user.
+  This bug affects releases 0.52 onwards. Ref CVE-2012-0920.
+  Thanks to Danny Fullerton of Mantor Organization for reporting
+  the bug.
+
+- Compile fix, only apply IPV6 socket options if they are available in headers
+  Thanks to Gustavo Zacarias for the patch
+
+- Overwrite session key memory on exit
+
+- Fix minor memory leak in unusual PAM authentication configurations.
+  Thanks to Stathis Voukelatos
+
+- Other small code cleanups
+
+2011.54 - Tuesday 8 November 2011
+
+- Building statically works again, broke in 0.53 and 0.53.1
+
+- Fix crash when forwarding with -R
+
+- Fixed various leaks found by Klocwork analysis software, thanks to them for
+  running it
+
+- Set IPTOS_LOWDELAY for IPv6, thanks to Dave Taht
+
+- Bind to sockets with IPV6_V6ONLY so that it works properly on systems
+  regardless of the system-wide setting
+
+- Added ALLOW_BLANK_PASSWORD option. Dropbear also now allows public key logins
+  to accounts with a blank password. Thanks to Rob Landley
+
+- Fixed case where "-K 1" keepalive for dbclient would cause a SSH_MSG_IGNORE
+  packet to be sent
+
+- Avoid some memory allocations in big number maths routines, improves
+  performance slightly
+
+- Fix symlink target for installdropbearmulti with DESTDIR set, thanks to
+  Scottie Shore
+
+- When requesting server allocated remote ports (-R 0:host:port) print a
+  message informing what the port is, thanks to Ali Onur Uyar.
+
+- New version numbering scheme.
+
+Source repository has now migrated to Mercurial at 
+https://secure.ucc.asn.au/hg/dropbear/graph/default
+
+0.53.1 - Wednesday 2 March 2011
+
+- -lcrypt needs to be before object files for static linking
+
+- Compile fix when both client and agent forwarding are disabled
+
+- Fix DROPBEAR_PRNGD_SOCKET mode
+
+- Don't allow setting zlib memLevel since it seems buggy
+
+0.53 - Thurs 24 February 2011
+
+- Various performance/memory use improvements
+
+- Client agent forwarding now works, using OpenSSH's ssh-agent
+
+- Improve robustness of client multihop mode
+
+- Fix a prime generation bug in bundled libtommath. This is unlikely to have
+  generated any bad keys in the wild.
+  See 
+  https://bugzilla.redhat.com/show_bug.cgi?id=615088
+  http://bugs.gentoo.org/show_bug.cgi?id=328383
+  http://bugs.gentoo.org/show_bug.cgi?id=328409
+
+- Attempt to build against system libtomcrypt/libtommath if available. This
+  can be disabled with ./configure --enable-bundled-libtom
+
+- Make -K (keepalive) and -I (idle timeout) work together sensibly in the client.
+  The idle timeout is no longer reset by SSH_MSG_IGNORE packets.
+
+- Add diffie-hellman-group14-sha1 key exchange method
+
+- Compile fix if ENABLE_CLI_PROXYCMD is disabled
+
+- /usr/bin/X11/xauth is now the default path
+
+- Client remote forward (-L/-R) arguments now accept a listen address
+
+- In uClinux avoid trashing the parent process when a session exits
+
+- Blowfish is now disabled by default since it has large memory usage
+
+- Add option to change zlib windowbits/memlevel. Use less memory by default
+
+- DROPBEAR_SMALL_CODE is now disabled by default
+
+- SSH_ORIGINAL_COMMAND environment variable is set by the server when an
+  authorized_keys command is specified.
+
+- Set SSH_TTY and SSH_CONNECTION environment variables in the server
+
+- Client banner is now printed to standard error rather than standard output
+
+- Capitalisation in many log messages has been made consistent. This may affect
+  scripts that parse logfiles.
+
+0.52 - Wed 12 November 2008
 
 - Add "netcat-alike" option (-B) to dbclient, allowing Dropbear to tunnel
   standard input/output to a TCP port-forwarded remote host.
@@ -12,13 +122,19 @@
 
 - Combine netcat-alike and proxy support to allow "multihop" connections, with
   comma-separated host syntax.  Allows running
+
 	  dbclient user1@host1,user2@host2,user3@host3
+
   to end up at host3 via the other two, using SSH TCP forwarding. It's a bit
   like onion-routing. All connections are established from the local machine.
   The comma-separated syntax can also be used for scp/rsync, eg
-	  scp -S dbclient matt@martello,root@wrt,canyons:/tmp/dump .
+
+	  rsync -a -e dbclient m@gateway,m2@host,martello:/home/matt/ ~/backup/
+
   to bounce through a few hosts.
 
+- Add -I "idle timeout" option (contributed by Farrell Aultman)
+
 - Allow restrictions on authorized_keys logins such as restricting commands
   to be run etc. This is a subset of those allowed by OpenSSH, doesn't
   yet allow restricting source host.
--- a/Makefile.in	Thu Nov 06 13:33:06 2008 +0000
+++ b/Makefile.in	Wed May 16 22:54:51 2012 +0800
@@ -16,11 +16,17 @@
 LTC=libtomcrypt/libtomcrypt.a
 LTM=libtommath/libtommath.a
 
+ifeq (@BUNDLED_LIBTOM@, 1)
+LIBTOM_DEPS=$(LTC) $(LTM)
+CFLAGS+=-I$(srcdir)/libtomcrypt/src/headers/ 
+LIBS+=$(LTC) $(LTM)
+endif
+
 COMMONOBJS=dbutil.o buffer.o \
 		dss.o bignum.o \
 		signkey.o rsa.o random.o \
 		queue.o \
-		atomicio.o compat.o  fake-rfc2553.o
+		atomicio.o compat.o  fake-rfc2553.o 
 
 SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
 		svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \
@@ -29,7 +35,8 @@
 
 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-authinteract.o
+		cli-authpubkey.o cli-tcpfwd.o cli-channel.o cli-authinteract.o \
+		cli-agentfwd.o list.o
 
 CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
 			common-channel.o common-chansession.o termcodes.o loginrec.o \
@@ -40,7 +47,7 @@
 
 CONVERTOBJS=dropbearconvert.o keyimport.o
 
-SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o
+SCPOBJS=scp.o progressmeter.o atomicio.o scpmisc.o compat.o
 
 HEADERS=options.h dbutil.h session.h packet.h algo.h ssh.h buffer.h kex.h \
 		dss.h bignum.h signkey.h rsa.h random.h service.h auth.h \
@@ -49,7 +56,7 @@
 		loginrec.h atomicio.h x11fwd.h agentfwd.h tcpfwd.h compat.h \
 		listener.h fake-rfc2553.h
 
-dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) 
+dropbearobjs=$(COMMONOBJS) $(CLISVROBJS) $(SVROBJS) @CRYPTLIB@ 
 dbclientobjs=$(COMMONOBJS) $(CLISVROBJS) $(CLIOBJS)
 dropbearkeyobjs=$(COMMONOBJS) $(KEYOBJS)
 dropbearconvertobjs=$(COMMONOBJS) $(CONVERTOBJS)
@@ -69,8 +76,8 @@
 STRIP=@STRIP@
 INSTALL=@INSTALL@
 CPPFLAGS=@CPPFLAGS@
-CFLAGS=-I. -I$(srcdir) -I$(srcdir)/libtomcrypt/src/headers/ $(CPPFLAGS) @CFLAGS@
-LIBS=$(LTC) $(LTM) @LIBS@
+CFLAGS+=-I. -I$(srcdir) $(CPPFLAGS) @CFLAGS@
+LIBS+=@LIBS@
 LDFLAGS=@LDFLAGS@
 
 EXEEXT=@EXEEXT@
@@ -106,10 +113,6 @@
 	CFLAGS+=-DPROGRESS_METER
 endif
 
-#%: $(HEADERS)
-#%: $(HEADERS) Makefile
-# TODO
-
 all: $(TARGETS)
 
 strip: $(TARGETS)
@@ -126,12 +129,14 @@
 	-chgrp 0 $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT)
 
 insmultidropbear: dropbearmulti
+	$(INSTALL) -d -m 755 $(DESTDIR)$(sbindir)
 	-rm -f $(DESTDIR)$(sbindir)/dropbear$(EXEEXT)
-	-ln -s $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/dropbear$(EXEEXT) 
+	-ln -s $(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(sbindir)/dropbear$(EXEEXT) 
 
 insmulti%: dropbearmulti
+	$(INSTALL) -d -m 755 $(DESTDIR)$(bindir)
 	-rm -f $(DESTDIR)$(bindir)/$*$(EXEEXT) 
-	-ln -s $(DESTDIR)$(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$*$(EXEEXT) 
+	-ln -s $(bindir)/dropbearmulti$(EXEEXT) $(DESTDIR)$(bindir)/$*$(EXEEXT) 
 
 # dropbear should go in sbin, so it needs a seperate rule
 inst_dropbear: dropbear
@@ -153,8 +158,7 @@
 dropbearkey: $(dropbearkeyobjs)
 dropbearconvert: $(dropbearconvertobjs)
 
-dropbear dbclient dropbearkey dropbearconvert: $(HEADERS)  $(LTC) $(LTM) \
-													Makefile
+dropbear dbclient dropbearkey dropbearconvert: $(HEADERS) $(LIBTOM_DEPS) Makefile
 	$(CC) $(LDFLAGS) -o $@$(EXEEXT) $($@objs) $(LIBS)
 
 # scp doesn't use the libs so is special.
@@ -165,13 +169,13 @@
 # multi-binary compilation.
 MULTIOBJS=
 ifeq ($(MULTI),1)
-	MULTIOBJS=dbmulti.o $(sort $(foreach prog, $(PROGRAMS), $($(prog)objs)))
+	MULTIOBJS=dbmulti.o $(sort $(foreach prog, $(PROGRAMS), $($(prog)objs))) @CRYPTLIB@ 
 	CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI
 endif
 
 dropbearmulti: multilink 
 
-multibinary: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile
+multibinary: $(HEADERS) $(MULTIOBJS) $(LIBTOM_DEPS) Makefile
 	$(CC) $(LDFLAGS) -o dropbearmulti$(EXEEXT) $(MULTIOBJS) $(LIBS)
 
 multilink: multibinary $(addprefix link, $(PROGRAMS))
--- a/agentfwd.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/agentfwd.h	Wed May 16 22:54:51 2012 +0800
@@ -23,21 +23,41 @@
  * SOFTWARE. */
 #ifndef _AGENTFWD_H_
 #define _AGENTFWD_H_
-#ifndef DISABLE_AGENTFWD
 
 #include "includes.h"
 #include "chansession.h"
 #include "channel.h"
+#include "auth.h"
+#include "list.h"
 
-int agentreq(struct ChanSess * chansess);
-void agentsetauth(struct ChanSess *chansess);
-void agentcleanup(struct ChanSess * chansess);
-void agentset(struct ChanSess *chansess);
+#ifdef ENABLE_CLI_AGENTFWD
+
+/* An agent reply can be reasonably large, as it can
+ * contain a list of all public keys held by the agent.
+ * 10000 is arbitrary */
+#define MAX_AGENT_REPLY  10000
+
+/* client functions */
+void cli_load_agent_keys(m_list * ret_list);
+void agent_buf_sign(buffer *sigblob, sign_key *key, 
+    const unsigned char *data, unsigned int len);
+void cli_setup_agent(struct Channel *channel);
 
 #ifdef __hpux
 #define seteuid(a)       setresuid(-1, (a), -1)
 #define setegid(a)       setresgid(-1, (a), -1)
 #endif
 
-#endif /* DROPBEAR_AGENTFWD */
+extern const struct ChanType cli_chan_agent;
+
+#endif /* ENABLE_CLI_AGENTFWD */
+
+#ifdef ENABLE_SVR_AGENTFWD
+
+int svr_agentreq(struct ChanSess * chansess);
+void svr_agentcleanup(struct ChanSess * chansess);
+void svr_agentset(struct ChanSess *chansess);
+
+#endif /* ENABLE_SVR_AGENTFWD */
+
 #endif /* _AGENTFWD_H_ */
--- a/algo.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/algo.h	Wed May 16 22:54:51 2012 +0800
@@ -50,7 +50,8 @@
 extern algo_type sshhostkey[];
 extern algo_type sshciphers[];
 extern algo_type sshhashes[];
-extern algo_type sshcompress[];
+extern algo_type ssh_compress[];
+extern algo_type ssh_nocompress[];
 
 extern const struct dropbear_cipher dropbear_nocipher;
 extern const struct dropbear_cipher_mode dropbear_mode_none;
--- a/auth.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/auth.h	Wed May 16 22:54:51 2012 +0800
@@ -26,6 +26,7 @@
 #define _AUTH_H_
 
 #include "includes.h"
+#include "signkey.h"
 #include "chansession.h"
 
 void svr_authinitialise();
@@ -73,6 +74,7 @@
 int cli_auth_pubkey();
 void cli_auth_interactive();
 char* getpass_or_cancel(char* prompt);
+void cli_auth_pubkey_cleanup();
 
 
 #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
@@ -97,7 +99,6 @@
  * relatively little extraneous bits when used for the client rather than the
  * server */
 struct AuthState {
-
 	char *username; /* This is the username the client presents to check. It
 					   is updated each run through, used for auth checking */
 	unsigned char authtypes; /* Flags indicating which auth types are still 
@@ -120,19 +121,6 @@
 #ifdef ENABLE_SVR_PUBKEY_OPTIONS
 	struct PubKeyOptions* pubkey_options;
 #endif
-
-};
-
-struct SignKeyList;
-/* A singly linked list of signing keys */
-struct SignKeyList {
-
-	sign_key *key;
-	int type; /* The type of key */
-	struct SignKeyList *next;
-	/* filename? or the buffer? for encrypted keys, so we can later get
-	 * the private key portion */
-
 };
 
 #ifdef ENABLE_SVR_PUBKEY_OPTIONS
@@ -145,7 +133,6 @@
 	int no_pty_flag;
 	/* "command=" option. */
 	unsigned char * forced_command;
-
 };
 #endif
 
--- a/bignum.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/bignum.c	Wed May 16 22:54:51 2012 +0800
@@ -31,7 +31,7 @@
 void m_mp_init(mp_int *mp) {
 
 	if (mp_init(mp) != MP_OKAY) {
-		dropbear_exit("mem alloc error");
+		dropbear_exit("Mem alloc error");
 	}
 }
 
@@ -45,7 +45,7 @@
     va_start(args, mp);        /* init args to next argument from caller */
     while (cur_arg != NULL) {
         if (mp_init(cur_arg) != MP_OKAY) {
-			dropbear_exit("mem alloc error");
+			dropbear_exit("Mem alloc error");
         }
         cur_arg = va_arg(args, mp_int*);
     }
@@ -55,7 +55,7 @@
 void bytes_to_mp(mp_int *mp, const unsigned char* bytes, unsigned int len) {
 
 	if (mp_read_unsigned_bin(mp, (unsigned char*)bytes, len) != MP_OKAY) {
-		dropbear_exit("mem alloc error");
+		dropbear_exit("Mem alloc error");
 	}
 }
 
--- a/buffer.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/buffer.c	Wed May 16 22:54:51 2012 +0800
@@ -106,7 +106,7 @@
 /* Set the length of the buffer */
 void buf_setlen(buffer* buf, unsigned int len) {
 	if (len > buf->size) {
-		dropbear_exit("bad buf_setlen");
+		dropbear_exit("Bad buf_setlen");
 	}
 	buf->len = len;
 }
@@ -114,7 +114,7 @@
 /* Increment the length of the buffer */
 void buf_incrlen(buffer* buf, unsigned int incr) {
 	if (incr > BUF_MAX_INCR || buf->len + incr > buf->size) {
-		dropbear_exit("bad buf_incrlen");
+		dropbear_exit("Bad buf_incrlen");
 	}
 	buf->len += incr;
 }
@@ -122,7 +122,7 @@
 void buf_setpos(buffer* buf, unsigned int pos) {
 
 	if (pos > buf->len) {
-		dropbear_exit("bad buf_setpos");
+		dropbear_exit("Bad buf_setpos");
 	}
 	buf->pos = pos;
 }
@@ -130,7 +130,7 @@
 /* increment the postion by incr, increasing the buffer length if required */
 void buf_incrwritepos(buffer* buf, unsigned int incr) {
 	if (incr > BUF_MAX_INCR || buf->pos + incr > buf->size) {
-		dropbear_exit("bad buf_incrwritepos");
+		dropbear_exit("Bad buf_incrwritepos");
 	}
 	buf->pos += incr;
 	if (buf->pos > buf->len) {
@@ -144,7 +144,7 @@
 	if (incr > BUF_MAX_INCR ||
 			(unsigned int)((int)buf->pos + incr) > buf->len 
 			|| ((int)buf->pos + incr) < 0) {
-		dropbear_exit("bad buf_incrpos");
+		dropbear_exit("Bad buf_incrpos");
 	}
 	buf->pos += incr;
 }
@@ -155,7 +155,7 @@
 	/* This check is really just ==, but the >= allows us to check for the
 	 * bad case of pos > len, which should _never_ happen. */
 	if (buf->pos >= buf->len) {
-		dropbear_exit("bad buf_getbyte");
+		dropbear_exit("Bad buf_getbyte");
 	}
 	return buf->data[buf->pos++];
 }
@@ -185,7 +185,7 @@
 unsigned char* buf_getptr(buffer* buf, unsigned int len) {
 
 	if (buf->pos + len > buf->len) {
-		dropbear_exit("bad buf_getptr");
+		dropbear_exit("Bad buf_getptr");
 	}
 	return &buf->data[buf->pos];
 }
@@ -195,7 +195,7 @@
 unsigned char* buf_getwriteptr(buffer* buf, unsigned int len) {
 
 	if (buf->pos + len > buf->size) {
-		dropbear_exit("bad buf_getwriteptr");
+		dropbear_exit("Bad buf_getwriteptr");
 	}
 	return &buf->data[buf->pos];
 }
@@ -209,7 +209,7 @@
 	unsigned char* ret;
 	len = buf_getint(buf);
 	if (len > MAX_STRING_LEN) {
-		dropbear_exit("string too long");
+		dropbear_exit("String too long");
 	}
 
 	if (retlen != NULL) {
@@ -223,6 +223,20 @@
 	return ret;
 }
 
+/* Return a string as a newly allocated buffer */
+buffer * buf_getstringbuf(buffer *buf) {
+	buffer *ret;
+	unsigned char* str;
+	unsigned int len;
+	str = buf_getstring(buf, &len);
+	ret = m_malloc(sizeof(*ret));
+	ret->data = str;
+	ret->len = len;
+	ret->size = len;
+	ret->pos = 0;
+	return ret;
+}
+
 /* Just increment the buffer position the same as if we'd used buf_getstring,
  * but don't bother copying/malloc()ing for it */
 void buf_eatstring(buffer *buf) {
--- a/buffer.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/buffer.h	Wed May 16 22:54:51 2012 +0800
@@ -55,6 +55,7 @@
 unsigned char* buf_getptr(buffer* buf, unsigned int len);
 unsigned char* buf_getwriteptr(buffer* buf, unsigned int len);
 unsigned char* buf_getstring(buffer* buf, unsigned int *retlen);
+buffer * buf_getstringbuf(buffer *buf);
 void buf_eatstring(buffer *buf);
 void buf_putint(buffer* buf, unsigned int val);
 void buf_putstring(buffer* buf, const unsigned char* str, unsigned int len);
--- a/channel.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/channel.h	Wed May 16 22:54:51 2012 +0800
@@ -58,7 +58,7 @@
 	unsigned int recvmaxpacket, transmaxpacket;
 	void* typedata; /* a pointer to type specific data */
 	int writefd; /* read from wire, written to insecure side */
-	int readfd; /* read from insecure size, written to wire */
+	int readfd; /* read from insecure side, 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 */
@@ -69,6 +69,10 @@
 	int sent_close, recv_close;
 	int recv_eof, sent_eof;
 
+	/* Set after running the ChanType-specific close hander
+	 * to ensure we don't run it twice (nor type->checkclose()). */
+	int close_handler_done;
+
 	int initconn; /* used for TCP forwarding, whether the channel has been
 					 fully initialised */
 
--- a/chansession.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/chansession.h	Wed May 16 22:54:51 2012 +0800
@@ -50,6 +50,10 @@
 
 	/* exit details */
 	struct exitinfo exit;
+
+	/* Used to set $SSH_CONNECTION in the child session. 
+	Is only set temporarily before forking */
+	char *connection_string;
 	
 #ifndef DISABLE_X11FWD
 	struct Listener * x11listener;
@@ -60,11 +64,15 @@
 	unsigned char x11singleconn;
 #endif
 
-#ifndef DISABLE_AGENTFWD
+#ifdef ENABLE_SVR_AGENTFWD
 	struct Listener * agentlistener;
 	char * agentfile;
 	char * agentdir;
 #endif
+
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+	char *original_command;
+#endif
 };
 
 struct ChildPid {
@@ -81,6 +89,7 @@
 #ifdef ENABLE_CLI_NETCAT
 void cli_send_netcat_request();
 #endif
+void cli_start_send_channel_request(struct Channel *channel, unsigned char *type);
 
 void svr_chansessinitialise();
 extern const struct ChanType svrchansess;
--- a/circbuffer.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/circbuffer.c	Wed May 16 22:54:51 2012 +0800
@@ -33,7 +33,7 @@
 	circbuffer *cbuf = NULL;
 
 	if (size > MAX_CBUF_SIZE) {
-		dropbear_exit("bad cbuf size");
+		dropbear_exit("Bad cbuf size");
 	}
 
 	cbuf = (circbuffer*)m_malloc(sizeof(circbuffer));
@@ -48,6 +48,7 @@
 
 void cbuf_free(circbuffer * cbuf) {
 
+	m_burn(cbuf->data, cbuf->size);
 	m_free(cbuf->data);
 	m_free(cbuf);
 }
@@ -101,7 +102,7 @@
 
 unsigned char* cbuf_readptr(circbuffer *cbuf, unsigned int len) {
 	if (len > cbuf_readlen(cbuf)) {
-		dropbear_exit("bad cbuf read");
+		dropbear_exit("Bad cbuf read");
 	}
 
 	return &cbuf->data[cbuf->readpos];
@@ -110,7 +111,7 @@
 unsigned char* cbuf_writeptr(circbuffer *cbuf, unsigned int len) {
 
 	if (len > cbuf_writelen(cbuf)) {
-		dropbear_exit("bad cbuf write");
+		dropbear_exit("Bad cbuf write");
 	}
 
 	return &cbuf->data[cbuf->writepos];
@@ -118,7 +119,7 @@
 
 void cbuf_incrwrite(circbuffer *cbuf, unsigned int len) {
 	if (len > cbuf_writelen(cbuf)) {
-		dropbear_exit("bad cbuf write");
+		dropbear_exit("Bad cbuf write");
 	}
 
 	cbuf->used += len;
@@ -129,7 +130,7 @@
 
 void cbuf_incrread(circbuffer *cbuf, unsigned int len) {
 	if (len > cbuf_readlen(cbuf)) {
-		dropbear_exit("bad cbuf read");
+		dropbear_exit("Bad cbuf read");
 	}
 
 	dropbear_assert(cbuf->used >= len);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-agentfwd.c	Wed May 16 22:54:51 2012 +0800
@@ -0,0 +1,312 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * 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"
+
+#ifdef ENABLE_CLI_AGENTFWD
+
+#include "agentfwd.h"
+#include "session.h"
+#include "ssh.h"
+#include "dbutil.h"
+#include "chansession.h"
+#include "channel.h"
+#include "packet.h"
+#include "buffer.h"
+#include "random.h"
+#include "listener.h"
+#include "runopts.h"
+#include "atomicio.h"
+#include "signkey.h"
+#include "auth.h"
+
+/* The protocol implemented to talk to OpenSSH's SSH2 agent is documented in
+   PROTOCOL.agent in recent OpenSSH source distributions (5.1p1 has it). */
+
+static int new_agent_chan(struct Channel * channel);
+
+const struct ChanType cli_chan_agent = {
+	0, /* sepfds */
+	"[email protected]",
+	new_agent_chan,
+	NULL,
+	NULL,
+	NULL
+};
+
+static int connect_agent() {
+
+	int fd = -1;
+	char* agent_sock = NULL;
+
+	agent_sock = getenv("SSH_AUTH_SOCK");
+	if (agent_sock == NULL)
+		return -1;
+
+	fd = connect_unix(agent_sock);
+
+	if (fd < 0) {
+		dropbear_log(LOG_INFO, "Failed to connect to agent");
+	}
+
+	return fd;
+}
+
+// handle a request for a connection to the locally running ssh-agent
+// or forward.
+static int new_agent_chan(struct Channel * channel) {
+
+	int fd = -1;
+
+	if (!cli_opts.agent_fwd)
+		return SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
+
+	fd = connect_agent();
+	if (fd < 0) {
+		return SSH_OPEN_CONNECT_FAILED;
+	}
+
+	setnonblocking(fd);
+
+	ses.maxfd = MAX(ses.maxfd, fd);
+
+	channel->readfd = fd;
+	channel->writefd = fd;
+
+	// success
+	return 0;
+}
+
+/* Sends a request to the agent, returning a newly allocated buffer
+ * with the response */
+/* This function will block waiting for a response - it will
+ * only be used by client authentication (not for forwarded requests)
+ * won't cause problems for interactivity. */
+/* Packet format (from draft-ylonen)
+   4 bytes     Length, msb first.  Does not include length itself.
+   1 byte      Packet type.  The value 255 is reserved for future extensions.
+   data        Any data, depending on packet type.  Encoding as in the ssh packet
+               protocol.
+*/
+static buffer * agent_request(unsigned char type, buffer *data) {
+
+	buffer * payload = NULL;
+	buffer * inbuf = NULL;
+	size_t readlen = 0;
+	ssize_t ret;
+	const int fd = cli_opts.agent_fd;
+	unsigned int data_len = 0;
+	if (data)
+	{
+		data_len = data->len;
+	}
+
+	payload = buf_new(4 + 1 + data_len);
+
+	buf_putint(payload, 1 + data_len);
+	buf_putbyte(payload, type);
+	if (data) {
+		buf_putbytes(payload, data->data, data->len);
+	}
+	buf_setpos(payload, 0);
+
+	ret = atomicio(write, fd, buf_getptr(payload, payload->len), payload->len);
+	if ((size_t)ret != payload->len) {
+		TRACE(("write failed fd %d for agent_request, %s", fd, strerror(errno)))
+		goto out;
+	}
+
+	buf_free(payload);
+	payload = NULL;
+	TRACE(("Wrote out bytes for agent_request"))
+	/* Now we read the response */
+	inbuf = buf_new(4);
+	ret = atomicio(read, fd, buf_getwriteptr(inbuf, 4), 4);
+	if (ret != 4) {
+		TRACE(("read of length failed for agent_request"))
+		goto out;
+	}
+	buf_setpos(inbuf, 0);
+	buf_setlen(inbuf, ret);
+
+	readlen = buf_getint(inbuf);
+	if (readlen > MAX_AGENT_REPLY) {
+		TRACE(("agent reply is too big"));
+		goto out;
+	}
+	
+	TRACE(("agent_request readlen is %d", readlen))
+
+	buf_resize(inbuf, readlen);
+	buf_setpos(inbuf, 0);
+	ret = atomicio(read, fd, buf_getwriteptr(inbuf, readlen), readlen);
+	if ((size_t)ret != readlen) {
+		TRACE(("read of data failed for agent_request"))
+		goto out;
+	}
+	buf_incrwritepos(inbuf, readlen);
+	buf_setpos(inbuf, 0);
+	TRACE(("agent_request success, length %d", readlen))
+
+out:
+	if (payload)
+		buf_free(payload);
+
+	return inbuf;
+}
+
+static void agent_get_key_list(m_list * ret_list)
+{
+	buffer * inbuf = NULL;
+	unsigned int num = 0;
+	unsigned char packet_type;
+	unsigned int i;
+	int ret;
+
+	inbuf = agent_request(SSH2_AGENTC_REQUEST_IDENTITIES, NULL);
+	if (!inbuf) {
+		TRACE(("agent_request failed returning identities"))
+		goto out;
+	}
+
+	/* The reply has a format of:
+		byte			SSH2_AGENT_IDENTITIES_ANSWER
+		uint32			num_keys
+  	   Followed by zero or more consecutive keys, encoded as:
+       	 string			key_blob
+    	 string			key_comment
+	 */
+	packet_type = buf_getbyte(inbuf);
+	if (packet_type != SSH2_AGENT_IDENTITIES_ANSWER) {
+		goto out;
+	}
+
+	num = buf_getint(inbuf);
+	for (i = 0; i < num; i++) {
+		sign_key * pubkey = NULL;
+		int key_type = DROPBEAR_SIGNKEY_ANY;
+		buffer * key_buf;
+
+		/* each public key is encoded as a string */
+		key_buf = buf_getstringbuf(inbuf);
+		pubkey = new_sign_key();
+		ret = buf_get_pub_key(key_buf, pubkey, &key_type);
+		buf_free(key_buf);
+		if (ret != DROPBEAR_SUCCESS) {
+			/* This is slack, properly would cleanup vars etc */
+			dropbear_exit("Bad pubkey received from agent");
+		}
+		pubkey->type = key_type;
+		pubkey->source = SIGNKEY_SOURCE_AGENT;
+
+		list_append(ret_list, pubkey);
+
+		/* We'll ignore the comment for now. might want it later.*/
+		buf_eatstring(inbuf);
+	}
+
+out:
+	if (inbuf) {
+		buf_free(inbuf);
+		inbuf = NULL;
+	}
+}
+
+void cli_setup_agent(struct Channel *channel) {
+	if (!getenv("SSH_AUTH_SOCK")) {
+		return;
+	}
+	
+	cli_start_send_channel_request(channel, "[email protected]");
+	/* Don't want replies */
+	buf_putbyte(ses.writepayload, 0);
+	encrypt_packet();
+}
+
+/* Returned keys are prepended to ret_list, which will
+   be updated. */
+void cli_load_agent_keys(m_list *ret_list) {
+	/* agent_fd will be closed after successful auth */
+	cli_opts.agent_fd = connect_agent();
+	if (cli_opts.agent_fd < 0) {
+		return;
+	}
+
+	agent_get_key_list(ret_list);
+}
+
+void agent_buf_sign(buffer *sigblob, sign_key *key, 
+		const unsigned char *data, unsigned int len) {
+	buffer *request_data = NULL;
+	buffer *response = NULL;
+	unsigned int siglen;
+	int packet_type;
+	
+	/* Request format
+	byte			SSH2_AGENTC_SIGN_REQUEST
+	string			key_blob
+	string			data
+	uint32			flags
+	*/
+	request_data = buf_new(MAX_PUBKEY_SIZE + len + 12);
+	buf_put_pub_key(request_data, key, key->type);
+	
+	buf_putstring(request_data, data, len);
+	buf_putint(request_data, 0);
+	
+	response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data);
+	
+	if (!response) {
+		goto fail;
+	}
+	
+	packet_type = buf_getbyte(response);
+	if (packet_type != SSH2_AGENT_SIGN_RESPONSE) {
+		goto fail;
+	}
+	
+	/* Response format
+	byte			SSH2_AGENT_SIGN_RESPONSE
+	string			signature_blob
+	*/
+	siglen = buf_getint(response);
+	buf_putbytes(sigblob, buf_getptr(response, siglen), siglen);
+	goto cleanup;
+	
+fail:
+	/* XXX don't fail badly here. instead propagate a failure code back up to
+	   the cli auth pubkey code, and just remove this key from the list of 
+	   ones to try. */
+	dropbear_exit("Agent failed signing key");
+
+cleanup:
+	if (request_data) {
+		buf_free(request_data);
+	}
+	if (response) {
+		buf_free(response);
+	}
+}
+
+#endif
--- a/cli-algo.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-algo.c	Wed May 16 22:54:51 2012 +0800
@@ -67,7 +67,7 @@
 			remotealgos[count] = &algolist[i+1];
 			count++;
 		}
-		if (count == MAX_PROPOSED_ALGO) {
+		if (count >= MAX_PROPOSED_ALGO) {
 			break;
 		}
 	}
--- a/cli-auth.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-auth.c	Wed May 16 22:54:51 2012 +0800
@@ -91,7 +91,7 @@
 		}
 	}
 
-	printf("%s\n", banner);
+	fprintf(stderr, "%s\n", banner);
 
 out:
 	m_free(banner);
@@ -234,6 +234,10 @@
 	ses.authstate.authdone = 1;
 	cli_ses.state = USERAUTH_SUCCESS_RCVD;
 	cli_ses.lastauthtype = AUTH_TYPE_NONE;
+
+#ifdef ENABLE_CLI_PUBKEY_AUTH
+	cli_auth_pubkey_cleanup();
+#endif
 }
 
 void cli_auth_try() {
@@ -253,7 +257,7 @@
 #endif
 
 #ifdef ENABLE_CLI_INTERACT_AUTH
-	if (ses.keys->trans_algo_crypt->cipherdesc == NULL) {
+	if (ses.keys->trans.algo_crypt->cipherdesc == NULL) {
 		fprintf(stderr, "Sorry, I won't let you use interactive auth unencrypted.\n");
 	}
 	else if (!finished && ses.authstate.authtypes & AUTH_TYPE_INTERACT) {
@@ -268,7 +272,7 @@
 #endif
 
 #ifdef ENABLE_CLI_PASSWORD_AUTH
-	if (ses.keys->trans_algo_crypt->cipherdesc == NULL) {
+	if (ses.keys->trans.algo_crypt->cipherdesc == NULL) {
 		fprintf(stderr, "Sorry, I won't let you use password auth unencrypted.\n");
 	}
 	else if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
--- a/cli-authinteract.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-authinteract.c	Wed May 16 22:54:51 2012 +0800
@@ -131,6 +131,7 @@
 		response_len = strlen(response);
 		buf_putstring(ses.writepayload, response, response_len);
 		m_burn(response, response_len);
+		m_free(prompt);
 		m_free(response);
 	}
 
--- a/cli-authpubkey.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-authpubkey.c	Wed May 16 22:54:51 2012 +0800
@@ -30,6 +30,7 @@
 #include "ssh.h"
 #include "runopts.h"
 #include "auth.h"
+#include "agentfwd.h"
 
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign);
@@ -37,30 +38,23 @@
 /* Called when we receive a SSH_MSG_USERAUTH_FAILURE for a pubkey request.
  * We use it to remove the key we tried from the list */
 void cli_pubkeyfail() {
-
-	struct SignKeyList *keyitem;
-	struct SignKeyList **previtem;
-
-	TRACE(("enter cli_pubkeyfail"))
-	previtem = &cli_opts.privkeys;
-
-	/* Find the key we failed with, and remove it */
-	for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) {
-		if (keyitem == cli_ses.lastprivkey) {
-			*previtem = keyitem->next;
+	m_list_elem *iter;
+	for (iter = cli_opts.privkeys->first; iter; iter = iter->next) {
+		sign_key *iter_key = (sign_key*)iter->item;
+		
+		if (iter_key == cli_ses.lastprivkey)
+		{
+			/* found the failing key */
+			list_remove(iter);
+			sign_key_free(iter_key);
+			cli_ses.lastprivkey = NULL;
+			return;
 		}
-		previtem = &keyitem;
 	}
-
-	sign_key_free(cli_ses.lastprivkey->key); /* It won't be used again */
-	m_free(cli_ses.lastprivkey);
-
-	TRACE(("leave cli_pubkeyfail"))
 }
 
 void recv_msg_userauth_pk_ok() {
-
-	struct SignKeyList *keyitem = NULL;
+	m_list_elem *iter;
 	buffer* keybuf = NULL;
 	char* algotype = NULL;
 	unsigned int algolen;
@@ -80,9 +74,9 @@
 
 	/* Iterate through our keys, find which one it was that matched, and
 	 * send a real request with that key */
-	for (keyitem = cli_opts.privkeys; keyitem != NULL; keyitem = keyitem->next) {
-
-		if (keyitem->type != keytype) {
+	for (iter = cli_opts.privkeys->first; iter; iter = iter->next) {
+		sign_key *key = (sign_key*)iter->item;
+		if (key->type != keytype) {
 			/* Types differed */
 			TRACE(("types differed"))
 			continue;
@@ -90,7 +84,7 @@
 
 		/* Now we compare the contents of the key */
 		keybuf->pos = keybuf->len = 0;
-		buf_put_pub_key(keybuf, keyitem->key, keytype);
+		buf_put_pub_key(keybuf, key, keytype);
 		buf_setpos(keybuf, 0);
 		buf_incrpos(keybuf, 4); /* first int is the length of the remainder (ie
 								   remotelen) which has already been taken from
@@ -114,11 +108,11 @@
 	}
 	buf_free(keybuf);
 
-	if (keyitem != NULL) {
+	if (iter != NULL) {
 		TRACE(("matching key"))
 		/* XXX TODO: if it's an encrypted key, here we ask for their
 		 * password */
-		send_msg_userauth_pubkey(keyitem->key, keytype, 1);
+		send_msg_userauth_pubkey((sign_key*)iter->item, keytype, 1);
 	} else {
 		TRACE(("That was whacky. We got told that a key was valid, but it didn't match our list. Sounds like dodgy code on Dropbear's part"))
 	}
@@ -126,6 +120,27 @@
 	TRACE(("leave recv_msg_userauth_pk_ok"))
 }
 
+void cli_buf_put_sign(buffer* buf, sign_key *key, int type, 
+			const unsigned char *data, unsigned int len)
+{
+#ifdef ENABLE_CLI_AGENTFWD
+	if (key->source == SIGNKEY_SOURCE_AGENT) {
+		/* Format the agent signature ourselves, as buf_put_sign would. */
+		buffer *sigblob;
+		sigblob = buf_new(MAX_PUBKEY_SIZE);
+		agent_buf_sign(sigblob, key, data, len);
+		buf_setpos(sigblob, 0);
+		buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
+				sigblob->len);
+
+		buf_free(sigblob);
+	} else 
+#endif /* ENABLE_CLI_AGENTFWD */
+	{
+		buf_put_sign(buf, key, type, data, len);
+	}
+}
+
 /* TODO: make it take an agent reference to use as well */
 static void send_msg_userauth_pubkey(sign_key *key, int type, int realsign) {
 
@@ -161,7 +176,7 @@
 		sigbuf = buf_new(4 + SHA1_HASH_SIZE + ses.writepayload->len);
 		buf_putstring(sigbuf, ses.session_id, SHA1_HASH_SIZE);
 		buf_putbytes(sigbuf, ses.writepayload->data, ses.writepayload->len);
-		buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len);
+		cli_buf_put_sign(ses.writepayload, key, type, sigbuf->data, sigbuf->len);
 		buf_free(sigbuf); /* Nothing confidential in the buffer */
 	}
 
@@ -169,20 +184,43 @@
 	TRACE(("leave send_msg_userauth_pubkey"))
 }
 
+/* Returns 1 if a key was tried */
 int cli_auth_pubkey() {
 
 	TRACE(("enter cli_auth_pubkey"))
 
-	if (cli_opts.privkeys != NULL) {
+#ifdef ENABLE_CLI_AGENTFWD
+	if (!cli_opts.agent_keys_loaded) {
+		/* get the list of available keys from the agent */
+		cli_load_agent_keys(cli_opts.privkeys);
+		cli_opts.agent_keys_loaded = 1;
+	}
+#endif
+
+	if (cli_opts.privkeys->first) {
+		sign_key * key = (sign_key*)cli_opts.privkeys->first->item;
 		/* Send a trial request */
-		send_msg_userauth_pubkey(cli_opts.privkeys->key,
-				cli_opts.privkeys->type, 0);
-		cli_ses.lastprivkey = cli_opts.privkeys;
+		send_msg_userauth_pubkey(key, key->type, 0);
+		cli_ses.lastprivkey = key;
 		TRACE(("leave cli_auth_pubkey-success"))
 		return 1;
 	} else {
+		/* no more keys left */
 		TRACE(("leave cli_auth_pubkey-failure"))
 		return 0;
 	}
 }
+
+void cli_auth_pubkey_cleanup() {
+
+#ifdef ENABLE_CLI_AGENTFWD
+	m_close(cli_opts.agent_fd);
+	cli_opts.agent_fd = -1;
+#endif
+
+	while (cli_opts.privkeys->first) {
+		sign_key * key = list_remove(cli_opts.privkeys->first);
+		sign_key_free(key);
+	}
+}
 #endif /* Pubkey auth */
--- a/cli-chansession.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-chansession.c	Wed May 16 22:54:51 2012 +0800
@@ -33,13 +33,12 @@
 #include "runopts.h"
 #include "termcodes.h"
 #include "chansession.h"
+#include "agentfwd.h"
 
 static void cli_closechansess(struct Channel *channel);
 static int cli_initchansess(struct Channel *channel);
 static void cli_chansessreq(struct Channel *channel);
 
-static void start_channel_request(struct Channel *channel, unsigned char *type);
-
 static void send_chansess_pty_req(struct Channel *channel);
 static void send_chansess_shell_req(struct Channel *channel);
 
@@ -92,7 +91,7 @@
 
 }
 
-static void start_channel_request(struct Channel *channel, 
+void cli_start_send_channel_request(struct Channel *channel, 
 		unsigned char *type) {
 
 	CHECKCLEARTOWRITE();
@@ -287,7 +286,7 @@
 
 	TRACE(("enter send_chansess_pty_req"))
 
-	start_channel_request(channel, "pty-req");
+	cli_start_send_channel_request(channel, "pty-req");
 
 	/* Don't want replies */
 	buf_putbyte(ses.writepayload, 0);
@@ -309,7 +308,7 @@
 
 	/* Set up a window-change handler */
 	if (signal(SIGWINCH, sigwinch_handler) == SIG_ERR) {
-		dropbear_exit("signal error");
+		dropbear_exit("Signal error");
 	}
 	TRACE(("leave send_chansess_pty_req"))
 }
@@ -330,7 +329,7 @@
 		reqtype = "shell";
 	}
 
-	start_channel_request(channel, reqtype);
+	cli_start_send_channel_request(channel, reqtype);
 
 	/* XXX TODO */
 	buf_putbyte(ses.writepayload, 0); /* Don't want replies */
@@ -361,6 +360,12 @@
 
 	cli_init_stdpipe_sess(channel);
 
+#ifdef ENABLE_CLI_AGENTFWD
+	if (cli_opts.agent_fwd) {
+		cli_setup_agent(channel);
+	}
+#endif
+
 	if (cli_opts.wantpty) {
 		send_chansess_pty_req(channel);
 	}
@@ -376,20 +381,20 @@
 
 #ifdef ENABLE_CLI_NETCAT
 
+static const struct ChanType cli_chan_netcat = {
+	0, /* sepfds */
+	"direct-tcpip",
+	cli_init_stdpipe_sess, /* inithandler */
+	NULL,
+	NULL,
+	cli_closechansess
+};
+
 void cli_send_netcat_request() {
 
 	const unsigned char* source_host = "127.0.0.1";
 	const int source_port = 22;
 
-	const struct ChanType cli_chan_netcat = {
-		0, /* sepfds */
-		"direct-tcpip",
-		cli_init_stdpipe_sess, /* inithandler */
-		NULL,
-		NULL,
-		cli_closechansess
-	};
-
 	cli_opts.wantpty = 0;
 
 	if (send_msg_channel_open_init(STDIN_FILENO, &cli_chan_netcat) 
@@ -424,16 +429,3 @@
 	TRACE(("leave cli_send_chansess_request"))
 
 }
-
-
-#if 0
-	while (cli_opts.localfwds != NULL) {
-		ret = cli_localtcp(cli_opts.localfwds->listenport,
-				cli_opts.localfwds->connectaddr,
-				cli_opts.localfwds->connectport);
-		if (ret == DROPBEAR_FAILURE) {
-			dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d",
-					cli_opts.localfwds->listenport,
-					cli_opts.localfwds->connectaddr,
-					cli_opts.localfwds->connectport);
-#endif
--- a/cli-kex.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-kex.c	Wed May 16 22:54:51 2012 +0800
@@ -304,7 +304,7 @@
 		fseek(hostsfile, 0, SEEK_END); /* In case it wasn't opened append */
 		buf_setpos(line, 0);
 		buf_setlen(line, 0);
-		buf_putbytes(line, ses.remotehost, hostlen);
+		buf_putbytes(line, cli_opts.remotehost, hostlen);
 		buf_putbyte(line, ' ');
 		buf_putbytes(line, algoname, algolen);
 		buf_putbyte(line, ' ');
@@ -327,4 +327,5 @@
 	if (line != NULL) {
 		buf_free(line);
 	}
+	m_free(fingerprint);
 }
--- a/cli-main.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-main.c	Wed May 16 22:54:51 2012 +0800
@@ -29,10 +29,12 @@
 #include "runopts.h"
 #include "session.h"
 
-static void cli_dropbear_exit(int exitcode, const char* format, va_list param);
+static void cli_dropbear_exit(int exitcode, const char* format, va_list param) ATTRIB_NORETURN;
 static void cli_dropbear_log(int priority, const char* format, va_list param);
 
+#ifdef ENABLE_CLI_PROXYCMD
 static void cli_proxy_cmd(int *sock_in, int *sock_out);
+#endif
 
 #if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
 #if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
@@ -43,8 +45,6 @@
 
 	int sock_in, sock_out;
 	char* error = NULL;
-	char* hostandport;
-	int len;
 
 	_dropbear_exit = cli_dropbear_exit;
 	_dropbear_log = cli_dropbear_log;
@@ -63,6 +63,7 @@
 #ifdef ENABLE_CLI_PROXYCMD
 	if (cli_opts.proxycmd) {
 		cli_proxy_cmd(&sock_in, &sock_out);
+		m_free(cli_opts.proxycmd);
 	} else
 #endif
 	{
@@ -75,14 +76,7 @@
 		dropbear_exit("%s", error);
 	}
 
-	/* Set up the host:port log */
-	len = strlen(cli_opts.remotehost);
-	len += 10; /* 16 bit port and leeway*/
-	hostandport = (char*)m_malloc(len);
-	snprintf(hostandport, len, "%s:%s", 
-			cli_opts.remotehost, cli_opts.remoteport);
-
-	cli_session(sock_in, sock_out, hostandport);
+	cli_session(sock_in, sock_out);
 
 	/* not reached */
 	return -1;
@@ -94,11 +88,11 @@
 	char fmtbuf[300];
 
 	if (!sessinitdone) {
-		snprintf(fmtbuf, sizeof(fmtbuf), "exited: %s",
+		snprintf(fmtbuf, sizeof(fmtbuf), "Exited: %s",
 				format);
 	} else {
 		snprintf(fmtbuf, sizeof(fmtbuf), 
-				"connection to %s@%s:%s exited: %s", 
+				"Connection to %s@%s:%s exited: %s", 
 				cli_opts.username, cli_opts.remotehost, 
 				cli_opts.remoteport, format);
 	}
@@ -132,6 +126,7 @@
 	dropbear_exit("Failed to run '%s'\n", cmd);
 }
 
+#ifdef ENABLE_CLI_PROXYCMD
 static void cli_proxy_cmd(int *sock_in, int *sock_out) {
 	int ret;
 
@@ -144,3 +139,4 @@
 		*sock_in = *sock_out = -1;
 	}
 }
+#endif // ENABLE_CLI_PROXYCMD
--- a/cli-runopts.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-runopts.c	Wed May 16 22:54:51 2012 +0800
@@ -29,6 +29,7 @@
 #include "dbutil.h"
 #include "algo.h"
 #include "tcpfwd.h"
+#include "list.h"
 
 cli_runopts cli_opts; /* GLOBAL */
 
@@ -40,7 +41,7 @@
 static void loadidentityfile(const char* filename);
 #endif
 #ifdef ENABLE_CLI_ANYTCPFWD
-static void addforward(const char* str, struct TCPFwdList** fwdlist);
+static void addforward(const char* str, m_list *fwdlist);
 #endif
 #ifdef ENABLE_CLI_NETCAT
 static void add_netcat(const char *str);
@@ -66,15 +67,19 @@
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 					"-i <identityfile>   (multiple allowed)\n"
 #endif
+#ifdef ENABLE_CLI_AGENTFWD
+					"-A    Enable agent auth forwarding\n"
+#endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
-					"-L <listenport:remotehost:remoteport> Local port forwarding\n"
+					"-L <[listenaddress:]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"
+					"-R <[listenaddress:]listenport:remotehost:remoteport> Remote port forwarding\n"
 #endif
 					"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
 					"-K <keepalive>  (0 is never, default %d)\n"
+					"-I <idle_timeout>  (0 is never, default %d)\n"
 #ifdef ENABLE_CLI_NETCAT
 					"-B <endhost:endport> Netcat-alike forwarding\n"
 #endif				
@@ -85,12 +90,11 @@
 					"-v    verbose (compiled with DEBUG_TRACE)\n"
 #endif
 					,DROPBEAR_VERSION, cli_opts.progname,
-					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE);
+					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
 					
 }
 
 void cli_getopts(int argc, char ** argv) {
-
 	unsigned int i, j;
 	char ** next = 0;
 	unsigned int cmdlen;
@@ -110,6 +114,8 @@
 
 	char* recv_window_arg = NULL;
 	char* keepalive_arg = NULL;
+	char* idle_timeout_arg = NULL;
+	char *host_arg = NULL;
 
 	/* see printhelp() for options */
 	cli_opts.progname = argv[0];
@@ -123,18 +129,26 @@
 	cli_opts.always_accept_key = 0;
 	cli_opts.is_subsystem = 0;
 #ifdef ENABLE_CLI_PUBKEY_AUTH
-	cli_opts.privkeys = NULL;
+	cli_opts.privkeys = list_new();
 #endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
-	cli_opts.localfwds = NULL;
+	cli_opts.localfwds = list_new();
 	opts.listen_fwd_all = 0;
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
-	cli_opts.remotefwds = NULL;
+	cli_opts.remotefwds = list_new();
+#endif
+#ifdef ENABLE_CLI_AGENTFWD
+	cli_opts.agent_fwd = 0;
+	cli_opts.agent_fd = -1;
+	cli_opts.agent_keys_loaded = 0;
 #endif
 #ifdef ENABLE_CLI_PROXYCMD
 	cli_opts.proxycmd = NULL;
 #endif
+#ifndef DISABLE_ZLIB
+	opts.enable_compress = 1;
+#endif
 	/* not yet
 	opts.ipv4 = 1;
 	opts.ipv6 = 1;
@@ -156,7 +170,7 @@
 #ifdef ENABLE_CLI_REMOTETCPFWD
 		if (nextisremote) {
 			TRACE(("nextisremote true"))
-			addforward(argv[i], &cli_opts.remotefwds);
+			addforward(argv[i], cli_opts.remotefwds);
 			nextisremote = 0;
 			continue;
 		}
@@ -164,7 +178,7 @@
 #ifdef ENABLE_CLI_LOCALTCPFWD
 		if (nextislocal) {
 			TRACE(("nextislocal true"))
-			addforward(argv[i], &cli_opts.localfwds);
+			addforward(argv[i], cli_opts.localfwds);
 			nextislocal = 0;
 			continue;
 		}
@@ -261,6 +275,14 @@
 				case 'K':
 					next = &keepalive_arg;
 					break;
+				case 'I':
+					next = &idle_timeout_arg;
+					break;
+#ifdef ENABLE_CLI_AGENTFWD
+				case 'A':
+					cli_opts.agent_fwd = 1;
+					break;
+#endif
 #ifdef DEBUG_TRACE
 				case 'v':
 					debug_trace = 1;
@@ -299,12 +321,8 @@
 
 			/* Either the hostname or commands */
 
-			if (cli_opts.remotehost == NULL) {
-#ifdef ENABLE_CLI_MULTIHOP
-				parse_multihop_hostname(argv[i], argv[0]);
-#else
-				parse_hostname(argv[i]);
-#endif
+			if (host_arg == NULL) {
+				host_arg = argv[i];
 			} else {
 
 				/* this is part of the commands to send - after this we
@@ -333,7 +351,7 @@
 
 	/* And now a few sanity checks and setup */
 
-	if (cli_opts.remotehost == NULL) {
+	if (host_arg == NULL) {
 		printhelp();
 		exit(EXIT_FAILURE);
 	}
@@ -354,7 +372,7 @@
 
 	if (cli_opts.backgrounded && cli_opts.cmd == NULL
 			&& cli_opts.no_cmd == 0) {
-		dropbear_exit("command required for -f");
+		dropbear_exit("Command required for -f");
 	}
 	
 	if (recv_window_arg) {
@@ -364,9 +382,19 @@
 		}
 	}
 	if (keepalive_arg) {
-		if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
+		unsigned int val;
+		if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
 			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
 		}
+		opts.keepalive_secs = val;
+	}
+
+	if (idle_timeout_arg) {
+		unsigned int val;
+		if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
+			dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
+		}
+		opts.idle_timeout_secs = val;
 	}
 
 #ifdef ENABLE_CLI_NETCAT
@@ -374,36 +402,73 @@
 		dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
 	}
 #endif
-	
+
+	/* The hostname gets set up last, since
+	 * in multi-hop mode it will require knowledge
+	 * of other flags such as -i */
+#ifdef ENABLE_CLI_MULTIHOP
+	parse_multihop_hostname(host_arg, argv[0]);
+#else
+	parse_hostname(host_arg);
+#endif
 }
 
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 static void loadidentityfile(const char* filename) {
-
-	struct SignKeyList * nextkey;
 	sign_key *key;
 	int keytype;
 
 	key = new_sign_key();
 	keytype = DROPBEAR_SIGNKEY_ANY;
 	if ( readhostkey(filename, key, &keytype) != DROPBEAR_SUCCESS ) {
-
 		fprintf(stderr, "Failed loading keyfile '%s'\n", filename);
 		sign_key_free(key);
-
 	} else {
-
-		nextkey = (struct SignKeyList*)m_malloc(sizeof(struct SignKeyList));
-		nextkey->key = key;
-		nextkey->next = cli_opts.privkeys;
-		nextkey->type = keytype;
-		cli_opts.privkeys = nextkey;
+		key->type = keytype;
+		key->source = SIGNKEY_SOURCE_RAW_FILE;
+		key->filename = m_strdup(filename);
+		list_append(cli_opts.privkeys, key);
 	}
 }
 #endif
 
 #ifdef ENABLE_CLI_MULTIHOP
 
+static char*
+multihop_passthrough_args() {
+	char *ret;
+	int total;
+	unsigned int len = 0;
+	m_list_elem *iter;
+	/* Fill out -i and -W options that make sense for all
+	 * the intermediate processes */
+	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
+	{
+		sign_key * key = (sign_key*)iter->item;
+		len += 3 + strlen(key->filename);
+	}
+	len += 20; // space for -W <size>, terminator.
+	ret = m_malloc(len);
+	total = 0;
+
+	if (opts.recv_window != DEFAULT_RECV_WINDOW)
+	{
+		int written = snprintf(ret+total, len-total, "-W %d", opts.recv_window);
+		total += written;
+	}
+
+	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
+	{
+		sign_key * key = (sign_key*)iter->item;
+		const size_t size = len - total;
+		int written = snprintf(ret+total, size, "-i %s", key->filename);
+		dropbear_assert((unsigned int)written < size);
+		total += written;
+	}
+
+	return ret;
+}
+
 /* Sets up 'onion-forwarding' connections. This will spawn
  * a separate dbclient process for each hop.
  * As an example, if the cmdline is
@@ -418,7 +483,8 @@
  */
 static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
 	char *userhostarg = NULL;
-	char *last_hop = NULL;;
+	char *hostbuf = NULL;
+	char *last_hop = NULL;
 	char *remainder = NULL;
 
 	/* both scp and rsync parse a user@host argument
@@ -430,11 +496,12 @@
 			&& strchr(cli_opts.username, ',') 
 			&& strchr(cli_opts.username, '@')) {
 		unsigned int len = strlen(orighostarg) + strlen(cli_opts.username) + 2;
-		userhostarg = m_malloc(len);
-		snprintf(userhostarg, len, "%s@%s", cli_opts.username, orighostarg);
+		hostbuf = m_malloc(len);
+		snprintf(hostbuf, len, "%s@%s", cli_opts.username, orighostarg);
 	} else {
-		userhostarg = m_strdup(orighostarg);
+		hostbuf = m_strdup(orighostarg);
 	}
+	userhostarg = hostbuf;
 
 	last_hop = strrchr(userhostarg, ',');
 	if (last_hop) {
@@ -452,19 +519,28 @@
 	if (last_hop) {
 		/* Set up the proxycmd */
 		unsigned int cmd_len = 0;
+		char *passthrough_args = multihop_passthrough_args();
 		if (cli_opts.proxycmd) {
 			dropbear_exit("-J can't be used with multihop mode");
 		}
 		if (cli_opts.remoteport == NULL) {
 			cli_opts.remoteport = "22";
 		}
-		cmd_len = strlen(remainder) 
+		cmd_len = strlen(argv0) + strlen(remainder) 
 			+ strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
-			+ strlen(argv0) + 30;
+			+ strlen(passthrough_args)
+			+ 30;
 		cli_opts.proxycmd = m_malloc(cmd_len);
-		snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s", 
-				argv0, cli_opts.remotehost, cli_opts.remoteport, remainder);
+		snprintf(cli_opts.proxycmd, cmd_len, "%s -B %s:%s %s %s", 
+				argv0, cli_opts.remotehost, cli_opts.remoteport, 
+				passthrough_args, remainder);
+#ifndef DISABLE_ZLIB
+		/* The stream will be incompressible since it's encrypted. */
+		opts.enable_compress = 0;
+#endif
+		m_free(passthrough_args);
 	}
+	m_free(hostbuf);
 }
 #endif /* !ENABLE_CLI_MULTIHOP */
 
@@ -553,14 +629,16 @@
 }
 
 #ifdef ENABLE_CLI_ANYTCPFWD
-/* Turn a "listenport:remoteaddr:remoteport" string into into a forwarding
+/* Turn a "[listenaddr:]listenport:remoteaddr:remoteport" string into into a forwarding
  * set, and add it to the forwarding list */
-static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
+static void addforward(const char* origstr, m_list *fwdlist) {
 
+	char *part1 = NULL, *part2 = NULL, *part3 = NULL, *part4 = NULL;
+	char * listenaddr = NULL;
 	char * listenport = NULL;
+	char * connectaddr = NULL;
 	char * connectport = NULL;
-	char * connectaddr = NULL;
-	struct TCPFwdList* newfwd = NULL;
+	struct TCPFwdEntry* newfwd = NULL;
 	char * str = NULL;
 
 	TRACE(("enter addforward"))
@@ -569,25 +647,43 @@
 	   is never free()d. */ 
 	str = m_strdup(origstr);
 
-	listenport = str;
+	part1 = str;
 
-	connectaddr = strchr(str, ':');
-	if (connectaddr == NULL) {
-		TRACE(("connectaddr == NULL"))
+	part2 = strchr(str, ':');
+	if (part2 == NULL) {
+		TRACE(("part2 == NULL"))
+		goto fail;
+	}
+	*part2 = '\0';
+	part2++;
+
+	part3 = strchr(part2, ':');
+	if (part3 == NULL) {
+		TRACE(("part3 == NULL"))
 		goto fail;
 	}
-	*connectaddr = '\0';
-	connectaddr++;
+	*part3 = '\0';
+	part3++;
+
+	part4 = strchr(part3, ':');
+	if (part4) {
+		*part4 = '\0';
+		part4++;
+	}
 
-	connectport = strchr(connectaddr, ':');
-	if (connectport == NULL) {
-		TRACE(("connectport == NULL"))
-		goto fail;
+	if (part4) {
+		listenaddr = part1;
+		listenport = part2;
+		connectaddr = part3;
+		connectport = part4;
+	} else {
+		listenaddr = NULL;
+		listenport = part1;
+		connectaddr = part2;
+		connectport = part3;
 	}
-	*connectport = '\0';
-	connectport++;
 
-	newfwd = (struct TCPFwdList*)m_malloc(sizeof(struct TCPFwdList));
+	newfwd = m_malloc(sizeof(struct TCPFwdEntry));
 
 	/* Now we check the ports - note that the port ints are unsigned,
 	 * the check later only checks for >= MAX_PORT */
@@ -601,6 +697,7 @@
 		goto fail;
 	}
 
+	newfwd->listenaddr = listenaddr;
 	newfwd->connectaddr = connectaddr;
 
 	if (newfwd->listenport > 65535) {
@@ -614,8 +711,7 @@
 	}
 
 	newfwd->have_reply = 0;
-	newfwd->next = *fwdlist;
-	*fwdlist = newfwd;
+	list_append(fwdlist, newfwd);
 
 	TRACE(("leave addforward: done"))
 	return;
--- a/cli-service.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-service.c	Wed May 16 22:54:51 2012 +0800
@@ -72,7 +72,7 @@
 			&& strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) {
 
 		if (ses.authstate.authdone != 1) {
-			dropbear_exit("request for connection before auth");
+			dropbear_exit("Request for connection before auth");
 		}
 
 		cli_ses.state = SERVICE_CONN_ACCEPT_RCVD;
@@ -81,5 +81,5 @@
 		return;
 	}
 
-	dropbear_exit("unrecognised service accept");
+	dropbear_exit("Unrecognised service accept");
 }
--- a/cli-session.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-session.c	Wed May 16 22:54:51 2012 +0800
@@ -35,6 +35,7 @@
 #include "service.h"
 #include "runopts.h"
 #include "chansession.h"
+#include "agentfwd.h"
 
 static void cli_remoteclosed();
 static void cli_sessionloop();
@@ -75,16 +76,19 @@
 #ifdef ENABLE_CLI_REMOTETCPFWD
 	&cli_chan_tcpremote,
 #endif
+#ifdef ENABLE_CLI_AGENTFWD
+	&cli_chan_agent,
+#endif
 	NULL /* Null termination */
 };
 
-void cli_session(int sock_in, int sock_out, char* remotehost) {
+void cli_session(int sock_in, int sock_out) {
 
 	seedrandom();
 
 	crypto_init();
 
-	common_session_init(sock_in, sock_out, remotehost);
+	common_session_init(sock_in, sock_out);
 
 	chaninitialise(cli_chantypes);
 
@@ -209,7 +213,7 @@
 				   is confusing, though stdout/stderr could be useful. */
 				devnull = open(_PATH_DEVNULL, O_RDONLY);
 				if (devnull < 0) {
-					dropbear_exit("opening /dev/null: %d %s",
+					dropbear_exit("Opening /dev/null: %d %s",
 							errno, strerror(errno));
 				}
 				dup2(devnull, STDIN_FILENO);
@@ -231,7 +235,7 @@
 				cli_send_netcat_request();
 			} else 
 #endif
-				if (!cli_opts.no_cmd) {
+			if (!cli_opts.no_cmd) {
 				cli_send_chansess_request();
 			}
 			TRACE(("leave cli_sessionloop: running"))
@@ -294,7 +298,7 @@
 	m_close(ses.sock_out);
 	ses.sock_in = -1;
 	ses.sock_out = -1;
-	dropbear_exit("remote closed the connection");
+	dropbear_exit("Remote closed the connection");
 }
 
 /* Operates in-place turning dirty (untrusted potentially containing control
--- a/cli-tcpfwd.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/cli-tcpfwd.c	Wed May 16 22:54:51 2012 +0800
@@ -45,7 +45,9 @@
 #endif
 
 #ifdef ENABLE_CLI_LOCALTCPFWD
-static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
+static int cli_localtcp(const char* listenaddr, 
+		unsigned int listenport, 
+		const char* remoteaddr,
 		unsigned int remoteport);
 static const struct ChanType cli_chan_tcplocal = {
 	1, /* sepfds */
@@ -59,33 +61,33 @@
 
 #ifdef ENABLE_CLI_LOCALTCPFWD
 void setup_localtcp() {
-
+	m_list_elem *iter;
 	int ret;
 
 	TRACE(("enter setup_localtcp"))
 
-	if (cli_opts.localfwds == NULL) {
-		TRACE(("cli_opts.localfwds == NULL"))
-	}
-
-	while (cli_opts.localfwds != NULL) {
-		ret = cli_localtcp(cli_opts.localfwds->listenport,
-				cli_opts.localfwds->connectaddr,
-				cli_opts.localfwds->connectport);
+	for (iter = cli_opts.localfwds->first; iter; iter = iter->next) {
+		struct TCPFwdEntry * fwd = (struct TCPFwdEntry*)iter->item;
+		ret = cli_localtcp(
+				fwd->listenaddr,
+				fwd->listenport,
+				fwd->connectaddr,
+				fwd->connectport);
 		if (ret == DROPBEAR_FAILURE) {
-			dropbear_log(LOG_WARNING, "Failed local port forward %d:%s:%d",
-					cli_opts.localfwds->listenport,
-					cli_opts.localfwds->connectaddr,
-					cli_opts.localfwds->connectport);
-		}
-
-		cli_opts.localfwds = cli_opts.localfwds->next;
+			dropbear_log(LOG_WARNING, "Failed local port forward %s:%d:%s:%d",
+					fwd->listenaddr,
+					fwd->listenport,
+					fwd->connectaddr,
+					fwd->connectport);
+		}		
 	}
 	TRACE(("leave setup_localtcp"))
 
 }
 
-static int cli_localtcp(unsigned int listenport, const char* remoteaddr,
+static int cli_localtcp(const char* listenaddr, 
+		unsigned int listenport, 
+		const char* remoteaddr,
 		unsigned int remoteport) {
 
 	struct TCPListener* tcpinfo = NULL;
@@ -99,10 +101,17 @@
 	tcpinfo->sendaddr = m_strdup(remoteaddr);
 	tcpinfo->sendport = remoteport;
 
-	if (opts.listen_fwd_all) {
-		tcpinfo->listenaddr = m_strdup("");
-	} else {
-		tcpinfo->listenaddr = m_strdup("localhost");
+	if (listenaddr)
+	{
+		tcpinfo->listenaddr = m_strdup(listenaddr);
+	}
+	else
+	{
+		if (opts.listen_fwd_all) {
+			tcpinfo->listenaddr = m_strdup("");
+		} else {
+			tcpinfo->listenaddr = m_strdup("localhost");
+		}
 	}
 	tcpinfo->listenport = listenport;
 
@@ -120,22 +129,15 @@
 #endif /* ENABLE_CLI_LOCALTCPFWD */
 
 #ifdef  ENABLE_CLI_REMOTETCPFWD
-static void send_msg_global_request_remotetcp(int port) {
+static void send_msg_global_request_remotetcp(const char *addr, 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, 1); /* want_reply */
-	if (opts.listen_fwd_all) {
-		listenspec = "";
-	} else {
-		listenspec = "localhost";
-	}
-	/* TODO: IPv6? */;
-	buf_putstring(ses.writepayload, listenspec, strlen(listenspec));
+	buf_putstring(ses.writepayload, addr, strlen(addr));
 	buf_putint(ses.writepayload, port);
 
 	encrypt_packet();
@@ -146,90 +148,97 @@
 /* The only global success/failure messages are for remotetcp.
  * Since there isn't any identifier in these messages, we have to rely on them
  * being in the same order as we sent the requests. This is the ordering
- * of the cli_opts.remotefwds list */
+ * of the cli_opts.remotefwds list.
+ * If the requested remote port is 0 the listen port will be
+ * dynamically allocated by the server and the port number will be returned
+ * to client and the port number reported to the user. */
 void cli_recv_msg_request_success() {
-
-	/* Nothing in the packet. We just mark off that we have received the reply,
+	/* We just mark off that we have received the reply,
 	 * so that we can report failure for later ones. */
-	struct TCPFwdList * iter = NULL;
-
-	iter = cli_opts.remotefwds;
-	while (iter != NULL) {
-		if (!iter->have_reply)
-		{
-			iter->have_reply = 1;
+	m_list_elem * iter = NULL;
+	for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
+		struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
+		if (!fwd->have_reply) {
+			fwd->have_reply = 1;
+			if (fwd->listenport == 0) {
+				/* The server should let us know which port was allocated if we requestd port 0 */
+				int allocport = buf_getint(ses.payload);
+				if (allocport > 0) {
+					dropbear_log(LOG_INFO, "Allocated port %d for remote forward to %s:%d", 
+							allocport, fwd->connectaddr, fwd->connectport);
+				}
+			}
 			return;
 		}
-		iter = iter->next;
 	}
 }
 
 void cli_recv_msg_request_failure() {
-	struct TCPFwdList * iter = NULL;
-
-	iter = cli_opts.remotefwds;
-	while (iter != NULL) {
-		if (!iter->have_reply)
-		{
-			iter->have_reply = 1;
-			dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", iter->listenport, iter->connectaddr, iter->connectport);
+	m_list_elem *iter;
+	for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
+		struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
+		if (!fwd->have_reply) {
+			fwd->have_reply = 1;
+			dropbear_log(LOG_WARNING, "Remote TCP forward request failed (port %d -> %s:%d)", fwd->listenport, fwd->connectaddr, fwd->connectport);
 			return;
 		}
-		iter = iter->next;
 	}
 }
 
 void setup_remotetcp() {
-
-	struct TCPFwdList * iter = NULL;
-
+	m_list_elem *iter;
 	TRACE(("enter setup_remotetcp"))
 
-	if (cli_opts.remotefwds == NULL) {
-		TRACE(("cli_opts.remotefwds == NULL"))
+	for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
+		struct TCPFwdEntry *fwd = (struct TCPFwdEntry*)iter->item;
+		if (!fwd->listenaddr)
+		{
+			// we store the addresses so that we can compare them
+			// when the server sends them back
+			if (opts.listen_fwd_all) {
+				fwd->listenaddr = m_strdup("");
+			} else {
+				fwd->listenaddr = m_strdup("localhost");
+			}
+		}
+		send_msg_global_request_remotetcp(fwd->listenaddr, fwd->listenport);
 	}
 
-	iter = cli_opts.remotefwds;
-
-	while (iter != NULL) {
-		send_msg_global_request_remotetcp(iter->listenport);
-		iter = iter->next;
-	}
 	TRACE(("leave setup_remotetcp"))
 }
 
 static int newtcpforwarded(struct Channel * channel) {
 
+    char *origaddr = NULL;
 	unsigned int origport;
-	struct TCPFwdList * iter = NULL;
+	m_list_elem * iter = NULL;
+	struct TCPFwdEntry *fwd;
 	char portstring[NI_MAXSERV];
 	int sock;
 	int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
 
-	/* We don't care what address they connected to */
-	buf_eatstring(ses.payload);
-
+	origaddr = buf_getstring(ses.payload, NULL);
 	origport = buf_getint(ses.payload);
 
 	/* Find which port corresponds */
-	iter = cli_opts.remotefwds;
-
-	while (iter != NULL) {
-		if (origport == iter->listenport) {
+	for (iter = cli_opts.remotefwds->first; iter; iter = iter->next) {
+		fwd = (struct TCPFwdEntry*)iter->item;
+		if (origport == fwd->listenport
+				&& (strcmp(origaddr, fwd->listenaddr) == 0)) {
 			break;
 		}
-		iter = iter->next;
 	}
 
 	if (iter == NULL) {
 		/* We didn't request forwarding on that port */
-		dropbear_log(LOG_INFO, "Server send unrequested port, from port %d", 
-										origport);
+        cleantext(origaddr);
+		dropbear_log(LOG_INFO, "Server sent unrequested forward from \"%s:%d\"", 
+                origaddr, origport);
 		goto out;
 	}
 	
-	snprintf(portstring, sizeof(portstring), "%d", iter->connectport);
-	sock = connect_remote(iter->connectaddr, portstring, 1, NULL);
+	snprintf(portstring, sizeof(portstring), "%d", fwd->connectport);
+	sock = connect_remote(fwd->connectaddr, portstring, 1, NULL);
 	if (sock < 0) {
 		TRACE(("leave newtcpdirect: sock failed"))
 		err = SSH_OPEN_CONNECT_FAILED;
@@ -246,6 +255,7 @@
 	err = SSH_OPEN_IN_PROGRESS;
 
 out:
+	m_free(origaddr);
 	TRACE(("leave newtcpdirect: err %d", err))
 	return err;
 }
--- a/common-algo.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/common-algo.c	Wed May 16 22:54:51 2012 +0800
@@ -31,7 +31,9 @@
 
 static int void_cipher(const unsigned char* in, unsigned char* out,
 		unsigned long len, void *cipher_state) {
-	memcpy(out, in, len);
+	if (in != out) {
+		memmove(out, in, len);
+	}
 	return CRYPT_OK;
 }
 
@@ -104,6 +106,14 @@
 static const struct dropbear_hash dropbear_sha1_96 = 
 	{&sha1_desc, 20, 12};
 #endif
+#ifdef DROPBEAR_SHA2_256_HMAC
+static const struct dropbear_hash dropbear_sha2_256 = 
+	{&sha256_desc, 32, 32};
+#endif
+#ifdef DROPBEAR_SHA2_512_HMAC
+static const struct dropbear_hash dropbear_sha2_512 =
+	{&sha512_desc, 64, 64};
+#endif
 #ifdef DROPBEAR_MD5_HMAC
 static const struct dropbear_hash dropbear_md5 = 
 	{&md5_desc, 16, 16};
@@ -153,10 +163,16 @@
 #ifdef DROPBEAR_NONE_CIPHER
 	{"none", 0, (void*)&dropbear_nocipher, 1, &dropbear_mode_none},
 #endif
-	{NULL, 0, NULL, 0}
+	{NULL, 0, NULL, 0, NULL}
 };
 
 algo_type sshhashes[] = {
+#ifdef DROPBEAR_SHA2_256_HMAC
+//	{"hmac-sha2-256", 0, &dropbear_sha2_256, 1, NULL},
+#endif
+#ifdef DROPBEAR_SHA2_512_HMAC
+//	{"hmac-sha2-512", 0, &dropbear_sha2_512, 1, NULL},
+#endif
 #ifdef DROPBEAR_SHA1_96_HMAC
 	{"hmac-sha1-96", 0, &dropbear_sha1_96, 1, NULL},
 #endif
@@ -164,19 +180,24 @@
 	{"hmac-sha1", 0, &dropbear_sha1, 1, NULL},
 #endif
 #ifdef DROPBEAR_MD5_HMAC
-	{"hmac-md5", 0, (void*)&dropbear_md5, 1},
+	{"hmac-md5", 0, (void*)&dropbear_md5, 1, NULL},
 #endif
 #ifdef DROPBEAR_NONE_INTEGRITY
-	{"none", 0, (void*)&dropbear_nohash, 1},
+	{"none", 0, (void*)&dropbear_nohash, 1, NULL},
 #endif
-	{NULL, 0, NULL, 0}
+	{NULL, 0, NULL, 0, NULL}
 };
 
-algo_type sshcompress[] = {
 #ifndef DISABLE_ZLIB
+algo_type ssh_compress[] = {
 	{"zlib", DROPBEAR_COMP_ZLIB, NULL, 1, NULL},
 	{"[email protected]", DROPBEAR_COMP_ZLIB_DELAY, NULL, 1, NULL},
+	{"none", DROPBEAR_COMP_NONE, NULL, 1, NULL},
+	{NULL, 0, NULL, 0, NULL}
+};
 #endif
+
+algo_type ssh_nocompress[] = {
 	{"none", DROPBEAR_COMP_NONE, NULL, 1, NULL},
 	{NULL, 0, NULL, 0, NULL}
 };
@@ -193,6 +214,7 @@
 
 algo_type sshkex[] = {
 	{"diffie-hellman-group1-sha1", DROPBEAR_KEX_DH_GROUP1, NULL, 1, NULL},
+	{"diffie-hellman-group14-sha1", DROPBEAR_KEX_DH_GROUP14, NULL, 1, NULL},
 	{NULL, 0, NULL, 0, NULL}
 };
 
@@ -229,13 +251,13 @@
 	
 	for (i = 0; regciphers[i] != NULL; i++) {
 		if (register_cipher(regciphers[i]) == -1) {
-			dropbear_exit("error registering crypto");
+			dropbear_exit("Error registering crypto");
 		}
 	}
 
 	for (i = 0; reghashes[i] != NULL; i++) {
 		if (register_hash(reghashes[i]) == -1) {
-			dropbear_exit("error registering crypto");
+			dropbear_exit("Error registering crypto");
 		}
 	}
 }
--- a/common-channel.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/common-channel.c	Wed May 16 22:54:51 2012 +0800
@@ -138,6 +138,7 @@
 	newchan->index = i;
 	newchan->sent_close = newchan->recv_close = 0;
 	newchan->sent_eof = newchan->recv_eof = 0;
+	newchan->close_handler_done = 0;
 
 	newchan->remotechan = remotechan;
 	newchan->transwindow = transwindow;
@@ -270,7 +271,9 @@
 				cbuf_getused(channel->writebuf),
 				channel->extrabuf ? cbuf_getused(channel->extrabuf) : 0))
 
-	if (!channel->flushing && channel->type->check_close
+	if (!channel->flushing 
+		&& !channel->close_handler_done
+		&& channel->type->check_close
 		&& channel->type->check_close(channel))
 	{
 		channel->flushing = 1;
@@ -281,7 +284,8 @@
 	   channel, to ensure that the shell has exited (and the exit status 
 	   retrieved) before we close things up. */
 	if (!channel->type->check_close	
-			|| channel->type->check_close(channel)) {
+		|| channel->close_handler_done
+		|| channel->type->check_close(channel)) {
 		close_allowed = 1;
 	}
 
@@ -363,9 +367,11 @@
 /* Send the close message and set the channel as closed */
 static void send_msg_channel_close(struct Channel *channel) {
 
-	TRACE(("enter send_msg_channel_close"))
-	if (channel->type->closehandler) {
+	TRACE(("enter send_msg_channel_close %p", channel))
+	if (channel->type->closehandler 
+			&& !channel->close_handler_done) {
 		channel->type->closehandler(channel);
+		channel->close_handler_done = 1;
 	}
 	
 	CHECKCLEARTOWRITE();
@@ -568,16 +574,17 @@
 
 	struct Channel *channel;
 
-	TRACE(("enter recv_msg_channel_request"))
-	
 	channel = getchannel();
 
+	TRACE(("enter recv_msg_channel_request %p", channel))
+
 	if (channel->sent_close) {
 		TRACE(("leave recv_msg_channel_request: already closed channel"))
 		return;
 	}
 
-	if (channel->type->reqhandler) {
+	if (channel->type->reqhandler 
+			&& !channel->close_handler_done) {
 		channel->type->reqhandler(channel);
 	} else {
 		send_msg_channel_failure(channel);
@@ -646,6 +653,8 @@
 					len, errno, fd))
 		return;
 	}
+
+	TRACE(("send_msg_channel_data: len %d fd %d", len, fd))
 	buf_incrwritepos(ses.writepayload, len);
 	/* ... real size here */
 	buf_setpos(ses.writepayload, size_pos);
@@ -688,10 +697,10 @@
 	TRACE(("enter recv_msg_channel_data"))
 
 	if (channel->recv_eof) {
-		dropbear_exit("received data after eof");
+		dropbear_exit("Received data after eof");
 	}
 
- 	if (fd < 0) {
+	if (fd < 0) {
 		/* If we have encountered failed write, the far side might still
 		 * be sending data without having yet received our close notification.
 		 * We just drop the data. */
@@ -1006,7 +1015,7 @@
 	channel = getchannel();
 
 	if (!channel->await_open) {
-		dropbear_exit("unexpected channel reply");
+		dropbear_exit("Unexpected channel reply");
 	}
 	channel->await_open = 0;
 
@@ -1038,7 +1047,7 @@
 	channel = getchannel();
 
 	if (!channel->await_open) {
-		dropbear_exit("unexpected channel reply");
+		dropbear_exit("Unexpected channel reply");
 	}
 	channel->await_open = 0;
 
--- a/common-kex.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/common-kex.c	Wed May 16 22:54:51 2012 +0800
@@ -33,9 +33,11 @@
 #include "packet.h"
 #include "bignum.h"
 #include "random.h"
+#include "runopts.h"
 
 /* diffie-hellman-group1-sha1 value for p */
-static const unsigned char dh_p_val[] = {
+#define DH_P_1_LEN 128
+static const unsigned char dh_p_1[DH_P_1_LEN] = {
 	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 +49,34 @@
 	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)
 
+/* diffie-hellman-group14-sha1 value for p */
+#define DH_P_14_LEN 256
+static const unsigned char dh_p_14[DH_P_14_LEN] = {
+	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,
+	0x3B, 0x13, 0x9B, 0x22, 0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+	0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B, 0x30, 0x2B, 0x0A, 0x6D,
+	0xF2, 0x5F, 0x14, 0x37, 0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+	0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6, 0xF4, 0x4C, 0x42, 0xE9,
+	0xA6, 0x37, 0xED, 0x6B, 0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+	0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5, 0xAE, 0x9F, 0x24, 0x11,
+	0x7C, 0x4B, 0x1F, 0xE6, 0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+	0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05, 0x98, 0xDA, 0x48, 0x36,
+	0x1C, 0x55, 0xD3, 0x9A, 0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+	0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96, 0x1C, 0x62, 0xF3, 0x56,
+	0x20, 0x85, 0x52, 0xBB, 0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+	0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04, 0xF1, 0x74, 0x6C, 0x08,
+	0xCA, 0x18, 0x21, 0x7C, 0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+	0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03, 0x9B, 0x27, 0x83, 0xA2,
+	0xEC, 0x07, 0xA2, 0x8F, 0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+	0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18, 0x39, 0x95, 0x49, 0x7C,
+	0xEA, 0x95, 0x6A, 0xE5, 0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+	0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68, 0xFF, 0xFF, 0xFF, 0xFF,
+	0xFF, 0xFF, 0xFF, 0xFF};
+
+/* Same for group1 and group14 */
 static const int DH_G_VAL = 2;
 
 static void kexinitialise();
@@ -91,10 +119,10 @@
 	buf_put_algolist(ses.writepayload, sshhashes);
 
 	/* compression_algorithms_client_to_server */
-	buf_put_algolist(ses.writepayload, sshcompress);
+	buf_put_algolist(ses.writepayload, ses.compress_algos);
 
 	/* compression_algorithms_server_to_client */
-	buf_put_algolist(ses.writepayload, sshcompress);
+	buf_put_algolist(ses.writepayload, ses.compress_algos);
 
 	/* languages_client_to_server */
 	buf_putstring(ses.writepayload, "", 0);
@@ -180,8 +208,16 @@
 
 /* Set up the kex for the first time */
 void kexfirstinitialise() {
+	ses.kexstate.donefirstkex = 0;
 
-	ses.kexstate.donefirstkex = 0;
+#ifndef DISABLE_ZLIB
+	if (opts.enable_compress) {
+		ses.compress_algos = ssh_compress;
+	} else
+#endif
+	{
+		ses.compress_algos = ssh_nocompress;
+	}
 	kexinitialise();
 }
 
@@ -212,32 +248,34 @@
  * already initialised hash_state hs, which should already have processed
  * the dh_K and hash, since these are common. X is the letter 'A', 'B' etc.
  * out must have at least min(SHA1_HASH_SIZE, outlen) bytes allocated.
- * The output will only be expanded once, as we are assured that
- * outlen <= 2*SHA1_HASH_SIZE for all known hashes.
  *
  * See Section 7.2 of rfc4253 (ssh transport) for details */
 static void hashkeys(unsigned char *out, int outlen, 
 		const hash_state * hs, const unsigned char X) {
 
 	hash_state hs2;
-	unsigned char k2[SHA1_HASH_SIZE]; /* used to extending */
+	int offset;
 
 	memcpy(&hs2, hs, sizeof(hash_state));
 	sha1_process(&hs2, &X, 1);
 	sha1_process(&hs2, ses.session_id, SHA1_HASH_SIZE);
 	sha1_done(&hs2, out);
-	if (SHA1_HASH_SIZE < outlen) {
+	for (offset = SHA1_HASH_SIZE; 
+			offset < outlen; 
+			offset += SHA1_HASH_SIZE)
+	{
 		/* need to extend */
+		unsigned char k2[SHA1_HASH_SIZE];
 		memcpy(&hs2, hs, sizeof(hash_state));
-		sha1_process(&hs2, out, SHA1_HASH_SIZE);
+		sha1_process(&hs2, out, offset);
 		sha1_done(&hs2, k2);
-		memcpy(&out[SHA1_HASH_SIZE], k2, outlen - SHA1_HASH_SIZE);
+		memcpy(&out[offset], k2, MIN(outlen - offset, SHA1_HASH_SIZE));
 	}
 }
 
 /* Generate the actual encryption/integrity keys, using the results of the
- * key exchange, as specified in section 5.2 of the IETF secsh-transport
- * draft. This occurs after the DH key-exchange.
+ * key exchange, as specified in section 7.2 of the transport rfc 4253.
+ * This occurs after the DH key-exchange.
  *
  * ses.newkeys is the new set of keys which are generated, these are only
  * taken into use after both sides have sent a newkeys message */
@@ -255,7 +293,6 @@
 	hash_state hs;
 	unsigned int C2S_keysize, S2C_keysize;
 	char mactransletter, macrecvletter; /* Client or server specific */
-	int recv_cipher = 0, trans_cipher = 0;
 
 	TRACE(("enter gen_new_keys"))
 	/* the dh_K and hash are the start of all hashes, we make use of that */
@@ -272,8 +309,8 @@
 	    recv_IV		= S2C_IV;
 	    trans_key	= C2S_key;
 	    recv_key	= S2C_key;
-	    C2S_keysize = ses.newkeys->trans_algo_crypt->keysize;
-	    S2C_keysize = ses.newkeys->recv_algo_crypt->keysize;
+	    C2S_keysize = ses.newkeys->trans.algo_crypt->keysize;
+	    S2C_keysize = ses.newkeys->recv.algo_crypt->keysize;
 		mactransletter = 'E';
 		macrecvletter = 'F';
 	} else {
@@ -281,8 +318,8 @@
 	    recv_IV		= C2S_IV;
 	    trans_key	= S2C_key;
 	    recv_key	= C2S_key;
-	    C2S_keysize = ses.newkeys->recv_algo_crypt->keysize;
-	    S2C_keysize = ses.newkeys->trans_algo_crypt->keysize;
+	    C2S_keysize = ses.newkeys->recv.algo_crypt->keysize;
+	    S2C_keysize = ses.newkeys->trans.algo_crypt->keysize;
 		mactransletter = 'F';
 		macrecvletter = 'E';
 	}
@@ -292,39 +329,41 @@
 	hashkeys(C2S_key, C2S_keysize, &hs, 'C');
 	hashkeys(S2C_key, S2C_keysize, &hs, 'D');
 
-	if (ses.newkeys->recv_algo_crypt->cipherdesc != NULL) {
-		recv_cipher = find_cipher(ses.newkeys->recv_algo_crypt->cipherdesc->name);
+	if (ses.newkeys->recv.algo_crypt->cipherdesc != NULL) {
+		int recv_cipher = find_cipher(ses.newkeys->recv.algo_crypt->cipherdesc->name);
 		if (recv_cipher < 0)
-			dropbear_exit("crypto error");
-		if (ses.newkeys->recv_crypt_mode->start(recv_cipher, 
+			dropbear_exit("Crypto error");
+		if (ses.newkeys->recv.crypt_mode->start(recv_cipher, 
 				recv_IV, recv_key, 
-				ses.newkeys->recv_algo_crypt->keysize, 0, 
-				&ses.newkeys->recv_cipher_state) != CRYPT_OK) {
-			dropbear_exit("crypto error");
+				ses.newkeys->recv.algo_crypt->keysize, 0, 
+				&ses.newkeys->recv.cipher_state) != CRYPT_OK) {
+			dropbear_exit("Crypto error");
 		}
 	}
 
-	if (ses.newkeys->trans_algo_crypt->cipherdesc != NULL) {
-		trans_cipher = find_cipher(ses.newkeys->trans_algo_crypt->cipherdesc->name);
+	if (ses.newkeys->trans.algo_crypt->cipherdesc != NULL) {
+		int trans_cipher = find_cipher(ses.newkeys->trans.algo_crypt->cipherdesc->name);
 		if (trans_cipher < 0)
-			dropbear_exit("crypto error");
-		if (ses.newkeys->trans_crypt_mode->start(trans_cipher, 
+			dropbear_exit("Crypto error");
+		if (ses.newkeys->trans.crypt_mode->start(trans_cipher, 
 				trans_IV, trans_key, 
-				ses.newkeys->trans_algo_crypt->keysize, 0, 
-				&ses.newkeys->trans_cipher_state) != CRYPT_OK) {
-			dropbear_exit("crypto error");
+				ses.newkeys->trans.algo_crypt->keysize, 0, 
+				&ses.newkeys->trans.cipher_state) != CRYPT_OK) {
+			dropbear_exit("Crypto error");
 		}
 	}
 
 	/* MAC keys */
-	if (ses.newkeys->trans_algo_mac->hashdesc != NULL) {
-		hashkeys(ses.newkeys->transmackey, 
-				ses.newkeys->trans_algo_mac->keysize, &hs, mactransletter);
+	if (ses.newkeys->trans.algo_mac->hashdesc != NULL) {
+		hashkeys(ses.newkeys->trans.mackey, 
+				ses.newkeys->trans.algo_mac->keysize, &hs, mactransletter);
 	}
-	if (ses.newkeys->recv_algo_mac->hashdesc != NULL) {
-		hashkeys(ses.newkeys->recvmackey, 
-				ses.newkeys->recv_algo_mac->keysize, &hs, macrecvletter);
+	if (ses.newkeys->recv.algo_mac->hashdesc != NULL) {
+		hashkeys(ses.newkeys->recv.mackey, 
+				ses.newkeys->recv.algo_mac->keysize, &hs, macrecvletter);
 	}
+	ses.newkeys->trans.hash_index = find_hash(ses.newkeys->trans.algo_mac->hashdesc->name),
+	ses.newkeys->recv.hash_index = find_hash(ses.newkeys->recv.algo_mac->hashdesc->name),
 
 #ifndef DISABLE_ZLIB
 	gen_new_zstreams();
@@ -336,21 +375,26 @@
 	ses.keys = ses.newkeys;
 	ses.newkeys = NULL;
 
+	m_burn(C2S_IV, sizeof(C2S_IV));
+	m_burn(C2S_key, sizeof(C2S_key));
+	m_burn(S2C_IV, sizeof(S2C_IV));
+	m_burn(S2C_key, sizeof(S2C_key));
+
 	TRACE(("leave gen_new_keys"))
 }
 
 #ifndef DISABLE_ZLIB
 
 int is_compress_trans() {
-	return ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB
+	return ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB
 		|| (ses.authstate.authdone
-			&& ses.keys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
+			&& ses.keys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
 }
 
 int is_compress_recv() {
-	return ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB
+	return ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB
 		|| (ses.authstate.authdone
-			&& ses.keys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
+			&& ses.keys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY);
 }
 
 /* Set up new zlib compression streams, close the old ones. Only
@@ -358,47 +402,49 @@
 static void gen_new_zstreams() {
 
 	/* create new zstreams */
-	if (ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB
-			|| ses.newkeys->recv_algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
-		ses.newkeys->recv_zstream = (z_streamp)m_malloc(sizeof(z_stream));
-		ses.newkeys->recv_zstream->zalloc = Z_NULL;
-		ses.newkeys->recv_zstream->zfree = Z_NULL;
+	if (ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB
+			|| ses.newkeys->recv.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
+		ses.newkeys->recv.zstream = (z_streamp)m_malloc(sizeof(z_stream));
+		ses.newkeys->recv.zstream->zalloc = Z_NULL;
+		ses.newkeys->recv.zstream->zfree = Z_NULL;
 		
-		if (inflateInit(ses.newkeys->recv_zstream) != Z_OK) {
+		if (inflateInit(ses.newkeys->recv.zstream) != Z_OK) {
 			dropbear_exit("zlib error");
 		}
 	} else {
-		ses.newkeys->recv_zstream = NULL;
+		ses.newkeys->recv.zstream = NULL;
 	}
 
-	if (ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB
-			|| ses.newkeys->trans_algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
-		ses.newkeys->trans_zstream = (z_streamp)m_malloc(sizeof(z_stream));
-		ses.newkeys->trans_zstream->zalloc = Z_NULL;
-		ses.newkeys->trans_zstream->zfree = Z_NULL;
+	if (ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB
+			|| ses.newkeys->trans.algo_comp == DROPBEAR_COMP_ZLIB_DELAY) {
+		ses.newkeys->trans.zstream = (z_streamp)m_malloc(sizeof(z_stream));
+		ses.newkeys->trans.zstream->zalloc = Z_NULL;
+		ses.newkeys->trans.zstream->zfree = Z_NULL;
 	
-		if (deflateInit(ses.newkeys->trans_zstream, Z_DEFAULT_COMPRESSION) 
+		if (deflateInit2(ses.newkeys->trans.zstream, Z_DEFAULT_COMPRESSION,
+					Z_DEFLATED, DROPBEAR_ZLIB_WINDOW_BITS, 
+					DROPBEAR_ZLIB_MEM_LEVEL, Z_DEFAULT_STRATEGY)
 				!= Z_OK) {
 			dropbear_exit("zlib error");
 		}
 	} else {
-		ses.newkeys->trans_zstream = NULL;
+		ses.newkeys->trans.zstream = NULL;
 	}
 
 	/* clean up old keys */
-	if (ses.keys->recv_zstream != NULL) {
-		if (inflateEnd(ses.keys->recv_zstream) == Z_STREAM_ERROR) {
+	if (ses.keys->recv.zstream != NULL) {
+		if (inflateEnd(ses.keys->recv.zstream) == Z_STREAM_ERROR) {
 			/* Z_DATA_ERROR is ok, just means that stream isn't ended */
-			dropbear_exit("crypto error");
+			dropbear_exit("Crypto error");
 		}
-		m_free(ses.keys->recv_zstream);
+		m_free(ses.keys->recv.zstream);
 	}
-	if (ses.keys->trans_zstream != NULL) {
-		if (deflateEnd(ses.keys->trans_zstream) == Z_STREAM_ERROR) {
+	if (ses.keys->trans.zstream != NULL) {
+		if (deflateEnd(ses.keys->trans.zstream) == Z_STREAM_ERROR) {
 			/* Z_DATA_ERROR is ok, just means that stream isn't ended */
-			dropbear_exit("crypto error");
+			dropbear_exit("Crypto error");
 		}
-		m_free(ses.keys->trans_zstream);
+		m_free(ses.keys->trans.zstream);
 	}
 }
 #endif /* DISABLE_ZLIB */
@@ -487,8 +533,20 @@
 	TRACE(("leave recv_msg_kexinit"))
 }
 
+static void load_dh_p(mp_int * dh_p)
+{
+	switch (ses.newkeys->algo_kex) {
+		case DROPBEAR_KEX_DH_GROUP1:
+			bytes_to_mp(dh_p, dh_p_1, DH_P_1_LEN);
+			break;
+		case DROPBEAR_KEX_DH_GROUP14:
+			bytes_to_mp(dh_p, dh_p_14, DH_P_14_LEN);
+			break;
+	}
+}
+
 /* Initialises and generate one side of the diffie-hellman key exchange values.
- * See the ietf-secsh-transport draft, section 6, for details */
+ * See the transport rfc 4253 section 8 for details */
 /* dh_pub and dh_priv MUST be already initialised */
 void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv) {
 
@@ -501,7 +559,7 @@
 	m_mp_init_multi(&dh_g, &dh_p, &dh_q, NULL);
 
 	/* read the prime and generator*/
-	bytes_to_mp(&dh_p, (unsigned char*)dh_p_val, DH_P_LEN);
+	load_dh_p(&dh_p);
 	
 	if (mp_set_int(&dh_g, DH_G_VAL) != MP_OKAY) {
 		dropbear_exit("Diffie-Hellman error");
@@ -539,7 +597,7 @@
 
 	/* read the prime and generator*/
 	m_mp_init(&dh_p);
-	bytes_to_mp(&dh_p, dh_p_val, DH_P_LEN);
+	load_dh_p(&dh_p);
 
 	/* Check that dh_pub_them (dh_e or dh_f) is in the range [1, p-1] */
 	if (mp_cmp(dh_pub_them, &dh_p) != MP_LT 
@@ -674,7 +732,7 @@
 	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);
+	c2s_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess);
 	if (c2s_comp_algo == NULL) {
 		erralgo = "comp c->s";
 		goto error;
@@ -682,7 +740,7 @@
 	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);
+	s2c_comp_algo = ses.buf_match_algo(ses.payload, ses.compress_algos, &goodguess);
 	if (s2c_comp_algo == NULL) {
 		erralgo = "comp s->c";
 		goto error;
@@ -706,36 +764,36 @@
 
 	/* Handle the asymmetry */
 	if (IS_DROPBEAR_CLIENT) {
-		ses.newkeys->recv_algo_crypt = 
+		ses.newkeys->recv.algo_crypt = 
 			(struct dropbear_cipher*)s2c_cipher_algo->data;
-		ses.newkeys->trans_algo_crypt = 
+		ses.newkeys->trans.algo_crypt = 
 			(struct dropbear_cipher*)c2s_cipher_algo->data;
-		ses.newkeys->recv_crypt_mode = 
+		ses.newkeys->recv.crypt_mode = 
 			(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
-		ses.newkeys->trans_crypt_mode =
+		ses.newkeys->trans.crypt_mode =
 			(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
-		ses.newkeys->recv_algo_mac = 
+		ses.newkeys->recv.algo_mac = 
 			(struct dropbear_hash*)s2c_hash_algo->data;
-		ses.newkeys->trans_algo_mac = 
+		ses.newkeys->trans.algo_mac = 
 			(struct dropbear_hash*)c2s_hash_algo->data;
-		ses.newkeys->recv_algo_comp = s2c_comp_algo->val;
-		ses.newkeys->trans_algo_comp = c2s_comp_algo->val;
+		ses.newkeys->recv.algo_comp = s2c_comp_algo->val;
+		ses.newkeys->trans.algo_comp = c2s_comp_algo->val;
 	} else {
 		/* SERVER */
-		ses.newkeys->recv_algo_crypt = 
+		ses.newkeys->recv.algo_crypt = 
 			(struct dropbear_cipher*)c2s_cipher_algo->data;
-		ses.newkeys->trans_algo_crypt = 
+		ses.newkeys->trans.algo_crypt = 
 			(struct dropbear_cipher*)s2c_cipher_algo->data;
-		ses.newkeys->recv_crypt_mode =
+		ses.newkeys->recv.crypt_mode =
 			(struct dropbear_cipher_mode*)c2s_cipher_algo->mode;
-		ses.newkeys->trans_crypt_mode =
+		ses.newkeys->trans.crypt_mode =
 			(struct dropbear_cipher_mode*)s2c_cipher_algo->mode;
-		ses.newkeys->recv_algo_mac = 
+		ses.newkeys->recv.algo_mac = 
 			(struct dropbear_hash*)c2s_hash_algo->data;
-		ses.newkeys->trans_algo_mac = 
+		ses.newkeys->trans.algo_mac = 
 			(struct dropbear_hash*)s2c_hash_algo->data;
-		ses.newkeys->recv_algo_comp = c2s_comp_algo->val;
-		ses.newkeys->trans_algo_comp = s2c_comp_algo->val;
+		ses.newkeys->recv.algo_comp = c2s_comp_algo->val;
+		ses.newkeys->trans.algo_comp = s2c_comp_algo->val;
 	}
 
 	/* reserved for future extensions */
@@ -743,5 +801,5 @@
 	return;
 
 error:
-	dropbear_exit("no matching algo %s", erralgo);
+	dropbear_exit("No matching algo %s", erralgo);
 }
--- a/common-session.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/common-session.c	Wed May 16 22:54:51 2012 +0800
@@ -52,24 +52,26 @@
 
 
 /* called only at the start of a session, set up initial state */
-void common_session_init(int sock_in, int sock_out, char* remotehost) {
+void common_session_init(int sock_in, int sock_out) {
 
 	TRACE(("enter session_init"))
 
-	ses.remotehost = remotehost;
-
 	ses.sock_in = sock_in;
 	ses.sock_out = sock_out;
 	ses.maxfd = MAX(sock_in, sock_out);
 
 	ses.connect_time = 0;
+	ses.last_trx_packet_time = 0;
 	ses.last_packet_time = 0;
 	
 	if (pipe(ses.signal_pipe) < 0) {
-		dropbear_exit("signal pipe failed");
+		dropbear_exit("Signal pipe failed");
 	}
 	setnonblocking(ses.signal_pipe[0]);
 	setnonblocking(ses.signal_pipe[1]);
+
+	ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[0]);
+	ses.maxfd = MAX(ses.maxfd, ses.signal_pipe[1]);
 	
 	kexfirstinitialise(); /* initialise the kex state */
 
@@ -77,7 +79,6 @@
 	ses.transseq = 0;
 
 	ses.readbuf = NULL;
-	ses.decryptreadbuf = NULL;
 	ses.payload = NULL;
 	ses.recvseq = 0;
 
@@ -94,22 +95,22 @@
 	/* set all the algos to none */
 	ses.keys = (struct key_context*)m_malloc(sizeof(struct key_context));
 	ses.newkeys = NULL;
-	ses.keys->recv_algo_crypt = &dropbear_nocipher;
-	ses.keys->trans_algo_crypt = &dropbear_nocipher;
-	ses.keys->recv_crypt_mode = &dropbear_mode_none;
-	ses.keys->trans_crypt_mode = &dropbear_mode_none;
+	ses.keys->recv.algo_crypt = &dropbear_nocipher;
+	ses.keys->trans.algo_crypt = &dropbear_nocipher;
+	ses.keys->recv.crypt_mode = &dropbear_mode_none;
+	ses.keys->trans.crypt_mode = &dropbear_mode_none;
 	
-	ses.keys->recv_algo_mac = &dropbear_nohash;
-	ses.keys->trans_algo_mac = &dropbear_nohash;
+	ses.keys->recv.algo_mac = &dropbear_nohash;
+	ses.keys->trans.algo_mac = &dropbear_nohash;
 
 	ses.keys->algo_kex = -1;
 	ses.keys->algo_hostkey = -1;
-	ses.keys->recv_algo_comp = DROPBEAR_COMP_NONE;
-	ses.keys->trans_algo_comp = DROPBEAR_COMP_NONE;
+	ses.keys->recv.algo_comp = DROPBEAR_COMP_NONE;
+	ses.keys->trans.algo_comp = DROPBEAR_COMP_NONE;
 
 #ifndef DISABLE_ZLIB
-	ses.keys->recv_zstream = NULL;
-	ses.keys->trans_zstream = NULL;
+	ses.keys->recv.zstream = NULL;
+	ses.keys->trans.zstream = NULL;
 #endif
 
 	/* key exchange buffers */
@@ -258,7 +259,7 @@
 		ses.remoteclosed();
 	}
 
-    /* If they send more than 50 lines, something is wrong */
+	/* If they send more than 50 lines, something is wrong */
 	for (i = 0; i < 50; i++) {
 		len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf));
 
@@ -283,11 +284,11 @@
 		memcpy(ses.remoteident, linebuf, len);
 	}
 
-    /* Shall assume that 2.x will be backwards compatible. */
-    if (strncmp(ses.remoteident, "SSH-2.", 6) != 0
-            && strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) {
-        dropbear_exit("Incompatible remote version '%s'", ses.remoteident);
-    }
+	/* Shall assume that 2.x will be backwards compatible. */
+	if (strncmp(ses.remoteident, "SSH-2.", 6) != 0
+			&& strncmp(ses.remoteident, "SSH-1.99-", 9) != 0) {
+		dropbear_exit("Incompatible remote version '%s'", ses.remoteident);
+	}
 
 	TRACE(("remoteident: %s", ses.remoteident))
 
@@ -399,9 +400,14 @@
 	}
 	
 	if (opts.keepalive_secs > 0 
-		&& now - ses.last_packet_time >= opts.keepalive_secs) {
+			&& now - ses.last_trx_packet_time >= opts.keepalive_secs) {
 		send_msg_ignore();
 	}
+
+	if (opts.idle_timeout_secs > 0 && ses.last_packet_time > 0
+			&& now - ses.last_packet_time >= opts.idle_timeout_secs) {
+		dropbear_close("Idle timeout");
+	}
 }
 
 static long select_timeout() {
@@ -414,6 +420,8 @@
 		ret = MIN(AUTH_TIMEOUT, ret);
 	if (opts.keepalive_secs > 0)
 		ret = MIN(opts.keepalive_secs, ret);
+    if (opts.idle_timeout_secs > 0)
+        ret = MIN(opts.idle_timeout_secs, ret);
 	return ret;
 }
 
@@ -445,6 +453,16 @@
 	ses.authstate.pw_name = m_strdup(pw->pw_name);
 	ses.authstate.pw_dir = m_strdup(pw->pw_dir);
 	ses.authstate.pw_shell = m_strdup(pw->pw_shell);
-	ses.authstate.pw_passwd = m_strdup(pw->pw_passwd);
+	{
+		char *passwd_crypt = pw->pw_passwd;
+#ifdef HAVE_SHADOW_H
+		/* get the shadow password if possible */
+		struct spwd *spasswd = getspnam(ses.authstate.pw_name);
+		if (spasswd && spasswd->sp_pwdp) {
+			passwd_crypt = spasswd->sp_pwdp;
+		}
+#endif
+		ses.authstate.pw_passwd = m_strdup(passwd_crypt);
+	}
 }
 
--- a/configure.in	Thu Nov 06 13:33:06 2008 +0000
+++ b/configure.in	Wed May 16 22:54:51 2012 +0800
@@ -82,7 +82,8 @@
 	],,,)
 
 # Checks for libraries.
-AC_CHECK_LIB(crypt, crypt, LIBS="$LIBS -lcrypt")
+AC_CHECK_LIB(crypt, crypt, CRYPTLIB="-lcrypt")
+AC_SUBST(CRYPTLIB)	
 
 # Check if zlib is needed
 AC_ARG_WITH(zlib,
@@ -145,6 +146,7 @@
 		if test "x$enableval" = "xyes"; then
 			AC_CHECK_LIB(pam, pam_authenticate, , AC_MSG_ERROR([*** PAM missing - install first or check config.log ***]))
 			AC_MSG_NOTICE(Enabling PAM)
+			AC_CHECK_FUNCS(pam_fail_delay)
 		else
 			AC_DEFINE(DISABLE_PAM,, Use PAM)
 			AC_MSG_NOTICE(Disabling PAM)
@@ -361,6 +363,25 @@
 AC_CHECK_FUNCS(setutxent utmpxname)
 AC_CHECK_FUNCS(logout updwtmp logwtmp)
 
+AC_ARG_ENABLE(bundled-libtom,
+	[  --enable-bundled-libtom       Use bundled libtomcrypt/libtommath even if a system version exists],
+	[ 
+		BUNDLED_LIBTOM=1
+		AC_MSG_NOTICE(Forcing bundled libtom*)
+	],
+	[
+		BUNDLED_LIBTOM=0
+		AC_CHECK_LIB(tomcrypt, register_cipher, , BUNDLED_LIBTOM=1)
+		AC_CHECK_LIB(tommath, mp_exptmod, , BUNDLED_LIBTOM=1)
+	]
+)
+
+if test $BUNDLED_LIBTOM = 1 ; then
+	AC_DEFINE(BUNDLED_LIBTOM,,Use bundled libtom) 
+fi
+
+AC_SUBST(BUNDLED_LIBTOM)
+
 dnl Added from OpenSSH 3.6.1p2's configure.ac
 
 dnl allow user to disable some login recording features
@@ -595,7 +616,7 @@
 AC_FUNC_MEMCMP
 AC_FUNC_SELECT_ARGTYPES
 AC_TYPE_SIGNAL
-AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo])
+AC_CHECK_FUNCS([dup2 getspnam getusershell memset putenv select socket strdup clearenv strlcpy strlcat daemon basename _getpty getaddrinfo freeaddrinfo getnameinfo fork])
 
 AC_SEARCH_LIBS(basename, gen, AC_DEFINE(HAVE_BASENAME))
 
@@ -669,5 +690,13 @@
 AC_OUTPUT(Makefile)
 AC_OUTPUT(libtomcrypt/Makefile)
 AC_OUTPUT(libtommath/Makefile)
+
+AC_MSG_NOTICE()
+if test $BUNDLED_LIBTOM = 1 ; then
+AC_MSG_NOTICE(Using bundled libtomcrypt and libtommath)
+else
+AC_MSG_NOTICE(Using system libtomcrypt and libtommath)
+fi
+
 AC_MSG_NOTICE()
 AC_MSG_NOTICE(Now edit options.h to choose features.)
--- a/dbclient.1	Thu Nov 06 13:33:06 2008 +0000
+++ b/dbclient.1	Wed May 16 22:54:51 2012 +0800
@@ -36,7 +36,7 @@
 .I idfile
 (multiple allowed).
 .TP
-.B \-L \fIlistenport\fR:\fIhost\fR:\fIport\fR
+.B \-L [\fIlistenaddress\fR]:\fIlistenport\fR:\fIhost\fR:\fIport\fR
 Local port forwarding.
 Forward the port
 .I listenport
@@ -45,7 +45,7 @@
 on the host
 .IR host .
 .TP
-.B \-R \fIlistenport\fR:\fIhost\fR:\fIport\fR
+.B \-R [\fIlistenaddress\fR]:\fIlistenport\fR:\fIhost\fR:\fIport\fR
 Remote port forwarding.
 Forward the port
 .I listenport
@@ -82,6 +82,11 @@
 Always accept hostkeys if they are unknown. If a hostkey mismatch occurs the
 connection will abort as normal.
 .TP
+.B \-A
+Forward agent connections to the remote host. dbclient will use any
+OpenSSH-style agent program if available ($SSH_AUTH_SOCK will be set) for
+public key authentication.  Forwarding is only enabled if -A is specified.
+.TP
 .B \-W \fIwindowsize
 Specify the per-channel receive window buffer size. Increasing this 
 may improve network performance at the expense of memory use. Use -h to see the
@@ -93,11 +98,16 @@
 a certain period of inactivity. The trade-off is that a session may be
 closed if there is a temporary lapse of network connectivity. A setting
 if 0 disables keepalives.
+.TP
+.B \-I \fIidle_timeout
+Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds.
+.TP
 .B \-J \fIproxy_command
 Use the standard input/output of the program \fIproxy_command\fR rather than using
 a normal TCP connection. A hostname should be still be provided, as this is used for
 comparing saved hostkeys.
-.B \B \fIendhost:endport
+.TP
+.B \-B \fIendhost:endport
 "Netcat-alike" mode, where Dropbear will connect to the given host, then create a
 forwarded connection to \fIendhost\fR. This will then be presented as dbclient's
 standard input/output.
@@ -112,6 +122,10 @@
 
 scp -S dbclient matt@martello,root@wrt,canyons:/tmp/dump .
 
+Note that hostnames are resolved by the prior hop (so "canyons" would be resolved by the host "wrt")
+in the example above, the same way as other -L TCP forwarded hosts are. Host keys are 
+checked locally based on the given hostname.
+
 .SH ENVIRONMENT
 .TP
 .B DROPBEAR_PASSWORD
--- a/dbutil.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/dbutil.c	Wed May 16 22:54:51 2012 +0800
@@ -57,11 +57,11 @@
 #define MAX_FMT 100
 
 static void generic_dropbear_exit(int exitcode, const char* format, 
-		va_list param);
+		va_list param) ATTRIB_NORETURN;
 static void generic_dropbear_log(int priority, const char* format, 
 		va_list param);
 
-void (*_dropbear_exit)(int exitcode, const char* format, va_list param) 
+void (*_dropbear_exit)(int exitcode, const char* format, va_list param) ATTRIB_NORETURN
 						= generic_dropbear_exit;
 void (*_dropbear_log)(int priority, const char* format, va_list param)
 						= generic_dropbear_log;
@@ -111,7 +111,7 @@
 }
 
 void fail_assert(const char* expr, const char* file, int line) {
-	dropbear_exit("failed assertion (%s:%d): `%s'", file, line, expr);
+	dropbear_exit("Failed assertion (%s:%d): `%s'", file, line, expr);
 }
 
 static void generic_dropbear_log(int UNUSED(priority), const char* format, 
@@ -161,10 +161,12 @@
 	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. */
+	/* set the TOS bit for either ipv4 or ipv6 */
 #ifdef IPTOS_LOWDELAY
 	val = IPTOS_LOWDELAY;
+#if defined(IPPROTO_IPV6) && defined(IPV6_TCLASS)
+	setsockopt(sock, IPPROTO_IPV6, IPV6_TCLASS, (void*)&val, sizeof(val));
+#endif
 	setsockopt(sock, IPPROTO_IP, IP_TOS, (void*)&val, sizeof(val));
 #endif
 
@@ -254,6 +256,16 @@
 		linger.l_linger = 5;
 		setsockopt(sock, SOL_SOCKET, SO_LINGER, (void*)&linger, sizeof(linger));
 
+#if defined(IPPROTO_IPV6) && defined(IPV6_V6ONLY)
+		if (res->ai_family == AF_INET6) {
+			int on = 1;
+			if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, 
+						&on, sizeof(on)) == -1) {
+				dropbear_log(LOG_WARNING, "Couldn't set IPV6_V6ONLY");
+			}
+		}
+#endif
+
 		set_sock_priority(sock);
 
 		if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) {
@@ -295,6 +307,29 @@
 	return nsock;
 }
 
+/* Connect to a given unix socket. The socket is blocking */
+#ifdef ENABLE_CONNECT_UNIX
+int connect_unix(const char* path) {
+	struct sockaddr_un addr;
+	int fd = -1;
+
+	memset((void*)&addr, 0x0, sizeof(addr));
+	addr.sun_family = AF_UNIX;
+	strlcpy(addr.sun_path, path, sizeof(addr.sun_path));
+	fd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (fd < 0) {
+		TRACE(("Failed to open unix socket"))
+		return -1;
+	}
+	if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
+		TRACE(("Failed to connect to '%s' socket", path))
+		m_close(fd);
+		return -1;
+	}
+	return fd;
+}
+#endif
+
 /* Connect via TCP to a host. Connection will try ipv4 or ipv6, will
  * return immediately if nonblocking is set. On failure, if errstring
  * wasn't null, it will be a newly malloced error message */
@@ -341,15 +376,7 @@
 		}
 
 		if (nonblocking) {
-			if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
-				close(sock);
-				sock = -1;
-				if (errstring != NULL && *errstring == NULL) {
-					*errstring = m_strdup("Failed non-blocking");
-				}
-				TRACE(("Failed non-blocking: %s", strerror(errno)))
-				continue;
-			}
+			setnonblocking(sock);
 		}
 
 		if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) {
@@ -416,7 +443,7 @@
 		return DROPBEAR_FAILURE;
 	}
 
-#ifdef __uClinux__
+#ifdef USE_VFORK
 	pid = vfork();
 #else
 	pid = fork();
@@ -441,7 +468,7 @@
 			(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
 			(ret_errfd && dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
 			TRACE(("leave noptycommand: error redirecting FDs"))
-			dropbear_exit("child dup2() failure");
+			dropbear_exit("Child dup2() failure");
 		}
 
 		close(infds[FDOUT]);
@@ -525,14 +552,47 @@
 	execv(usershell, argv);
 }
 
+void get_socket_address(int fd, char **local_host, char **local_port,
+						char **remote_host, char **remote_port, int host_lookup)
+{
+	struct sockaddr_storage addr;
+	socklen_t addrlen;
+	
+	if (local_host || local_port) {
+		addrlen = sizeof(addr);
+		if (getsockname(fd, (struct sockaddr*)&addr, &addrlen) < 0) {
+			dropbear_exit("Failed socket address: %s", strerror(errno));
+		}
+		getaddrstring(&addr, local_host, local_port, host_lookup);		
+	}
+	if (remote_host || remote_port) {
+		addrlen = sizeof(addr);
+		if (getpeername(fd, (struct sockaddr*)&addr, &addrlen) < 0) {
+			dropbear_exit("Failed socket address: %s", strerror(errno));
+		}
+		getaddrstring(&addr, remote_host, remote_port, host_lookup);		
+	}
+}
+
 /* Return a string representation of the socket address passed. The return
  * value is allocated with malloc() */
-unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport) {
+void getaddrstring(struct sockaddr_storage* addr, 
+			char **ret_host, char **ret_port,
+			int host_lookup) {
 
-	char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
-	char *retstring = NULL;
+	char host[NI_MAXHOST+1], serv[NI_MAXSERV+1];
+	unsigned int len;
 	int ret;
-	unsigned int len;
+	
+	int flags = NI_NUMERICSERV | NI_NUMERICHOST;
+
+#ifndef DO_HOST_LOOKUP
+	host_lookup = 0;
+#endif
+	
+	if (host_lookup) {
+		flags = NI_NUMERICSERV;
+	}
 
 	len = sizeof(struct sockaddr_storage);
 	/* Some platforms such as Solaris 8 require that len is the length
@@ -550,67 +610,28 @@
 #endif
 #endif
 
-	ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf), 
-			sbuf, sizeof(sbuf), NI_NUMERICSERV | NI_NUMERICHOST);
+	ret = getnameinfo((struct sockaddr*)addr, len, host, sizeof(host)-1, 
+			serv, sizeof(serv)-1, flags);
 
 	if (ret != 0) {
-		/* This is a fairly bad failure - it'll fallback to IP if it
-		 * just can't resolve */
-		dropbear_exit("failed lookup (%d, %d)", ret, errno);
-	}
-
-	if (withport) {
-		len = strlen(hbuf) + 2 + strlen(sbuf);
-		retstring = (char*)m_malloc(len);
-		snprintf(retstring, len, "%s:%s", hbuf, sbuf);
-	} else {
-		retstring = m_strdup(hbuf);
+		if (host_lookup) {
+			/* On some systems (Darwin does it) we get EINTR from getnameinfo
+			 * somehow. Eew. So we'll just return the IP, since that doesn't seem
+			 * to exhibit that behaviour. */
+			getaddrstring(addr, ret_host, ret_port, 0);
+			return;
+		} else {
+			/* if we can't do a numeric lookup, something's gone terribly wrong */
+			dropbear_exit("Failed lookup: %s", gai_strerror(ret));
+		}
 	}
 
-	return retstring;
-
-}
-
-/* Get the hostname corresponding to the address addr. On failure, the IP
- * address is returned. The return value is allocated with strdup() */
-char* getaddrhostname(struct sockaddr_storage * addr) {
-
-	char hbuf[NI_MAXHOST];
-	char sbuf[NI_MAXSERV];
-	int ret;
-	unsigned int len;
-#ifdef DO_HOST_LOOKUP
-	const int flags = NI_NUMERICSERV;
-#else
-	const int flags = NI_NUMERICHOST | NI_NUMERICSERV;
-#endif
-
-	len = sizeof(struct sockaddr_storage);
-	/* Some platforms such as Solaris 8 require that len is the length
-	 * of the specific structure. */
-#ifdef HAVE_STRUCT_SOCKADDR_STORAGE_SS_FAMILY
-	if (addr->ss_family == AF_INET) {
-		len = sizeof(struct sockaddr_in);
+	if (ret_host) {
+		*ret_host = m_strdup(host);
 	}
-#ifdef AF_INET6
-	if (addr->ss_family == AF_INET6) {
-		len = sizeof(struct sockaddr_in6);
+	if (ret_port) {
+		*ret_port = m_strdup(serv);
 	}
-#endif
-#endif
-
-
-	ret = getnameinfo((struct sockaddr*)addr, len, hbuf, sizeof(hbuf),
-			sbuf, sizeof(sbuf), flags);
-
-	if (ret != 0) {
-		/* On some systems (Darwin does it) we get EINTR from getnameinfo
-		 * somehow. Eew. So we'll just return the IP, since that doesn't seem
-		 * to exhibit that behaviour. */
-		return getaddrstring(addr, 0);
-	}
-
-	return m_strdup(hbuf);
 }
 
 #ifdef DEBUG_TRACE
@@ -779,12 +800,6 @@
 	return ret;
 }
 
-void __m_free(void* ptr) {
-	if (ptr != NULL) {
-		free(ptr);
-	}
-}
-
 void * m_realloc(void* ptr, size_t size) {
 
 	void *ret;
@@ -809,7 +824,7 @@
 	if (data == NULL)
 		return;
 	while (len--) {
-		*p++ = 0x66;
+		*p++ = 0x0;
 	}
 }
 
--- a/dbutil.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/dbutil.h	Wed May 16 22:54:51 2012 +0800
@@ -33,28 +33,49 @@
 void startsyslog();
 #endif
 
-extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param);
+#ifdef __GNUC__
+#define ATTRIB_PRINTF(fmt,args) __attribute__((format(printf, fmt, args))) 
+#else
+#define ATTRIB_PRINTF(fmt,args)
+#endif
+
+#ifdef __GNUC__
+#define ATTRIB_NORETURN __attribute__((noreturn))
+#else
+#define ATTRIB_NORETURN
+#endif
+
+extern void (*_dropbear_exit)(int exitcode, const char* format, va_list param) ATTRIB_NORETURN;
 extern void (*_dropbear_log)(int priority, const char* format, va_list param);
 
-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);
+void dropbear_exit(const char* format, ...) ATTRIB_PRINTF(1,2) ATTRIB_NORETURN;
+
+void dropbear_close(const char* format, ...) ATTRIB_PRINTF(1,2) ;
+void dropbear_log(int priority, const char* format, ...) ATTRIB_PRINTF(2,3) ;
+
+void fail_assert(const char* expr, const char* file, int line) ATTRIB_NORETURN;
+
 #ifdef DEBUG_TRACE
-void dropbear_trace(const char* format, ...);
+void dropbear_trace(const char* format, ...) ATTRIB_PRINTF(1,2);
 void printhex(const char * label, const unsigned char * buf, int len);
 extern int debug_trace;
 #endif
+
 char * stripcontrol(const char * text);
-unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
+void get_socket_address(int fd, char **local_host, char **local_port,
+		char **remote_host, char **remote_port, int host_lookup);
+void getaddrstring(struct sockaddr_storage* addr, 
+		char **ret_host, char **ret_port, int host_lookup);
 int dropbear_listen(const char* address, const char* port,
 		int *socks, unsigned int sockcount, char **errstring, int *maxfd);
 int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
 		int *writefd, int *readfd, int *errfd, pid_t *pid);
 void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell);
+#ifdef ENABLE_CONNECT_UNIX
+int connect_unix(const char* addr);
+#endif
 int connect_remote(const char* remotehost, const char* remoteport,
 		int nonblocking, char ** errstring);
-char* getaddrhostname(struct sockaddr_storage * addr);
 int buf_readfile(buffer* buf, const char* filename);
 int buf_getline(buffer * line, FILE * authfile);
 
@@ -62,8 +83,7 @@
 void * m_malloc(size_t size);
 void * m_strdup(const char * str);
 void * m_realloc(void* ptr, size_t size);
-#define m_free(X) __m_free(X); (X) = NULL;
-void __m_free(void* ptr);
+#define m_free(X) free(X); (X) = NULL;
 void m_burn(void* data, unsigned int len);
 void setnonblocking(int fd);
 void disallow_core();
--- a/debian/changelog	Thu Nov 06 13:33:06 2008 +0000
+++ b/debian/changelog	Wed May 16 22:54:51 2012 +0800
@@ -1,3 +1,33 @@
+dropbear (2012.55-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Wed, 22 Feb 2012 22:54:00 +0800
+
+dropbear (2011.54-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Tues, 8 Nov 2011 22:54:00 +0800
+
+dropbear (0.53.1-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Wed, 2 Mar 2011 22:54:00 +0900
+
+dropbear (0.53-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Thu, 24 Feb 2011 22:54:00 +0900
+
+dropbear (0.52-0.1) unstable; urgency=low
+
+  * New upstream release.
+
+ -- Matt Johnston <[email protected]>  Wed, 12 Nov 2008 22:54:00 +0900
+
 dropbear (0.51-0.1) unstable; urgency=low
 
   * New upstream release.
--- a/debian/dropbear.postinst	Thu Nov 06 13:33:06 2008 +0000
+++ b/debian/dropbear.postinst	Wed May 16 22:54:51 2012 +0800
@@ -71,7 +71,7 @@
 fi
 
 if test -n "$2" && dpkg --compare-versions "$2" lt '0.50-4' &&
-update-service --check dropbear; then
+update-service --check dropbear 2>/dev/null; then
   update-service --remove /etc/dropbear 2>/dev/null || :
   sleep 6
   rm -rf /var/run/dropbear /var/run/dropbear.log
--- a/debug.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/debug.h	Wed May 16 22:54:51 2012 +0800
@@ -39,7 +39,7 @@
  * Caution: Don't use this in an unfriendly environment (ie unfirewalled),
  * since the printing may not sanitise strings etc. This will add a reasonable
  * amount to your executable size. */
-#define DEBUG_TRACE
+/*#define DEBUG_TRACE */
 
 /* All functions writing to the cleartext payload buffer call
  * CHECKCLEARTOWRITE() before writing. This is only really useful if you're
--- a/dropbear.8	Thu Nov 06 13:33:06 2008 +0000
+++ b/dropbear.8	Wed May 16 22:54:51 2012 +0800
@@ -7,7 +7,7 @@
 .I banner\fR] [\-d
 .I dsskey\fR] [\-r
 .I rsakey\fR] [\-p
-.IR port ]
+.IR [address:]port ]
 .SH DESCRIPTION
 .B dropbear
 is a SSH 2 server designed to be small enough to be used in small memory
@@ -24,7 +24,10 @@
 dsskeyfile.
 Use the contents of the file
 .I dsskey
-for the dss host key (default: /etc/dropbear/dropbear_dss_host_key).
+for the DSS host key (default: /etc/dropbear/dropbear_dss_host_key). 
+Note that 
+some SSH implementations
+use the term "DSA" rather than "DSS", they mean the same thing.
 This file is generated with
 .BR dropbearkey (8).
 .TP
@@ -94,6 +97,9 @@
 a certain period of inactivity. The trade-off is that a session may be
 closed if there is a temporary lapse of network connectivity. A setting
 if 0 disables keepalives.
+.TP
+.B \-I \fIidle_timeout
+Disconnect the session if no traffic is transmitted or received for \fIidle_timeout\fR seconds.
 .SH FILES
 
 .TP
@@ -148,6 +154,34 @@
 disabled at compile-time). This can also be disabled per-user
 by creating a file ~/.hushlogin .
 
+.SH ENVIRONMENT VARIABLES
+Dropbear sets the standard variables USER, LOGNAME, HOME, SHELL, PATH, and TERM.
+
+The variables below are set for sessions as appropriate. 
+
+.TP
+.B SSH_TTY
+This is set to the allocated TTY if a PTY was used.
+
+.TP
+.B SSH_CONNECTION
+Contains "<remote_ip> <remote_port> <local_ip> <local_port>".
+
+.TP
+.B DISPLAY
+Set X11 forwarding is used.
+
+.TP
+.B SSH_ORIGINAL_COMMAND
+If a 'command=' authorized_keys option was used, the original command is specified
+in this variable. If a shell was requested this is set to an empty value.
+
+.TP
+.B SSH_AUTH_SOCK
+Set to a forwarded ssh-agent connection.
+
+
+
 .SH AUTHOR
 Matt Johnston ([email protected]).
 .br
--- a/dropbearkey.8	Thu Nov 06 13:33:06 2008 +0000
+++ b/dropbearkey.8	Wed May 16 22:54:51 2012 +0800
@@ -11,13 +11,16 @@
 .IR bits ]
 .SH DESCRIPTION
 .B dropbearkey
-generates a type
-.I rsa
+generates a
+.I RSA
 or
-.I dss
-SSH private key, and saves it to a file for the use with the
+.I DSS
+format SSH private key, and saves it to a file for the use with the
 .BR dropbear (8)
 SSH 2 server.
+Note that 
+some SSH implementations
+use the term "DSA" rather than "DSS", they mean the same thing.
 .SH OPTIONS
 .TP
 .B \-t \fItype
--- a/dropbearkey.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/dropbearkey.c	Wed May 16 22:54:51 2012 +0800
@@ -23,7 +23,7 @@
  * SOFTWARE. */
 
 /* The format of the keyfiles is basically a raw dump of the buffer. Data types
- * are specified in the transport draft - string is a 32-bit len then the
+ * are specified in the transport rfc 4253 - string is a 32-bit len then the
  * non-null-terminated string, mp_int is a 32-bit len then the bignum data.
  * The actual functions are buf_put_rsa_priv_key() and buf_put_dss_priv_key()
 
--- a/dss.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/dss.c	Wed May 16 22:54:51 2012 +0800
@@ -43,7 +43,7 @@
  * The key will have the same format as buf_put_dss_key.
  * These should be freed with dss_key_free.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int buf_get_dss_pub_key(buffer* buf, dss_key *key) {
+int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key) {
 
 	TRACE(("enter buf_get_dss_pub_key"))
 	dropbear_assert(key != NULL);
@@ -76,7 +76,7 @@
 /* Same as buf_get_dss_pub_key, but reads a private "x" key at the end.
  * Loads a private dss key from a buffer
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int buf_get_dss_priv_key(buffer* buf, dss_key *key) {
+int buf_get_dss_priv_key(buffer* buf, dropbear_dss_key *key) {
 
 	int ret = DROPBEAR_FAILURE;
 
@@ -99,7 +99,7 @@
 	
 
 /* Clear and free the memory used by a public or private key */
-void dss_key_free(dss_key *key) {
+void dss_key_free(dropbear_dss_key *key) {
 
 	TRACE(("enter dsa_key_free"))
 	if (key == NULL) {
@@ -138,7 +138,7 @@
  * mpint	g
  * mpint	y
  */
-void buf_put_dss_pub_key(buffer* buf, dss_key *key) {
+void buf_put_dss_pub_key(buffer* buf, dropbear_dss_key *key) {
 
 	dropbear_assert(key != NULL);
 	buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
@@ -150,7 +150,7 @@
 }
 
 /* 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) {
+void buf_put_dss_priv_key(buffer* buf, dropbear_dss_key *key) {
 
 	dropbear_assert(key != NULL);
 	buf_put_dss_pub_key(buf, key);
@@ -161,7 +161,7 @@
 #ifdef DROPBEAR_SIGNKEY_VERIFY
 /* Verify a DSS signature (in buf) made on data by the key given. 
  * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
+int buf_dss_verify(buffer* buf, dropbear_dss_key *key, const unsigned char* data,
 		unsigned int len) {
 
 	unsigned char msghash[SHA1_HASH_SIZE];
@@ -270,7 +270,7 @@
 	size = mp_unsigned_bin_size(mp);
 	ret = m_malloc(size);
 	if (mp_to_unsigned_bin(mp, ret) != MP_OKAY) {
-		dropbear_exit("mem alloc error");
+		dropbear_exit("Mem alloc error");
 	}
 	if (len != NULL) {
 		*len = size;
@@ -292,7 +292,7 @@
  *
  * Now we aren't relying on the random number generation to protect the private
  * key x, which is a long term secret */
-void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
+void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, const unsigned char* data,
 		unsigned int len) {
 
 	unsigned char msghash[SHA1_HASH_SIZE];
@@ -342,7 +342,7 @@
 	m_mp_init(&dss_protok);
 	bytes_to_mp(&dss_protok, proto_k, SHA512_HASH_SIZE);
 	if (mp_mod(&dss_protok, key->q, &dss_k) != MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 	mp_clear(&dss_protok);
 	m_burn(proto_k, SHA512_HASH_SIZE);
@@ -355,30 +355,30 @@
 
 	/* g^k mod p */
 	if (mp_exptmod(key->g, &dss_k, key->p, &dss_temp1) !=  MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 	/* r = (g^k mod p) mod q */
 	if (mp_mod(&dss_temp1, key->q, &dss_r) != MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 
 	/* x*r mod q */
 	if (mp_mulmod(&dss_r, key->x, key->q, &dss_temp1) != MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 	/* (SHA1(M) + xr) mod q) */
 	if (mp_addmod(&dss_m, &dss_temp1, key->q, &dss_temp2) != MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 	
 	/* (k^-1) mod q */
 	if (mp_invmod(&dss_k, key->q, &dss_temp1) != MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 
 	/* s = (k^-1(SHA1(M) + xr)) mod q */
 	if (mp_mulmod(&dss_temp1, &dss_temp2, key->q, &dss_s) != MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 
 	buf_putstring(buf, SSH_SIGNKEY_DSS, SSH_SIGNKEY_DSS_LEN);
@@ -392,7 +392,7 @@
 	}
 	if (mp_to_unsigned_bin(&dss_r, buf_getwriteptr(buf, writelen)) 
 			!= MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 	mp_clear(&dss_r);
 	buf_incrwritepos(buf, writelen);
@@ -405,7 +405,7 @@
 	}
 	if (mp_to_unsigned_bin(&dss_s, buf_getwriteptr(buf, writelen)) 
 			!= MP_OKAY) {
-		dropbear_exit("dss error");
+		dropbear_exit("DSS error");
 	}
 	mp_clear(&dss_s);
 	buf_incrwritepos(buf, writelen);
--- a/dss.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/dss.h	Wed May 16 22:54:51 2012 +0800
@@ -32,29 +32,28 @@
 
 #define DSS_SIGNATURE_SIZE 4+SSH_SIGNKEY_DSS_LEN+4+2*SHA1_HASH_SIZE
 
-struct DSS_key {
+typedef struct {
 
 	mp_int* p;
 	mp_int* q;
 	mp_int* g;
 	mp_int* y;
+	/* x is the private part */
 	mp_int* x;
 
-};
+} dropbear_dss_key;
 
-typedef struct DSS_key dss_key;
-
-void buf_put_dss_sign(buffer* buf, dss_key *key, const unsigned char* data,
+void buf_put_dss_sign(buffer* buf, dropbear_dss_key *key, const unsigned char* data,
 		unsigned int len);
 #ifdef DROPBEAR_SIGNKEY_VERIFY
-int buf_dss_verify(buffer* buf, dss_key *key, const unsigned char* data,
+int buf_dss_verify(buffer* buf, dropbear_dss_key *key, const unsigned char* data,
 		unsigned int len);
 #endif
-int buf_get_dss_pub_key(buffer* buf, dss_key *key);
-int buf_get_dss_priv_key(buffer* buf, dss_key *key);
-void buf_put_dss_pub_key(buffer* buf, dss_key *key);
-void buf_put_dss_priv_key(buffer* buf, dss_key *key);
-void dss_key_free(dss_key *key);
+int buf_get_dss_pub_key(buffer* buf, dropbear_dss_key *key);
+int buf_get_dss_priv_key(buffer* buf, dropbear_dss_key *key);
+void buf_put_dss_pub_key(buffer* buf, dropbear_dss_key *key);
+void buf_put_dss_priv_key(buffer* buf, dropbear_dss_key *key);
+void dss_key_free(dropbear_dss_key *key);
 
 #endif /* DROPBEAR_DSS */
 
--- a/fake-rfc2553.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/fake-rfc2553.c	Wed May 16 22:54:51 2012 +0800
@@ -1,7 +1,6 @@
+/* Taken for Dropbear from OpenSSH 5.5p1 */
+
 /*
- *
- * Taken from OpenSSH 3.8.1p1
- * 
  * Copyright (C) 2000-2003 Damien Miller.  All rights reserved.
  * Copyright (C) 1999 WIDE Project.  All rights reserved.
  * 
@@ -40,7 +39,11 @@
 
 #include "includes.h"
 
-/* RCSID("$.Id: fake-rfc2553.c,v 1.5 2003/09/22 02:08:23 dtucker Exp $");*/
+#include <stdlib.h>
+#include <string.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
 
 #ifndef HAVE_GETNAMEINFO
 int getnameinfo(const struct sockaddr *sa, size_t salen, char *host, 
@@ -50,6 +53,8 @@
 	struct hostent *hp;
 	char tmpserv[16];
 
+	if (sa->sa_family != AF_UNSPEC && sa->sa_family != AF_INET)
+		return (EAI_FAMILY);
 	if (serv != NULL) {
 		snprintf(tmpserv, sizeof(tmpserv), "%d", ntohs(sin->sin_port));
 		if (strlcpy(serv, tmpserv, servlen) >= servlen)
@@ -94,6 +99,8 @@
 		return ("memory allocation failure.");
 	case EAI_NONAME:
 		return ("nodename nor servname provided, or not known");
+	case EAI_FAMILY:
+		return ("ai_family not supported");
 	default:
 		return ("unknown/invalid error.");
 	}
@@ -158,6 +165,9 @@
 	u_long addr;
 
 	port = 0;
+	if (hints && hints->ai_family != AF_UNSPEC &&
+	    hints->ai_family != AF_INET)
+		return (EAI_FAMILY);
 	if (servname != NULL) {
 		char *cp;
 
--- a/fake-rfc2553.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/fake-rfc2553.h	Wed May 16 22:54:51 2012 +0800
@@ -1,6 +1,6 @@
-/* Taken from OpenSSH 3.8.1p1 */
+/* Taken for Dropbear from OpenSSH 5.5p1 */
 
-/* $.Id: fake-rfc2553.h,v 1.9 2004/03/10 10:06:33 dtucker Exp $ */
+/* $Id: fake-rfc2553.h,v 1.16 2008/07/14 11:37:37 djm Exp $ */
 
 /*
  * Copyright (C) 2000-2003 Damien Miller.  All rights reserved.
@@ -43,6 +43,10 @@
 #define _FAKE_RFC2553_H
 
 #include "includes.h"
+#include <sys/types.h>
+#if defined(HAVE_NETDB_H)
+# include <netdb.h>
+#endif
 
 /*
  * First, socket and INET6 related definitions 
@@ -75,6 +79,7 @@
 	u_int16_t	sin6_port;
 	u_int32_t	sin6_flowinfo;
 	struct in6_addr	sin6_addr;
+	u_int32_t	sin6_scope_id;
 };
 #endif /* !HAVE_STRUCT_SOCKADDR_IN6 */
 
@@ -115,9 +120,19 @@
 #endif /* !NI_MAXHOST */
 
 #ifndef EAI_NODATA
-# define EAI_NODATA	1
-# define EAI_MEMORY	2
-# define EAI_NONAME	3
+# define EAI_NODATA	(INT_MAX - 1)
+#endif
+#ifndef EAI_MEMORY
+# define EAI_MEMORY	(INT_MAX - 2)
+#endif
+#ifndef EAI_NONAME
+# define EAI_NONAME	(INT_MAX - 3)
+#endif
+#ifndef EAI_SYSTEM
+# define EAI_SYSTEM	(INT_MAX - 4)
+#endif
+#ifndef EAI_FAMILY
+# define EAI_FAMILY	(INT_MAX - 5)
 #endif
 
 #ifndef HAVE_STRUCT_ADDRINFO
@@ -143,7 +158,7 @@
 #endif /* !HAVE_GETADDRINFO */
 
 #if !defined(HAVE_GAI_STRERROR) && !defined(HAVE_CONST_GAI_STRERROR_PROTO)
-#define gai_strerror(a)		(ssh_gai_strerror(a))
+#define gai_strerror(a)		(_ssh_compat_gai_strerror(a))
 char *gai_strerror(int);
 #endif /* !HAVE_GAI_STRERROR */
 
--- a/gendss.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/gendss.c	Wed May 16 22:54:51 2012 +0800
@@ -37,17 +37,17 @@
 
 #ifdef DROPBEAR_DSS
 
-static void getq(dss_key *key);
-static void getp(dss_key *key, unsigned int size);
-static void getg(dss_key *key);
-static void getx(dss_key *key);
-static void gety(dss_key *key);
+static void getq(dropbear_dss_key *key);
+static void getp(dropbear_dss_key *key, unsigned int size);
+static void getg(dropbear_dss_key *key);
+static void getx(dropbear_dss_key *key);
+static void gety(dropbear_dss_key *key);
 
-dss_key * gen_dss_priv_key(unsigned int size) {
+dropbear_dss_key * gen_dss_priv_key(unsigned int size) {
 
-	dss_key *key;
+	dropbear_dss_key *key;
 
-	key = (dss_key*)m_malloc(sizeof(dss_key));
+	key = m_malloc(sizeof(*key));
 
 	key->p = (mp_int*)m_malloc(sizeof(mp_int));
 	key->q = (mp_int*)m_malloc(sizeof(mp_int));
@@ -68,7 +68,7 @@
 	
 }
 
-static void getq(dss_key *key) {
+static void getq(dropbear_dss_key *key) {
 
 	char buf[QSIZE];
 
@@ -81,12 +81,12 @@
 
 	/* 18 rounds are required according to HAC */
 	if (mp_prime_next_prime(key->q, 18, 0) != MP_OKAY) {
-		fprintf(stderr, "dss key generation failed\n");
+		fprintf(stderr, "DSS key generation failed\n");
 		exit(1);
 	}
 }
 
-static void getp(dss_key *key, unsigned int size) {
+static void getp(dropbear_dss_key *key, unsigned int size) {
 
 	DEF_MP_INT(tempX);
 	DEF_MP_INT(tempC);
@@ -100,7 +100,7 @@
 
 	/* 2*q */
 	if (mp_mul_d(key->q, 2, &temp2q) != MP_OKAY) {
-		fprintf(stderr, "dss key generation failed\n");
+		fprintf(stderr, "DSS key generation failed\n");
 		exit(1);
 	}
 	
@@ -117,25 +117,25 @@
 
 		/* C = X mod 2q */
 		if (mp_mod(&tempX, &temp2q, &tempC) != MP_OKAY) {
-			fprintf(stderr, "dss key generation failed\n");
+			fprintf(stderr, "DSS key generation failed\n");
 			exit(1);
 		}
 
 		/* P = X - (C - 1) = X - C + 1*/
 		if (mp_sub(&tempX, &tempC, &tempP) != MP_OKAY) {
-			fprintf(stderr, "dss key generation failed\n");
+			fprintf(stderr, "DSS key generation failed\n");
 			exit(1);
 		}
 		
 		if (mp_add_d(&tempP, 1, key->p) != MP_OKAY) {
-			fprintf(stderr, "dss key generation failed\n");
+			fprintf(stderr, "DSS key generation failed\n");
 			exit(1);
 		}
 
 		/* now check for prime, 5 rounds is enough according to HAC */
 		/* result == 1  =>  p is prime */
 		if (mp_prime_is_prime(key->p, 5, &result) != MP_OKAY) {
-			fprintf(stderr, "dss key generation failed\n");
+			fprintf(stderr, "DSS key generation failed\n");
 			exit(1);
 		}
 	} while (!result);
@@ -145,7 +145,7 @@
 	m_free(buf);
 }
 
-static void getg(dss_key * key) {
+static void getg(dropbear_dss_key * key) {
 
 	DEF_MP_INT(div);
 	DEF_MP_INT(h);
@@ -155,11 +155,11 @@
 
 	/* get div=(p-1)/q */
 	if (mp_sub_d(key->p, 1, &val) != MP_OKAY) {
-		fprintf(stderr, "dss key generation failed\n");
+		fprintf(stderr, "DSS key generation failed\n");
 		exit(1);
 	}
 	if (mp_div(&val, key->q, &div, NULL) != MP_OKAY) {
-		fprintf(stderr, "dss key generation failed\n");
+		fprintf(stderr, "DSS key generation failed\n");
 		exit(1);
 	}
 
@@ -168,12 +168,12 @@
 	do {
 		/* now keep going with g=h^div mod p, until g > 1 */
 		if (mp_exptmod(&h, &div, key->p, key->g) != MP_OKAY) {
-			fprintf(stderr, "dss key generation failed\n");
+			fprintf(stderr, "DSS key generation failed\n");
 			exit(1);
 		}
 
 		if (mp_add_d(&h, 1, &h) != MP_OKAY) {
-			fprintf(stderr, "dss key generation failed\n");
+			fprintf(stderr, "DSS key generation failed\n");
 			exit(1);
 		}
 	
@@ -182,15 +182,15 @@
 	mp_clear_multi(&div, &h, &val, NULL);
 }
 
-static void getx(dss_key *key) {
+static void getx(dropbear_dss_key *key) {
 
 	gen_random_mpint(key->q, key->x);
 }
 
-static void gety(dss_key *key) {
+static void gety(dropbear_dss_key *key) {
 
 	if (mp_exptmod(key->g, key->x, key->p, key->y) != MP_OKAY) {
-		fprintf(stderr, "dss key generation failed\n");
+		fprintf(stderr, "DSS key generation failed\n");
 		exit(1);
 	}
 }
--- a/gendss.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/gendss.h	Wed May 16 22:54:51 2012 +0800
@@ -29,7 +29,7 @@
 
 #ifdef DROPBEAR_DSS
 
-dss_key * gen_dss_priv_key(unsigned int size);
+dropbear_dss_key * gen_dss_priv_key(unsigned int size);
 
 #endif /* DROPBEAR_DSS */
 
--- a/genrsa.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/genrsa.c	Wed May 16 22:54:51 2012 +0800
@@ -37,14 +37,14 @@
 		mp_int* rsa_e, unsigned int size);
 
 /* mostly taken from libtomcrypt's rsa key generation routine */
-rsa_key * gen_rsa_priv_key(unsigned int size) {
+dropbear_rsa_key * gen_rsa_priv_key(unsigned int size) {
 
-	rsa_key * key;
+	dropbear_rsa_key * key;
 	DEF_MP_INT(pminus);
 	DEF_MP_INT(qminus);
 	DEF_MP_INT(lcm);
 
-	key = (rsa_key*)m_malloc(sizeof(rsa_key));
+	key = m_malloc(sizeof(*key));
 
 	key->e = (mp_int*)m_malloc(sizeof(mp_int));
 	key->n = (mp_int*)m_malloc(sizeof(mp_int));
@@ -58,7 +58,7 @@
 	seedrandom();
 
 	if (mp_set_int(key->e, RSA_E) != MP_OKAY) {
-		fprintf(stderr, "rsa generation failed\n");
+		fprintf(stderr, "RSA generation failed\n");
 		exit(1);
 	}
 
@@ -66,20 +66,20 @@
 	getrsaprime(key->q, &qminus, key->e, size/2);
 
 	if (mp_mul(key->p, key->q, key->n) != MP_OKAY) {
-		fprintf(stderr, "rsa generation failed\n");
+		fprintf(stderr, "RSA generation failed\n");
 		exit(1);
 	}
 
 	/* lcm(p-1, q-1) */
 	if (mp_lcm(&pminus, &qminus, &lcm) != MP_OKAY) {
-		fprintf(stderr, "rsa generation failed\n");
+		fprintf(stderr, "RSA generation failed\n");
 		exit(1);
 	}
 
 	/* de = 1 mod lcm(p-1,q-1) */
 	/* therefore d = (e^-1) mod lcm(p-1,q-1) */
 	if (mp_invmod(key->e, &lcm, key->d) != MP_OKAY) {
-		fprintf(stderr, "rsa generation failed\n");
+		fprintf(stderr, "RSA generation failed\n");
 		exit(1);
 	}
 
@@ -108,18 +108,18 @@
 
 		/* find the next integer which is prime, 8 round of miller-rabin */
 		if (mp_prime_next_prime(prime, 8, 0) != MP_OKAY) {
-			fprintf(stderr, "rsa generation failed\n");
+			fprintf(stderr, "RSA generation failed\n");
 			exit(1);
 		}
 
 		/* subtract one to get p-1 */
 		if (mp_sub_d(prime, 1, primeminus) != MP_OKAY) {
-			fprintf(stderr, "rsa generation failed\n");
+			fprintf(stderr, "RSA generation failed\n");
 			exit(1);
 		}
 		/* check relative primality to e */
 		if (mp_gcd(primeminus, rsa_e, &temp_gcd) != MP_OKAY) {
-			fprintf(stderr, "rsa generation failed\n");
+			fprintf(stderr, "RSA generation failed\n");
 			exit(1);
 		}
 	} while (mp_cmp_d(&temp_gcd, 1) != MP_EQ); /* while gcd(p-1, e) != 1 */
--- a/genrsa.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/genrsa.h	Wed May 16 22:54:51 2012 +0800
@@ -29,7 +29,7 @@
 
 #ifdef DROPBEAR_RSA
 
-rsa_key * gen_rsa_priv_key(unsigned int size);
+dropbear_rsa_key * gen_rsa_priv_key(unsigned int size);
 
 #endif /* DROPBEAR_RSA */
 
--- a/includes.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/includes.h	Wed May 16 22:54:51 2012 +0800
@@ -120,8 +120,14 @@
 #include <libgen.h>
 #endif
 
+#ifdef BUNDLED_LIBTOM
 #include "libtomcrypt/src/headers/tomcrypt.h"
 #include "libtommath/tommath.h"
+#else
+#include <tomcrypt.h>
+#include <tommath.h>
+#endif
+
 
 #include "compat.h"
 #include "fake-rfc2553.h"
--- a/kex.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/kex.h	Wed May 16 22:54:51 2012 +0800
@@ -52,8 +52,8 @@
 	unsigned sentkexinit : 1; /*set when we've sent/recv kexinit packet */
 	unsigned recvkexinit : 1;
 	unsigned firstfollows : 1; /* true when first_kex_packet_follows is set */
-	unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/
-	unsigned recvnewkeys : 1;
+	unsigned sentnewkeys : 1; /* set once we've send MSG_NEWKEYS (will be cleared once we have also received */
+	unsigned recvnewkeys : 1; /* set once we've received MSG_NEWKEYS (cleared once we have also sent */
 
 	unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed,
 								  ie the transport layer has been set up */
--- a/libtomcrypt/src/headers/tomcrypt_custom.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/libtomcrypt/src/headers/tomcrypt_custom.h	Wed May 16 22:54:51 2012 +0800
@@ -78,7 +78,7 @@
 /* #define LTC_CLEAN_STACK */
 
 /* disable all file related functions */
-/* #define LTC_NO_FILE */
+#define LTC_NO_FILE
 
 /* disable all forms of ASM */
 /* #define LTC_NO_ASM */
@@ -118,14 +118,18 @@
 #define LTC_CTR_MODE
 #endif
 
-#if defined(DROPBEAR_DSS) && defined(DSS_PROTOK)
-#define SHA512
+#define SHA1
+
+#ifdef DROPBEAR_MD5
+#define MD5
 #endif
 
-#define SHA1
+#ifdef DROPBEAR_SHA256
+#define SHA256
+#endif
 
-#ifdef DROPBEAR_MD5_HMAC
-#define MD5
+#ifdef DROPBEAR_SHA512
+#define SHA512
 #endif
 
 #define LTC_HMAC
--- a/libtommath/Makefile.in	Thu Nov 06 13:33:06 2008 +0000
+++ b/libtommath/Makefile.in	Wed May 16 22:54:51 2012 +0800
@@ -170,8 +170,8 @@
 	rm -f *.bat *.pdf *.o *.a *.obj *.lib *.exe *.dll etclib/*.o demo/demo.o test ltmtest mpitest mtest/mtest mtest/mtest.exe \
         *.idx *.toc *.log *.aux *.dvi *.lof *.ind *.ilg *.ps *.log *.s mpi.c *.da *.dyn *.dpi tommath.tex `find . -type f | grep [~] | xargs` *.lo *.la
 	rm -rf .libs
-	cd etc ; MAKE=${MAKE} ${MAKE} clean
-	cd pics ; MAKE=${MAKE} ${MAKE} clean
+	-cd etc && MAKE=${MAKE} ${MAKE} clean
+	-cd pics && MAKE=${MAKE} ${MAKE} clean
 
 #zipup the project (take that!)
 no_oops: clean
--- a/libtommath/bn_mp_exptmod_fast.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/libtommath/bn_mp_exptmod_fast.c	Wed May 16 22:54:51 2012 +0800
@@ -67,13 +67,13 @@
 
   /* init M array */
   /* init first cell */
-  if ((err = mp_init(&M[1])) != MP_OKAY) {
+  if ((err = mp_init_size(&M[1], P->alloc)) != MP_OKAY) {
      return err;
   }
 
   /* now init the second half of the array */
   for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
-    if ((err = mp_init(&M[x])) != MP_OKAY) {
+    if ((err = mp_init_size(&M[x], P->alloc)) != MP_OKAY) {
       for (y = 1<<(winsize-1); y < x; y++) {
         mp_clear (&M[y]);
       }
@@ -133,7 +133,7 @@
   }
 
   /* setup result */
-  if ((err = mp_init (&res)) != MP_OKAY) {
+  if ((err = mp_init_size (&res, P->alloc)) != MP_OKAY) {
     goto LBL_M;
   }
 
--- a/libtommath/bn_mp_init_copy.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/libtommath/bn_mp_init_copy.c	Wed May 16 22:54:51 2012 +0800
@@ -20,7 +20,7 @@
 {
   int     res;
 
-  if ((res = mp_init (a)) != MP_OKAY) {
+  if ((res = mp_init_size (a, b->used)) != MP_OKAY) {
     return res;
   }
   return mp_copy (b, a);
--- a/libtommath/bn_mp_mod.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/libtommath/bn_mp_mod.c	Wed May 16 22:54:51 2012 +0800
@@ -22,7 +22,7 @@
   mp_int  t;
   int     res;
 
-  if ((res = mp_init (&t)) != MP_OKAY) {
+  if ((res = mp_init_size (&t, b->used)) != MP_OKAY) {
     return res;
   }
 
--- a/libtommath/bn_mp_mulmod.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/libtommath/bn_mp_mulmod.c	Wed May 16 22:54:51 2012 +0800
@@ -21,7 +21,7 @@
   int     res;
   mp_int  t;
 
-  if ((res = mp_init (&t)) != MP_OKAY) {
+  if ((res = mp_init_size (&t, c->used)) != MP_OKAY) {
     return res;
   }
 
--- a/libtommath/bn_mp_prime_next_prime.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/libtommath/bn_mp_prime_next_prime.c	Wed May 16 22:54:51 2012 +0800
@@ -143,7 +143,7 @@
 
       /* is this prime? */
       for (x = 0; x < t; x++) {
-          mp_set(&b, ltm_prime_tab[t]);
+          mp_set(&b, ltm_prime_tab[x]);
           if ((err = mp_prime_miller_rabin(a, &b, &res)) != MP_OKAY) {
              goto LBL_ERR;
           }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/list.c	Wed May 16 22:54:51 2012 +0800
@@ -0,0 +1,49 @@
+#include "options.h"
+#include "dbutil.h"
+#include "list.h"
+
+void list_append(m_list *list, void *item) {
+	m_list_elem *elem;
+	
+	elem = m_malloc(sizeof(*elem));
+	elem->item = item;
+	elem->list = list;
+	elem->next = NULL;
+	if (!list->first) {
+		list->first = elem;
+		elem->prev = NULL;
+	} else {
+		elem->prev = list->last;
+		list->last->next = elem;
+	}
+	list->last = elem;
+}
+
+m_list * list_new() {
+	m_list *ret = m_malloc(sizeof(m_list));
+	ret->first = ret->last = NULL;
+	return ret;
+}
+
+void * list_remove(m_list_elem *elem) {
+	void *item = elem->item;
+	m_list *list = elem->list;
+	if (list->first == elem)
+	{
+		list->first = elem->next;
+	}
+	if (list->last == elem)
+	{
+		list->last = elem->prev;
+	}
+	if (elem->prev)
+	{
+		elem->prev->next = elem->next;
+	}
+	if (elem->next)
+	{
+		elem->next->prev = elem->prev;
+	}
+	m_free(elem);
+	return item;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/list.h	Wed May 16 22:54:51 2012 +0800
@@ -0,0 +1,28 @@
+#ifndef _DROPBEAR_LIST_H
+#define _DROPBEAR_LIST_H
+
+struct _m_list;
+
+struct _m_list_elem {
+    void *item;
+	struct _m_list_elem *next;
+	struct _m_list_elem *prev;
+    struct _m_list *list;
+};
+	
+typedef struct _m_list_elem m_list_elem;
+
+struct _m_list {
+    m_list_elem *first;
+    m_list_elem *last;
+};
+
+typedef struct _m_list m_list;
+
+m_list * list_new();
+void list_append(m_list *list, void *item);
+/* returns the item for the element removed */
+void * list_remove(m_list_elem *elem);
+
+
+#endif /* _DROPBEAR_LIST_H */
--- a/options.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/options.h	Wed May 16 22:54:51 2012 +0800
@@ -46,9 +46,10 @@
 /*#define NO_FAST_EXPTMOD*/
 
 /* Set this if you want to use the DROPBEAR_SMALL_CODE option. This can save
-several kB in binary size, however will make the symmetrical ciphers (AES, DES
-etc) slower (perhaps by 50%). Recommended for most small systems. */
-#define DROPBEAR_SMALL_CODE
+several kB in binary size however will make the symmetrical ciphers and hashes
+slower, perhaps by 50%. Recommended for small systems that aren't doing
+much traffic. */
+/*#define DROPBEAR_SMALL_CODE*/
 
 /* Enable X11 Forwarding - server only */
 #define ENABLE_X11FWD
@@ -63,8 +64,9 @@
 #define ENABLE_SVR_LOCALTCPFWD
 #define ENABLE_SVR_REMOTETCPFWD
 
-/* Enable Authentication Agent Forwarding - server only for now */
-#define ENABLE_AGENTFWD
+/* Enable Authentication Agent Forwarding */
+#define ENABLE_SVR_AGENTFWD
+#define ENABLE_CLI_AGENTFWD
 
 
 /* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to
@@ -82,17 +84,21 @@
  * Protocol RFC 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
 #define DROPBEAR_3DES
 #define DROPBEAR_AES256
-#define DROPBEAR_BLOWFISH
+/* Compiling in Blowfish will add ~6kB to runtime heap memory usage */
+/*#define DROPBEAR_BLOWFISH*/
 #define DROPBEAR_TWOFISH256
 #define DROPBEAR_TWOFISH128
-*/
+
+/* Enable "Counter Mode" for ciphers. This is more secure than normal
+ * CBC mode against certain attacks. This adds around 1kB to binary 
+ * size and is recommended for most cases */
+#define DROPBEAR_ENABLE_CTR_MODE
 
 /* You can compile with no encryption if you want. In some circumstances
- * this could be safe securitywise, though make sure you know what
+ * this could be safe security-wise, though make sure you know what
  * you're doing. Anyone can see everything that goes over the wire, so
  * the only safe auth method is public key. You'll have to disable all other
  * ciphers above in the client if you want to use this, or implement cipher
@@ -101,38 +107,32 @@
  * The best way to do things is probably make normal compile of dropbear with
  * all ciphers including "none" as the server, then recompile a special
  * "dbclient-insecure" client. */
-#define DROPBEAR_NONE_CIPHER
-
-/* Enable "Counter Mode" for ciphers. This is more secure than normal
- * CBC mode against certain attacks. This adds around 1kB to binary 
- * size and is recommended for most cases */
-#define DROPBEAR_ENABLE_CTR_MODE
+/* #define DROPBEAR_NONE_CIPHER */
 
 /* Message Integrity - at least one required.
  * Protocol RFC requires sha1 and recommends sha1-96.
- * sha1-96 may be of use for slow links, as it has a smaller overhead.
+ * sha1-96 is 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
- * for the random number generator and public-key cryptography anyway.
+ * There's no reason to disable sha1 or sha1-96 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 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_SHA2_256_HMAC*/
+/*#define DROPBEAR_SHA2_512_HMAC*/
 #define DROPBEAR_MD5_HMAC
-*/
 
 /* You can also disable integrity. Don't bother disabling this if you're
- * still using a cipher, it's relatively cheap. Don't disable this if you're
- * using 'none' cipher, since it's dead simple to run arbitrary commands
- * on the remote host. Beware.
+ * still using a cipher, it's relatively cheap. If you disable this it's dead
+ * simple to run arbitrary commands on the remote host. Beware.
  * Note again, for the client you will have to disable other hashes above
  * to use this. */
-#define DROPBEAR_NONE_INTEGRITY
+/* #define DROPBEAR_NONE_INTEGRITY */
 
 /* Hostkey/public key algorithms - at least one required, these are used
  * for hostkey as well as for verifying signatures with pubkey auth.
@@ -149,9 +149,21 @@
 /* Define DSS_PROTOK to use PuTTY's method of generating the value k for dss,
  * rather than just from the random byte source. Undefining this will save you
  * ~4k in binary size with static uclibc, but your DSS hostkey could be exposed
- * if the random number source isn't good. In general this isn't required */
+ * if the random number source isn't good. It happened to Sony. 
+ * On systems with a decent random source this isn't required. */
 /* #define DSS_PROTOK */
 
+/* Control the memory/performance/compression tradeoff for zlib.
+ * Set windowBits=8 for least memory usage, see your system's
+ * zlib.h for full details.
+ * Default settings (windowBits=15) will use 256kB for compression
+ * windowBits=8 will use 129kB for compression.
+ * Both modes will use ~35kB for decompression (using windowBits=15 for
+ * interoperability) */
+#ifndef DROPBEAR_ZLIB_WINDOW_BITS
+#define DROPBEAR_ZLIB_WINDOW_BITS 15 
+#endif
+
 /* Whether to do reverse DNS lookups. */
 #define DO_HOST_LOOKUP
 
@@ -167,10 +179,11 @@
 /* Authentication Types - at least one required.
    RFC Draft requires pubkey auth, and recommends password */
 
-/* Note: PAM auth is quite simple, and only works for PAM modules which just do
+/* Note: PAM auth is quite simple and only works for PAM modules which just do
  * a simple "Login: " "Password: " (you can edit the strings in svr-authpam.c).
- * It's useful for systems like OS X where standard password crypts don't work,
- * but there's an interface via a PAM module - don't bother using it otherwise.
+ * It's useful for systems like OS X where standard password crypts don't work
+ * but there's an interface via a PAM module. It won't work for more complex
+ * PAM challenge/response.
  * You can't enable both PASSWORD and PAM. */
 
 #define ENABLE_SVR_PASSWORD_AUTH
@@ -178,11 +191,17 @@
 /*#define ENABLE_SVR_PAM_AUTH*/
 #define ENABLE_SVR_PUBKEY_AUTH
 
-/* Wether to ake public key options in authorized_keys file into account */
+/* Whether to take public key options in 
+ * authorized_keys file into account */
 #ifdef ENABLE_SVR_PUBKEY_AUTH
 #define ENABLE_SVR_PUBKEY_OPTIONS
 #endif
 
+/* Define this to allow logging in to accounts that have no password specified.
+ * Public key logins are allowed for blank-password accounts regardless of this
+ * setting. */
+/* #define ALLOW_BLANK_PASSWORD */
+
 #define ENABLE_CLI_PASSWORD_AUTH
 #define ENABLE_CLI_PUBKEY_AUTH
 #define ENABLE_CLI_INTERACT_AUTH
@@ -244,7 +263,7 @@
 /* The command to invoke for xauth when using X11 forwarding.
  * "-q" for quiet */
 #ifndef XAUTH_COMMAND
-#define XAUTH_COMMAND "/usr/X11R6/bin/xauth -q"
+#define XAUTH_COMMAND "/usr/bin/X11/xauth -q"
 #endif
 
 /* if you want to enable running an sftp server (such as the one included with
@@ -270,18 +289,28 @@
    significant difference to network performance. 24kB was empirically
    chosen for a 100mbit ethernet network. The value can be altered at
    runtime with the -W argument. */
+#ifndef DEFAULT_RECV_WINDOW
 #define DEFAULT_RECV_WINDOW 24576
+#endif
 /* Maximum size of a received SSH data packet - this _MUST_ be >= 32768
    in order to interoperate with other implementations */
+#ifndef RECV_MAX_PAYLOAD_LEN
 #define RECV_MAX_PAYLOAD_LEN 32768
+#endif
 /* Maximum size of a transmitted data packet - this can be any value,
    though increasing it may not make a significant difference. */
+#ifndef TRANS_MAX_PAYLOAD_LEN
 #define TRANS_MAX_PAYLOAD_LEN 16384
+#endif
 
 /* Ensure that data is transmitted every KEEPALIVE seconds. This can
 be overridden at runtime with -K. 0 disables keepalives */
 #define DEFAULT_KEEPALIVE 0
 
+/* Ensure that data is received within IDLE_TIMEOUT seconds. This can
+be overridden at runtime with -I. 0 disables idle timeouts */
+#define DEFAULT_IDLE_TIMEOUT 0
+
 /* The default path. This will often get replaced by the shell */
 #define DEFAULT_PATH "/usr/bin:/bin"
 
--- a/packet.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/packet.c	Wed May 16 22:54:51 2012 +0800
@@ -35,11 +35,13 @@
 #include "auth.h"
 #include "channel.h"
 
-static void read_packet_init();
-static void writemac(buffer * outputbuffer, buffer * clearwritebuf);
-static int checkmac(buffer* hashbuf, buffer* readbuf);
+static int read_packet_init();
+static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
+		buffer * clear_buf, unsigned int clear_len, 
+		unsigned char *output_mac);
+static int checkmac();
 
-#define ZLIB_COMPRESS_INCR 20 /* this is 12 bytes + 0.1% of 8000 bytes */
+#define ZLIB_COMPRESS_INCR 100
 #define ZLIB_DECOMPRESS_INCR 100
 #ifndef DISABLE_ZLIB
 static buffer* buf_decompress(buffer* buf, unsigned int len);
@@ -51,6 +53,8 @@
 
 	int len, written;
 	buffer * writebuf = NULL;
+	time_t now;
+	unsigned packet_type;
 	
 	TRACE(("enter write_packet"))
 	dropbear_assert(!isempty(&ses.writequeue));
@@ -58,7 +62,10 @@
 	/* Get the next buffer in the queue of encrypted packets to write*/
 	writebuf = (buffer*)examine(&ses.writequeue);
 
-	len = writebuf->len - writebuf->pos;
+	/* The last byte of the buffer is not to be transmitted, but is 
+	 * a cleartext packet_type indicator */
+	packet_type = writebuf->data[writebuf->len-1];
+	len = writebuf->len - 1 - writebuf->pos;
 	dropbear_assert(len > 0);
 	/* Try to write as much as possible */
 	written = write(ses.sock_out, buf_getptr(writebuf, len), len);
@@ -68,11 +75,16 @@
 			TRACE(("leave writepacket: EINTR"))
 			return;
 		} else {
-			dropbear_exit("error writing");
+			dropbear_exit("Error writing");
 		}
 	} 
 	
-	ses.last_packet_time = time(NULL);
+	now = time(NULL);
+	ses.last_trx_packet_time = now;
+
+	if (packet_type != SSH_MSG_IGNORE) {
+		ses.last_packet_time = now;
+	}
 
 	if (written == 0) {
 		ses.remoteclosed();
@@ -101,18 +113,18 @@
 	unsigned char blocksize;
 
 	TRACE(("enter read_packet"))
-	blocksize = ses.keys->recv_algo_crypt->blocksize;
+	blocksize = ses.keys->recv.algo_crypt->blocksize;
 	
 	if (ses.readbuf == NULL || ses.readbuf->len < blocksize) {
+		int ret;
 		/* In the first blocksize of a packet */
 
 		/* Read the first blocksize of the packet, so we can decrypt it and
 		 * find the length of the whole packet */
-		read_packet_init();
+		ret = read_packet_init();
 
-		/* If we don't have the length of decryptreadbuf, we didn't read
-		 * a whole blocksize and should exit */
-		if (ses.decryptreadbuf->len == 0) {
+		if (ret == DROPBEAR_FAILURE) {
+			/* didn't read enough to determine the length */
 			TRACE(("leave read_packet: packetinit done"))
 			return;
 		}
@@ -120,7 +132,6 @@
 
 	/* Attempt to read the remainder of the packet, note that there
 	 * mightn't be any available (EAGAIN) */
-	dropbear_assert(ses.readbuf != NULL);
 	maxlen = ses.readbuf->len - ses.readbuf->pos;
 	len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen);
 
@@ -133,7 +144,7 @@
 			TRACE(("leave read_packet: EINTR or EAGAIN"))
 			return;
 		} else {
-			dropbear_exit("error reading: %s", strerror(errno));
+			dropbear_exit("Error reading: %s", strerror(errno));
 		}
 	}
 
@@ -150,71 +161,75 @@
 
 /* Function used to read the initial portion of a packet, and determine the
  * length. Only called during the first BLOCKSIZE of a packet. */
-static void read_packet_init() {
+/* Returns DROPBEAR_SUCCESS if the length is determined, 
+ * DROPBEAR_FAILURE otherwise */
+static int read_packet_init() {
 
 	unsigned int maxlen;
-	int len;
-	unsigned char blocksize;
-	unsigned char macsize;
+	int slen;
+	unsigned int len;
+	unsigned int blocksize;
+	unsigned int macsize;
 
 
-	blocksize = ses.keys->recv_algo_crypt->blocksize;
-	macsize = ses.keys->recv_algo_mac->hashsize;
+	blocksize = ses.keys->recv.algo_crypt->blocksize;
+	macsize = ses.keys->recv.algo_mac->hashsize;
 
 	if (ses.readbuf == NULL) {
 		/* start of a new packet */
 		ses.readbuf = buf_new(INIT_READBUF);
-		dropbear_assert(ses.decryptreadbuf == NULL);
-		ses.decryptreadbuf = buf_new(blocksize);
 	}
 
 	maxlen = blocksize - ses.readbuf->pos;
 			
 	/* read the rest of the packet if possible */
-	len = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen),
+	slen = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen),
 			maxlen);
-	if (len == 0) {
+	if (slen == 0) {
 		ses.remoteclosed();
 	}
-	if (len < 0) {
+	if (slen < 0) {
 		if (errno == EINTR) {
 			TRACE(("leave read_packet_init: EINTR"))
-			return;
+			return DROPBEAR_FAILURE;
 		}
-		dropbear_exit("error reading: %s", strerror(errno));
+		dropbear_exit("Error reading: %s", strerror(errno));
 	}
 
-	buf_incrwritepos(ses.readbuf, len);
+	buf_incrwritepos(ses.readbuf, slen);
 
-	if ((unsigned int)len != maxlen) {
+	if ((unsigned int)slen != maxlen) {
 		/* don't have enough bytes to determine length, get next time */
-		return;
+		return DROPBEAR_FAILURE;
 	}
 
 	/* now we have the first block, need to get packet length, so we decrypt
 	 * the first block (only need first 4 bytes) */
 	buf_setpos(ses.readbuf, 0);
-	if (ses.keys->recv_crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), 
-				buf_getwriteptr(ses.decryptreadbuf,blocksize),
+	if (ses.keys->recv.crypt_mode->decrypt(buf_getptr(ses.readbuf, blocksize), 
+				buf_getwriteptr(ses.readbuf, blocksize),
 				blocksize,
-				&ses.keys->recv_cipher_state) != CRYPT_OK) {
-		dropbear_exit("error decrypting");
+				&ses.keys->recv.cipher_state) != CRYPT_OK) {
+		dropbear_exit("Error decrypting");
 	}
-	buf_setlen(ses.decryptreadbuf, blocksize);
-	len = buf_getint(ses.decryptreadbuf) + 4 + macsize;
+	len = buf_getint(ses.readbuf) + 4 + macsize;
 
-	buf_setpos(ses.readbuf, blocksize);
+	TRACE(("packet size is %d, block %d mac %d", len, blocksize, macsize))
+
 
 	/* check packet length */
 	if ((len > RECV_MAX_PACKET_LEN) ||
 		(len < MIN_PACKET_LEN + macsize) ||
 		((len - macsize) % blocksize != 0)) {
-		dropbear_exit("bad packet size %d", len);
+		dropbear_exit("Integrity error (bad packet size %d)", len);
 	}
 
-	buf_resize(ses.readbuf, len);
+	if (len > ses.readbuf->size) {
+		buf_resize(ses.readbuf, len);		
+	}
 	buf_setlen(ses.readbuf, len);
-
+	buf_setpos(ses.readbuf, blocksize);
+	return DROPBEAR_SUCCESS;
 }
 
 /* handle the received packet */
@@ -226,69 +241,58 @@
 	unsigned int len;
 
 	TRACE(("enter decrypt_packet"))
-	blocksize = ses.keys->recv_algo_crypt->blocksize;
-	macsize = ses.keys->recv_algo_mac->hashsize;
+	blocksize = ses.keys->recv.algo_crypt->blocksize;
+	macsize = ses.keys->recv.algo_mac->hashsize;
 
 	ses.kexstate.datarecv += ses.readbuf->len;
 
 	/* we've already decrypted the first blocksize in read_packet_init */
 	buf_setpos(ses.readbuf, blocksize);
 
-	buf_resize(ses.decryptreadbuf, ses.readbuf->len - macsize);
-	buf_setlen(ses.decryptreadbuf, ses.decryptreadbuf->size);
-	buf_setpos(ses.decryptreadbuf, blocksize);
-
-	/* decrypt it */
-	while (ses.readbuf->pos < ses.readbuf->len - macsize) {
-		if (ses.keys->recv_crypt_mode->decrypt(
-					buf_getptr(ses.readbuf, blocksize), 
-					buf_getwriteptr(ses.decryptreadbuf, blocksize),
-					blocksize,
-					&ses.keys->recv_cipher_state) != CRYPT_OK) {
-			dropbear_exit("error decrypting");
-		}
-		buf_incrpos(ses.readbuf, blocksize);
-		buf_incrwritepos(ses.decryptreadbuf, blocksize);
+	/* decrypt it in-place */
+	len = ses.readbuf->len - macsize - ses.readbuf->pos;
+	if (ses.keys->recv.crypt_mode->decrypt(
+				buf_getptr(ses.readbuf, len), 
+				buf_getwriteptr(ses.readbuf, len),
+				len,
+				&ses.keys->recv.cipher_state) != CRYPT_OK) {
+		dropbear_exit("Error decrypting");
 	}
+	buf_incrpos(ses.readbuf, len);
 
 	/* check the hmac */
-	buf_setpos(ses.readbuf, ses.readbuf->len - macsize);
-	if (checkmac(ses.readbuf, ses.decryptreadbuf) != DROPBEAR_SUCCESS) {
+	if (checkmac() != DROPBEAR_SUCCESS) {
 		dropbear_exit("Integrity error");
 	}
 
-	/* readbuf no longer required */
-	buf_free(ses.readbuf);
-	ses.readbuf = NULL;
-
 	/* get padding length */
-	buf_setpos(ses.decryptreadbuf, PACKET_PADDING_OFF);
-	padlen = buf_getbyte(ses.decryptreadbuf);
+	buf_setpos(ses.readbuf, PACKET_PADDING_OFF);
+	padlen = buf_getbyte(ses.readbuf);
 		
 	/* payload length */
 	/* - 4 - 1 is for LEN and PADLEN values */
-	len = ses.decryptreadbuf->len - padlen - 4 - 1;
+	len = ses.readbuf->len - padlen - 4 - 1 - macsize;
 	if ((len > RECV_MAX_PAYLOAD_LEN) || (len < 1)) {
-		dropbear_exit("bad packet size");
+		dropbear_exit("Bad packet size %d", len);
 	}
 
-	buf_setpos(ses.decryptreadbuf, PACKET_PAYLOAD_OFF);
+	buf_setpos(ses.readbuf, PACKET_PAYLOAD_OFF);
 
 #ifndef DISABLE_ZLIB
 	if (is_compress_recv()) {
 		/* decompress */
-		ses.payload = buf_decompress(ses.decryptreadbuf, len);
+		ses.payload = buf_decompress(ses.readbuf, len);
 	} else 
 #endif
 	{
 		/* copy payload */
 		ses.payload = buf_new(len);
-		memcpy(ses.payload->data, buf_getptr(ses.decryptreadbuf, len), len);
+		memcpy(ses.payload->data, buf_getptr(ses.readbuf, len), len);
 		buf_incrlen(ses.payload, len);
 	}
 
-	buf_free(ses.decryptreadbuf);
-	ses.decryptreadbuf = NULL;
+	buf_free(ses.readbuf);
+	ses.readbuf = NULL;
 	buf_setpos(ses.payload, 0);
 
 	ses.recvseq++;
@@ -296,49 +300,22 @@
 	TRACE(("leave decrypt_packet"))
 }
 
-/* Checks the mac in hashbuf, for the data in readbuf.
+/* Checks the mac at the end of a decrypted readbuf.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-static int checkmac(buffer* macbuf, buffer* sourcebuf) {
-
-	unsigned int macsize;
-	hmac_state hmac;
-	unsigned char tempbuf[MAX_MAC_LEN];
-	unsigned long bufsize;
-	unsigned int len;
-
-	macsize = ses.keys->recv_algo_mac->hashsize;
-	if (macsize == 0) {
-		return DROPBEAR_SUCCESS;
-	}
+static int checkmac() {
 
-	/* calculate the mac */
-	if (hmac_init(&hmac, 
-				find_hash(ses.keys->recv_algo_mac->hashdesc->name), 
-				ses.keys->recvmackey, 
-				ses.keys->recv_algo_mac->keysize) 
-				!= CRYPT_OK) {
-		dropbear_exit("HMAC error");
-	}
+	unsigned char mac_bytes[MAX_MAC_LEN];
+	unsigned int mac_size, contents_len;
 	
-	/* sequence number */
-	STORE32H(ses.recvseq, tempbuf);
-	if (hmac_process(&hmac, tempbuf, 4) != CRYPT_OK) {
-		dropbear_exit("HMAC error");
-	}
+	mac_size = ses.keys->trans.algo_mac->hashsize;
+	contents_len = ses.readbuf->len - mac_size;
 
-	buf_setpos(sourcebuf, 0);
-	len = sourcebuf->len;
-	if (hmac_process(&hmac, buf_getptr(sourcebuf, len), len) != CRYPT_OK) {
-		dropbear_exit("HMAC error");
-	}
-
-	bufsize = sizeof(tempbuf);
-	if (hmac_done(&hmac, tempbuf, &bufsize) != CRYPT_OK) {
-		dropbear_exit("HMAC error");
-	}
+	buf_setpos(ses.readbuf, 0);
+	make_mac(ses.recvseq, &ses.keys->recv, ses.readbuf, contents_len, mac_bytes);
 
 	/* compare the hash */
-	if (memcmp(tempbuf, buf_getptr(macbuf, macsize), macsize) != 0) {
+	buf_setpos(ses.readbuf, contents_len);
+	if (memcmp(mac_bytes, buf_getptr(ses.readbuf, mac_size), mac_size) != 0) {
 		return DROPBEAR_FAILURE;
 	} else {
 		return DROPBEAR_SUCCESS;
@@ -353,7 +330,7 @@
 	buffer * ret;
 	z_streamp zstream;
 
-	zstream = ses.keys->recv_zstream;
+	zstream = ses.keys->recv.zstream;
 	ret = buf_new(len);
 
 	zstream->avail_in = len;
@@ -449,52 +426,75 @@
 void encrypt_packet() {
 
 	unsigned char padlen;
-	unsigned char blocksize, macsize;
-	buffer * writebuf; /* the packet which will go on the wire */
-	buffer * clearwritebuf; /* unencrypted, possibly compressed */
-	unsigned char type;
-	unsigned int clear_len;
+	unsigned char blocksize, mac_size;
+	buffer * writebuf; /* the packet which will go on the wire. This is 
+	                      encrypted in-place. */
+	unsigned char packet_type;
+	unsigned int len, encrypt_buf_size;
+	unsigned char mac_bytes[MAX_MAC_LEN];
 	
-	type = ses.writepayload->data[0];
 	TRACE(("enter encrypt_packet()"))
-	TRACE(("encrypt_packet type is %d", type))
+
+	buf_setpos(ses.writepayload, 0);
+	packet_type = buf_getbyte(ses.writepayload);
+	buf_setpos(ses.writepayload, 0);
+
+	TRACE(("encrypt_packet type is %d", packet_type))
 	
-	if (!ses.dataallowed && !packet_is_okay_kex(type)) {
+	if ((!ses.dataallowed && !packet_is_okay_kex(packet_type))
+			|| ses.kexstate.sentnewkeys) {
 		/* During key exchange only particular packets are allowed.
-			Since this type isn't OK we just enqueue it to send 
+			Since this packet_type isn't OK we just enqueue it to send 
 			after the KEX, see maybe_flush_reply_queue */
+
+		/* We also enqueue packets here when we have sent a MSG_NEWKEYS
+		 * packet but are yet to received one. For simplicity we just switch
+		 * over all the keys at once. This is the 'ses.kexstate.sentnewkeys'
+		 * case. */
 		enqueue_reply_packet();
 		return;
 	}
 		
-	blocksize = ses.keys->trans_algo_crypt->blocksize;
-	macsize = ses.keys->trans_algo_mac->hashsize;
-
-	/* Encrypted packet len is payload+5, then worst case is if we are 3 away
-	 * from a blocksize multiple. In which case we need to pad to the
-	 * multiple, then add another blocksize (or MIN_PACKET_LEN) */
-	clear_len = (ses.writepayload->len+4+1) + MIN_PACKET_LEN + 3;
+	blocksize = ses.keys->trans.algo_crypt->blocksize;
+	mac_size = ses.keys->trans.algo_mac->hashsize;
 
+	/* Encrypted packet len is payload+5. We need to then make sure
+	 * there is enough space for padding or MIN_PACKET_LEN. 
+	 * Add extra 3 since we need at least 4 bytes of padding */
+	encrypt_buf_size = (ses.writepayload->len+4+1) 
+		+ MAX(MIN_PACKET_LEN, blocksize) + 3
+	/* add space for the MAC at the end */
+				+ mac_size
 #ifndef DISABLE_ZLIB
-	clear_len += ZLIB_COMPRESS_INCR; /* bit of a kludge, but we can't know len*/
+	/* some extra in case 'compression' makes it larger */
+				+ ZLIB_COMPRESS_INCR
 #endif
-	clearwritebuf = buf_new(clear_len);
-	buf_setlen(clearwritebuf, PACKET_PAYLOAD_OFF);
-	buf_setpos(clearwritebuf, PACKET_PAYLOAD_OFF);
+	/* and an extra cleartext (stripped before transmission) byte for the
+	 * packet type */
+				+ 1;
 
-	buf_setpos(ses.writepayload, 0);
+	writebuf = buf_new(encrypt_buf_size);
+	buf_setlen(writebuf, PACKET_PAYLOAD_OFF);
+	buf_setpos(writebuf, PACKET_PAYLOAD_OFF);
 
 #ifndef DISABLE_ZLIB
 	/* compression */
 	if (is_compress_trans()) {
-		buf_compress(clearwritebuf, ses.writepayload, ses.writepayload->len);
+		int compress_delta;
+		buf_compress(writebuf, ses.writepayload, ses.writepayload->len);
+		compress_delta = (writebuf->len - PACKET_PAYLOAD_OFF) - ses.writepayload->len;
+
+		/* Handle the case where 'compress' increased the size. */
+		if (compress_delta > ZLIB_COMPRESS_INCR) {
+			buf_resize(writebuf, writebuf->size + compress_delta);
+		}
 	} else
 #endif
 	{
-		memcpy(buf_getwriteptr(clearwritebuf, ses.writepayload->len),
+		memcpy(buf_getwriteptr(writebuf, ses.writepayload->len),
 				buf_getptr(ses.writepayload, ses.writepayload->len),
 				ses.writepayload->len);
-		buf_incrwritepos(clearwritebuf, ses.writepayload->len);
+		buf_incrwritepos(writebuf, ses.writepayload->len);
 	}
 
 	/* finished with payload */
@@ -503,53 +503,48 @@
 
 	/* length of padding - packet length must be a multiple of blocksize,
 	 * with a minimum of 4 bytes of padding */
-	padlen = blocksize - (clearwritebuf->len) % blocksize;
+	padlen = blocksize - (writebuf->len) % blocksize;
 	if (padlen < 4) {
 		padlen += blocksize;
 	}
 	/* check for min packet length */
-	if (clearwritebuf->len + padlen < MIN_PACKET_LEN) {
+	if (writebuf->len + padlen < MIN_PACKET_LEN) {
 		padlen += blocksize;
 	}
 
-	buf_setpos(clearwritebuf, 0);
+	buf_setpos(writebuf, 0);
 	/* packet length excluding the packetlength uint32 */
-	buf_putint(clearwritebuf, clearwritebuf->len + padlen - 4);
+	buf_putint(writebuf, writebuf->len + padlen - 4);
 
 	/* padding len */
-	buf_putbyte(clearwritebuf, padlen);
+	buf_putbyte(writebuf, padlen);
 	/* actual padding */
-	buf_setpos(clearwritebuf, clearwritebuf->len);
-	buf_incrlen(clearwritebuf, padlen);
-	genrandom(buf_getptr(clearwritebuf, padlen), padlen);
+	buf_setpos(writebuf, writebuf->len);
+	buf_incrlen(writebuf, padlen);
+	genrandom(buf_getptr(writebuf, padlen), padlen);
 
-	/* do the actual encryption */
-	buf_setpos(clearwritebuf, 0);
-	/* create a new writebuffer, this is freed when it has been put on the 
-	 * wire by writepacket() */
-	writebuf = buf_new(clearwritebuf->len + macsize);
+	make_mac(ses.transseq, &ses.keys->trans, writebuf, writebuf->len, mac_bytes);
 
-	/* encrypt it */
-	while (clearwritebuf->pos < clearwritebuf->len) {
-		if (ses.keys->trans_crypt_mode->encrypt(
-					buf_getptr(clearwritebuf, blocksize),
-					buf_getwriteptr(writebuf, blocksize),
-					blocksize,
-					&ses.keys->trans_cipher_state) != CRYPT_OK) {
-			dropbear_exit("error encrypting");
-		}
-		buf_incrpos(clearwritebuf, blocksize);
-		buf_incrwritepos(writebuf, blocksize);
+	/* do the actual encryption, in-place */
+	buf_setpos(writebuf, 0);
+	/* encrypt it in-place*/
+	len = writebuf->len;
+	if (ses.keys->trans.crypt_mode->encrypt(
+				buf_getptr(writebuf, len),
+				buf_getwriteptr(writebuf, len),
+				len,
+				&ses.keys->trans.cipher_state) != CRYPT_OK) {
+		dropbear_exit("Error encrypting");
 	}
+	buf_incrpos(writebuf, len);
 
-	/* now add a hmac and we're done */
-	writemac(writebuf, clearwritebuf);
+    /* stick the MAC on it */
+    buf_putbytes(writebuf, mac_bytes, mac_size);
 
-	/* clearwritebuf is finished with */
-	buf_free(clearwritebuf);
-	clearwritebuf = NULL;
-
-	/* enqueue the packet for sending */
+	/* The last byte of the buffer stores the cleartext packet_type. It is not
+	 * transmitted but is used for transmit timeout purposes */
+	buf_putbyte(writebuf, packet_type);
+	/* enqueue the packet for sending. It will get freed after transmission. */
 	buf_setpos(writebuf, 0);
 	enqueue(&ses.writequeue, (void*)writebuf);
 
@@ -562,47 +557,43 @@
 
 
 /* Create the packet mac, and append H(seqno|clearbuf) to the output */
-static void writemac(buffer * outputbuffer, buffer * clearwritebuf) {
-
-	unsigned int macsize;
+/* output_mac must have ses.keys->trans.algo_mac->hashsize bytes. */
+static void make_mac(unsigned int seqno, const struct key_context_directional * key_state,
+		buffer * clear_buf, unsigned int clear_len, 
+		unsigned char *output_mac) {
 	unsigned char seqbuf[4];
-	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) {
+	if (key_state->algo_mac->hashsize > 0) {
 		/* calculate the mac */
 		if (hmac_init(&hmac, 
-					find_hash(ses.keys->trans_algo_mac->hashdesc->name), 
-					ses.keys->transmackey, 
-					ses.keys->trans_algo_mac->keysize) != CRYPT_OK) {
+					key_state->hash_index,
+					key_state->mackey,
+					key_state->algo_mac->keysize) != CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
 	
 		/* sequence number */
-		STORE32H(ses.transseq, seqbuf);
+		STORE32H(seqno, seqbuf);
 		if (hmac_process(&hmac, seqbuf, 4) != CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
 	
 		/* the actual contents */
-		buf_setpos(clearwritebuf, 0);
+		buf_setpos(clear_buf, 0);
 		if (hmac_process(&hmac, 
-					buf_getptr(clearwritebuf, 
-						clearwritebuf->len),
-					clearwritebuf->len) != CRYPT_OK) {
+					buf_getptr(clear_buf, clear_len),
+					clear_len) != CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
 	
-		bufsize = sizeof(tempbuf);
-		if (hmac_done(&hmac, tempbuf, &bufsize) 
-				!= CRYPT_OK) {
+        bufsize = MAX_MAC_LEN;
+		if (hmac_done(&hmac, output_mac, &bufsize) != CRYPT_OK) {
 			dropbear_exit("HMAC error");
 		}
-		buf_putbytes(outputbuffer, tempbuf, macsize);
 	}
 	TRACE(("leave writemac"))
 }
@@ -619,29 +610,29 @@
 
 	while (1) {
 
-		ses.keys->trans_zstream->avail_in = endpos - src->pos;
-		ses.keys->trans_zstream->next_in = 
-			buf_getptr(src, ses.keys->trans_zstream->avail_in);
+		ses.keys->trans.zstream->avail_in = endpos - src->pos;
+		ses.keys->trans.zstream->next_in = 
+			buf_getptr(src, ses.keys->trans.zstream->avail_in);
 
-		ses.keys->trans_zstream->avail_out = dest->size - dest->pos;
-		ses.keys->trans_zstream->next_out =
-			buf_getwriteptr(dest, ses.keys->trans_zstream->avail_out);
+		ses.keys->trans.zstream->avail_out = dest->size - dest->pos;
+		ses.keys->trans.zstream->next_out =
+			buf_getwriteptr(dest, ses.keys->trans.zstream->avail_out);
 
-		result = deflate(ses.keys->trans_zstream, Z_SYNC_FLUSH);
+		result = deflate(ses.keys->trans.zstream, Z_SYNC_FLUSH);
 
-		buf_setpos(src, endpos - ses.keys->trans_zstream->avail_in);
-		buf_setlen(dest, dest->size - ses.keys->trans_zstream->avail_out);
+		buf_setpos(src, endpos - ses.keys->trans.zstream->avail_in);
+		buf_setlen(dest, dest->size - ses.keys->trans.zstream->avail_out);
 		buf_setpos(dest, dest->len);
 
 		if (result != Z_OK) {
 			dropbear_exit("zlib error");
 		}
 
-		if (ses.keys->trans_zstream->avail_in == 0) {
+		if (ses.keys->trans.zstream->avail_in == 0) {
 			break;
 		}
 
-		dropbear_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(),
--- a/packet.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/packet.h	Wed May 16 22:54:51 2012 +0800
@@ -44,6 +44,6 @@
 #define PACKET_PADDING_OFF 4
 #define PACKET_PAYLOAD_OFF 5
 
-#define INIT_READBUF 200
+#define INIT_READBUF 128
 
 #endif /* _PACKET_H_ */
--- a/process-packet.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/process-packet.c	Wed May 16 22:54:51 2012 +0800
@@ -52,6 +52,8 @@
 
 	ses.lastpacket = type;
 
+    ses.last_packet_time = time(NULL);
+
 	/* These packets we can receive at any time */
 	switch(type) {
 
@@ -63,20 +65,19 @@
 		case SSH_MSG_UNIMPLEMENTED:
 			/* debugging XXX */
 			TRACE(("SSH_MSG_UNIMPLEMENTED"))
-			dropbear_exit("received SSH_MSG_UNIMPLEMENTED");
+			dropbear_exit("Received SSH_MSG_UNIMPLEMENTED");
 			
 		case SSH_MSG_DISCONNECT:
 			/* TODO cleanup? */
 			dropbear_close("Disconnect received");
 	}
 
-
 	/* This applies for KEX, where the spec says the next packet MUST be
 	 * NEWKEYS */
 	if (ses.requirenext != 0) {
 		if (ses.requirenext != type) {
 			/* TODO send disconnect? */
-			dropbear_exit("unexpected packet type %d, expected %d", type,
+			dropbear_exit("Unexpected packet type %d, expected %d", type,
 					ses.requirenext);
 		} else {
 			/* Got what we expected */
@@ -98,7 +99,7 @@
 	 * NOTE: if the protocol changes and new types are added, revisit this 
 	 * assumption */
 	if ( !ses.authstate.authdone && type > MAX_UNAUTH_PACKET_TYPE ) {
-		dropbear_exit("received message %d before userauth", type);
+		dropbear_exit("Received message %d before userauth", type);
 	}
 
 	for (i = 0; ; i++) {
--- a/random.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/random.c	Wed May 16 22:54:51 2012 +0800
@@ -64,32 +64,23 @@
 #ifdef DROPBEAR_RANDOM_DEV
 	readfd = open(DROPBEAR_RANDOM_DEV, O_RDONLY);
 	if (readfd < 0) {
-		dropbear_exit("couldn't open random device");
+		dropbear_exit("Couldn't open random device");
 	}
 #endif
 
 #ifdef DROPBEAR_PRNGD_SOCKET
-	memset((void*)&egdsock, 0x0, sizeof(egdsock));
-	egdsock.sun_family = AF_UNIX;
-	strlcpy(egdsock.sun_path, DROPBEAR_PRNGD_SOCKET,
-			sizeof(egdsock.sun_path));
+	readfd = connect_unix(DROPBEAR_PRNGD_SOCKET);
 
-	readfd = socket(PF_UNIX, SOCK_STREAM, 0);
 	if (readfd < 0) {
-		dropbear_exit("couldn't open random device");
-	}
-	/* todo - try various common locations */
-	if (connect(readfd, (struct sockaddr*)&egdsock, 
-			sizeof(struct sockaddr_un)) < 0) {
-		dropbear_exit("couldn't open random device");
+		dropbear_exit("Couldn't open random device");
 	}
 
 	if (buflen > 255)
-		dropbear_exit("can't request more than 255 bytes from egd");
+		dropbear_exit("Can't request more than 255 bytes from egd");
 	egdcmd[0] = 0x02;	/* blocking read */
 	egdcmd[1] = (unsigned char)buflen;
 	if (write(readfd, egdcmd, 2) < 0)
-		dropbear_exit("can't send command to egd");
+		dropbear_exit("Can't send command to egd");
 #endif
 
 	/* read the actual random data */
@@ -118,7 +109,7 @@
 			if (readlen < 0 && errno == EINTR) {
 				continue;
 			}
-			dropbear_exit("error reading random source");
+			dropbear_exit("Error reading random source");
 		}
 		readpos += readlen;
 	} while (readpos < buflen);
--- a/rsa.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/rsa.c	Wed May 16 22:54:51 2012 +0800
@@ -38,7 +38,7 @@
 
 #ifdef DROPBEAR_RSA 
 
-static void rsa_pad_em(rsa_key * key,
+static void rsa_pad_em(dropbear_rsa_key * key,
 		const unsigned char * data, unsigned int len,
 		mp_int * rsa_em);
 
@@ -46,7 +46,7 @@
  * The key will have the same format as buf_put_rsa_key.
  * These should be freed with rsa_key_free.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int buf_get_rsa_pub_key(buffer* buf, rsa_key *key) {
+int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key) {
 
     int ret = DROPBEAR_FAILURE;
 	TRACE(("enter buf_get_rsa_pub_key"))
@@ -67,7 +67,7 @@
 	}
 
 	if (mp_count_bits(key->n) < MIN_RSA_KEYLEN) {
-		dropbear_log(LOG_WARNING, "rsa key too short");
+		dropbear_log(LOG_WARNING, "RSA key too short");
 	    goto out;
 	}
 
@@ -84,7 +84,7 @@
 /* Same as buf_get_rsa_pub_key, but reads private bits at the end.
  * Loads a private rsa key from a buffer
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int buf_get_rsa_priv_key(buffer* buf, rsa_key *key) {
+int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key) {
     int ret = DROPBEAR_FAILURE;
 
 	TRACE(("enter buf_get_rsa_priv_key"))
@@ -137,7 +137,7 @@
 	
 
 /* Clear and free the memory used by a public or private key */
-void rsa_key_free(rsa_key *key) {
+void rsa_key_free(dropbear_rsa_key *key) {
 
 	TRACE(("enter rsa_key_free"))
 
@@ -175,7 +175,7 @@
  * mp_int	e
  * mp_int	n
  */
-void buf_put_rsa_pub_key(buffer* buf, rsa_key *key) {
+void buf_put_rsa_pub_key(buffer* buf, dropbear_rsa_key *key) {
 
 	TRACE(("enter buf_put_rsa_pub_key"))
 	dropbear_assert(key != NULL);
@@ -189,7 +189,7 @@
 }
 
 /* Same as buf_put_rsa_pub_key, but with the private "x" key appended */
-void buf_put_rsa_priv_key(buffer* buf, rsa_key *key) {
+void buf_put_rsa_priv_key(buffer* buf, dropbear_rsa_key *key) {
 
 	TRACE(("enter buf_put_rsa_priv_key"))
 
@@ -213,7 +213,7 @@
 #ifdef DROPBEAR_SIGNKEY_VERIFY
 /* Verify a signature in buf, made on data by the key given.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
+int buf_rsa_verify(buffer * buf, dropbear_rsa_key *key, const unsigned char* data,
 		unsigned int len) {
 
 	unsigned int slen;
@@ -270,7 +270,7 @@
 
 /* Sign the data presented with key, writing the signature contents
  * to the buffer */
-void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data,
+void buf_put_rsa_sign(buffer* buf, dropbear_rsa_key *key, const unsigned char* data,
 		unsigned int len) {
 
 	unsigned int nsize, ssize;
@@ -302,26 +302,26 @@
 
 	/* rsa_s used as a temp var*/
 	if (mp_exptmod(&rsa_tmp2, key->e, key->n, &rsa_s) != MP_OKAY) {
-		dropbear_exit("rsa error");
+		dropbear_exit("RSA error");
 	}
 	if (mp_invmod(&rsa_tmp2, key->n, &rsa_tmp3) != MP_OKAY) {
-		dropbear_exit("rsa error");
+		dropbear_exit("RSA error");
 	}
 	if (mp_mulmod(&rsa_tmp1, &rsa_s, key->n, &rsa_tmp2) != MP_OKAY) {
-		dropbear_exit("rsa error");
+		dropbear_exit("RSA error");
 	}
 
 	/* rsa_tmp2 is em' */
 	/* s' = (em')^d mod n */
 	if (mp_exptmod(&rsa_tmp2, key->d, key->n, &rsa_tmp1) != MP_OKAY) {
-		dropbear_exit("rsa error");
+		dropbear_exit("RSA error");
 	}
 
 	/* rsa_tmp1 is s' */
 	/* rsa_tmp3 is r^(-1) mod n */
 	/* s = (s')r^(-1) mod n */
 	if (mp_mulmod(&rsa_tmp1, &rsa_tmp3, key->n, &rsa_s) != MP_OKAY) {
-		dropbear_exit("rsa error");
+		dropbear_exit("RSA error");
 	}
 
 #else
@@ -329,7 +329,7 @@
 	/* s = em^d mod n */
 	/* rsa_tmp1 is em */
 	if (mp_exptmod(&rsa_tmp1, key->d, key->n, &rsa_s) != MP_OKAY) {
-		dropbear_exit("rsa error");
+		dropbear_exit("RSA error");
 	}
 
 #endif /* RSA_BLINDING */
@@ -351,7 +351,7 @@
 	}
 
 	if (mp_to_unsigned_bin(&rsa_s, buf_getwriteptr(buf, ssize)) != MP_OKAY) {
-		dropbear_exit("rsa error");
+		dropbear_exit("RSA error");
 	}
 	buf_incrwritepos(buf, ssize);
 	mp_clear(&rsa_s);
@@ -376,7 +376,7 @@
  *
  * rsa_em must be a pointer to an initialised mp_int.
  */
-static void rsa_pad_em(rsa_key * key,
+static void rsa_pad_em(dropbear_rsa_key * key,
 		const unsigned char * data, unsigned int len, 
 		mp_int * rsa_em) {
 
--- a/rsa.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/rsa.h	Wed May 16 22:54:51 2012 +0800
@@ -32,29 +32,28 @@
 
 #define RSA_SIGNATURE_SIZE 4+7+4+40
 
-struct RSA_key {
+typedef struct {
 
 	mp_int* n;
 	mp_int* e;
+	/* d, p, and q are private parts */
 	mp_int* d;
 	mp_int* p;
 	mp_int* q;
 
-};
+} dropbear_rsa_key;
 
-typedef struct RSA_key rsa_key;
-
-void buf_put_rsa_sign(buffer* buf, rsa_key *key, const unsigned char* data,
+void buf_put_rsa_sign(buffer* buf, dropbear_rsa_key *key, const unsigned char* data,
 		unsigned int len);
 #ifdef DROPBEAR_SIGNKEY_VERIFY
-int buf_rsa_verify(buffer * buf, rsa_key *key, const unsigned char* data,
+int buf_rsa_verify(buffer * buf, dropbear_rsa_key *key, const unsigned char* data,
 		unsigned int len);
 #endif
-int buf_get_rsa_pub_key(buffer* buf, rsa_key *key);
-int buf_get_rsa_priv_key(buffer* buf, rsa_key *key);
-void buf_put_rsa_pub_key(buffer* buf, rsa_key *key);
-void buf_put_rsa_priv_key(buffer* buf, rsa_key *key);
-void rsa_key_free(rsa_key *key);
+int buf_get_rsa_pub_key(buffer* buf, dropbear_rsa_key *key);
+int buf_get_rsa_priv_key(buffer* buf, dropbear_rsa_key *key);
+void buf_put_rsa_pub_key(buffer* buf, dropbear_rsa_key *key);
+void buf_put_rsa_priv_key(buffer* buf, dropbear_rsa_key *key);
+void rsa_key_free(dropbear_rsa_key *key);
 
 #endif /* DROPBEAR_RSA */
 
--- a/runopts.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/runopts.h	Wed May 16 22:54:51 2012 +0800
@@ -37,7 +37,16 @@
 	int listen_fwd_all;
 #endif
 	unsigned int recv_window;
-	unsigned int keepalive_secs;
+	time_t keepalive_secs;
+	time_t idle_timeout_secs;
+
+#ifndef DISABLE_ZLIB
+	/* TODO: add a commandline flag. Currently this is on by default if compression
+	 * is compiled in, but disabled for a client's non-final multihop stages. (The
+	 * intermediate stages are compressed streams, so are uncompressible. */
+	int enable_compress;
+#endif
+
 
 } runopts;
 
@@ -111,13 +120,20 @@
 	int backgrounded;
 	int is_subsystem;
 #ifdef ENABLE_CLI_PUBKEY_AUTH
-	struct SignKeyList *privkeys; /* Keys to use for public-key auth */
+	m_list *privkeys; /* Keys to use for public-key auth */
 #endif
 #ifdef ENABLE_CLI_REMOTETCPFWD
-	struct TCPFwdList * remotefwds;
+	m_list * remotefwds;
 #endif
 #ifdef ENABLE_CLI_LOCALTCPFWD
-	struct TCPFwdList * localfwds;
+	m_list * localfwds;
+#endif
+#ifdef ENABLE_CLI_AGENTFWD
+	int agent_fwd;
+	int agent_keys_loaded; /* whether pubkeys has been populated with a 
+							  list of keys held by the agent */
+	int agent_fd; /* The agent fd is only set during authentication. Forwarded
+	                 agent sessions have their own file descriptors */
 #endif
 
 #ifdef ENABLE_CLI_NETCAT
@@ -127,7 +143,6 @@
 #ifdef ENABLE_CLI_PROXYCMD
 	char *proxycmd;
 #endif
-
 } cli_runopts;
 
 extern cli_runopts cli_opts;
--- a/scp.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/scp.c	Wed May 16 22:54:51 2012 +0800
@@ -130,22 +130,22 @@
 			fprintf(stderr, " %s", a->list[i]);
 		fprintf(stderr, "\n");
 	}
-#ifdef __uClinux__
+#ifdef USE_VFORK
 	pid = vfork();
 #else
 	pid = fork();
-#endif /* __uClinux__ */
+#endif
 	if (pid == -1)
 		fatal("do_local_cmd: fork: %s", strerror(errno));
 
 	if (pid == 0) {
 		execvp(a->list[0], a->list);
 		perror(a->list[0]);
-#ifdef __uClinux__
+#ifdef USE_VFORK
 		_exit(1);
 #else
 		exit(1);
-#endif /* __uClinux__ */
+#endif
 	}
 
 	do_cmd_pid = pid;
@@ -171,6 +171,16 @@
  * assigns the input and output file descriptors on success.
  */
 
+static void
+arg_setup(char *host, char *remuser, char *cmd)
+{
+	replacearg(&args, 0, "%s", ssh_program);
+	if (remuser != NULL)
+		addargs(&args, "-l%s", remuser);
+	addargs(&args, "%s", host);
+	addargs(&args, "%s", cmd);
+}
+
 int
 do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
 {
@@ -198,22 +208,18 @@
 	close(reserved[0]);
 	close(reserved[1]);
 
-    /* uClinux needs to build the args here before vforking,
-       otherwise we do it later on. */
-#ifdef __uClinux__
-		replacearg(&args, 0, "%s", ssh_program);
-		if (remuser != NULL)
-			addargs(&args, "-l%s", remuser);
-		addargs(&args, "%s", host);
-		addargs(&args, "%s", cmd);
-#endif /* __uClinux__ */
+	/* uClinux needs to build the args here before vforking,
+	   otherwise we do it later on. */
+#ifdef USE_VFORK
+	arg_setup(host, remuser, cmd);
+#endif
 
 	/* Fork a child to execute the command on the remote host using ssh. */
-#ifdef __uClinux__
+#ifdef USE_VFORK
 	do_cmd_pid = vfork();
 #else
 	do_cmd_pid = fork();
-#endif /* __uClinux__ */
+#endif
 
 	if (do_cmd_pid == 0) {
 		/* Child. */
@@ -224,27 +230,22 @@
 		close(pin[0]);
 		close(pout[1]);
 
-#ifndef __uClinux__
-		replacearg(&args, 0, "%s", ssh_program);
-		if (remuser != NULL)
-			addargs(&args, "-l%s", remuser);
-		addargs(&args, "%s", host);
-		addargs(&args, "%s", cmd);
-#endif /* __uClinux__ */
+#ifdef USE_VFORK
+		arg_setup(host, remuser, cmd);
+#endif
 
 		execvp(ssh_program, args.list);
 		perror(ssh_program);
-#ifndef __uClinux__
-		exit(1);
+#ifdef USE_VFORK
+		_exit(1);
 #else
-		_exit(1);
-#endif /* __uClinux__ */
+		exit(1);
+#endif
 	} else if (do_cmd_pid == -1) {
 		fatal("fork: %s", strerror(errno));
 	}
 
-
-#ifdef __uClinux__
+#ifdef USE_VFORK
 	/* clean up command */
 	/* pop cmd */
 	xfree(args.list[args.num-1]);
@@ -260,7 +261,7 @@
 		args.list[args.num-1]=NULL;
 		args.num--;
 	}
-#endif /* __uClinux__ */
+#endif
 
 	/* Parent.  Close the other side, and return the local side. */
 	close(pin[0]);
@@ -343,7 +344,7 @@
 			addargs(&args, "-p%s", optarg);
 			break;
 		case 'B':
-			addargs(&args, "-oBatchmode yes");
+			fprintf(stderr, "Note: -B option is disabled in this version of scp");
 			break;
 		case 'l':
 			speed = strtod(optarg, &endp);
@@ -364,12 +365,12 @@
 			addargs(&args, "-v");
 			verbose_mode = 1;
 			break;
+		case 'q':
 #ifdef PROGRESS_METER
-		case 'q':
 			addargs(&args, "-q");
 			showprogress = 0;
+#endif
 			break;
-#endif
 
 		/* Server options. */
 		case 'd':
@@ -492,9 +493,13 @@
 			addargs(&alist, "%s", ssh_program);
 			if (verbose_mode)
 				addargs(&alist, "-v");
+#if 0
+			// Disabled since dbclient won't understand them
+			// and scp works fine without them.
 			addargs(&alist, "-x");
 			addargs(&alist, "-oClearAllForwardings yes");
 			addargs(&alist, "-n");
+#endif
 
 			*src++ = 0;
 			if (*src == 0)
@@ -769,7 +774,7 @@
 {
 	static struct timeval bwstart, bwend;
 	static int lamt, thresh = 16384;
-	u_int64_t waitlen;
+	uint64_t waitlen;
 	struct timespec ts, rm;
 
 	if (!timerisset(&bwstart)) {
--- a/session.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/session.h	Wed May 16 22:54:51 2012 +0800
@@ -37,11 +37,12 @@
 #include "packet.h"
 #include "tcpfwd.h"
 #include "chansession.h"
+#include "dbutil.h"
 
 extern int sessinitdone; /* Is set to 0 somewhere */
 extern int exitflag;
 
-void common_session_init(int sock_in, int sock_out, char* remotehost);
+void common_session_init(int sock_in, int sock_out);
 void session_loop(void(*loophandler)());
 void common_session_cleanup();
 void session_identification();
@@ -51,51 +52,45 @@
 void fill_passwd(const char* username);
 
 /* Server */
-void svr_session(int sock, int childpipe, char *remotehost, char *addrstring);
-void svr_dropbear_exit(int exitcode, const char* format, va_list param);
+void svr_session(int sock, int childpipe);
+void svr_dropbear_exit(int exitcode, const char* format, va_list param) ATTRIB_NORETURN;
 void svr_dropbear_log(int priority, const char* format, va_list param);
 
 /* Client */
-void cli_session(int sock_in, int sock_out, char *remotehost);
+void cli_session(int sock_in, int sock_out);
 void cli_session_cleanup();
 void cleantext(unsigned char* dirtytext);
 
-struct key_context {
-
-	const struct dropbear_cipher *recv_algo_crypt; /* NULL for none */
-	const struct dropbear_cipher *trans_algo_crypt; /* NULL for none */
-	const struct dropbear_cipher_mode *recv_crypt_mode;
-	const struct dropbear_cipher_mode *trans_crypt_mode;
-	const struct dropbear_hash *recv_algo_mac; /* NULL for none */
-	const struct dropbear_hash *trans_algo_mac; /* NULL for none */
-	char algo_kex;
-	char algo_hostkey;
-
-	char recv_algo_comp; /* compression */
-	char trans_algo_comp;
-	int allow_compress; /* whether compression has started (useful in 
-							[email protected] delayed compression case) */
+/* crypto parameters that are stored individually for transmit and receive */
+struct key_context_directional {
+	const struct dropbear_cipher *algo_crypt; /* NULL for none */
+	const struct dropbear_cipher_mode *crypt_mode;
+	const struct dropbear_hash *algo_mac; /* NULL for none */
+	int hash_index; /* lookup for libtomcrypt */
+	char algo_comp; /* compression */
 #ifndef DISABLE_ZLIB
-	z_streamp recv_zstream;
-	z_streamp trans_zstream;
+	z_streamp zstream;
 #endif
-
 	/* actual keys */
 	union {
 		symmetric_CBC cbc;
 #ifdef DROPBEAR_ENABLE_CTR_MODE
 		symmetric_CTR ctr;
 #endif
-	} recv_cipher_state;
-	union {
-		symmetric_CBC cbc;
-#ifdef DROPBEAR_ENABLE_CTR_MODE
-		symmetric_CTR ctr;
-#endif
-	} trans_cipher_state;
-	unsigned char recvmackey[MAX_MAC_KEY];
-	unsigned char transmackey[MAX_MAC_KEY];
+	} cipher_state;
+	unsigned char mackey[MAX_MAC_KEY];
+};
+
+struct key_context {
 
+	struct key_context_directional recv;
+	struct key_context_directional trans;
+
+	char algo_kex;
+	char algo_hostkey;
+
+	int allow_compress; /* whether compression has started (useful in 
+							[email protected] delayed compression case) */
 };
 
 struct packetlist;
@@ -116,8 +111,6 @@
 	int sock_in;
 	int sock_out;
 
-	unsigned char *remotehost; /* the peer hostname */
-
 	unsigned char *remoteident;
 
 	int maxfd; /* the maximum file descriptor to check with select() */
@@ -128,8 +121,7 @@
 							 throughout the code, as handlers fill out this
 							 buffer with the packet to send. */
 	struct Queue writequeue; /* A queue of encrypted packets to send */
-	buffer *readbuf; /* Encrypted */
-	buffer *decryptreadbuf; /* Post-decryption */
+	buffer *readbuf; /* From the wire, decrypted in-place */
 	buffer *payload; /* Post-decompression, the actual SSH packet */
 	unsigned int transseq, recvseq; /* Sequence IDs */
 
@@ -148,12 +140,16 @@
 
 	unsigned char lastpacket; /* What the last received packet type was */
 	
-    int signal_pipe[2]; /* stores endpoints of a self-pipe used for
+	int signal_pipe[2]; /* stores endpoints of a self-pipe used for
 						   race-free signal handling */
 						
-	time_t last_packet_time; /* time of the last packet transmission, for
+	time_t last_trx_packet_time; /* time of the last packet transmission, for
 							keepalive purposes */
 
+	time_t last_packet_time; /* time of the last packet transmission or receive, for
+								idle timeout purposes */
+
+
 	/* KEX/encryption related */
 	struct KEXState kexstate;
 	struct key_context *keys;
@@ -165,6 +161,9 @@
 	buffer* kexhashbuf; /* session hash buffer calculated from various packets*/
 	buffer* transkexinit; /* the kexinit packet we send should be kept so we
 							 can add it to the hash when generating keys */
+
+	/* Enables/disables compression */
+	algo_type *compress_algos;
 							
 	/* a list of queued replies that should be sent after a KEX has
 	   concluded (ie, while dataallowed was unset)*/
@@ -216,6 +215,13 @@
 	/* The numeric address they connected from, used for logging */
 	char * addrstring;
 
+	/* The resolved remote address, used for lastlog etc */
+	char *remotehost;
+
+#ifdef USE_VFORK
+	pid_t server_pid;
+#endif
+
 };
 
 typedef enum {
@@ -264,7 +270,7 @@
 									  info request from the server for
 									  interactive auth.*/
 #endif
-	struct SignKeyList *lastprivkey;
+	sign_key *lastprivkey;
 
 	int retval; /* What the command exit status was - we emulate it */
 #if 0
--- a/signkey.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/signkey.c	Wed May 16 22:54:51 2012 +0800
@@ -40,8 +40,10 @@
 #ifdef DROPBEAR_RSA
 	ret->rsakey = NULL;
 #endif
+	ret->filename = NULL;
+	ret->type = DROPBEAR_SIGNKEY_NONE;
+	ret->source = SIGNKEY_SOURCE_INVALID;
 	return ret;
-
 }
 
 /* Returns "ssh-dss" or "ssh-rsa" corresponding to the type. Exits fatally
@@ -60,7 +62,7 @@
 		return SSH_SIGNKEY_DSS;
 	}
 #endif
-	dropbear_exit("bad key type %d", type);
+	dropbear_exit("Bad key type %d", type);
 	return NULL; /* notreached */
 }
 
@@ -81,6 +83,8 @@
 	}
 #endif
 
+	TRACE(("signkey_type_from_name unexpected key type."))
+
 	return DROPBEAR_SIGNKEY_NONE;
 }
 
@@ -101,8 +105,11 @@
 	m_free(ident);
 
 	if (*type != DROPBEAR_SIGNKEY_ANY && *type != keytype) {
+		TRACE(("buf_get_pub_key bad type - got %d, expected %d", keytype, *type))
 		return DROPBEAR_FAILURE;
 	}
+	
+	TRACE(("buf_get_pub_key keytype is %d", keytype))
 
 	*type = keytype;
 
@@ -112,7 +119,7 @@
 #ifdef DROPBEAR_DSS
 	if (keytype == DROPBEAR_SIGNKEY_DSS) {
 		dss_key_free(key->dsskey);
-		key->dsskey = (dss_key*)m_malloc(sizeof(dss_key));
+		key->dsskey = m_malloc(sizeof(*key->dsskey));
 		ret = buf_get_dss_pub_key(buf, key->dsskey);
 		if (ret == DROPBEAR_FAILURE) {
 			m_free(key->dsskey);
@@ -122,7 +129,7 @@
 #ifdef DROPBEAR_RSA
 	if (keytype == DROPBEAR_SIGNKEY_RSA) {
 		rsa_key_free(key->rsakey);
-		key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key));
+		key->rsakey = m_malloc(sizeof(*key->rsakey));
 		ret = buf_get_rsa_pub_key(buf, key->rsakey);
 		if (ret == DROPBEAR_FAILURE) {
 			m_free(key->rsakey);
@@ -165,7 +172,7 @@
 #ifdef DROPBEAR_DSS
 	if (keytype == DROPBEAR_SIGNKEY_DSS) {
 		dss_key_free(key->dsskey);
-		key->dsskey = (dss_key*)m_malloc(sizeof(dss_key));
+		key->dsskey = m_malloc(sizeof(*key->dsskey));
 		ret = buf_get_dss_priv_key(buf, key->dsskey);
 		if (ret == DROPBEAR_FAILURE) {
 			m_free(key->dsskey);
@@ -175,7 +182,7 @@
 #ifdef DROPBEAR_RSA
 	if (keytype == DROPBEAR_SIGNKEY_RSA) {
 		rsa_key_free(key->rsakey);
-		key->rsakey = (rsa_key*)m_malloc(sizeof(rsa_key));
+		key->rsakey = m_malloc(sizeof(*key->rsakey));
 		ret = buf_get_rsa_priv_key(buf, key->rsakey);
 		if (ret == DROPBEAR_FAILURE) {
 			m_free(key->rsakey);
@@ -208,7 +215,7 @@
 	}
 #endif
 	if (pubkeys->len == 0) {
-		dropbear_exit("bad key types in buf_put_pub_key");
+		dropbear_exit("Bad key types in buf_put_pub_key");
 	}
 
 	buf_setpos(pubkeys, 0);
@@ -239,7 +246,7 @@
 	return;
 	}
 #endif
-	dropbear_exit("bad key types in put pub key");
+	dropbear_exit("Bad key types in put pub key");
 }
 
 void sign_key_free(sign_key *key) {
@@ -255,6 +262,8 @@
 	key->rsakey = NULL;
 #endif
 
+	m_free(key->filename);
+
 	m_free(key);
 	TRACE(("leave sign_key_free"))
 }
@@ -358,7 +367,6 @@
 		const unsigned char *data, unsigned int len) {
 
 	buffer *sigblob;
-
 	sigblob = buf_new(MAX_PUBKEY_SIZE);
 
 #ifdef DROPBEAR_DSS
@@ -372,9 +380,8 @@
 	}
 #endif
 	if (sigblob->len == 0) {
-		dropbear_exit("non-matching signing type");
+		dropbear_exit("Non-matching signing type");
 	}
-
 	buf_setpos(sigblob, 0);
 	buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
 			sigblob->len);
@@ -405,7 +412,7 @@
 			memcmp(ident, SSH_SIGNKEY_DSS, identlen) == 0) {
 		m_free(ident);
 		if (key->dsskey == NULL) {
-			dropbear_exit("no dss key to verify signature");
+			dropbear_exit("No DSS key to verify signature");
 		}
 		return buf_dss_verify(buf, key->dsskey, data, len);
 	}
@@ -415,14 +422,14 @@
 	if (memcmp(ident, SSH_SIGNKEY_RSA, identlen) == 0) {
 		m_free(ident);
 		if (key->rsakey == NULL) {
-			dropbear_exit("no rsa key to verify signature");
+			dropbear_exit("No RSA key to verify signature");
 		}
 		return buf_rsa_verify(buf, key->rsakey, data, len);
 	}
 #endif
 
 	m_free(ident);
-	dropbear_exit("non-matching signing type");
+	dropbear_exit("Non-matching signing type");
 	return DROPBEAR_FAILURE;
 }
 #endif /* DROPBEAR_SIGNKEY_VERIFY */
--- a/signkey.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/signkey.h	Wed May 16 22:54:51 2012 +0800
@@ -29,13 +29,27 @@
 #include "dss.h"
 #include "rsa.h"
 
+
+/* Sources for signing keys */
+typedef enum {
+	SIGNKEY_SOURCE_RAW_FILE,
+	SIGNKEY_SOURCE_AGENT,
+	SIGNKEY_SOURCE_INVALID,	
+} signkey_source;
+
 struct SIGN_key {
 
+	int type; /* The type of key (dss or rsa) */
+	signkey_source source;
+	char *filename;
+	/* the buffer? for encrypted keys, so we can later get
+	 * the private key portion */
+
 #ifdef DROPBEAR_DSS
-	dss_key * dsskey;
+	dropbear_dss_key * dsskey;
 #endif
 #ifdef DROPBEAR_RSA
-	rsa_key * rsakey;
+	dropbear_rsa_key * rsakey;
 #endif
 };
 
--- a/ssh.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/ssh.h	Wed May 16 22:54:51 2012 +0800
@@ -105,3 +105,14 @@
 #define SSH_SIGNKEY_DSS_LEN 7
 #define SSH_SIGNKEY_RSA "ssh-rsa"
 #define SSH_SIGNKEY_RSA_LEN 7
+
+/* Agent commands. These aren't part of the spec, and are defined
+ * only on the openssh implementation. */
+#define SSH_AGENT_FAILURE			5
+#define SSH_AGENT_SUCCESS			6
+#define SSH2_AGENTC_REQUEST_IDENTITIES		11
+#define SSH2_AGENT_IDENTITIES_ANSWER		12
+#define SSH2_AGENTC_SIGN_REQUEST		13
+#define SSH2_AGENT_SIGN_RESPONSE		14
+
+#define SSH2_AGENT_FAILURE			30
--- a/sshpty.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/sshpty.c	Wed May 16 22:54:51 2012 +0800
@@ -234,7 +234,7 @@
 
 		return 1;
 	}
-	dropbear_log(LOG_WARNING, "failed to open any /dev/pty?? devices");
+	dropbear_log(LOG_WARNING, "Failed to open any /dev/pty?? devices");
 	return 0;
 #endif /* HAVE_DEV_PTS_AND_PTC */
 #endif /* USE_DEV_PTMX */
--- a/svr-agentfwd.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-agentfwd.c	Wed May 16 22:54:51 2012 +0800
@@ -27,7 +27,7 @@
 
 #include "includes.h"
 
-#ifndef DISABLE_AGENTFWD
+#ifdef ENABLE_SVR_AGENTFWD
 
 #include "agentfwd.h"
 #include "session.h"
@@ -49,9 +49,8 @@
 
 /* Handles client requests to start agent forwarding, sets up listening socket.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
-int agentreq(struct ChanSess * chansess) {
-
-	int fd;
+int svr_agentreq(struct ChanSess * chansess) {
+	int fd = -1;
 
 	if (!svr_pubkey_allows_agentfwd()) {
 		return DROPBEAR_FAILURE;
@@ -91,8 +90,9 @@
 	return DROPBEAR_SUCCESS;
 
 fail:
+	m_close(fd);
 	/* cleanup */
-	agentcleanup(chansess);
+	svr_agentcleanup(chansess);
 
 	return DROPBEAR_FAILURE;
 }
@@ -118,7 +118,7 @@
 
 /* set up the environment variable pointing to the socket. This is called
  * just before command/shell execution, after dropping priveleges */
-void agentset(struct ChanSess * chansess) {
+void svr_agentset(struct ChanSess * chansess) {
 
 	char *path = NULL;
 	int len;
@@ -137,7 +137,7 @@
 }
 
 /* close the socket, remove the socket-file */
-void agentcleanup(struct ChanSess * chansess) {
+void svr_agentcleanup(struct ChanSess * chansess) {
 
 	char *path = NULL;
 	uid_t uid;
@@ -157,7 +157,7 @@
 		gid = getgid();
 		if ((setegid(ses.authstate.pw_gid)) < 0 ||
 			(seteuid(ses.authstate.pw_uid)) < 0) {
-			dropbear_exit("failed to set euid");
+			dropbear_exit("Failed to set euid");
 		}
 
 		/* 2 for "/" and "\0" */
@@ -172,7 +172,7 @@
 
 		if ((seteuid(uid)) < 0 ||
 			(setegid(gid)) < 0) {
-			dropbear_exit("failed to revert euid");
+			dropbear_exit("Failed to revert euid");
 		}
 
 		m_free(chansess->agentfile);
@@ -181,7 +181,7 @@
 
 }
 
-static const struct ChanType chan_agent = {
+static const struct ChanType chan_svr_agent = {
 	0, /* sepfds */
 	"[email protected]",
 	NULL,
@@ -194,7 +194,7 @@
 /* helper for accepting an agent request */
 static int send_msg_channel_open_agent(int fd) {
 
-	if (send_msg_channel_open_init(fd, &chan_agent) == DROPBEAR_SUCCESS) {
+	if (send_msg_channel_open_init(fd, &chan_svr_agent) == DROPBEAR_SUCCESS) {
 		encrypt_packet();
 		return DROPBEAR_SUCCESS;
 	} else {
@@ -220,7 +220,7 @@
 	gid = getgid();
 	if ((setegid(ses.authstate.pw_gid)) < 0 ||
 		(seteuid(ses.authstate.pw_uid)) < 0) {
-		dropbear_exit("failed to set euid");
+		dropbear_exit("Failed to set euid");
 	}
 
 	memset((void*)&addr, 0x0, sizeof(addr));
@@ -263,7 +263,7 @@
 out:
 	if ((seteuid(uid)) < 0 ||
 		(setegid(gid)) < 0) {
-		dropbear_exit("failed to revert euid");
+		dropbear_exit("Failed to revert euid");
 	}
 	return ret;
 }
--- a/svr-algo.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-algo.c	Wed May 16 22:54:51 2012 +0800
@@ -68,7 +68,7 @@
 			remotealgos[count] = &algolist[i+1];
 			count++;
 		}
-		if (count == MAX_PROPOSED_ALGO) {
+		if (count >= MAX_PROPOSED_ALGO) {
 			break;
 		}
 	}
--- a/svr-auth.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-auth.c	Wed May 16 22:54:51 2012 +0800
@@ -33,6 +33,7 @@
 #include "packet.h"
 #include "auth.h"
 #include "runopts.h"
+#include "random.h"
 
 static void authclear();
 static int checkusername(unsigned char *username, unsigned int userlen);
@@ -140,15 +141,6 @@
 		dropbear_exit("unknown service in auth");
 	}
 
-	/* user wants to know what methods are supported */
-	if (methodlen == AUTH_METHOD_NONE_LEN &&
-			strncmp(methodname, AUTH_METHOD_NONE,
-				AUTH_METHOD_NONE_LEN) == 0) {
-		TRACE(("recv_msg_userauth_request: 'none' request"))
-		send_msg_userauth_failure(0, 0);
-		goto out;
-	}
-	
 	/* check username is good before continuing */
 	if (checkusername(username, userlen) == DROPBEAR_FAILURE) {
 		/* username is invalid/no shell/etc - send failure */
@@ -157,6 +149,31 @@
 		goto out;
 	}
 
+	/* user wants to know what methods are supported */
+	if (methodlen == AUTH_METHOD_NONE_LEN &&
+			strncmp(methodname, AUTH_METHOD_NONE,
+				AUTH_METHOD_NONE_LEN) == 0) {
+		TRACE(("recv_msg_userauth_request: 'none' request"))
+#ifdef ALLOW_BLANK_PASSWORD
+		if (!svr_opts.noauthpass 
+				&& !(svr_opts.norootpass && ses.authstate.pw_uid == 0) 
+				&& ses.authstate.pw_passwd[0] == '\0') 
+		{
+			dropbear_log(LOG_NOTICE, 
+					"Auth succeeded with blank password for '%s' from %s",
+					ses.authstate.pw_name,
+					svr_ses.addrstring);
+			send_msg_userauth_success();
+			goto out;
+		}
+		else
+#endif
+		{
+			send_msg_userauth_failure(0, 0);
+			goto out;
+		}
+	}
+	
 #ifdef ENABLE_SVR_PASSWORD_AUTH
 	if (!svr_opts.noauthpass &&
 			!(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) {
@@ -204,8 +221,7 @@
 }
 
 
-/* Check that the username exists, has a non-empty password, and has a valid
- * shell.
+/* Check that the username exists and isn't disallowed (root), and has a valid shell.
  * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */
 static int checkusername(unsigned char *username, unsigned int userlen) {
 
@@ -221,7 +237,7 @@
 		strcmp(username, ses.authstate.username) != 0) {
 			/* the username needs resetting */
 			if (ses.authstate.username != NULL) {
-				dropbear_log(LOG_WARNING, "client trying multiple usernames from %s",
+				dropbear_log(LOG_WARNING, "Client trying multiple usernames from %s",
 							svr_ses.addrstring);
 				m_free(ses.authstate.username);
 			}
@@ -234,7 +250,7 @@
 	if (!ses.authstate.pw_name) {
 		TRACE(("leave checkusername: user '%s' doesn't exist", username))
 		dropbear_log(LOG_WARNING,
-				"login attempt for nonexistent user from %s",
+				"Login attempt for nonexistent user from %s",
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		return DROPBEAR_FAILURE;
@@ -248,15 +264,6 @@
 		return DROPBEAR_FAILURE;
 	}
 
-	/* check for an empty password */
-	if (ses.authstate.pw_passwd[0] == '\0') {
-		TRACE(("leave checkusername: empty pword"))
-		dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
-				ses.authstate.pw_name);
-		send_msg_userauth_failure(0, 1);
-		return DROPBEAR_FAILURE;
-	}
-
 	TRACE(("shell is %s", ses.authstate.pw_shell))
 
 	/* check that the shell is set */
@@ -280,7 +287,7 @@
 	/* no matching shell */
 	endusershell();
 	TRACE(("no matching shell"))
-	dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected",
+	dropbear_log(LOG_WARNING, "User '%s' has invalid shell, rejected",
 				ses.authstate.pw_name);
 	send_msg_userauth_failure(0, 1);
 	return DROPBEAR_FAILURE;
@@ -337,7 +344,11 @@
 	encrypt_packet();
 
 	if (incrfail) {
-		usleep(300000); /* XXX improve this */
+		unsigned int delay;
+		genrandom((unsigned char*)&delay, sizeof(delay));
+		/* We delay for 300ms +- 50ms, 0.1ms granularity */
+		delay = 250000 + (delay % 1000)*100;
+		usleep(delay);
 		ses.authstate.failcount++;
 	}
 
--- a/svr-authpam.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-authpam.c	Wed May 16 22:54:51 2012 +0800
@@ -56,11 +56,7 @@
 	struct UserDataS* userDatap = (struct UserDataS*) appdata_ptr;
 	unsigned int msg_len = 0;
 	unsigned int i = 0;
-
-	const char* message = (*msg)->msg;
-
-	/* make a copy we can strip */
-	char * compare_message = m_strdup(message);
+	char * compare_message = NULL;
 
 	TRACE(("enter pamConvFunc"))
 
@@ -71,15 +67,10 @@
 		dropbear_log(LOG_INFO, "pamConvFunc() called with >1 messages: not supported.");
 		return PAM_CONV_ERR;
 	}
+
+	/* make a copy we can strip */
+	compare_message = m_strdup((*msg)->msg);
 	
-	TRACE(("msg_style is %d", (*msg)->msg_style))
-	if (compare_message) {
-		TRACE(("message is '%s'", compare_message))
-	} else {
-		TRACE(("null message"))
-	}
-
-
 	/* Make the string lowercase. */
 	msg_len = strlen(compare_message);
 	for (i = 0; i < msg_len; i++) {
@@ -101,8 +92,9 @@
 			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 */
-				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (no echo)",
+				   different pam modules/implementations. If you need
+				   to add an entry here please mail the Dropbear developer */
+				dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (no echo)",
 						compare_message);
 				rc = PAM_CONV_ERR;
 				break;
@@ -123,12 +115,16 @@
 
 		case PAM_PROMPT_ECHO_ON:
 
-			if (!((strcmp(compare_message, "login:" ) == 0) 
-				|| (strcmp(compare_message, "please enter username:") == 0))) {
+			if (!(
+				(strcmp(compare_message, "login:" ) == 0) 
+				|| (strcmp(compare_message, "please enter username:") == 0)
+				|| (strcmp(compare_message, "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 */
-				dropbear_log(LOG_NOTICE, "PAM unknown prompt %s (with echo)",
+				   different pam modules/implementations. If you need
+				   to add an entry here please mail the Dropbear developer */
+				dropbear_log(LOG_NOTICE, "PAM unknown prompt '%s' (with echo)",
 						compare_message);
 				rc = PAM_CONV_ERR;
 				break;
@@ -212,7 +208,10 @@
 		goto cleanup;
 	}
 
+#ifdef HAVE_PAM_FAIL_DELAY
+	/* We have our own random delay code already, disable PAM's */
 	(void) pam_fail_delay(pamHandlep, 0 /* musec_delay */);
+#endif
 
 	/* (void) pam_set_item(pamHandlep, PAM_FAIL_DELAY, (void*) pamDelayFunc); */
 
@@ -220,7 +219,7 @@
 		dropbear_log(LOG_WARNING, "pam_authenticate() failed, rc=%d, %s\n", 
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
-				"bad PAM password attempt for '%s' from %s",
+				"Bad PAM password attempt for '%s' from %s",
 				ses.authstate.pw_name,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
@@ -231,7 +230,7 @@
 		dropbear_log(LOG_WARNING, "pam_acct_mgmt() failed, rc=%d, %s\n", 
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
-				"bad PAM password attempt for '%s' from %s",
+				"Bad PAM password attempt for '%s' from %s",
 				ses.authstate.pw_name,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
--- a/svr-authpasswd.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-authpasswd.c	Wed May 16 22:54:51 2012 +0800
@@ -36,9 +36,6 @@
  * appropriate */
 void svr_auth_password() {
 	
-#ifdef HAVE_SHADOW_H
-	struct spwd *spasswd = NULL;
-#endif
 	char * passwdcrypt = NULL; /* the crypt from /etc/passwd or /etc/shadow */
 	char * testcrypt = NULL; /* crypt generated from the user's password sent */
 	unsigned char * password;
@@ -47,29 +44,12 @@
 	unsigned int changepw;
 
 	passwdcrypt = ses.authstate.pw_passwd;
-#ifdef HAVE_SHADOW_H
-	/* get the shadow password if possible */
-	spasswd = getspnam(ses.authstate.pw_name);
-	if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
-		passwdcrypt = spasswd->sp_pwdp;
-	}
-#endif
 
 #ifdef DEBUG_HACKCRYPT
 	/* debugging crypt for non-root testing with shadows */
 	passwdcrypt = DEBUG_HACKCRYPT;
 #endif
 
-	/* check for empty password - need to do this again here
-	 * since the shadow password may differ to that tested
-	 * in auth.c */
-	if (passwdcrypt[0] == '\0') {
-		dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
-				ses.authstate.pw_name);
-		send_msg_userauth_failure(0, 1);
-		return;
-	}
-
 	/* check if client wants to change password */
 	changepw = buf_getbool(ses.payload);
 	if (changepw) {
@@ -85,21 +65,28 @@
 	m_burn(password, passwordlen);
 	m_free(password);
 
+	/* check for empty password */
+	if (passwdcrypt[0] == '\0') {
+		dropbear_log(LOG_WARNING, "User '%s' has blank password, rejected",
+				ses.authstate.pw_name);
+		send_msg_userauth_failure(0, 1);
+		return;
+	}
+
 	if (strcmp(testcrypt, passwdcrypt) == 0) {
 		/* successful authentication */
 		dropbear_log(LOG_NOTICE, 
-				"password auth succeeded for '%s' from %s",
+				"Password auth succeeded for '%s' from %s",
 				ses.authstate.pw_name,
 				svr_ses.addrstring);
 		send_msg_userauth_success();
 	} else {
 		dropbear_log(LOG_WARNING,
-				"bad password attempt for '%s' from %s",
+				"Bad password attempt for '%s' from %s",
 				ses.authstate.pw_name,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 	}
-
 }
 
 #endif
--- a/svr-authpubkey.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-authpubkey.c	Wed May 16 22:54:51 2012 +0800
@@ -135,12 +135,12 @@
 	if (buf_verify(ses.payload, key, buf_getptr(signbuf, signbuf->len),
 				signbuf->len) == DROPBEAR_SUCCESS) {
 		dropbear_log(LOG_NOTICE,
-				"pubkey auth succeeded for '%s' with key %s from %s",
+				"Pubkey auth succeeded for '%s' with key %s from %s",
 				ses.authstate.pw_name, fp, svr_ses.addrstring);
 		send_msg_userauth_success();
 	} else {
 		dropbear_log(LOG_WARNING,
-				"pubkey auth bad signature for '%s' with key %s from %s",
+				"Pubkey auth bad signature for '%s' with key %s from %s",
 				ses.authstate.pw_name, fp, svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 	}
@@ -198,7 +198,7 @@
 	/* check that we can use the algo */
 	if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
 		dropbear_log(LOG_WARNING,
-				"pubkey auth attempt with unknown algo for '%s' from %s",
+				"Pubkey auth attempt with unknown algo for '%s' from %s",
 				ses.authstate.pw_name, svr_ses.addrstring);
 		goto out;
 	}
--- a/svr-authpubkeyoptions.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-authpubkeyoptions.c	Wed May 16 22:54:51 2012 +0800
@@ -88,10 +88,21 @@
 	return 1;
 }
 
-/* Set chansession command to the one forced by 'command' public key option */
+/* Set chansession command to the one forced 
+ * by any 'command' public key option. */
 void svr_pubkey_set_forced_command(struct ChanSess *chansess) {
-	if (ses.authstate.pubkey_options)
-		chansess->cmd = ses.authstate.pubkey_options->forced_command;
+	if (ses.authstate.pubkey_options) {
+		if (chansess->cmd) {
+			/* original_command takes ownership */
+			chansess->original_command = chansess->cmd;
+		} else {
+			chansess->original_command = m_strdup("");
+		}
+		chansess->cmd = m_strdup(ses.authstate.pubkey_options->forced_command);
+#ifdef LOG_COMMANDS
+		dropbear_log(LOG_INFO, "Command forced to '%s'", chansess->original_command);
+#endif
+	}
 }
 
 /* Free potential public key options */
@@ -124,7 +135,6 @@
 	TRACE(("enter addpubkeyoptions"))
 
 	ses.authstate.pubkey_options = (struct PubKeyOptions*)m_malloc(sizeof( struct PubKeyOptions ));
-	memset(ses.authstate.pubkey_options, '\0', sizeof(*ses.authstate.pubkey_options));
 
 	buf_setpos(options_buf, 0);
 	while (options_buf->pos < options_buf->len) {
@@ -133,7 +143,7 @@
 			ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
 			goto next_option;
 		}
-#ifdef ENABLE_AGENTFWD
+#ifdef ENABLE_SVR_AGENTFWD
 		if (match_option(options_buf, "no-agent-forwarding") == DROPBEAR_SUCCESS) {
 			dropbear_log(LOG_WARNING, "Agent forwarding disabled.");
 			ses.authstate.pubkey_options->no_agent_forwarding_flag = 1;
--- a/svr-chansession.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-chansession.c	Wed May 16 22:54:51 2012 +0800
@@ -33,7 +33,6 @@
 #include "termcodes.h"
 #include "ssh.h"
 #include "random.h"
-#include "utmp.h"
 #include "x11fwd.h"
 #include "agentfwd.h"
 #include "runopts.h"
@@ -138,6 +137,7 @@
 
 	sa_chld.sa_handler = sesssigchild_handler;
 	sa_chld.sa_flags = SA_NOCLDSTOP;
+	sigemptyset(&sa_chld.sa_mask);
 	sigaction(SIGCHLD, &sa_chld, NULL);
 	TRACE(("leave sigchld handler"))
 }
@@ -218,10 +218,13 @@
 
 	struct ChanSess *chansess;
 
+	TRACE(("new chansess %p", channel))
+
 	dropbear_assert(channel->typedata == NULL);
 
 	chansess = (struct ChanSess*)m_malloc(sizeof(struct ChanSess));
 	chansess->cmd = NULL;
+	chansess->connection_string = NULL;
 	chansess->pid = 0;
 
 	/* pty details */
@@ -240,7 +243,7 @@
 	chansess->x11authcookie = NULL;
 #endif
 
-#ifndef DISABLE_AGENTFWD
+#ifdef ENABLE_SVR_AGENTFWD
 	chansess->agentlistener = NULL;
 	chansess->agentfile = NULL;
 	chansess->agentdir = NULL;
@@ -250,6 +253,14 @@
 
 }
 
+static struct logininfo* 
+chansess_login_alloc(struct ChanSess *chansess) {
+	struct logininfo * li;
+	li = login_alloc_entry(chansess->pid, ses.authstate.username,
+			svr_ses.remotehost, chansess->tty);
+	return li;
+}
+
 /* clean a session channel */
 static void closechansess(struct Channel *channel) {
 
@@ -271,10 +282,13 @@
 	m_free(chansess->cmd);
 	m_free(chansess->term);
 
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+	m_free(chansess->original_command);
+#endif
+
 	if (chansess->tty) {
 		/* write the utmp/wtmp login record */
-		li = login_alloc_entry(chansess->pid, ses.authstate.username,
-				ses.remotehost, chansess->tty);
+		li = chansess_login_alloc(chansess);
 		login_logout(li);
 		login_free_entry(li);
 
@@ -286,8 +300,8 @@
 	x11cleanup(chansess);
 #endif
 
-#ifndef DISABLE_AGENTFWD
-	agentcleanup(chansess);
+#ifdef ENABLE_SVR_AGENTFWD
+	svr_agentcleanup(chansess);
 #endif
 
 	/* clear child pid entries */
@@ -344,9 +358,9 @@
 	} else if (strcmp(type, "x11-req") == 0) {
 		ret = x11req(chansess);
 #endif
-#ifndef DISABLE_AGENTFWD
+#ifdef ENABLE_SVR_AGENTFWD
 	} else if (strcmp(type, "[email protected]") == 0) {
-		ret = agentreq(chansess);
+		ret = svr_agentreq(chansess);
 #endif
 	} else if (strcmp(type, "signal") == 0) {
 		ret = sessionsignal(chansess);
@@ -448,7 +462,7 @@
 	TRACE(("term mode str %d p->l %d p->p %d", 
 				len, ses.payload->len , ses.payload->pos));
 	if (len != ses.payload->len - ses.payload->pos) {
-		dropbear_exit("bad term mode string");
+		dropbear_exit("Bad term mode string");
 	}
 
 	if (len == 0) {
@@ -513,7 +527,7 @@
 		}
 	}
 	if (tcsetattr(chansess->master, TCSANOW, &termio) < 0) {
-		dropbear_log(LOG_INFO, "error setting terminal attributes");
+		dropbear_log(LOG_INFO, "Error setting terminal attributes");
 	}
 	TRACE(("leave get_termmodes"))
 }
@@ -543,7 +557,7 @@
 
 	/* allocate the pty */
 	if (chansess->master != -1) {
-		dropbear_exit("multiple pty requests");
+		dropbear_exit("Multiple pty requests");
 	}
 	if (pty_allocate(&chansess->master, &chansess->slave, namebuf, 64) == 0) {
 		TRACE(("leave sessionpty: failed to allocate pty"))
@@ -552,7 +566,7 @@
 	
 	chansess->tty = (char*)m_strdup(namebuf);
 	if (!chansess->tty) {
-		dropbear_exit("out of memory"); /* TODO disconnect */
+		dropbear_exit("Out of memory"); /* TODO disconnect */
 	}
 
 	pw = getpwnam(ses.authstate.pw_name);
@@ -570,6 +584,21 @@
 	return DROPBEAR_SUCCESS;
 }
 
+static char* make_connection_string() {
+	char *local_ip, *local_port, *remote_ip, *remote_port;
+	size_t len;
+	char *ret;
+	get_socket_address(ses.sock_in, &local_ip, &local_port, &remote_ip, &remote_port, 0);
+	len = strlen(local_ip) + strlen(local_port) + strlen(remote_ip) + strlen(remote_port) + 4;
+	ret = m_malloc(len);
+	snprintf(ret, len, "%s %s %s %s", remote_ip, remote_port, local_ip, local_port);
+	m_free(local_ip);
+	m_free(local_port);
+	m_free(remote_ip);
+	m_free(remote_port);
+	return ret;
+}
+
 /* Handle a command request from the client. This is used for both shell
  * and command-execution requests, and passes the command to
  * noptycommand or ptycommand as appropriate.
@@ -589,9 +618,6 @@
 		return DROPBEAR_FAILURE;
 	}
 
-	/* take public key option 'command' into account */
-	svr_pubkey_set_forced_command(chansess);
-
 	if (iscmd) {
 		/* "exec" */
 		if (chansess->cmd == NULL) {
@@ -616,17 +642,26 @@
 			}
 		}
 	}
+	
+	/* take public key option 'command' into account */
+	svr_pubkey_set_forced_command(chansess);
 
 #ifdef LOG_COMMANDS
 	if (chansess->cmd) {
-		dropbear_log(LOG_INFO, "user %s executing '%s'", 
+		dropbear_log(LOG_INFO, "User %s executing '%s'", 
 						ses.authstate.pw_name, chansess->cmd);
 	} else {
-		dropbear_log(LOG_INFO, "user %s executing login shell", 
+		dropbear_log(LOG_INFO, "User %s executing login shell", 
 						ses.authstate.pw_name);
 	}
 #endif
 
+	/* uClinux will vfork(), so there'll be a race as 
+	connection_string is freed below. */
+#ifndef USE_VFORK
+	chansess->connection_string = make_connection_string();
+#endif
+
 	if (chansess->term == NULL) {
 		/* no pty */
 		ret = noptycommand(channel, chansess);
@@ -635,6 +670,10 @@
 		ret = ptycommand(channel, chansess);
 	}
 
+#ifndef USE_VFORK
+	m_free(chansess->connection_string);
+#endif
+
 	if (ret == DROPBEAR_FAILURE) {
 		m_free(chansess->cmd);
 	}
@@ -660,6 +699,8 @@
 	ses.maxfd = MAX(ses.maxfd, channel->readfd);
 	ses.maxfd = MAX(ses.maxfd, channel->errfd);
 
+	sleep(1);
+
 	addchildpid(chansess, chansess->pid);
 
 	if (svr_ses.lastexit.exitpid != -1) {
@@ -673,6 +714,7 @@
 				TRACE(("found match for lastexitpid"))
 				svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
 				svr_ses.lastexit.exitpid = -1;
+				break;
 			}
 		}
 	}
@@ -699,11 +741,11 @@
 
 	/* we need to have a pty allocated */
 	if (chansess->master == -1 || chansess->tty == NULL) {
-		dropbear_log(LOG_WARNING, "no pty was allocated, couldn't execute");
+		dropbear_log(LOG_WARNING, "No pty was allocated, couldn't execute");
 		return DROPBEAR_FAILURE;
 	}
 	
-#ifdef __uClinux__
+#ifdef USE_VFORK
 	pid = vfork();
 #else
 	pid = fork();
@@ -736,13 +778,10 @@
 
 		/* write the utmp/wtmp login record - must be after changing the
 		 * terminal used for stdout with the dup2 above */
-		li= login_alloc_entry(getpid(), ses.authstate.username,
-				ses.remotehost, chansess->tty);
+		li = chansess_login_alloc(chansess);
 		login_login(li);
 		login_free_entry(li);
 
-		m_free(chansess->tty);
-
 #ifdef DO_MOTD
 		if (svr_opts.domotd) {
 			/* don't show the motd if ~/.hushlogin exists */
@@ -824,9 +863,9 @@
 	struct ChanSess *chansess = user_data;
 	char *usershell = NULL;
 
-    /* with uClinux we'll have vfork()ed, so don't want to overwrite the
-     * hostkey. can't think of a workaround to clear it */
-#ifndef __uClinux__
+	/* with uClinux we'll have vfork()ed, so don't want to overwrite the
+	 * hostkey. can't think of a workaround to clear it */
+#ifndef USE_VFORK
 	/* wipe the hostkey */
 	sign_key_free(svr_opts.hostkey);
 	svr_opts.hostkey = NULL;
@@ -855,10 +894,10 @@
 		if ((setgid(ses.authstate.pw_gid) < 0) ||
 			(initgroups(ses.authstate.pw_name, 
 						ses.authstate.pw_gid) < 0)) {
-			dropbear_exit("error changing user group");
+			dropbear_exit("Error changing user group");
 		}
 		if (setuid(ses.authstate.pw_uid) < 0) {
-			dropbear_exit("error changing user");
+			dropbear_exit("Error changing user");
 		}
 	} else {
 		/* ... but if the daemon is the same uid as the requested uid, we don't
@@ -869,7 +908,7 @@
 		 * differing groups won't be set (as with initgroups()). The solution
 		 * is for the sysadmin not to give out the UID twice */
 		if (getuid() != ses.authstate.pw_uid) {
-			dropbear_exit("couldn't	change user as non-root");
+			dropbear_exit("Couldn't	change user as non-root");
 		}
 	}
 
@@ -883,25 +922,39 @@
 		addnewvar("TERM", chansess->term);
 	}
 
+	if (chansess->tty) {
+		addnewvar("SSH_TTY", chansess->tty);
+	}
+	
+	if (chansess->connection_string) {
+		addnewvar("SSH_CONNECTION", chansess->connection_string);
+	}
+	
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+	if (chansess->original_command) {
+		addnewvar("SSH_ORIGINAL_COMMAND", chansess->original_command);
+	}
+#endif
+
 	/* change directory */
 	if (chdir(ses.authstate.pw_dir) < 0) {
-		dropbear_exit("error changing directory");
+		dropbear_exit("Error changing directory");
 	}
 
 #ifndef DISABLE_X11FWD
 	/* set up X11 forwarding if enabled */
 	x11setauth(chansess);
 #endif
-#ifndef DISABLE_AGENTFWD
+#ifdef ENABLE_SVR_AGENTFWD
 	/* set up agent env variable */
-	agentset(chansess);
+	svr_agentset(chansess);
 #endif
 
 	usershell = m_strdup(get_user_shell());
 	run_shell_command(chansess->cmd, ses.maxfd, usershell);
 
 	/* only reached on error */
-	dropbear_exit("child failed");
+	dropbear_exit("Child failed");
 }
 
 const struct ChanType svrchansess = {
@@ -928,6 +981,7 @@
 	svr_ses.lastexit.exitpid = -1; /* Nothing has exited yet */
 	sa_chld.sa_handler = sesssigchild_handler;
 	sa_chld.sa_flags = SA_NOCLDSTOP;
+	sigemptyset(&sa_chld.sa_mask);
 	if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
 		dropbear_exit("signal() error");
 	}
--- a/svr-kex.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-kex.c	Wed May 16 22:54:51 2012 +0800
@@ -70,7 +70,7 @@
  * that, the session hash is calculated, and signed with RSA or DSS. The
  * result is sent to the client. 
  *
- * See the ietf-secsh-transport draft, section 6, for details */
+ * See the transport rfc 4253 section 8 for details */
 static void send_msg_kexdh_reply(mp_int *dh_e) {
 
 	DEF_MP_INT(dh_y);
--- a/svr-main.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-main.c	Wed May 16 22:54:51 2012 +0800
@@ -77,22 +77,16 @@
 
 #ifdef INETD_MODE
 static void main_inetd() {
-
-	struct sockaddr_storage remoteaddr;
-	socklen_t remoteaddrlen;
-	char * addrstring = NULL;
+	char *host, *port = NULL;
 
 	/* Set up handlers, syslog, seed random */
 	commonsetup();
 
-	remoteaddrlen = sizeof(remoteaddr);
-	if (getpeername(0, (struct sockaddr*)&remoteaddr, &remoteaddrlen) < 0) {
-		dropbear_exit("Unable to getpeername: %s", strerror(errno));
-	}
-
 	/* In case our inetd was lax in logging source addresses */
-	addrstring = getaddrstring(&remoteaddr, 1);
-	dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
+	get_socket_address(0, NULL, NULL, &host, &port, 0);
+	dropbear_log(LOG_INFO, "Child connection from %s:%s", host, port);
+	m_free(host);
+	m_free(port);
 
 	/* Don't check the return value - it may just fail since inetd has
 	 * already done setsid() after forking (xinetd on Darwin appears to do
@@ -102,7 +96,7 @@
 	/* Start service program 
 	 * -1 is a dummy childpipe, just something we can close() without 
 	 * mattering. */
-	svr_session(0, -1, getaddrhostname(&remoteaddr), addrstring);
+	svr_session(0, -1);
 
 	/* notreached */
 }
@@ -133,7 +127,7 @@
 	for (i = 0; i < MAX_UNAUTH_CLIENTS; i++) {
 		childpipes[i] = -1;
 	}
-	bzero(preauth_addrs, sizeof(preauth_addrs));
+	memset(preauth_addrs, 0x0, sizeof(preauth_addrs));
 	
 	/* Set up the listening sockets */
 	listensockcount = listensockets(listensocks, MAX_LISTEN_ADDR, &maxsock);
@@ -218,14 +212,13 @@
 
 		/* handle each socket which has something to say */
 		for (i = 0; i < listensockcount; i++) {
-
-			struct sockaddr_storage remoteaddr;
-			socklen_t remoteaddrlen = 0;
 			size_t num_unauthed_for_addr = 0;
 			size_t num_unauthed_total = 0;
-			char * remote_addr_str = NULL;
+			char *remote_host = NULL, *remote_port = NULL;
 			pid_t fork_ret = 0;
 			size_t conn_idx = 0;
+			struct sockaddr_storage remoteaddr;
+			socklen_t remoteaddrlen;
 
 			if (!FD_ISSET(listensocks[i], &fds)) 
 				continue;
@@ -240,14 +233,14 @@
 			}
 
 			/* Limit the number of unauthenticated connections per IP */
-			remote_addr_str = getaddrstring(&remoteaddr, 0);
+			getaddrstring(&remoteaddr, &remote_host, NULL, 0);
 
 			num_unauthed_for_addr = 0;
 			num_unauthed_total = 0;
 			for (j = 0; j < MAX_UNAUTH_CLIENTS; j++) {
 				if (childpipes[j] >= 0) {
 					num_unauthed_total++;
-					if (strcmp(remote_addr_str, preauth_addrs[j]) == 0) {
+					if (strcmp(remote_host, preauth_addrs[j]) == 0) {
 						num_unauthed_for_addr++;
 					}
 				} else {
@@ -272,7 +265,7 @@
 			fork_ret = fork();
 #endif
 			if (fork_ret < 0) {
-				dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno));
+				dropbear_log(LOG_WARNING, "Error forking: %s", strerror(errno));
 				goto out;
 
 			} else if (fork_ret > 0) {
@@ -280,21 +273,21 @@
 				/* parent */
 				childpipes[conn_idx] = childpipe[0];
 				m_close(childpipe[1]);
-				preauth_addrs[conn_idx] = remote_addr_str;
-				remote_addr_str = NULL;
+				preauth_addrs[conn_idx] = remote_host;
+				remote_host = NULL;
 
 			} else {
 
 				/* child */
-				char * addrstring = NULL;
 #ifdef DEBUG_FORKGPROF
 				extern void _start(void), etext(void);
 				monstartup((u_long)&_start, (u_long)&etext);
 #endif /* DEBUG_FORKGPROF */
 
-				m_free(remote_addr_str);
-				addrstring = getaddrstring(&remoteaddr, 1);
-				dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
+				getaddrstring(&remoteaddr, NULL, &remote_port, 0);
+				dropbear_log(LOG_INFO, "Child connection from %s:%s", remote_host, remote_port);
+				m_free(remote_host);
+				m_free(remote_port);
 
 #ifndef DEBUG_NOFORK
 				if (setsid() < 0) {
@@ -310,9 +303,7 @@
 				m_close(childpipe[0]);
 
 				/* start the session */
-				svr_session(childsock, childpipe[1], 
-								getaddrhostname(&remoteaddr),
-								addrstring);
+				svr_session(childsock, childpipe[1]);
 				/* don't return */
 				dropbear_assert(0);
 			}
@@ -320,8 +311,8 @@
 out:
 			/* This section is important for the parent too */
 			m_close(childsock);
-			if (remote_addr_str) {
-				m_free(remote_addr_str);
+			if (remote_host) {
+				m_free(remote_host);
 			}
 		}
 	} /* for(;;) loop */
@@ -379,6 +370,7 @@
 	/* catch and reap zombie children */
 	sa_chld.sa_handler = sigchld_handler;
 	sa_chld.sa_flags = SA_NOCLDSTOP;
+	sigemptyset(&sa_chld.sa_mask);
 	if (sigaction(SIGCHLD, &sa_chld, NULL) < 0) {
 		dropbear_exit("signal() error");
 	}
--- a/svr-runopts.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-runopts.c	Wed May 16 22:54:51 2012 +0800
@@ -43,11 +43,11 @@
 					" before user login\n"
 					"		(default: none)\n"
 #ifdef DROPBEAR_DSS
-					"-d dsskeyfile	Use dsskeyfile for the dss host key\n"
+					"-d dsskeyfile	Use dsskeyfile for the DSS host key\n"
 					"		(default: %s)\n"
 #endif
 #ifdef DROPBEAR_RSA
-					"-r rsakeyfile	Use rsakeyfile for the rsa host key\n"
+					"-r rsakeyfile	Use rsakeyfile for the RSA host key\n"
 					"		(default: %s)\n"
 #endif
 					"-F		Don't fork into background\n"
@@ -81,7 +81,8 @@
 					"-i		Start for inetd\n"
 #endif
 					"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
-					"-K <keepalive>  (0 is never, default %d)\n"
+					"-K <keepalive>  (0 is never, default %d, in seconds)\n"
+					"-I <idle_timeout>  (0 is never, default %d, in seconds)\n"
 #ifdef DEBUG_TRACE
 					"-v		verbose (compiled with DEBUG_TRACE)\n"
 #endif
@@ -93,7 +94,7 @@
 					RSA_PRIV_FILENAME,
 #endif
 					DROPBEAR_MAX_PORTS, DROPBEAR_DEFPORT, DROPBEAR_PIDFILE,
-					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE);
+					DEFAULT_RECV_WINDOW, DEFAULT_KEEPALIVE, DEFAULT_IDLE_TIMEOUT);
 }
 
 void svr_getopts(int argc, char ** argv) {
@@ -103,6 +104,7 @@
 	int nextisport = 0;
 	char* recv_window_arg = NULL;
 	char* keepalive_arg = NULL;
+	char* idle_timeout_arg = NULL;
 
 	/* see printhelp() for options */
 	svr_opts.rsakeyfile = NULL;
@@ -123,6 +125,9 @@
 #ifdef ENABLE_SVR_REMOTETCPFWD
 	svr_opts.noremotetcp = 0;
 #endif
+#ifndef DISABLE_ZLIB
+	opts.enable_compress = 1;
+#endif
 	/* not yet
 	opts.ipv4 = 1;
 	opts.ipv6 = 1;
@@ -134,7 +139,8 @@
 	svr_opts.usingsyslog = 1;
 #endif
 	opts.recv_window = DEFAULT_RECV_WINDOW;
-	opts.keepalive_secs = DEFAULT_KEEPALIVE;	
+	opts.keepalive_secs = DEFAULT_KEEPALIVE;
+	opts.idle_timeout_secs = DEFAULT_IDLE_TIMEOUT;
 	
 #ifdef ENABLE_SVR_REMOTETCPFWD
 	opts.listen_fwd_all = 0;
@@ -218,6 +224,9 @@
 				case 'K':
 					next = &keepalive_arg;
 					break;
+				case 'I':
+					next = &idle_timeout_arg;
+					break;
 #if defined(ENABLE_SVR_PASSWORD_AUTH) || defined(ENABLE_SVR_PAM_AUTH)
 				case 's':
 					svr_opts.noauthpass = 1;
@@ -253,7 +262,7 @@
 		svr_opts.addresses[0] = m_strdup(DROPBEAR_DEFADDRESS);
 		svr_opts.portcount = 1;
 	}
-        
+
 	if (svr_opts.dsskeyfile == NULL) {
 		svr_opts.dsskeyfile = DSS_PRIV_FILENAME;
 	}
@@ -290,9 +299,19 @@
 	}
 	
 	if (keepalive_arg) {
-		if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
+		unsigned int val;
+		if (m_str_to_uint(keepalive_arg, &val) == DROPBEAR_FAILURE) {
 			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
 		}
+		opts.keepalive_secs = val;
+	}
+
+	if (idle_timeout_arg) {
+		unsigned int val;
+		if (m_str_to_uint(idle_timeout_arg, &val) == DROPBEAR_FAILURE) {
+			dropbear_exit("Bad idle_timeout '%s'", idle_timeout_arg);
+		}
+		opts.idle_timeout_secs = val;
 	}
 }
 
@@ -306,7 +325,7 @@
 		myspec = m_strdup(spec);
 
 		/* search for ':', that separates address and port */
-		svr_opts.ports[svr_opts.portcount] = strchr(myspec, ':');
+		svr_opts.ports[svr_opts.portcount] = strrchr(myspec, ':');
 
 		if (svr_opts.ports[svr_opts.portcount] == NULL) {
 			/* no ':' -> the whole string specifies just a port */
--- a/svr-service.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-service.c	Wed May 16 22:54:51 2012 +0800
@@ -57,7 +57,7 @@
 	if (len == SSH_SERVICE_CONNECTION_LEN &&
 			(strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) {
 		if (ses.authstate.authdone != 1) {
-			dropbear_exit("request for connection before auth");
+			dropbear_exit("Request for connection before auth");
 		}
 
 		send_msg_service_accept(name, len);
@@ -68,7 +68,7 @@
 
 	m_free(name);
 	/* TODO this should be a MSG_DISCONNECT */
-	dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST");
+	dropbear_exit("Unrecognised SSH_MSG_SERVICE_REQUEST");
 
 
 }
--- a/svr-session.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-session.c	Wed May 16 22:54:51 2012 +0800
@@ -52,9 +52,7 @@
 	{SSH_MSG_KEXINIT, recv_msg_kexinit},
 	{SSH_MSG_KEXDH_INIT, recv_msg_kexdh_init}, /* server */
 	{SSH_MSG_NEWKEYS, recv_msg_newkeys},
-#ifdef ENABLE_SVR_REMOTETCPFWD
 	{SSH_MSG_GLOBAL_REQUEST, recv_msg_global_request_remotetcp},
-#endif
 	{SSH_MSG_CHANNEL_REQUEST, recv_msg_channel_request},
 	{SSH_MSG_CHANNEL_OPEN, recv_msg_channel_open},
 	{SSH_MSG_CHANNEL_EOF, recv_msg_channel_eof},
@@ -74,23 +72,36 @@
 	NULL /* Null termination is mandatory. */
 };
 
-void svr_session(int sock, int childpipe, 
-		char* remotehost, char *addrstring) {
-
+void svr_session(int sock, int childpipe) {
+	char *host, *port;
+	size_t len;
     reseedrandom();
 
 	crypto_init();
-	common_session_init(sock, sock, remotehost);
+	common_session_init(sock, sock);
 
 	/* Initialise server specific parts of the session */
 	svr_ses.childpipe = childpipe;
-	svr_ses.addrstring = addrstring;
+#ifdef USE_VFORK
+	svr_ses.server_pid = getpid();
+#endif
 	svr_authinitialise();
 	chaninitialise(svr_chantypes);
 	svr_chansessinitialise();
 
 	ses.connect_time = time(NULL);
 
+	/* for logging the remote address */
+	get_socket_address(ses.sock_in, NULL, NULL, &host, &port, 0);
+	len = strlen(host) + strlen(port) + 2;
+	svr_ses.addrstring = m_malloc(len);
+	snprintf(svr_ses.addrstring, len, "%s:%s", host, port);
+	m_free(host);
+	m_free(port);
+
+	get_socket_address(ses.sock_in, NULL, NULL, 
+			&svr_ses.remotehost, NULL, 1);
+
 	/* set up messages etc */
 	ses.remoteclosed = svr_remoteclosed;
 
@@ -125,30 +136,37 @@
 	if (!sessinitdone) {
 		/* before session init */
 		snprintf(fmtbuf, sizeof(fmtbuf), 
-				"premature exit: %s", format);
+				"Premature exit: %s", format);
 	} else if (ses.authstate.authdone) {
 		/* user has authenticated */
 		snprintf(fmtbuf, sizeof(fmtbuf),
-				"exit after auth (%s): %s", 
+				"Exit (%s): %s", 
 				ses.authstate.pw_name, format);
 	} else if (ses.authstate.pw_name) {
 		/* we have a potential user */
 		snprintf(fmtbuf, sizeof(fmtbuf), 
-				"exit before auth (user '%s', %d fails): %s",
+				"Exit before auth (user '%s', %d fails): %s",
 				ses.authstate.pw_name, ses.authstate.failcount, format);
 	} else {
 		/* before userauth */
 		snprintf(fmtbuf, sizeof(fmtbuf), 
-				"exit before auth: %s", format);
+				"Exit before auth: %s", format);
 	}
 
 	_dropbear_log(LOG_INFO, fmtbuf, param);
 
-	/* free potential public key options */
-	svr_pubkey_options_cleanup();
+#ifdef USE_VFORK
+	/* For uclinux only the main server process should cleanup - we don't want
+	 * forked children doing that */
+	if (svr_ses.server_pid == getpid())
+#endif
+	{
+		/* free potential public key options */
+		svr_pubkey_options_cleanup();
 
-	/* must be after we've done with username etc */
-	common_session_cleanup();
+		/* must be after we've done with username etc */
+		common_session_cleanup();
+	}
 
 	exit(exitcode);
 
@@ -183,7 +201,7 @@
 		local_tm = localtime(&timesec);
 		if (local_tm == NULL
 			|| strftime(datestr, sizeof(datestr), "%b %d %H:%M:%S", 
-						localtime(&timesec)) == 0)
+						local_tm) == 0)
 		{
 			/* upon failure, just print the epoch-seconds time. */
 			snprintf(datestr, sizeof(datestr), "%d", (int)timesec);
--- a/svr-tcpfwd.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-tcpfwd.c	Wed May 16 22:54:51 2012 +0800
@@ -34,24 +34,31 @@
 #include "runopts.h"
 #include "auth.h"
 
-#ifdef ENABLE_SVR_REMOTETCPFWD
+static void send_msg_request_failure();
+
+static void send_msg_request_failure() {
+	CHECKCLEARTOWRITE();
+	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
+	encrypt_packet();
+}
+
+#ifndef ENABLE_SVR_REMOTETCPFWD
+
+/* This is better than SSH_MSG_UNIMPLEMENTED */
+void recv_msg_global_request_remotetcp() {
+		TRACE(("recv_msg_global_request_remotetcp: remote tcp forwarding not compiled in"))
+		send_msg_request_failure();
+}
+
+/* */
+#endif /* !ENABLE_SVR_REMOTETCPFWD */
 
 static void send_msg_request_success();
-static void send_msg_request_failure();
 static int svr_cancelremotetcp();
 static int svr_remotetcpreq();
 static int newtcpdirect(struct Channel * channel);
 
-
-const struct ChanType svr_chan_tcpdirect = {
-	1, /* sepfds */
-	"direct-tcpip",
-	newtcpdirect, /* init */
-	NULL, /* checkclose */
-	NULL, /* reqhandler */
-	NULL /* closehandler */
-};
-
+#ifdef ENABLE_SVR_REMOTETCPFWD
 static const struct ChanType svr_chan_tcpremote = {
 	1, /* sepfds */
 	"forwarded-tcpip",
@@ -117,14 +124,6 @@
 
 }
 
-static void send_msg_request_failure() {
-
-	CHECKCLEARTOWRITE();
-	buf_putbyte(ses.writepayload, SSH_MSG_REQUEST_FAILURE);
-	encrypt_packet();
-
-}
-
 static int matchtcp(void* typedata1, void* typedata2) {
 
 	const struct TCPListener *info1 = (struct TCPListener*)typedata1;
@@ -173,14 +172,14 @@
 static int svr_remotetcpreq() {
 
 	int ret = DROPBEAR_FAILURE;
-	unsigned char * bindaddr = NULL;
+	unsigned char * request_addr = NULL;
 	unsigned int addrlen;
 	struct TCPListener *tcpinfo = NULL;
 	unsigned int port;
 
 	TRACE(("enter remotetcpreq"))
 
-	bindaddr = buf_getstring(ses.payload, &addrlen);
+	request_addr = buf_getstring(ses.payload, &addrlen);
 	if (addrlen > MAX_IP_LEN) {
 		TRACE(("addr len too long: %d", addrlen))
 		goto out;
@@ -206,24 +205,46 @@
 	tcpinfo = (struct TCPListener*)m_malloc(sizeof(struct TCPListener));
 	tcpinfo->sendaddr = NULL;
 	tcpinfo->sendport = 0;
-	tcpinfo->listenaddr = bindaddr;
 	tcpinfo->listenport = port;
 	tcpinfo->chantype = &svr_chan_tcpremote;
 	tcpinfo->tcp_type = forwarded;
 
+	tcpinfo->request_listenaddr = request_addr;
+	if (!opts.listen_fwd_all || (strcmp(request_addr, "localhost") == 0) ) {
+        // NULL means "localhost only"
+		tcpinfo->listenaddr = NULL;
+	}
+	else
+	{
+		tcpinfo->listenaddr = request_addr;
+	}
+
 	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(bindaddr);
+		m_free(request_addr);
 		m_free(tcpinfo);
 	}
 	TRACE(("leave remotetcpreq"))
 	return ret;
 }
 
+#endif /* ENABLE_SVR_REMOTETCPFWD */
+
+#ifdef ENABLE_SVR_LOCALTCPFWD
+
+const struct ChanType svr_chan_tcpdirect = {
+	1, /* sepfds */
+	"direct-tcpip",
+	newtcpdirect, /* init */
+	NULL, /* checkclose */
+	NULL, /* reqhandler */
+	NULL /* closehandler */
+};
+
 /* Called upon creating a new direct tcp channel (ie we connect out to an
  * address */
 static int newtcpdirect(struct Channel * channel) {
@@ -288,4 +309,4 @@
 	return err;
 }
 
-#endif
+#endif /* ENABLE_SVR_LOCALTCPFWD */
--- a/svr-x11fwd.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/svr-x11fwd.c	Wed May 16 22:54:51 2012 +0800
@@ -175,7 +175,7 @@
 	m_free(chansess->x11authprot);
 	m_free(chansess->x11authcookie);
 
-	TRACE(("chansess %s", chansess))
+	TRACE(("chansess %x", chansess))
 	if (chansess->x11listener != NULL) {
 		remove_listener(chansess->x11listener);
 		chansess->x11listener = NULL;
@@ -233,7 +233,7 @@
 			continue;
 		}
 		/* otherwise it was an error we don't know about */
-		dropbear_log(LOG_DEBUG, "failed to bind x11 socket");
+		dropbear_log(LOG_DEBUG, "Failed to bind x11 socket");
 		break;
 	}
 	return -1;
--- a/sysoptions.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/sysoptions.h	Wed May 16 22:54:51 2012 +0800
@@ -4,7 +4,7 @@
  *******************************************************************/
 
 #ifndef DROPBEAR_VERSION
-#define DROPBEAR_VERSION "0.51"
+#define DROPBEAR_VERSION "2012.55"
 #endif
 
 #define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
@@ -60,6 +60,7 @@
 
 /* various algorithm identifiers */
 #define DROPBEAR_KEX_DH_GROUP1 0
+#define DROPBEAR_KEX_DH_GROUP14 1
 
 #define DROPBEAR_SIGNKEY_ANY 0
 #define DROPBEAR_SIGNKEY_RSA 1
@@ -89,7 +90,13 @@
 #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 */
+#if defined(DROPBEAR_SHA2_512_HMAC)
+#define MAX_MAC_KEY 64
+#elif defined(DROPBEAR_SHA2_256_HMAC)
+#define MAX_MAC_KEY 32
+#else
 #define MAX_MAC_KEY 20
+#endif
 
 #define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't
 						   explicitly specified for all protocols (just
@@ -98,6 +105,7 @@
 #define MAX_PROPOSED_ALGO 20
 
 /* size/count limits */
+/* From transport rfc */
 #define MIN_PACKET_LEN 16
 
 #define RECV_MAX_PACKET_LEN (MAX(35000, ((RECV_MAX_PAYLOAD_LEN)+100)))
@@ -122,7 +130,7 @@
 #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 */
+/* Sect. 8 of the transport rfc 4253, 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
@@ -142,14 +150,23 @@
 #define DROPBEAR_TWOFISH
 #endif
 
+#ifdef DROPBEAR_MD5_HMAC
+#define DROPBEAR_MD5
+#endif
+
+#ifdef DROPBEAR_SHA2_256_HMAC
+#define DROPBEAR_SHA256
+#endif
+
+#if (defined(DROPBEAR_DSS) && defined(DSS_PROTOK)) \
+	|| defined(DROPBEAR_SHA2_512_HMAC)
+#define DROPBEAR_SHA512
+#endif
+
 #ifndef ENABLE_X11FWD
 #define DISABLE_X11FWD
 #endif
 
-#ifndef ENABLE_AGENTFWD
-#define DISABLE_AGENTFWD
-#endif
-
 #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
 #define ENABLE_CLI_ANYTCPFWD 
 #endif
@@ -160,7 +177,7 @@
 
 #if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
 	defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
-	defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
+	defined(ENABLE_SVR_AGENTFWD) || defined(ENABLE_X11FWD)
 #define USING_LISTENERS
 #endif
 
@@ -168,10 +185,18 @@
 #define ENABLE_CLI_MULTIHOP
 #endif
 
+#if defined(ENABLE_CLI_AGENTFWD) || defined(DROPBEAR_PRNGD_SOCKET)
+#define ENABLE_CONNECT_UNIX
+#endif
+
 #if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
 #define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
 #endif
 
+/* Changing this is inadvisable, it appears to have problems
+ * with flushing compressed data */
+#define DROPBEAR_ZLIB_MEM_LEVEL 8
+
 #if defined(ENABLE_SVR_PASSWORD_AUTH) && defined(ENABLE_SVR_PAM_AUTH)
 #error "You can't turn on PASSWORD and PAM auth both at once. Fix it in options.h"
 #endif
@@ -202,5 +227,14 @@
 #define IS_DROPBEAR_CLIENT 1
 
 #else
-#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
-#endif
+/* Just building key utils? */
+#define IS_DROPBEAR_SERVER 0
+#define IS_DROPBEAR_CLIENT 0
+
+#endif /* neither DROPBEAR_SERVER nor DROPBEAR_CLIENT */
+
+#ifndef HAVE_FORK
+#define USE_VFORK
+#endif  /* don't HAVE_FORK */
+
+/* no include guard for this file */
--- a/tcp-accept.c	Thu Nov 06 13:33:06 2008 +0000
+++ b/tcp-accept.c	Wed May 16 22:54:51 2012 +0800
@@ -40,6 +40,7 @@
 
 	m_free(tcpinfo->sendaddr);
 	m_free(tcpinfo->listenaddr);
+	m_free(tcpinfo->request_listenaddr);
 	m_free(tcpinfo);
 }
 
@@ -61,6 +62,7 @@
 	if (getnameinfo((struct sockaddr*)&addr, len, ipstring, sizeof(ipstring),
 				portstring, sizeof(portstring), 
 				NI_NUMERICHOST | NI_NUMERICSERV) != 0) {
+		m_close(fd);
 		return;
 	}
 
@@ -77,10 +79,13 @@
 			dropbear_assert(tcpinfo->tcp_type == forwarded);
 			/* "forwarded-tcpip" */
 			/* address that was connected, port that was connected */
-			addr = tcpinfo->listenaddr;
+			addr = tcpinfo->request_listenaddr;
 			port = tcpinfo->listenport;
 		}
 
+		if (addr == NULL) {
+			addr = "localhost";
+		}
 		buf_putstring(ses.writepayload, addr, strlen(addr));
 		buf_putint(ses.writepayload, port);
 
@@ -104,21 +109,13 @@
 	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);
 
-	/* 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, 
+	nsocks = dropbear_listen(tcpinfo->listenaddr, portstring, socks, 
 			DROPBEAR_MAX_SOCKS, &errstring, &ses.maxfd);
 	if (nsocks < 0) {
 		dropbear_log(LOG_INFO, "TCP forward failed: %s", errstring);
--- a/tcpfwd.h	Thu Nov 06 13:33:06 2008 +0000
+++ b/tcpfwd.h	Wed May 16 22:54:51 2012 +0800
@@ -25,6 +25,7 @@
 #define _TCPFWD_H
 
 #include "channel.h"
+#include "list.h"
 
 struct TCPListener {
 
@@ -38,21 +39,21 @@
 	 * localhost, or a normal interface name. */
 	unsigned char *listenaddr;
 	unsigned int listenport;
+	/* The address that the remote host asked to listen on */
+	unsigned char *request_listenaddr;;
 
 	const struct ChanType *chantype;
 	enum {direct, forwarded} tcp_type;
 };
 
-/* A link in a list of forwards */
-struct TCPFwdList {
-
+/* A forwarding entry */
+struct TCPFwdEntry {
 	const unsigned char* connectaddr;
 	unsigned int connectport;
+	const unsigned char* listenaddr;
 	unsigned int listenport;
 	unsigned int have_reply; /* is set to 1 after a reply has been received
 								when setting up the forwarding */
-	struct TCPFwdList * next;
-
 };
 
 /* Server */
@@ -70,5 +71,4 @@
 /* Common */
 int listen_tcpfwd(struct TCPListener* tcpinfo);
 
-
 #endif