changeset 559:7f66b8e40f2d agent-client

propagate from branch 'au.asn.ucc.matt.dropbear' (head bbe4e11695a7b22bd89a722600eb4a4020b6fdf3) to branch 'au.asn.ucc.matt.dropbear.cli-agent' (head 276cf5e82276b6c879d246ba64739ec6868f5150)
author Matt Johnston <matt@ucc.asn.au>
date Wed, 29 Jul 2009 02:58:33 +0000
parents de3653483ac0 (diff) bcc5b69d15a6 (current diff)
children 52d7301e46bd
files debug.h options.h session.h
diffstat 29 files changed, 634 insertions(+), 177 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.in	Sun Jul 26 16:14:50 2009 +0000
+++ b/Makefile.in	Wed Jul 29 02:58:33 2009 +0000
@@ -20,7 +20,7 @@
 		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 +29,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 \
--- a/agentfwd.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/agentfwd.h	Wed Jul 29 02:58:33 2009 +0000
@@ -23,21 +23,32 @@
  * 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"
+
+/* 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
 
 int agentreq(struct ChanSess * chansess);
-void agentsetauth(struct ChanSess *chansess);
 void agentcleanup(struct ChanSess * chansess);
 void agentset(struct ChanSess *chansess);
 
+/* client functions */
+void load_agent_keys(m_list * ret_list);
+void agent_buf_sign(buffer *sigblob, sign_key *key, 
+    const unsigned char *data, unsigned int len);
+
 #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 /* _AGENTFWD_H_ */
--- a/auth.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/auth.h	Wed Jul 29 02:58:33 2009 +0000
@@ -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 */
@@ -123,19 +125,6 @@
 
 };
 
-struct SignKeyList;
-/* A singly linked list of signing keys */
-struct SignKeyList {
-
-	sign_key *key;
-	int type; /* The type of key */
-	struct SignKeyList *next;
-	char *filename;
-	/* the buffer? for encrypted keys, so we can later get
-	 * the private key portion */
-
-};
-
 #ifdef ENABLE_SVR_PUBKEY_OPTIONS
 struct PubKeyOptions;
 struct PubKeyOptions {
--- a/buffer.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/buffer.c	Wed Jul 29 02:58:33 2009 +0000
@@ -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	Sun Jul 26 16:14:50 2009 +0000
+++ b/buffer.h	Wed Jul 29 02:58:33 2009 +0000
@@ -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	Sun Jul 26 16:14:50 2009 +0000
+++ b/channel.h	Wed Jul 29 02:58:33 2009 +0000
@@ -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 */
--- a/chansession.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/chansession.h	Wed Jul 29 02:58:33 2009 +0000
@@ -60,7 +60,7 @@
 	unsigned char x11singleconn;
 #endif
 
-#ifndef DISABLE_AGENTFWD
+#ifdef ENABLE_SVR_AGENTFWD
 	struct Listener * agentlistener;
 	char * agentfile;
 	char * agentdir;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-agentfwd.c	Wed Jul 29 02:58:33 2009 +0000
@@ -0,0 +1,295 @@
+/*
+ * 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);
+
+	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();
+
+	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;
+	}
+}
+
+/* Returned keys are prepended to ret_list, which will
+   be updated. */
+void 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) {
+		dropbear_log(LOG_INFO, "Failed to connect to agent");
+		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 = buf_new(MAX_PUBKEY_SIZE + len + 12);
+	buffer *response;
+	unsigned int keylen, siglen;
+	int packet_type;
+	
+	/* Request format
+	byte			SSH2_AGENTC_SIGN_REQUEST
+	string			key_blob
+	string			data
+	uint32			flags
+	*/
+	/* We write the key, then figure how long it was and write that */
+	//buf_putint(request_data, 0);
+	buf_put_pub_key(request_data, key, key->type);
+	keylen = request_data->len - 4;
+	//buf_setpos(request_data, 0);
+	//buf_putint(request_data, keylen);
+	
+	//buf_setpos(request_data, request_data->len);
+	buf_putstring(request_data, data, len);
+	buf_putint(request_data, 0);
+	
+	response = agent_request(SSH2_AGENTC_SIGN_REQUEST, request_data);
+	buf_free(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);
+	buf_free(response);
+	
+	return;
+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");
+}
+
+#endif
--- a/cli-auth.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/cli-auth.c	Wed Jul 29 02:58:33 2009 +0000
@@ -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() {
--- a/cli-authpubkey.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/cli-authpubkey.c	Wed Jul 29 02:58:33 2009 +0000
@@ -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,31 +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->filename);
-	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;
@@ -81,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;
@@ -91,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
@@ -115,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"))
 	}
@@ -127,6 +120,25 @@
 	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)
+{
+	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 {
+		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) {
 
@@ -162,7 +174,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 */
 	}
 
@@ -170,20 +182,42 @@
 	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) {
+	if (cli_opts.agent_fwd &&
+			!cli_opts.agent_keys_loaded) {
+		/* get the list of available keys from the agent */
+		load_agent_keys(cli_opts.privkeys);
+		cli_opts.agent_keys_loaded = 1;
+	}
+
+	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	Sun Jul 26 16:14:50 2009 +0000
+++ b/cli-chansession.c	Wed Jul 29 02:58:33 2009 +0000
@@ -424,16 +424,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-runopts.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/cli-runopts.c	Wed Jul 29 02:58:33 2009 +0000
@@ -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,6 +67,9 @@
 #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"
 					"-g    Allow remote hosts to connect to forwarded ports\n"
@@ -125,14 +129,18 @@
 	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_keys_loaded = 0;
 #endif
 #ifdef ENABLE_CLI_PROXYCMD
 	cli_opts.proxycmd = NULL;
@@ -158,7 +166,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;
 		}
@@ -166,7 +174,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;
 		}
@@ -266,6 +274,11 @@
 				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;
@@ -394,8 +407,6 @@
 
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 static void loadidentityfile(const char* filename) {
-
-	struct SignKeyList * nextkey;
 	sign_key *key;
 	int keytype;
 
@@ -405,12 +416,10 @@
 		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->filename = m_strdup(filename);
-		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
@@ -422,12 +431,13 @@
 	char *ret;
 	int total;
 	unsigned int len = 0;
-	struct SignKeyList *nextkey;
+	m_list_elem *iter;
 	/* Fill out -i and -W options that make sense for all
 	 * the intermediate processes */
-	for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next)
+	for (iter = cli_opts.privkeys->first; iter; iter = iter->next)
 	{
-		len += 3 + strlen(nextkey->filename);
+		sign_key * key = (sign_key*)iter->item;
+		len += 3 + strlen(key->filename);
 	}
 	len += 20; // space for -W <size>, terminator.
 	ret = m_malloc(len);
@@ -439,10 +449,11 @@
 		total += written;
 	}
 
-	for (nextkey = cli_opts.privkeys; nextkey; nextkey = nextkey->next)
+	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", nextkey->filename);
+		int written = snprintf(ret+total, size, "-i %s", key->filename);
 		dropbear_assert(written < size);
 		total += written;
 	}
@@ -608,12 +619,12 @@
 #ifdef ENABLE_CLI_ANYTCPFWD
 /* Turn a "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 * listenport = NULL;
 	char * connectport = NULL;
 	char * connectaddr = NULL;
-	struct TCPFwdList* newfwd = NULL;
+	struct TCPFwdEntry* newfwd = NULL;
 	char * str = NULL;
 
 	TRACE(("enter addforward"))
@@ -640,7 +651,7 @@
 	*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 */
@@ -667,8 +678,7 @@
 	}
 
 	newfwd->have_reply = 0;
-	newfwd->next = *fwdlist;
-	*fwdlist = newfwd;
+	list_append(fwdlist, newfwd);
 
 	TRACE(("leave addforward: done"))
 	return;
--- a/cli-session.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/cli-session.c	Wed Jul 29 02:58:33 2009 +0000
@@ -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,6 +76,9 @@
 #ifdef ENABLE_CLI_REMOTETCPFWD
 	&cli_chan_tcpremote,
 #endif
+#ifdef ENABLE_CLI_AGENTFWD
+	&cli_chan_agent,
+#endif
 	NULL /* Null termination */
 };
 
--- a/cli-tcpfwd.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/cli-tcpfwd.c	Wed Jul 29 02:58:33 2009 +0000
@@ -59,27 +59,22 @@
 
 #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->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;
+					fwd->listenport,
+					fwd->connectaddr,
+					fwd->connectport);
+		}		
 	}
 	TRACE(("leave setup_localtcp"))
 
@@ -148,60 +143,47 @@
  * being in the same order as we sent the requests. This is the ordering
  * of the cli_opts.remotefwds list */
 void cli_recv_msg_request_success() {
-
 	/* Nothing in the packet. 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;
 			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;
+		send_msg_global_request_remotetcp(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) {
 
 	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;
@@ -212,13 +194,11 @@
 	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) {
 			break;
 		}
-		iter = iter->next;
 	}
 
 	if (iter == NULL) {
@@ -228,8 +208,8 @@
 		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;
--- a/dbutil.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/dbutil.c	Wed Jul 29 02:58:33 2009 +0000
@@ -295,6 +295,28 @@
 	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))
+		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 +363,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) {
@@ -568,7 +582,6 @@
 	}
 
 	return retstring;
-
 }
 
 /* Get the hostname corresponding to the address addr. On failure, the IP
--- a/dbutil.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/dbutil.h	Wed Jul 29 02:58:33 2009 +0000
@@ -52,6 +52,9 @@
 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);
--- a/debug.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/debug.h	Wed Jul 29 02:58:33 2009 +0000
@@ -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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/list.c	Wed Jul 29 02:58:33 2009 +0000
@@ -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;
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/list.h	Wed Jul 29 02:58:33 2009 +0000
@@ -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 */
\ No newline at end of file
--- a/options.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/options.h	Wed Jul 29 02:58:33 2009 +0000
@@ -65,7 +65,8 @@
 #define ENABLE_SVR_REMOTETCPFWD
 
 /* Enable Authentication Agent Forwarding - server only for now */
-#define ENABLE_AGENTFWD
+#define ENABLE_SVR_AGENTFWD
+#define ENABLE_CLI_AGENTFWD
 
 
 /* Note: Both ENABLE_CLI_PROXYCMD and ENABLE_CLI_NETCAT must be set to
--- a/random.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/random.c	Wed Jul 29 02:58:33 2009 +0000
@@ -69,12 +69,8 @@
 #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");
 	}
--- a/runopts.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/runopts.h	Wed Jul 29 02:58:33 2009 +0000
@@ -112,13 +112,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
--- a/session.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/session.h	Wed Jul 29 02:58:33 2009 +0000
@@ -265,7 +265,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	Sun Jul 26 16:14:50 2009 +0000
+++ b/signkey.c	Wed Jul 29 02:58:33 2009 +0000
@@ -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
@@ -81,6 +83,9 @@
 	}
 #endif
 
+	TRACE(("signkey_type_from_name unexpected key type."))
+	printhex("Key type", name, namelen);
+
 	return DROPBEAR_SIGNKEY_NONE;
 }
 
@@ -101,8 +106,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"))
 
 	*type = keytype;
 
@@ -255,6 +263,8 @@
 	key->rsakey = NULL;
 #endif
 
+	m_free(key->filename);
+
 	m_free(key);
 	TRACE(("leave sign_key_free"))
 }
@@ -358,7 +368,6 @@
 		const unsigned char *data, unsigned int len) {
 
 	buffer *sigblob;
-
 	sigblob = buf_new(MAX_PUBKEY_SIZE);
 
 #ifdef DROPBEAR_DSS
@@ -374,7 +383,6 @@
 	if (sigblob->len == 0) {
 		dropbear_exit("non-matching signing type");
 	}
-
 	buf_setpos(sigblob, 0);
 	buf_putstring(buf, buf_getptr(sigblob, sigblob->len),
 			sigblob->len);
--- a/signkey.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/signkey.h	Wed Jul 29 02:58:33 2009 +0000
@@ -29,8 +29,22 @@
 #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;
 #endif
--- a/ssh.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/ssh.h	Wed Jul 29 02:58:33 2009 +0000
@@ -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/svr-agentfwd.c	Sun Jul 26 16:14:50 2009 +0000
+++ b/svr-agentfwd.c	Wed Jul 29 02:58:33 2009 +0000
@@ -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 {
--- a/sysoptions.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/sysoptions.h	Wed Jul 29 02:58:33 2009 +0000
@@ -146,10 +146,6 @@
 #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 +156,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,6 +164,10 @@
 #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
--- a/tcpfwd.h	Sun Jul 26 16:14:50 2009 +0000
+++ b/tcpfwd.h	Wed Jul 29 02:58:33 2009 +0000
@@ -25,6 +25,7 @@
 #define _TCPFWD_H
 
 #include "channel.h"
+#include "list.h"
 
 struct TCPListener {
 
@@ -43,16 +44,13 @@
 	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;
 	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 +68,4 @@
 /* Common */
 int listen_tcpfwd(struct TCPListener* tcpinfo);
 
-
 #endif