changeset 500:d588e3ea557a agent-client

propagate from branch 'au.asn.ucc.matt.dropbear' (head 4fb35083f0f46ea667e7043e7d4314aecd3df46c) to branch 'au.asn.ucc.matt.dropbear.cli-agent' (head 833d0adef6cdbf43ea75283524c665e70b0ee1ee)
author Matt Johnston <matt@ucc.asn.au>
date Tue, 23 Sep 2008 16:05:04 +0000
parents f3ca5ebc319a (diff) 33d5e9fb0f78 (current diff)
children 568638be7203
files auth.h cli-runopts.c cli-session.c dbutil.c dbutil.h options.h runopts.h svr-agentfwd.c
diffstat 42 files changed, 1423 insertions(+), 586 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES	Thu Nov 29 11:38:06 2007 +0000
+++ b/CHANGES	Tue Sep 23 16:05:04 2008 +0000
@@ -1,3 +1,15 @@
+0.51 - Thu 27 March 2008
+
+- Make a copy of password fields rather erroneously relying on getwpnam()
+  to be safe to call multiple times
+
+- If $SSH_ASKPASS_ALWAYS environment variable is set (and $SSH_ASKPASS is
+  as well) always use that program, ignoring isatty() and $DISPLAY
+
+- Wait until a process exits before the server closes a connection, so
+  that an exit code can be sent. This fixes problems with exit codes not
+  being returned, which could cause scp to fail.
+
 0.50 - Wed 8 August 2007
 
 - Add DROPBEAR_PASSWORD environment variable to specify a dbclient password
--- a/Makefile.in	Thu Nov 29 11:38:06 2007 +0000
+++ b/Makefile.in	Tue Sep 23 16:05:04 2008 +0000
@@ -23,7 +23,7 @@
 		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-session.o svr-service.o \
+		svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \
 		svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o\
 		svr-tcpfwd.o svr-authpam.o
 
--- a/auth.h	Thu Nov 29 11:38:06 2007 +0000
+++ b/auth.h	Tue Sep 23 16:05:04 2008 +0000
@@ -26,6 +26,7 @@
 #define _AUTH_H_
 
 #include "includes.h"
+#include "chansession.h"
 
 void svr_authinitialise();
 void cli_authinitialise();
@@ -38,6 +39,25 @@
 void svr_auth_pubkey();
 void svr_auth_pam();
 
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+int svr_pubkey_allows_agentfwd();
+int svr_pubkey_allows_tcpfwd();
+int svr_pubkey_allows_x11fwd();
+int svr_pubkey_allows_pty();
+void svr_pubkey_set_forced_command(struct ChanSess *chansess);
+void svr_pubkey_options_cleanup();
+int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename);
+#else
+/* no option : success */
+#define svr_pubkey_allows_agentfwd() 1
+#define svr_pubkey_allows_tcpfwd() 1
+#define svr_pubkey_allows_x11fwd() 1
+#define svr_pubkey_allows_pty() 1
+static inline void svr_pubkey_set_forced_command(struct ChanSess *chansess) { }
+static inline void svr_pubkey_options_cleanup() { }
+#define svr_add_pubkey_options(x,y,z) DROPBEAR_SUCCESS
+#endif
+
 /* Client functions */
 void recv_msg_userauth_failure();
 void recv_msg_userauth_success();
@@ -91,8 +111,15 @@
 							   logged. */
 
 	/* These are only used for the server */
-	char *printableuser; /* stripped of control chars, used for logs etc */
-	struct passwd * pw;
+	uid_t pw_uid;
+	gid_t pw_gid;
+	char *pw_dir;
+	char *pw_shell;
+	char *pw_name;
+	char *pw_passwd;
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+	struct PubKeyOptions* pubkey_options;
+#endif
 
 };
 
@@ -113,4 +140,18 @@
 
 };
 
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+struct PubKeyOptions;
+struct PubKeyOptions {
+	/* Flags */
+	int no_port_forwarding_flag;
+	int no_agent_forwarding_flag;
+	int no_x11_forwarding_flag;
+	int no_pty_flag;
+	/* "command=" option. */
+	unsigned char * forced_command;
+
+};
+#endif
+
 #endif /* _AUTH_H_ */
--- a/chansession.h	Thu Nov 29 11:38:06 2007 +0000
+++ b/chansession.h	Tue Sep 23 16:05:04 2008 +0000
@@ -78,6 +78,9 @@
 void cli_send_chansess_request();
 void cli_tty_cleanup();
 void cli_chansess_winchange();
+#ifdef ENABLE_CLI_NETCAT
+void cli_send_netcat_request();
+#endif
 
 void svr_chansessinitialise();
 extern const struct ChanType svrchansess;
--- a/cli-authinteract.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/cli-authinteract.c	Tue Sep 23 16:05:04 2008 +0000
@@ -77,6 +77,11 @@
 
 	TRACE(("enter recv_msg_recv_userauth_info_request"))
 
+	/* Let the user know what password/host they are authing for */
+	if (!cli_ses.interact_request_received) {
+		fprintf(stderr, "Login for %s@%s\n", cli_opts.username,
+				cli_opts.remotehost);
+	}
 	cli_ses.interact_request_received = 1;
 
 	name = buf_getstring(ses.payload, NULL);
--- a/cli-chansession.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/cli-chansession.c	Tue Sep 23 16:05:04 2008 +0000
@@ -321,7 +321,11 @@
 	TRACE(("enter send_chansess_shell_req"))
 
 	if (cli_opts.cmd) {
-		reqtype = "exec";
+		if (cli_opts.is_subsystem) {
+			reqtype = "subsystem";
+		} else {
+			reqtype = "exec";
+		}
 	} else {
 		reqtype = "shell";
 	}
@@ -338,9 +342,8 @@
 	TRACE(("leave send_chansess_shell_req"))
 }
 
-static int cli_initchansess(struct Channel *channel) {
-
-
+/* Shared for normal client channel and netcat-alike */
+static int cli_init_stdpipe_sess(struct Channel *channel) {
 	channel->writefd = STDOUT_FILENO;
 	setnonblocking(STDOUT_FILENO);
 
@@ -351,6 +354,12 @@
 	setnonblocking(STDERR_FILENO);
 
 	channel->extrabuf = cbuf_new(opts.recv_window);
+	return 0;
+}
+
+static int cli_initchansess(struct Channel *channel) {
+
+	cli_init_stdpipe_sess(channel);
 
 	if (cli_opts.wantpty) {
 		send_chansess_pty_req(channel);
@@ -363,12 +372,48 @@
 	}
 
 	return 0; /* Success */
+}
 
+#ifdef ENABLE_CLI_NETCAT
+
+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) 
+			== DROPBEAR_FAILURE) {
+		dropbear_exit("Couldn't open initial channel");
+	}
+
+	buf_putstring(ses.writepayload, cli_opts.netcat_host, 
+			strlen(cli_opts.netcat_host));
+	buf_putint(ses.writepayload, cli_opts.netcat_port);
+
+	/* originator ip - localhost is accurate enough */
+	buf_putstring(ses.writepayload, source_host, strlen(source_host));
+	buf_putint(ses.writepayload, source_port);
+
+	encrypt_packet();
+	TRACE(("leave cli_send_chansess_request"))
 }
+#endif
 
 void cli_send_chansess_request() {
 
 	TRACE(("enter cli_send_chansess_request"))
+
 	if (send_msg_channel_open_init(STDIN_FILENO, &clichansess) 
 			== DROPBEAR_FAILURE) {
 		dropbear_exit("Couldn't open initial channel");
@@ -379,3 +424,16 @@
 	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-main.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/cli-main.c	Tue Sep 23 16:05:04 2008 +0000
@@ -32,6 +32,8 @@
 static void cli_dropbear_exit(int exitcode, const char* format, va_list param);
 static void cli_dropbear_log(int priority, const char* format, va_list param);
 
+static void cli_proxy_cmd(int *sock_in, int *sock_out);
+
 #if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
 #if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
 int cli_main(int argc, char ** argv) {
@@ -39,7 +41,7 @@
 int main(int argc, char ** argv) {
 #endif
 
-	int sock;
+	int sock_in, sock_out;
 	char* error = NULL;
 	char* hostandport;
 	int len;
@@ -58,10 +60,18 @@
 		dropbear_exit("signal() error");
 	}
 
-	sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 
-			0, &error);
+#ifdef ENABLE_CLI_PROXYCMD
+	if (cli_opts.proxycmd) {
+		cli_proxy_cmd(&sock_in, &sock_out);
+	} else
+#endif
+	{
+		int sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 
+				0, &error);
+		sock_in = sock_out = sock;
+	}
 
-	if (sock < 0) {
+	if (sock_in < 0) {
 		dropbear_exit("%s", error);
 	}
 
@@ -72,7 +82,7 @@
 	snprintf(hostandport, len, "%s:%s", 
 			cli_opts.remotehost, cli_opts.remoteport);
 
-	cli_session(sock, hostandport);
+	cli_session(sock_in, sock_out, hostandport);
 
 	/* not reached */
 	return -1;
@@ -112,3 +122,25 @@
 	fprintf(stderr, "%s: %s\n", cli_opts.progname, printbuf);
 
 }
+
+static void exec_proxy_cmd(void *user_data_cmd) {
+	const char *cmd = user_data_cmd;
+	char *usershell;
+
+	usershell = m_strdup(get_user_shell());
+	run_shell_command(cmd, ses.maxfd, usershell);
+	dropbear_exit("Failed to run '%s'\n", cmd);
+}
+
+static void cli_proxy_cmd(int *sock_in, int *sock_out) {
+	int ret;
+
+	fill_passwd(cli_opts.own_user);
+
+	ret = spawn_command(exec_proxy_cmd, cli_opts.proxycmd,
+			sock_out, sock_in, NULL, NULL);
+	if (ret == DROPBEAR_FAILURE) {
+		dropbear_exit("Failed running proxy command");
+		*sock_in = *sock_out = -1;
+	}
+}
--- a/cli-runopts.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/cli-runopts.c	Tue Sep 23 16:05:04 2008 +0000
@@ -33,18 +33,23 @@
 cli_runopts cli_opts; /* GLOBAL */
 
 static void printhelp();
-static void parsehostname(char* userhostarg);
+static void parse_hostname(const char* orighostarg);
+static void parse_multihop_hostname(const char* orighostarg, const char* argv0);
+static void fill_own_user();
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 static void loadidentityfile(const char* filename);
 #endif
 #ifdef ENABLE_CLI_ANYTCPFWD
-static void addforward(char* str, struct TCPFwdList** fwdlist);
+static void addforward(const char* str, struct TCPFwdList** fwdlist);
+#endif
+#ifdef ENABLE_CLI_NETCAT
+static void add_netcat(const char *str);
 #endif
 
 static void printhelp() {
 
 	fprintf(stderr, "Dropbear client v%s\n"
-					"Usage: %s [options] [user@]host [command]\n"
+					"Usage: %s [options] [user@]host[/port] [command]\n"
 					"Options are:\n"
 					"-p <remoteport>\n"
 					"-l <username>\n"
@@ -53,6 +58,7 @@
 					"-N    Don't run a remote command\n"
 					"-f    Run in background after auth\n"
 					"-y    Always accept remote host key if unknown\n"
+					"-s    Request a subsystem (use for sftp)\n"
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 					"-i <identityfile>   (multiple allowed)\n"
 #endif
@@ -68,6 +74,12 @@
 #endif
 					"-W <receive_window_buffer> (default %d, larger may be faster, max 1MB)\n"
 					"-K <keepalive>  (0 is never, default %d)\n"
+#ifdef ENABLE_CLI_NETCAT
+					"-B <endhost:endport> Netcat-alike bouncing\n"
+#endif				
+#ifdef ENABLE_CLI_PROXYCMD
+					"-J <proxy_program> Use program rather than tcp connection\n"
+#endif
 #ifdef DEBUG_TRACE
 					"-v    verbose\n"
 #endif
@@ -90,6 +102,9 @@
 #ifdef ENABLE_CLI_REMOTETCPFWD
 	int nextisremote = 0;
 #endif
+#ifdef ENABLE_CLI_NETCAT
+	int nextisnetcat = 0;
+#endif
 	char* dummy = NULL; /* Not used for anything real */
 
 	char* recv_window_arg = NULL;
@@ -105,6 +120,7 @@
 	cli_opts.backgrounded = 0;
 	cli_opts.wantpty = 9; /* 9 means "it hasn't been touched", gets set later */
 	cli_opts.always_accept_key = 0;
+	cli_opts.is_subsystem = 0;
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 	cli_opts.privkeys = NULL;
 #endif
@@ -119,12 +135,17 @@
 	cli_opts.agent_fwd = 0;
 	cli_opts.agent_keys_loaded = 0;
 #endif
+#ifdef ENABLE_CLI_PROXYCMD
+	cli_opts.proxycmd = NULL;
+#endif
 	/* not yet
 	opts.ipv4 = 1;
 	opts.ipv6 = 1;
 	*/
 	opts.recv_window = DEFAULT_RECV_WINDOW;
 
+	fill_own_user();
+
 	/* Iterate all the arguments */
 	for (i = 1; i < (unsigned int)argc; i++) {
 #ifdef ENABLE_CLI_PUBKEY_AUTH
@@ -151,6 +172,14 @@
 			continue;
 		}
 #endif
+#ifdef ENABLE_CLI_NETCAT
+		if (nextisnetcat) {
+			TRACE(("nextisnetcat true"))
+			add_netcat(argv[i]);
+			nextisnetcat = 0;
+			continue;
+		}
+#endif
 		if (next) {
 			/* The previous flag set a value to assign */
 			*next = argv[i];
@@ -193,6 +222,9 @@
 				case 'f':
 					cli_opts.backgrounded = 1;
 					break;
+				case 's':
+					cli_opts.is_subsystem = 1;
+					break;
 #ifdef ENABLE_CLI_LOCALTCPFWD
 				case 'L':
 					nextislocal = 1;
@@ -206,6 +238,16 @@
 					nextisremote = 1;
 					break;
 #endif
+#ifdef ENABLE_CLI_NETCAT
+				case 'B':
+					nextisnetcat = 1;
+					break;
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+				case 'J':
+					next = &cli_opts.proxycmd;
+					break;
+#endif
 				case 'l':
 					next = &cli_opts.username;
 					break;
@@ -266,9 +308,11 @@
 			/* Either the hostname or commands */
 
 			if (cli_opts.remotehost == NULL) {
-
-				parsehostname(argv[i]);
-
+#ifdef ENABLE_CLI_MULTIHOP
+				parse_multihop_hostname(argv[i], argv[0]);
+#else
+				parse_hostname(argv[i]);
+#endif
 			} else {
 
 				/* this is part of the commands to send - after this we
@@ -295,6 +339,8 @@
 		}
 	}
 
+	/* And now a few sanity checks and setup */
+
 	if (cli_opts.remotehost == NULL) {
 		printhelp();
 		exit(EXIT_FAILURE);
@@ -319,21 +365,23 @@
 		dropbear_exit("command required for -f");
 	}
 	
-	if (recv_window_arg)
-	{
+	if (recv_window_arg) {
 		opts.recv_window = atol(recv_window_arg);
-		if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW)
-		{
+		if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) {
 			dropbear_exit("Bad recv window '%s'", recv_window_arg);
 		}
 	}
 	if (keepalive_arg) {
-		opts.keepalive_secs = strtoul(keepalive_arg, NULL, 10);
-		if (opts.keepalive_secs == 0 && errno == EINVAL)
-		{
+		if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
 			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
 		}
 	}
+
+#ifdef ENABLE_CLI_NETCAT
+	if (cli_opts.cmd && cli_opts.netcat_host) {
+		dropbear_log(LOG_INFO, "Ignoring command '%s' in netcat mode", cli_opts.cmd);
+	}
+#endif
 	
 }
 
@@ -363,16 +411,77 @@
 }
 #endif
 
+#ifdef ENABLE_CLI_MULTIHOP
 
-/* Parses a [user@]hostname argument. userhostarg is the argv[i] corresponding
- * - note that it will be modified */
-static void parsehostname(char* orighostarg) {
+/* Sets up 'onion-forwarding' connections. This will spawn
+ * a separate dbclient process for each hop.
+ * As an example, if the cmdline is
+ *   dbclient wrt,madako,canyons
+ * then we want to run:
+ *   dbclient -J "dbclient -B canyons:22 wrt,madako" canyons
+ * and then the inner dbclient will recursively run:
+ *   dbclient -J "dbclient -B madako:22 wrt" madako
+ * etc for as many hosts as we want.
+ *
+ * Ports for hosts can be specified as host/port.
+ */
+static void parse_multihop_hostname(const char* orighostarg, const char* argv0) {
+	char *userhostarg = NULL;
+	char *last_hop = NULL;;
+	char *remainder = NULL;
+
+	/* both scp and rsync parse a user@host argument
+	 * and turn it into "-l user host". This breaks
+	 * for our multihop syntax, so we suture it back together.
+	 * This will break usernames that have both '@' and ',' in them,
+	 * though that should be fairly uncommon. */
+	if (cli_opts.username 
+			&& 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);
+	} else {
+		userhostarg = m_strdup(orighostarg);
+	}
 
-	uid_t uid;
-	struct passwd *pw = NULL; 
+	last_hop = strrchr(userhostarg, ',');
+	if (last_hop) {
+		if (last_hop == userhostarg) {
+			dropbear_exit("Bad multi-hop hostnames");
+		}
+		*last_hop = '\0';
+		last_hop++;
+		remainder = userhostarg;
+		userhostarg = last_hop;
+	}
+
+	parse_hostname(userhostarg);
+
+	if (last_hop) {
+		/* Set up the proxycmd */
+		unsigned int cmd_len = 0;
+		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) 
+			+ strlen(cli_opts.remotehost) + strlen(cli_opts.remoteport)
+			+ strlen(argv0) + 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);
+	}
+}
+#endif /* !ENABLE_CLI_MULTIHOP */
+
+/* Parses a [user@]hostname[/port] argument. */
+static void parse_hostname(const char* orighostarg) {
 	char *userhostarg = NULL;
+	char *port = NULL;
 
-	/* We probably don't want to be editing argvs */
 	userhostarg = m_strdup(orighostarg);
 
 	cli_opts.remotehost = strchr(userhostarg, '@');
@@ -387,14 +496,13 @@
 	}
 
 	if (cli_opts.username == NULL) {
-		uid = getuid();
-		
-		pw = getpwuid(uid);
-		if (pw == NULL || pw->pw_name == NULL) {
-			dropbear_exit("Unknown own user");
-		}
+		cli_opts.username = m_strdup(cli_opts.own_user);
+	}
 
-		cli_opts.username = m_strdup(pw->pw_name);
+	port = strchr(cli_opts.remotehost, '/');
+	if (port) {
+		*port = '\0';
+		cli_opts.remoteport = port+1;
 	}
 
 	if (cli_opts.remotehost[0] == '\0') {
@@ -402,10 +510,61 @@
 	}
 }
 
+#ifdef ENABLE_CLI_NETCAT
+static void add_netcat(const char* origstr) {
+	char *portstr = NULL;
+	
+	char * str = m_strdup(origstr);
+	
+	portstr = strchr(str, ':');
+	if (portstr == NULL) {
+		TRACE(("No netcat port"))
+		goto fail;
+	}
+	*portstr = '\0';
+	portstr++;
+	
+	if (strchr(portstr, ':')) {
+		TRACE(("Multiple netcat colons"))
+		goto fail;
+	}
+	
+	if (m_str_to_uint(portstr, &cli_opts.netcat_port) == DROPBEAR_FAILURE) {
+		TRACE(("bad netcat port"))
+		goto fail;
+	}
+	
+	if (cli_opts.netcat_port > 65535) {
+		TRACE(("too large netcat port"))
+		goto fail;
+	}
+	
+	cli_opts.netcat_host = str;
+	return;
+	
+fail:
+	dropbear_exit("Bad netcat endpoint '%s'", origstr);
+}
+#endif
+
+static void fill_own_user() {
+	uid_t uid;
+	struct passwd *pw = NULL; 
+
+	uid = getuid();
+
+	pw = getpwuid(uid);
+	if (pw == NULL || pw->pw_name == NULL) {
+		dropbear_exit("Unknown own user");
+	}
+
+	cli_opts.own_user = m_strdup(pw->pw_name);
+}
+
 #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(char* origstr, struct TCPFwdList** fwdlist) {
+static void addforward(const char* origstr, struct TCPFwdList** fwdlist) {
 
 	char * listenport = NULL;
 	char * connectport = NULL;
@@ -441,15 +600,13 @@
 
 	/* Now we check the ports - note that the port ints are unsigned,
 	 * the check later only checks for >= MAX_PORT */
-	newfwd->listenport = strtol(listenport, NULL, 10);
-	if (errno != 0) {
-		TRACE(("bad listenport strtol"))
+	if (m_str_to_uint(listenport, &newfwd->listenport) == DROPBEAR_FAILURE) {
+		TRACE(("bad listenport strtoul"))
 		goto fail;
 	}
 
-	newfwd->connectport = strtol(connectport, NULL, 10);
-	if (errno != 0) {
-		TRACE(("bad connectport strtol"))
+	if (m_str_to_uint(connectport, &newfwd->connectport) == DROPBEAR_FAILURE) {
+		TRACE(("bad connectport strtoul"))
 		goto fail;
 	}
 
--- a/cli-session.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/cli-session.c	Tue Sep 23 16:05:04 2008 +0000
@@ -77,13 +77,13 @@
 	NULL /* Null termination */
 };
 
-void cli_session(int sock, char* remotehost) {
+void cli_session(int sock_in, int sock_out, char* remotehost) {
 
 	seedrandom();
 
 	crypto_init();
 
-	common_session_init(sock, remotehost);
+	common_session_init(sock_in, sock_out, remotehost);
 
 	chaninitialise(cli_chantypes);
 
@@ -200,20 +200,6 @@
 			TRACE(("leave cli_sessionloop: cli_auth_try"))
 			return;
 
-			/*
-		case USERAUTH_SUCCESS_RCVD:
-			send_msg_service_request(SSH_SERVICE_CONNECTION);
-			cli_ses.state = SERVICE_CONN_REQ_SENT;
-			TRACE(("leave cli_sessionloop: sent ssh-connection service req"))
-			return;
-
-		case SERVICE_CONN_ACCEPT_RCVD:
-			cli_send_chansess_request();
-			TRACE(("leave cli_sessionloop: cli_send_chansess_request"))
-			cli_ses.state = SESSION_RUNNING;
-			return;
-			*/
-
 		case USERAUTH_SUCCESS_RCVD:
 
 			if (cli_opts.backgrounded) {
@@ -238,7 +224,13 @@
 #ifdef ENABLE_CLI_REMOTETCPFWD
 			setup_remotetcp();
 #endif
-			if (!cli_opts.no_cmd) {
+
+#ifdef ENABLE_CLI_NETCAT
+			if (cli_opts.netcat_host) {
+				cli_send_netcat_request();
+			} else 
+#endif
+				if (!cli_opts.no_cmd) {
 				cli_send_chansess_request();
 			}
 			TRACE(("leave cli_sessionloop: running"))
@@ -297,8 +289,10 @@
 
 	/* XXX TODO perhaps print a friendlier message if we get this but have
 	 * already sent/received disconnect message(s) ??? */
-	close(ses.sock);
-	ses.sock = -1;
+	m_close(ses.sock_in);
+	m_close(ses.sock_out);
+	ses.sock_in = -1;
+	ses.sock_out = -1;
 	dropbear_exit("remote closed the connection");
 }
 
--- a/common-channel.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/common-channel.c	Tue Sep 23 16:05:04 2008 +0000
@@ -261,6 +261,7 @@
 
 /* EOF/close handling */
 static void check_close(struct Channel *channel) {
+	int close_allowed = 0;
 
 	TRACE(("check_close: writefd %d, readfd %d, errfd %d, sent_close %d, recv_close %d",
 				channel->writefd, channel->readfd,
@@ -274,8 +275,17 @@
 	{
 		channel->flushing = 1;
 	}
+	
+	// if a type-specific check_close is defined we will only exit
+	// once that has been triggered. this is only used for a server "session"
+	// 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)) {
+		close_allowed = 1;
+	}
 
-	if (channel->recv_close && !write_pending(channel)) {
+	if (channel->recv_close && !write_pending(channel) && close_allowed) {
 		if (!channel->sent_close) {
 			TRACE(("Sending MSG_CHANNEL_CLOSE in response to same."))
 			send_msg_channel_close(channel);
@@ -312,9 +322,10 @@
 	}
 
 	/* And if we can't receive any more data from them either, close up */
-	if (!channel->sent_close
-			&& channel->readfd == FD_CLOSED
+	if (channel->readfd == FD_CLOSED
 			&& (ERRFD_IS_WRITE(channel) || channel->errfd == FD_CLOSED)
+			&& !channel->sent_close
+			&& close_allowed
 			&& !write_pending(channel)) {
 		TRACE(("sending close, readfd is closed"))
 		send_msg_channel_close(channel);
@@ -561,6 +572,11 @@
 	
 	channel = getchannel();
 
+	if (channel->sent_close) {
+		TRACE(("leave recv_msg_channel_request: already closed channel"))
+		return;
+	}
+
 	if (channel->type->reqhandler) {
 		channel->type->reqhandler(channel);
 	} else {
--- a/common-session.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/common-session.c	Tue Sep 23 16:05:04 2008 +0000
@@ -52,14 +52,15 @@
 
 
 /* called only at the start of a session, set up initial state */
-void common_session_init(int sock, char* remotehost) {
+void common_session_init(int sock_in, int sock_out, char* remotehost) {
 
 	TRACE(("enter session_init"))
 
 	ses.remotehost = remotehost;
 
-	ses.sock = sock;
-	ses.maxfd = sock;
+	ses.sock_in = sock_in;
+	ses.sock_out = sock_out;
+	ses.maxfd = MAX(sock_in, sock_out);
 
 	ses.connect_time = 0;
 	ses.last_packet_time = 0;
@@ -137,11 +138,11 @@
 		FD_ZERO(&writefd);
 		FD_ZERO(&readfd);
 		dropbear_assert(ses.payload == NULL);
-		if (ses.sock != -1) {
-			FD_SET(ses.sock, &readfd);
-			if (!isempty(&ses.writequeue)) {
-				FD_SET(ses.sock, &writefd);
-			}
+		if (ses.sock_in != -1) {
+			FD_SET(ses.sock_in, &readfd);
+		}
+		if (ses.sock_out != -1 && !isempty(&ses.writequeue)) {
+			FD_SET(ses.sock_out, &writefd);
 		}
 		
 		/* We get woken up when signal handlers write to this pipe.
@@ -183,12 +184,14 @@
 		checktimeouts();
 
 		/* process session socket's incoming/outgoing data */
-		if (ses.sock != -1) {
-			if (FD_ISSET(ses.sock, &writefd) && !isempty(&ses.writequeue)) {
+		if (ses.sock_out != -1) {
+			if (FD_ISSET(ses.sock_out, &writefd) && !isempty(&ses.writequeue)) {
 				write_packet();
 			}
+		}
 
-			if (FD_ISSET(ses.sock, &readfd)) {
+		if (ses.sock_in != -1) {
+			if (FD_ISSET(ses.sock_in, &readfd)) {
 				read_packet();
 			}
 			
@@ -248,14 +251,14 @@
 	int i;
 
 	/* write our version string, this blocks */
-	if (atomicio(write, ses.sock, LOCAL_IDENT "\r\n",
+	if (atomicio(write, ses.sock_out, LOCAL_IDENT "\r\n",
 				strlen(LOCAL_IDENT "\r\n")) == DROPBEAR_FAILURE) {
 		ses.remoteclosed();
 	}
 
     /* If they send more than 50 lines, something is wrong */
 	for (i = 0; i < 50; i++) {
-		len = ident_readln(ses.sock, linebuf, sizeof(linebuf));
+		len = ident_readln(ses.sock_in, linebuf, sizeof(linebuf));
 
 		if (len < 0 && errno != EINTR) {
 			/* It failed */
@@ -411,3 +414,35 @@
 		ret = MIN(opts.keepalive_secs, ret);
 	return ret;
 }
+
+const char* get_user_shell() {
+	/* an empty shell should be interpreted as "/bin/sh" */
+	if (ses.authstate.pw_shell[0] == '\0') {
+		return "/bin/sh";
+	} else {
+		return ses.authstate.pw_shell;
+	}
+}
+void fill_passwd(const char* username) {
+	struct passwd *pw = NULL;
+	if (ses.authstate.pw_name)
+		m_free(ses.authstate.pw_name);
+	if (ses.authstate.pw_dir)
+		m_free(ses.authstate.pw_dir);
+	if (ses.authstate.pw_shell)
+		m_free(ses.authstate.pw_shell);
+	if (ses.authstate.pw_passwd)
+		m_free(ses.authstate.pw_passwd);
+
+	pw = getpwnam(username);
+	if (!pw) {
+		return;
+	}
+	ses.authstate.pw_uid = pw->pw_uid;
+	ses.authstate.pw_gid = pw->pw_gid;
+	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);
+}
+
--- a/dbclient.1	Thu Nov 29 11:38:06 2007 +0000
+++ b/dbclient.1	Tue Sep 23 16:05:04 2008 +0000
@@ -86,6 +86,19 @@
 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.
+.SH ENVIRONMENT
+.TP
+.B SSH_ASKPASS
+dbclient can use an external program to request a password from a user.
+SSH_ASKPASS should be set to the path of a program that will return a password
+on standard output. This program will only be used if either DISPLAY is set and
+standard input is not a TTY, or the environment variable SSH_ASKPASS_ALWAYS is
+set.
+.TP
+.B DROPBEAR_PASSWORD
+A password to use for remote authentication can be specified in the environment
+variable DROPBEAR_PASSWORD. Care should be taken that the password is not
+exposed to other users on a multi-user system, or stored in accessible files.
 .SH AUTHOR
 Matt Johnston ([email protected]).
 .br
--- a/dbutil.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/dbutil.c	Tue Sep 23 16:05:04 2008 +0000
@@ -146,7 +146,7 @@
 	}
 
 	va_start(param, format);
-	fprintf(stderr, "TRACE: ");
+	fprintf(stderr, "TRACE (%d): ", getpid());
 	vfprintf(stderr, format, param);
 	fprintf(stderr, "\n");
 	va_end(param);
@@ -338,9 +338,10 @@
 	if (err) {
 		if (errstring != NULL && *errstring == NULL) {
 			int len;
-			len = 20 + strlen(gai_strerror(err));
+			len = 100 + strlen(gai_strerror(err));
 			*errstring = (char*)m_malloc(len);
-			snprintf(*errstring, len, "Error resolving: %s", gai_strerror(err));
+			snprintf(*errstring, len, "Error resolving '%s' port '%s'. %s", 
+					remotehost, remoteport, gai_strerror(err));
 		}
 		TRACE(("Error resolving: %s", gai_strerror(err)))
 		return -1;
@@ -398,6 +399,141 @@
 	return sock;
 }
 
+/* Sets up a pipe for a, returning three non-blocking file descriptors
+ * and the pid. exec_fn is the function that will actually execute the child process,
+ * it will be run after the child has fork()ed, and is passed exec_data.
+ * If ret_errfd == NULL then stderr will not be captured.
+ * ret_pid can be passed as  NULL to discard the pid. */
+int spawn_command(void(*exec_fn)(void *user_data), void *exec_data,
+		int *ret_writefd, int *ret_readfd, int *ret_errfd, pid_t *ret_pid) {
+	int infds[2];
+	int outfds[2];
+	int errfds[2];
+	pid_t pid;
+
+	const int FDIN = 0;
+	const int FDOUT = 1;
+
+	/* redirect stdin/stdout/stderr */
+	if (pipe(infds) != 0) {
+		return DROPBEAR_FAILURE;
+	}
+	if (pipe(outfds) != 0) {
+		return DROPBEAR_FAILURE;
+	}
+	if (ret_errfd && pipe(errfds) != 0) {
+		return DROPBEAR_FAILURE;
+	}
+
+#ifdef __uClinux__
+	pid = vfork();
+#else
+	pid = fork();
+#endif
+
+	if (pid < 0) {
+		return DROPBEAR_FAILURE;
+	}
+
+	if (!pid) {
+		/* child */
+
+		TRACE(("back to normal sigchld"))
+		/* Revert to normal sigchld handling */
+		if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
+			dropbear_exit("signal() error");
+		}
+
+		/* redirect stdin/stdout */
+
+		if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
+			(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");
+		}
+
+		close(infds[FDOUT]);
+		close(infds[FDIN]);
+		close(outfds[FDIN]);
+		close(outfds[FDOUT]);
+		if (ret_errfd)
+		{
+			close(errfds[FDIN]);
+			close(errfds[FDOUT]);
+		}
+
+		exec_fn(exec_data);
+		/* not reached */
+		return DROPBEAR_FAILURE;
+	} else {
+		/* parent */
+		close(infds[FDIN]);
+		close(outfds[FDOUT]);
+
+		setnonblocking(outfds[FDIN]);
+		setnonblocking(infds[FDOUT]);
+
+		if (ret_errfd) {
+			close(errfds[FDOUT]);
+			setnonblocking(errfds[FDIN]);
+		}
+
+		if (ret_pid) {
+			*ret_pid = pid;
+		}
+
+		*ret_writefd = infds[FDOUT];
+		*ret_readfd = outfds[FDIN];
+		if (ret_errfd) {
+			*ret_errfd = errfds[FDIN];
+		}
+		return DROPBEAR_SUCCESS;
+	}
+}
+
+/* Runs a command with "sh -c". Will close FDs (except stdin/stdout/stderr) and
+ * re-enabled SIGPIPE. If cmd is NULL, will run a login shell.
+ */
+void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
+	char * argv[4];
+	char * baseshell = NULL;
+	unsigned int i;
+
+	baseshell = basename(usershell);
+
+	if (cmd != NULL) {
+		argv[0] = baseshell;
+	} else {
+		/* a login shell should be "-bash" for "/bin/bash" etc */
+		int len = strlen(baseshell) + 2; /* 2 for "-" */
+		argv[0] = (char*)m_malloc(len);
+		snprintf(argv[0], len, "-%s", baseshell);
+	}
+
+	if (cmd != NULL) {
+		argv[1] = "-c";
+		argv[2] = (char*)cmd;
+		argv[3] = NULL;
+	} else {
+		/* construct a shell of the form "-bash" etc */
+		argv[1] = NULL;
+	}
+
+	/* Re-enable SIGPIPE for the executed process */
+	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
+		dropbear_exit("signal() error");
+	}
+
+	/* close file descriptors except stdin/stdout/stderr
+	 * Need to be sure FDs are closed here to avoid reading files as root */
+	for (i = 3; i <= maxfd; i++) {
+		m_close(i);
+	}
+
+	execv(usershell, argv);
+}
+
 /* 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) {
@@ -708,3 +844,17 @@
 	lim.rlim_cur = lim.rlim_max = 0;
 	setrlimit(RLIMIT_CORE, &lim);
 }
+
+/* Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE, with the result in *val */
+int m_str_to_uint(const char* str, unsigned int *val) {
+	errno = 0;
+	*val = strtoul(str, NULL, 10);
+	/* The c99 spec doesn't actually seem to define EINVAL, but most platforms
+	 * I've looked at mention it in their manpage */
+	if ((*val == 0 && errno == EINVAL)
+		|| (*val == ULONG_MAX && errno == ERANGE)) {
+		return DROPBEAR_FAILURE;
+	} else {
+		return DROPBEAR_SUCCESS;
+	}
+}
--- a/dbutil.h	Thu Nov 29 11:38:06 2007 +0000
+++ b/dbutil.h	Tue Sep 23 16:05:04 2008 +0000
@@ -49,6 +49,9 @@
 unsigned char * getaddrstring(struct sockaddr_storage* addr, int withport);
 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
@@ -67,6 +70,7 @@
 void m_burn(void* data, unsigned int len);
 void setnonblocking(int fd);
 void disallow_core();
+int m_str_to_uint(const char* str, unsigned int *val);
 
 /* Used to force mp_ints to be initialised */
 #define DEF_MP_INT(X) mp_int X = {0, 0, 0, NULL}
--- a/debian/README.runit	Thu Nov 29 11:38:06 2007 +0000
+++ b/debian/README.runit	Tue Sep 23 16:05:04 2008 +0000
@@ -31,16 +31,16 @@
 
  # vi /etc/dropbear/run
 
-Finally enable the service by linking dropbear's service directory to
-/var/service/.  The service will be started within five seconds, and
-automatically at boot time.  The sysv init script is disabled; see the
-runsvctrl(8) program for information on how to control services handled by
-runit.  See the svlogd(8) program on how to configure the log service.
+Finally enable the service through runit's update-service(8) program, the
+service will be started within five seconds, and automatically at boot
+time, and the sysv init script will automatically be disabled; see the
+sv(8) program for information on how to control services handled by runit.
+See the svlogd(8) program on how to configure the log service.
 
- # ln -s /etc/dropbear /var/service/
+ # update-service --add /etc/dropbear
 
 Optionally check the status of the service a few seconds later
 
- # runsvstat -l /var/service/dropbear
+ # sv status dropbear
 
- -- Gerrit Pape <[email protected]>, Sun, 16 May 2004 15:52:34 +0000
+ -- Gerrit Pape <[email protected]>, Fri, 02 Mar 2007 20:41:08 +0000
--- a/debian/changelog	Thu Nov 29 11:38:06 2007 +0000
+++ b/debian/changelog	Tue Sep 23 16:05:04 2008 +0000
@@ -1,14 +1,75 @@
-dropbear (0.50-0.1) unstable; urgency=low
+dropbear (0.51-0.1) unstable; urgency=low
 
   * New upstream release.
 
- -- Matt Johnston <[email protected]>  Wed, 8 Aug 2007 11:22:33 +0800
+ -- Matt Johnston <[email protected]>  Thu, 27 Mar 2008 19:14:00 +0900
+
+dropbear (0.50-4) unstable; urgency=low
+
+  * debian/dropbear.init: apply patch from Petter Reinholdtsen: add LSB
+    formatted dependency info in init.d script (closes: #466257).
+  * debian/rules: no longer include symlinks for ./supervise/ subdirectories.
+  * debian/dropbear.postinst: upgrade from << 0.50-4: if dropbear is managed
+    by runit, remove service, and re-add using update-service(8).
+  * debian/control: Standards-Version: 3.7.3.0.
+  * debian/rules: target clean: don't ignore errors but check for readable
+    ./Makefile.
+
+ -- Gerrit Pape <[email protected]>  Thu, 06 Mar 2008 19:06:58 +0000
+
+dropbear (0.50-3) unstable; urgency=low
 
-dropbear (0.49-0.1) unstable; urgency=low
+  * debian/dropbear.init: use the update-service(8) program from the runit
+    package instead of directly checking for the symlink in /var/service/.
+  * debian/README.runit: talk about update-service(8) instead of symlinks
+    in /var/service/.
+
+ -- Gerrit Pape <[email protected]>  Fri, 15 Feb 2008 00:32:37 +0000
+
+dropbear (0.50-2) unstable; urgency=low
+
+  * debian/dropbear.README.Debian: no longer talk about entropy from
+    /dev/random, /dev/urandom is now used by default (thx Joey Hess,
+    closes: #441515).
+
+ -- Gerrit Pape <[email protected]>  Mon, 24 Sep 2007 16:49:17 +0000
+
+dropbear (0.50-1) unstable; urgency=low
 
-  * New upstream release.
+  * debian/README.runit: minor.
+  * new upstream version.
+  * debian/diff/0001-options.h-use-dev-urandom-instead-of-dev-random-a.diff:
+    remove; fixed upstream.
+
+ -- Gerrit Pape <[email protected]>  Thu, 09 Aug 2007 23:01:01 +0000
+
+dropbear (0.49-2) unstable; urgency=low
+
+  * debian/rules: apply diffs from debian/diff/ with patch -p1 instead of
+    -p0.
+  * debian/diff/0001-options.h-use-dev-urandom-instead-of-dev-random-a.diff:
+    new; options.h: use /dev/urandom instead of /dev/random as
+    DROPBEAR_RANDOM_DEV (closes: #386976).
+  * debian/rules: target clean: remove libtomcrypt/Makefile,
+    libtommath/Makefile.
 
- -- Matt Johnston <[email protected]>  Fri, 23 Feb 2007 00:44:00 +0900
+ -- Gerrit Pape <[email protected]>  Sat, 09 Jun 2007 08:59:59 +0000
+
+dropbear (0.49-1) unstable; urgency=high
+
+  * new upstream release, fixes
+    * CVE-2007-1099: dropbear dbclient insufficient warning on hostkey
+      mismatch (closes: #412899).
+    * dbclient uses static "Password:" prompt instead of using the server's
+      prompt (closes: #394996).
+  * debian/control: Suggests: openssh-client, not ssh (closes: #405686);
+    Standards-Version: 3.7.2.2.
+  * debian/README.Debian: ssh -> openssh-server, openssh-client; remove
+    'Replacing OpenSSH "sshd" with Dropbear' part, this is simply done by not
+    installing the openssh-server package.
+  * debian/README.runit: runsvstat -> sv status.
+
+ -- Gerrit Pape <[email protected]>  Fri,  2 Mar 2007 20:48:18 +0000
 
 dropbear (0.48.1-1) unstable; urgency=medium
 
--- a/debian/control	Thu Nov 29 11:38:06 2007 +0000
+++ b/debian/control	Tue Sep 23 16:05:04 2008 +0000
@@ -3,12 +3,12 @@
 Priority: optional
 Maintainer: Gerrit Pape <[email protected]>
 Build-Depends: libz-dev
-Standards-Version: 3.6.2.1
+Standards-Version: 3.7.3.0
 
 Package: dropbear
 Architecture: any
 Depends: ${shlibs:Depends}
-Suggests: ssh, runit
+Suggests: openssh-client, runit
 Description: lightweight SSH2 server and client
  dropbear is a SSH 2 server and client designed to be small enough to
  be used in small memory environments, while still being functional and
--- a/debian/dropbear.README.Debian	Thu Nov 29 11:38:06 2007 +0000
+++ b/debian/dropbear.README.Debian	Tue Sep 23 16:05:04 2008 +0000
@@ -1,52 +1,19 @@
 Dropbear for Debian
 -------------------
 
-This package will attempt to listen on port 22. If the OpenSSH 
-package ("ssh") is installed, the file /etc/default/dropbear 
-will be set up so that the server does not start by default.
-
-You can run Dropbear concurrently with OpenSSH 'sshd' by 
-modifying /etc/default/dropbear so that "NO_START" is set to 
-"0" and changing the port number that Dropbear runs on. Follow 
-the instructions in the file.
-
-This package suggests you install the "ssh" package. This package 
-provides the "ssh" client program, as well as the "/usr/bin/scp" 
-binary you will need to be able to retrieve files from a server 
-running Dropbear via SCP.
-
-Replacing OpenSSH "sshd" with Dropbear
---------------------------------------
+This package will attempt to setup the Dropbear ssh server to listen on
+port 22.  If the OpenSSH server package ("openssh-server") is installed,
+the file /etc/default/dropbear will be set up so that the server does not
+start by default.
 
-You will still want to have the "ssh" package installed, as it 
-provides the "ssh" and "scp" binaries. When you install this 
-package, it checks for existing OpenSSH host keys and if found, 
-converts them to the Dropbear format.
-
-If this appears to have worked, you should be able to change over 
-by following these steps:
+You can run Dropbear concurrently with OpenSSH 'sshd' by modifying
+/etc/default/dropbear so that "NO_START" is set to "0", and changing the
+port number that Dropbear runs on.  Follow the instructions in the file.
 
-1. Stop the OpenSSH server
-   % /etc/init.d/ssh stop
-2. Prevent the OpenSSH server from starting in the future
-   % touch /etc/ssh/sshd_not_to_be_run
-3. Modify the Dropbear defaults file, set NO_START to 0 and 
-   ensure DROPBEAR_PORT is set to 22.
-   % editor /etc/default/dropbear
-4. Restart the Dropbear server.
-   % /etc/init.d/dropbear restart
+This package suggests you install the "openssh-client" package, which
+provides the "ssh" client program, as well as the "/usr/bin/scp" binary
+you will need to be able to retrieve files via SCP from a server running
+Dropbear.
 
 See the Dropbear homepage for more information:
   http://matt.ucc.asn.au/dropbear/dropbear.html
-
-
-Entropy from /dev/random
-------------------------
-
-The dropbear binary package is configured at compile time to read
-entropy from /dev/random. If /dev/random on a system blocks when
-reading data from it, client logins may be delayed until the client
-times out. The dropbear server writes a notice to the logs when it
-sees /dev/random blocking.  A workaround for such systems is to
-re-compile the package with DROPBEAR_RANDOM_DEV set to /dev/urandom
-in options.h.
--- a/debian/dropbear.init	Thu Nov 29 11:38:06 2007 +0000
+++ b/debian/dropbear.init	Tue Sep 23 16:05:04 2008 +0000
@@ -1,4 +1,11 @@
 #!/bin/sh
+### BEGIN INIT INFO
+# Provides:          dropbear
+# Required-Start:    $remote_fs $syslog
+# Required-Stop:     $remote_fs $syslog
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+### END INIT INFO
 #
 # Do not configure this file. Edit /etc/default/dropbear instead!
 #
@@ -17,8 +24,8 @@
 cancel() { echo "$1" >&2; exit 0; };
 test ! -r /etc/default/dropbear || . /etc/default/dropbear
 test -x "$DAEMON" || cancel "$DAEMON does not exist or is not executable."
-test ! -h /var/service/dropbear || \
-  cancel '/var/service/dropbear exists, service is controlled through runit.'
+test ! -x /usr/sbin/update-service || ! update-service --check dropbear ||
+  cancel 'The dropbear service is controlled through runit, use the sv(8) program'
 
 test -z "$DROPBEAR_BANNER" || \
   DROPBEAR_EXTRA_ARGS="$DROPBEAR_EXTRA_ARGS -b $DROPBEAR_BANNER"
--- a/debian/dropbear.postinst	Thu Nov 29 11:38:06 2007 +0000
+++ b/debian/dropbear.postinst	Tue Sep 23 16:05:04 2008 +0000
@@ -69,3 +69,11 @@
     /etc/init.d/dropbear restart
   fi
 fi
+
+if test -n "$2" && dpkg --compare-versions "$2" lt '0.50-4' &&
+update-service --check dropbear; then
+  update-service --remove /etc/dropbear 2>/dev/null || :
+  sleep 6
+  rm -rf /var/run/dropbear /var/run/dropbear.log
+  update-service --add /etc/dropbear || :
+fi
--- a/debian/rules	Thu Nov 29 11:38:06 2007 +0000
+++ b/debian/rules	Tue Sep 23 16:05:04 2008 +0000
@@ -28,7 +28,7 @@
 patch: deb-checkdir patch-stamp
 patch-stamp:
 	for i in `ls -1 debian/diff/*.diff || :`; do \
-	  patch -p0 <$$i || exit 1; \
+	  patch -p1 <$$i || exit 1; \
 	done
 	touch patch-stamp
 
@@ -46,10 +46,11 @@
 	touch build-stamp
 
 clean: deb-checkdir deb-checkuid
-	-$(MAKE) distclean
+	test ! -r Makefile || $(MAKE) distclean
+	rm -f libtomcrypt/Makefile libtommath/Makefile
 	test ! -e patch-stamp || \
 	  for i in `ls -1r debian/diff/*.diff || :`; do \
-	    patch -p0 -R <$$i; \
+	    patch -p1 -R <$$i; \
 	  done
 	rm -f patch-stamp build-stamp config.log config.status
 	rm -rf '$(DIR)'
@@ -76,8 +77,6 @@
 	install -d -m0755 '$(DIR)'/etc/dropbear/log
 	install -m0755 debian/service/log '$(DIR)'/etc/dropbear/log/run
 	ln -s /var/log/dropbear '$(DIR)'/etc/dropbear/log/main
-	ln -s /var/run/dropbear '$(DIR)'/etc/dropbear/supervise
-	ln -s /var/run/dropbear.log '$(DIR)'/etc/dropbear/log/supervise
 	# man pages
 	install -d -m0755 '$(DIR)'/usr/share/man/man8
 	for i in dropbear.8 dropbearkey.8; do \
--- a/debug.h	Thu Nov 29 11:38:06 2007 +0000
+++ b/debug.h	Tue Sep 23 16:05:04 2008 +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
@@ -67,6 +67,11 @@
 #define TRACE(X)
 #endif /*DEBUG_TRACE*/
 
+/* To debug with GDB it is easier to run with no forking of child processes.
+   You will need to pass "-F" as well. */
+/* #define DEBUG_NOFORK */
+
+
 /* For testing as non-root on shadowed systems, include the crypt of a password
  * here. You can then log in as any user with this password. Ensure that you
  * make your own password, and are careful about using this. This will also
--- a/genrsa.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/genrsa.c	Tue Sep 23 16:05:04 2008 +0000
@@ -62,17 +62,13 @@
 		exit(1);
 	}
 
-	/* PuTTY doesn't like it if the modulus isn't a multiple of 8 bits,
-	 * so we just generate them until we get one which is OK */
 	getrsaprime(key->p, &pminus, key->e, size/2);
-	do {
-		getrsaprime(key->q, &qminus, key->e, size/2);
+	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");
-			exit(1);
-		}
-	} while (mp_count_bits(key->n) % 8 != 0);
+	if (mp_mul(key->p, key->q, key->n) != MP_OKAY) {
+		fprintf(stderr, "rsa generation failed\n");
+		exit(1);
+	}
 
 	/* lcm(p-1, q-1) */
 	if (mp_lcm(&pminus, &qminus, &lcm) != MP_OKAY) {
--- a/keyimport.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/keyimport.c	Tue Sep 23 16:05:04 2008 +0000
@@ -701,7 +701,6 @@
 	int nnumbers = -1, pos, len, seqlen, i;
 	char *header = NULL, *footer = NULL;
 	char zero[1];
-	unsigned char iv[8];
 	int ret = 0;
 	FILE *fp;
 	int keytype = -1;
--- a/options.h	Thu Nov 29 11:38:06 2007 +0000
+++ b/options.h	Tue Sep 23 16:05:04 2008 +0000
@@ -60,6 +60,10 @@
 #define ENABLE_CLI_LOCALTCPFWD
 #define ENABLE_CLI_REMOTETCPFWD
 
+/* Allow using -J <proxycommand> to run the connection through a 
+   pipe to a program, rather the normal TCP connection */
+#define ENABLE_CLI_PROXYCMD
+
 #define ENABLE_SVR_LOCALTCPFWD
 #define ENABLE_SVR_REMOTETCPFWD
 
@@ -67,6 +71,10 @@
 #define ENABLE_SVR_AGENTFWD
 #define ENABLE_CLI_AGENTFWD
 
+/* Enable "Netcat mode". TODO describe here. */
+#define ENABLE_CLI_NETCAT
+
+
 /* Encryption - at least one required.
  * RFC Draft requires 3DES and recommends AES128 for interoperability.
  * Including multiple keysize variants the same cipher 
@@ -134,9 +142,15 @@
  * You can't enable both PASSWORD and PAM. */
 
 #define ENABLE_SVR_PASSWORD_AUTH
-/*#define ENABLE_SVR_PAM_AUTH */ /* requires ./configure --enable-pam */
+/* PAM requires ./configure --enable-pam */
+/* #define ENABLE_SVR_PAM_AUTH */
 #define ENABLE_SVR_PUBKEY_AUTH
 
+/* Wether to ake public key options in authorized_keys file into account */
+#ifdef ENABLE_SVR_PUBKEY_AUTH
+#define ENABLE_SVR_PUBKEY_OPTIONS
+#endif
+
 #define ENABLE_CLI_PASSWORD_AUTH
 #define ENABLE_CLI_PUBKEY_AUTH
 #define ENABLE_CLI_INTERACT_AUTH
@@ -236,209 +250,8 @@
 be overridden at runtime with -K. 0 disables keepalives */
 #define DEFAULT_KEEPALIVE 0
 
-/*******************************************************************
- * You shouldn't edit below here unless you know you need to.
- *******************************************************************/
-
-#ifndef DROPBEAR_VERSION
-#define DROPBEAR_VERSION "0.50"
-#endif
-
-#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
-#define PROGNAME "dropbear"
-
-/* Spec recommends after one hour or 1 gigabyte of data. One hour
- * is a bit too verbose, so we try 8 hours */
-#ifndef KEX_REKEY_TIMEOUT
-#define KEX_REKEY_TIMEOUT (3600 * 8)
-#endif
-#ifndef KEX_REKEY_DATA
-#define KEX_REKEY_DATA (1<<30) /* 2^30 == 1GB, this value must be < INT_MAX */
-#endif
-/* Close connections to clients which haven't authorised after AUTH_TIMEOUT */
-#ifndef AUTH_TIMEOUT
-#define AUTH_TIMEOUT 300 /* we choose 5 minutes */
-#endif
-
-/* Minimum key sizes for DSS and RSA */
-#ifndef MIN_DSS_KEYLEN
-#define MIN_DSS_KEYLEN 512
-#endif
-#ifndef MIN_RSA_KEYLEN
-#define MIN_RSA_KEYLEN 512
-#endif
-
-#define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */
-#define MAX_BANNER_LINES 20 /* How many lines the client will display */
-
-/* the number of NAME=VALUE pairs to malloc for environ, if we don't have
- * the clearenv() function */
-#define ENV_SIZE 100
-
-#define MAX_CMD_LEN 1024 /* max length of a command */
-#define MAX_TERM_LEN 200 /* max length of TERM name */
-
-#define MAX_HOST_LEN 254 /* max hostname len for tcp fwding */
-#define MAX_IP_LEN 15 /* strlen("255.255.255.255") == 15 */
-
-#define DROPBEAR_MAX_PORTS 10 /* max number of ports which can be specified,
-								 ipv4 and ipv6 don't count twice */
-
-/* Each port might have at least a v4 and a v6 address */
-#define MAX_LISTEN_ADDR (DROPBEAR_MAX_PORTS*3)
-
-#define _PATH_TTY "/dev/tty"
-
-#define _PATH_CP "/bin/cp"
-
-/* success/failure defines */
-#define DROPBEAR_SUCCESS 0
-#define DROPBEAR_FAILURE -1
-
-/* various algorithm identifiers */
-#define DROPBEAR_KEX_DH_GROUP1 0
-
-#define DROPBEAR_SIGNKEY_ANY 0
-#define DROPBEAR_SIGNKEY_RSA 1
-#define DROPBEAR_SIGNKEY_DSS 2
-#define DROPBEAR_SIGNKEY_NONE 3
-
-#define DROPBEAR_COMP_NONE 0
-#define DROPBEAR_COMP_ZLIB 1
-
-/* Required for pubkey auth */
-#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
-#define DROPBEAR_SIGNKEY_VERIFY
-#endif
-
-/* SHA1 is 20 bytes == 160 bits */
-#define SHA1_HASH_SIZE 20
-/* SHA512 is 64 bytes == 512 bits */
-#define SHA512_HASH_SIZE 64
-/* MD5 is 16 bytes = 128 bits */
-#define MD5_HASH_SIZE 16
-
-/* largest of MD5 and SHA1 */
-#define MAX_MAC_LEN SHA1_HASH_SIZE
-
-
-#define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */
-#define MAX_IV_LEN 20 /* must be same as max blocksize, 
-						 and >= SHA1_HASH_SIZE */
-#define MAX_MAC_KEY 20
-
-#define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't
-						   explicitly specified for all protocols (just
-						   for algos) but seems valid */
-
-#define MAX_PROPOSED_ALGO 20
-
-/* size/count limits */
-#define MIN_PACKET_LEN 16
-
-#define RECV_MAX_PACKET_LEN (MAX(35000, ((RECV_MAX_PAYLOAD_LEN)+100)))
-
-/* for channel code */
-#define TRANS_MAX_WINDOW 500000000 /* 500MB is sufficient, stopping overflow */
-#define TRANS_MAX_WIN_INCR 500000000 /* overflow prevention */
-
-#define RECV_WINDOWEXTEND (opts.recv_window / 3) /* We send a "window extend" every
-								RECV_WINDOWEXTEND bytes */
-#define MAX_RECV_WINDOW (1024*1024) /* 1 MB should be enough */
-
-#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11
-							connection, so can't be _too_ small */
-
-#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
-							   is the max length for a password etc */
-
-/* For a 4096 bit DSS key, empirically determined */
-#define MAX_PUBKEY_SIZE 1700
-/* For a 4096 bit DSS key, empirically determined */
-#define MAX_PRIVKEY_SIZE 1700
-
-/* The maximum size of the bignum portion of the kexhash buffer */
-/* Sect. 8 of the transport draft, K_S + e + f + K */
-#define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130)
-
-#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit
-								in a few years time.... */
-
-#define DROPBEAR_MAX_CLI_PASS 1024
-
-#define DROPBEAR_MAX_CLI_INTERACT_PROMPTS 80 /* The number of prompts we'll 
-												accept for keyb-interactive
-												auth */
-
-#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
-#define DROPBEAR_AES_CBC
-#endif
-
-#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
-#define DROPBEAR_TWOFISH_CBC
-#endif
-
-#ifndef ENABLE_X11FWD
-#define DISABLE_X11FWD
-#endif
-
-#ifndef ENABLE_AGENTFWD
-#define DISABLE_AGENTFWD
-#endif
-
-#if defined(DROPBEAR_PRNGD_SOCKET) || defined(ENABLE_CLI_AGENTFWD)
-#define ENABLE_CONNECT_UNIX
-#endif
-
-#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
-#define ENABLE_CLI_ANYTCPFWD 
-#endif
-
-#if defined(ENABLE_CLI_LOCALTCPFWD) || defined(ENABLE_SVR_REMOTETCPFWD)
-#define DROPBEAR_TCP_ACCEPT
-#endif
-
-#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
-	defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
-	defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
-#define USING_LISTENERS
-#endif
-
-#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
-#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
-#endif
-
-#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
-
-#if defined(DROPBEAR_RANDOM_DEV) && defined(DROPBEAR_PRNGD_SOCKET)
-#error "You can't turn on DROPBEAR_PRNGD_SOCKET and DROPBEAR_RANDOM_DEV at once"
-#endif
-
-#if !defined(DROPBEAR_RANDOM_DEV) && !defined(DROPBEAR_PRNGD_SOCKET)
-#error "You must choose one of DROPBEAR_PRNGD_SOCKET or DROPBEAR_RANDOM_DEV in options.h"
-#endif
-
-/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant
- * code, if we're just compiling as client or server */
-#if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT)
-
-#define IS_DROPBEAR_SERVER (ses.isserver == 1)
-#define IS_DROPBEAR_CLIENT (ses.isserver == 0)
-
-#elif defined(DROPBEAR_SERVER)
-
-#define IS_DROPBEAR_SERVER 1
-#define IS_DROPBEAR_CLIENT 0
-
-#elif defined(DROPBEAR_CLIENT)
-
-#define IS_DROPBEAR_SERVER 0
-#define IS_DROPBEAR_CLIENT 1
-
-#else
-#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
-#endif
+/* Some other defines (that mostly should be left alone) are defined
+ * in sysoptions.h */
+#include "sysoptions.h"
 
 #endif /* _OPTIONS_H_ */
--- a/packet.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/packet.c	Tue Sep 23 16:05:04 2008 +0000
@@ -61,7 +61,7 @@
 	len = writebuf->len - writebuf->pos;
 	dropbear_assert(len > 0);
 	/* Try to write as much as possible */
-	written = write(ses.sock, buf_getptr(writebuf, len), len);
+	written = write(ses.sock_out, buf_getptr(writebuf, len), len);
 
 	if (written < 0) {
 		if (errno == EINTR) {
@@ -122,7 +122,7 @@
 	 * mightn't be any available (EAGAIN) */
 	dropbear_assert(ses.readbuf != NULL);
 	maxlen = ses.readbuf->len - ses.readbuf->pos;
-	len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);
+	len = read(ses.sock_in, buf_getptr(ses.readbuf, maxlen), maxlen);
 
 	if (len == 0) {
 		ses.remoteclosed();
@@ -171,7 +171,7 @@
 	maxlen = blocksize - ses.readbuf->pos;
 			
 	/* read the rest of the packet if possible */
-	len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen),
+	len = read(ses.sock_in, buf_getwriteptr(ses.readbuf, maxlen),
 			maxlen);
 	if (len == 0) {
 		ses.remoteclosed();
--- a/runopts.h	Thu Nov 29 11:38:06 2007 +0000
+++ b/runopts.h	Tue Sep 23 16:05:04 2008 +0000
@@ -37,7 +37,7 @@
 	int listen_fwd_all;
 #endif
 	unsigned int recv_window;
-	time_t keepalive_secs;
+	unsigned int keepalive_secs;
 
 } runopts;
 
@@ -101,6 +101,7 @@
 	char *remotehost;
 	char *remoteport;
 
+	char *own_user;
 	char *username;
 
 	char *cmd;
@@ -108,6 +109,7 @@
 	int always_accept_key;
 	int no_cmd;
 	int backgrounded;
+	int is_subsystem;
 #ifdef ENABLE_CLI_PUBKEY_AUTH
 	struct SignKeyList *privkeys; /* Keys to use for public-key auth */
 #endif
@@ -123,6 +125,14 @@
 							  list of keys held by the agent */
 #endif
 
+#ifdef ENABLE_CLI_NETCAT
+	char *netcat_host;
+	unsigned int netcat_port;
+#endif
+#ifdef ENABLE_CLI_PROXYCMD
+	char *proxycmd;
+#endif
+
 } cli_runopts;
 
 extern cli_runopts cli_opts;
--- a/session.h	Thu Nov 29 11:38:06 2007 +0000
+++ b/session.h	Tue Sep 23 16:05:04 2008 +0000
@@ -41,12 +41,14 @@
 extern int sessinitdone; /* Is set to 0 somewhere */
 extern int exitflag;
 
-void common_session_init(int sock, char* remotehost);
+void common_session_init(int sock_in, int sock_out, char* remotehost);
 void session_loop(void(*loophandler)());
 void common_session_cleanup();
 void session_identification();
 void send_msg_ignore();
 
+const char* get_user_shell();
+void fill_passwd(const char* username);
 
 /* Server */
 void svr_session(int sock, int childpipe, char *remotehost, char *addrstring);
@@ -54,7 +56,7 @@
 void svr_dropbear_log(int priority, const char* format, va_list param);
 
 /* Client */
-void cli_session(int sock, char *remotehost);
+void cli_session(int sock_in, int sock_out, char *remotehost);
 void cli_session_cleanup();
 void cleantext(unsigned char* dirtytext);
 
@@ -97,7 +99,8 @@
 							(cleared after auth once we're not
 							respecting AUTH_TIMEOUT any more) */
 
-	int sock;
+	int sock_in;
+	int sock_out;
 
 	unsigned char *remotehost; /* the peer hostname */
 
--- a/svr-agentfwd.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-agentfwd.c	Tue Sep 23 16:05:04 2008 +0000
@@ -39,6 +39,7 @@
 #include "buffer.h"
 #include "random.h"
 #include "listener.h"
+#include "auth.h"
 
 #define AGENTDIRPREFIX "/tmp/dropbear-"
 
@@ -52,6 +53,10 @@
 
 	int fd;
 
+	if (!svr_pubkey_allows_agentfwd()) {
+		return DROPBEAR_FAILURE;
+	}
+
 	if (chansess->agentlistener != NULL) {
 		return DROPBEAR_FAILURE;
 	}
@@ -150,8 +155,8 @@
 		 * for themselves */
 		uid = getuid();
 		gid = getgid();
-		if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
-			(seteuid(ses.authstate.pw->pw_uid)) < 0) {
+		if ((setegid(ses.authstate.pw_gid)) < 0 ||
+			(seteuid(ses.authstate.pw_uid)) < 0) {
 			dropbear_exit("failed to set euid");
 		}
 
@@ -213,8 +218,8 @@
 	/* drop to user privs to make the dir/file */
 	uid = getuid();
 	gid = getgid();
-	if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
-		(seteuid(ses.authstate.pw->pw_uid)) < 0) {
+	if ((setegid(ses.authstate.pw_gid)) < 0 ||
+		(seteuid(ses.authstate.pw_uid)) < 0) {
 		dropbear_exit("failed to set euid");
 	}
 
--- a/svr-auth.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-auth.c	Tue Sep 23 16:05:04 2008 +0000
@@ -42,6 +42,10 @@
 void svr_authinitialise() {
 
 	ses.authstate.failcount = 0;
+	ses.authstate.pw_name = NULL;
+	ses.authstate.pw_dir = NULL;
+	ses.authstate.pw_shell = NULL;
+	ses.authstate.pw_passwd = NULL;
 	authclear();
 	
 }
@@ -60,7 +64,19 @@
 		ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
 	}
 #endif
-
+	if (ses.authstate.pw_name) {
+		m_free(ses.authstate.pw_name);
+	}
+	if (ses.authstate.pw_shell) {
+		m_free(ses.authstate.pw_shell);
+	}
+	if (ses.authstate.pw_dir) {
+		m_free(ses.authstate.pw_dir);
+	}
+	if (ses.authstate.pw_passwd) {
+		m_free(ses.authstate.pw_passwd);
+	}
+	
 }
 
 /* Send a banner message if specified to the client. The client might
@@ -143,7 +159,7 @@
 
 #ifdef ENABLE_SVR_PASSWORD_AUTH
 	if (!svr_opts.noauthpass &&
-			!(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
+			!(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) {
 		/* user wants to try password auth */
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
@@ -156,7 +172,7 @@
 
 #ifdef ENABLE_SVR_PAM_AUTH
 	if (!svr_opts.noauthpass &&
-			!(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
+			!(svr_opts.norootpass && ses.authstate.pw_uid == 0) ) {
 		/* user wants to try password auth */
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
@@ -187,6 +203,7 @@
 	m_free(methodname);
 }
 
+
 /* Check that the username exists, has a non-empty password, and has a valid
  * shell.
  * returns DROPBEAR_SUCCESS on valid username, DROPBEAR_FAILURE on failure */
@@ -194,7 +211,6 @@
 
 	char* listshell = NULL;
 	char* usershell = NULL;
-	
 	TRACE(("enter checkusername"))
 	if (userlen > MAX_USERNAME_LEN) {
 		return DROPBEAR_FAILURE;
@@ -210,13 +226,12 @@
 				m_free(ses.authstate.username);
 			}
 			authclear();
-			ses.authstate.pw = getpwnam((char*)username);
+			fill_passwd(username);
 			ses.authstate.username = m_strdup(username);
-			m_free(ses.authstate.printableuser);
 	}
 
 	/* check that user exists */
-	if (ses.authstate.pw == NULL) {
+	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",
@@ -225,11 +240,8 @@
 		return DROPBEAR_FAILURE;
 	}
 
-	/* We can set it once we know its a real user */
-	ses.authstate.printableuser = m_strdup(ses.authstate.pw->pw_name);
-
 	/* check for non-root if desired */
-	if (svr_opts.norootlogin && ses.authstate.pw->pw_uid == 0) {
+	if (svr_opts.norootlogin && ses.authstate.pw_uid == 0) {
 		TRACE(("leave checkusername: root login disabled"))
 		dropbear_log(LOG_WARNING, "root login rejected");
 		send_msg_userauth_failure(0, 1);
@@ -237,18 +249,18 @@
 	}
 
 	/* check for an empty password */
-	if (ses.authstate.pw->pw_passwd[0] == '\0') {
+	if (ses.authstate.pw_passwd[0] == '\0') {
 		TRACE(("leave checkusername: empty pword"))
 		dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
-				ses.authstate.printableuser);
+				ses.authstate.pw_name);
 		send_msg_userauth_failure(0, 1);
 		return DROPBEAR_FAILURE;
 	}
 
-	TRACE(("shell is %s", ses.authstate.pw->pw_shell))
+	TRACE(("shell is %s", ses.authstate.pw_shell))
 
 	/* check that the shell is set */
-	usershell = ses.authstate.pw->pw_shell;
+	usershell = ses.authstate.pw_shell;
 	if (usershell[0] == '\0') {
 		/* empty shell in /etc/passwd means /bin/sh according to passwd(5) */
 		usershell = "/bin/sh";
@@ -269,7 +281,7 @@
 	endusershell();
 	TRACE(("no matching shell"))
 	dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected",
-				ses.authstate.printableuser);
+				ses.authstate.pw_name);
 	send_msg_userauth_failure(0, 1);
 	return DROPBEAR_FAILURE;
 	
@@ -277,7 +289,7 @@
 	endusershell();
 	TRACE(("matching shell"))
 
-	TRACE(("uid = %d", ses.authstate.pw->pw_uid))
+	TRACE(("uid = %d", ses.authstate.pw_uid))
 	TRACE(("leave checkusername"))
 	return DROPBEAR_SUCCESS;
 
@@ -334,10 +346,10 @@
 		/* XXX - send disconnect ? */
 		TRACE(("Max auth tries reached, exiting"))
 
-		if (ses.authstate.printableuser == NULL) {
+		if (ses.authstate.pw_name == NULL) {
 			userstr = "is invalid";
 		} else {
-			userstr = ses.authstate.printableuser;
+			userstr = ses.authstate.pw_name;
 		}
 		dropbear_exit("Max auth tries reached - user '%s' from %s",
 				userstr, svr_ses.addrstring);
@@ -360,7 +372,7 @@
 	ses.connect_time = 0;
 
 
-	if (ses.authstate.pw->pw_uid == 0) {
+	if (ses.authstate.pw_uid == 0) {
 		ses.allowprivport = 1;
 	}
 
--- a/svr-authpam.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-authpam.c	Tue Sep 23 16:05:04 2008 +0000
@@ -195,7 +195,7 @@
 	/* used to pass data to the PAM conversation function - don't bother with
 	 * strdup() etc since these are touched only by our own conversation
 	 * function (above) which takes care of it */
-	userData.user = ses.authstate.printableuser;
+	userData.user = ses.authstate.pw_name;
 	userData.passwd = password;
 
 	/* Init pam */
@@ -221,7 +221,7 @@
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
 				"bad PAM password attempt for '%s' from %s",
-				ses.authstate.printableuser,
+				ses.authstate.pw_name,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		goto cleanup;
@@ -232,7 +232,7 @@
 				rc, pam_strerror(pamHandlep, rc));
 		dropbear_log(LOG_WARNING,
 				"bad PAM password attempt for '%s' from %s",
-				ses.authstate.printableuser,
+				ses.authstate.pw_name,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 		goto cleanup;
@@ -240,7 +240,7 @@
 
 	/* successful authentication */
 	dropbear_log(LOG_NOTICE, "PAM password auth succeeded for '%s' from %s",
-			ses.authstate.printableuser,
+			ses.authstate.pw_name,
 			svr_ses.addrstring);
 	send_msg_userauth_success();
 
--- a/svr-authpasswd.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-authpasswd.c	Tue Sep 23 16:05:04 2008 +0000
@@ -46,10 +46,10 @@
 
 	unsigned int changepw;
 
-	passwdcrypt = ses.authstate.pw->pw_passwd;
+	passwdcrypt = ses.authstate.pw_passwd;
 #ifdef HAVE_SHADOW_H
 	/* get the shadow password if possible */
-	spasswd = getspnam(ses.authstate.printableuser);
+	spasswd = getspnam(ses.authstate.pw_name);
 	if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
 		passwdcrypt = spasswd->sp_pwdp;
 	}
@@ -65,7 +65,7 @@
 	 * in auth.c */
 	if (passwdcrypt[0] == '\0') {
 		dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
-				ses.authstate.printableuser);
+				ses.authstate.pw_name);
 		send_msg_userauth_failure(0, 1);
 		return;
 	}
@@ -89,13 +89,13 @@
 		/* successful authentication */
 		dropbear_log(LOG_NOTICE, 
 				"password auth succeeded for '%s' from %s",
-				ses.authstate.printableuser,
+				ses.authstate.pw_name,
 				svr_ses.addrstring);
 		send_msg_userauth_success();
 	} else {
 		dropbear_log(LOG_WARNING,
 				"bad password attempt for '%s' from %s",
-				ses.authstate.printableuser,
+				ses.authstate.pw_name,
 				svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 	}
--- a/svr-authpubkey.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-authpubkey.c	Tue Sep 23 16:05:04 2008 +0000
@@ -21,6 +21,37 @@
  * 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. */
+/*
+ * This file incorporates work covered by the following copyright and  
+ * permission notice:
+ *
+ * 	Copyright (c) 2000 Markus Friedl.  All rights reserved.
+ * 	
+ * 	Redistribution and use in source and binary forms, with or without
+ * 	modification, are permitted provided that the following conditions
+ * 	are met:
+ * 	1. Redistributions of source code must retain the above copyright
+ * 	   notice, this list of conditions and the following disclaimer.
+ * 	2. Redistributions in binary form must reproduce the above copyright
+ * 	   notice, this list of conditions and the following disclaimer in the
+ * 	   documentation and/or other materials provided with the distribution.
+ * 	
+ * 	THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * 	IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * 	OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * 	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * 	INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * 	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * 	DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * 	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * 	(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * 	THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This copyright and permission notice applies to the code parsing public keys
+ * options string which can also be found in OpenSSH auth2-pubkey.c file 
+ * (user_key_allowed2). It has been adapted to work with buffers.
+ *
+ */
 
 /* Process a pubkey auth request */
 
@@ -105,12 +136,12 @@
 				signbuf->len) == DROPBEAR_SUCCESS) {
 		dropbear_log(LOG_NOTICE,
 				"pubkey auth succeeded for '%s' with key %s from %s",
-				ses.authstate.printableuser, fp, svr_ses.addrstring);
+				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",
-				ses.authstate.printableuser, fp, svr_ses.addrstring);
+				ses.authstate.pw_name, fp, svr_ses.addrstring);
 		send_msg_userauth_failure(0, 1);
 	}
 	m_free(fp);
@@ -159,14 +190,16 @@
 	int ret = DROPBEAR_FAILURE;
 	buffer * line = NULL;
 	unsigned int len, pos;
-	
+	buffer * options_buf = NULL;
+	int line_num;
+
 	TRACE(("enter checkpubkey"))
 
 	/* 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",
-				ses.authstate.printableuser, svr_ses.addrstring);
+				ses.authstate.pw_name, svr_ses.addrstring);
 		goto out;
 	}
 
@@ -178,12 +211,12 @@
 
 	/* we don't need to check pw and pw_dir for validity, since
 	 * its been done in checkpubkeyperms. */
-	len = strlen(ses.authstate.pw->pw_dir);
+	len = strlen(ses.authstate.pw_dir);
 	/* allocate max required pathname storage,
 	 * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
 	filename = m_malloc(len + 22);
 	snprintf(filename, len + 22, "%s/.ssh/authorized_keys", 
-				ses.authstate.pw->pw_dir);
+				ses.authstate.pw_dir);
 
 	/* open the file */
 	authfile = fopen(filename, "r");
@@ -193,25 +226,82 @@
 	TRACE(("checkpubkey: opened authorized_keys OK"))
 
 	line = buf_new(MAX_AUTHKEYS_LINE);
+	line_num = 0;
 
 	/* iterate through the lines */
 	do {
+		/* new line : potentially new options */
+		if (options_buf) {
+			buf_free(options_buf);
+			options_buf = NULL;
+		}
 
 		if (buf_getline(line, authfile) == DROPBEAR_FAILURE) {
 			/* EOF reached */
 			TRACE(("checkpubkey: authorized_keys EOF reached"))
 			break;
 		}
+		line_num++;
 
 		if (line->len < MIN_AUTHKEYS_LINE) {
 			TRACE(("checkpubkey: line too short"))
 			continue; /* line is too short for it to be a valid key */
 		}
 
-		/* check the key type - this also stops us from using keys
-		 * which have options with them */
+		/* check the key type - will fail if there are options */
+		TRACE(("a line!"))
+
 		if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) {
-			continue;
+			int is_comment = 0;
+			char *options_start = NULL;
+			int options_len = 0;
+			int escape, quoted;
+			
+			/* skip over any comments or leading whitespace */
+			while (line->pos < line->len) {
+				const char c = buf_getbyte(line);
+				if (c == ' ' || c == '\t') {
+					continue;
+				} else if (c == '#') {
+					is_comment = 1;
+					break;
+				}
+				buf_incrpos(line, -1);
+				break;
+			}
+			if (is_comment) {
+				/* next line */
+				continue;
+			}
+
+			/* remember start of options */
+			options_start = buf_getptr(line, 1);
+			quoted = 0;
+			escape = 0;
+			options_len = 0;
+			
+			/* figure out where the options are */
+			while (line->pos < line->len) {
+				const char c = buf_getbyte(line);
+				if (!quoted && (c == ' ' || c == '\t')) {
+					break;
+				}
+				escape = (!escape && c == '\\');
+				if (!escape && c == '"') {
+					quoted = !quoted;
+				}
+				options_len++;
+			}
+			options_buf = buf_new(options_len);
+			buf_putbytes(options_buf, options_start, options_len);
+
+			/* compare the algorithm */
+			if (line->pos + algolen > line->len) {
+				continue;
+			}
+			if (strncmp(buf_getptr(line, algolen), algo, algolen) != 0) { 
+				continue;
+			}
 		}
 		buf_incrpos(line, algolen);
 		
@@ -232,6 +322,11 @@
 		TRACE(("checkpubkey: line pos = %d len = %d", line->pos, line->len))
 
 		ret = cmp_base64_key(keyblob, keybloblen, algo, algolen, line, NULL);
+
+		if (ret == DROPBEAR_SUCCESS && options_buf) {
+			ret = svr_add_pubkey_options(options_buf, line_num, filename);
+		}
+
 		if (ret == DROPBEAR_SUCCESS) {
 			break;
 		}
@@ -248,6 +343,9 @@
 		buf_free(line);
 	}
 	m_free(filename);
+	if (options_buf) {
+		buf_free(options_buf);
+	}
 	TRACE(("leave checkpubkey: ret=%d", ret))
 	return ret;
 }
@@ -266,18 +364,18 @@
 
 	TRACE(("enter checkpubkeyperms"))
 
-	if (ses.authstate.pw->pw_dir == NULL) {
+	if (ses.authstate.pw_dir == NULL) {
 		goto out;
 	}
 
-	if ((len = strlen(ses.authstate.pw->pw_dir)) == 0) {
+	if ((len = strlen(ses.authstate.pw_dir)) == 0) {
 		goto out;
 	}
 
 	/* allocate max required pathname storage,
 	 * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
 	filename = m_malloc(len + 22);
-	strncpy(filename, ses.authstate.pw->pw_dir, len+1);
+	strncpy(filename, ses.authstate.pw_dir, len+1);
 
 	/* check ~ */
 	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
@@ -320,7 +418,7 @@
 		return DROPBEAR_FAILURE;
 	}
 	/* check ownership - user or root only*/
-	if (filestat.st_uid != ses.authstate.pw->pw_uid
+	if (filestat.st_uid != ses.authstate.pw_uid
 			&& filestat.st_uid != 0) {
 		badperm = 1;
 		TRACE(("wrong ownership"))
@@ -343,5 +441,4 @@
 	return DROPBEAR_SUCCESS;
 }
 
-
-#endif 
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/svr-authpubkeyoptions.c	Tue Sep 23 16:05:04 2008 +0000
@@ -0,0 +1,202 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * Copyright (c) 2008 Frederic Moulins
+ * 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. 
+ *
+ * This file incorporates work covered by the following copyright and  
+ * permission notice:
+ *
+ * 	Author: Tatu Ylonen <[email protected]>
+ * 	Copyright (c) 1995 Tatu Ylonen <[email protected]>, Espoo, Finland
+ * 	              All rights reserved
+ * 	As far as I am concerned, the code I have written for this software
+ * 	can be used freely for any purpose.  Any derived versions of this
+ * 	software must be clearly marked as such, and if the derived work is
+ * 	incompatible with the protocol description in the RFC file, it must be
+ * 	called by a name other than "ssh" or "Secure Shell".
+ *
+ * This copyright and permission notice applies to the code parsing public keys
+ * options string which can also be found in OpenSSH auth-options.c file 
+ * (auth_parse_options).
+ *
+ */
+
+/* Process pubkey options during a pubkey auth request */
+#include "includes.h"
+#include "session.h"
+#include "dbutil.h"
+#include "signkey.h"
+#include "auth.h"
+
+#ifdef ENABLE_SVR_PUBKEY_OPTIONS
+
+/* Returns 1 if pubkey allows agent forwarding,
+ * 0 otherwise */
+int svr_pubkey_allows_agentfwd() {
+	if (ses.authstate.pubkey_options 
+		&& ses.authstate.pubkey_options->no_agent_forwarding_flag) {
+		return 0;
+	}
+	return 1;
+}
+
+/* Returns 1 if pubkey allows tcp forwarding,
+ * 0 otherwise */
+int svr_pubkey_allows_tcpfwd() {
+	if (ses.authstate.pubkey_options 
+		&& ses.authstate.pubkey_options->no_port_forwarding_flag) {
+		return 0;
+	}
+	return 1;
+}
+
+/* Returns 1 if pubkey allows x11 forwarding,
+ * 0 otherwise */
+int svr_pubkey_allows_x11fwd() {
+	if (ses.authstate.pubkey_options 
+		&& ses.authstate.pubkey_options->no_x11_forwarding_flag) {
+		return 0;
+	}
+	return 1;
+}
+
+/* Returns 1 if pubkey allows pty, 0 otherwise */
+int svr_pubkey_allows_pty() {
+	if (ses.authstate.pubkey_options 
+		&& ses.authstate.pubkey_options->no_pty_flag) {
+		return 0;
+	}
+	return 1;
+}
+
+/* Set chansession command to the one forced by '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;
+}
+
+/* Free potential public key options */
+void svr_pubkey_options_cleanup() {
+	if (ses.authstate.pubkey_options) {
+		m_free(ses.authstate.pubkey_options);
+		ses.authstate.pubkey_options = NULL;
+	}
+}
+
+/* helper for svr_add_pubkey_options. returns DROPBEAR_SUCCESS if the option is matched,
+   and increments the options_buf */
+static int match_option(buffer *options_buf, const char *opt_name) {
+	const int len = strlen(opt_name);
+	if (options_buf->len - options_buf->pos < len) {
+		return DROPBEAR_FAILURE;
+	}
+	if (strncasecmp(buf_getptr(options_buf, len), opt_name, len) == 0) {
+		buf_incrpos(options_buf, len);
+		return DROPBEAR_SUCCESS;
+	}
+	return DROPBEAR_FAILURE;
+}
+
+/* Parse pubkey options and set ses.authstate.pubkey_options accordingly.
+ * Returns DROPBEAR_SUCCESS if key is ok for auth, DROPBEAR_FAILURE otherwise */
+int svr_add_pubkey_options(buffer *options_buf, int line_num, const char* filename) {
+	int ret = DROPBEAR_FAILURE;
+
+	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) {
+		if (match_option(options_buf, "no-port-forwarding") == DROPBEAR_SUCCESS) {
+			dropbear_log(LOG_WARNING, "Port forwarding disabled.");
+			ses.authstate.pubkey_options->no_port_forwarding_flag = 1;
+			goto next_option;
+		}
+#ifdef ENABLE_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;
+			goto next_option;
+		}
+#endif
+#ifdef ENABLE_X11FWD
+		if (match_option(options_buf, "no-X11-forwarding") == DROPBEAR_SUCCESS) {
+			dropbear_log(LOG_WARNING, "X11 forwarding disabled.");
+			ses.authstate.pubkey_options->no_x11_forwarding_flag = 1;
+			goto next_option;
+		}
+#endif
+		if (match_option(options_buf, "no-pty") == DROPBEAR_SUCCESS) {
+			dropbear_log(LOG_WARNING, "Pty allocation disabled.");
+			ses.authstate.pubkey_options->no_pty_flag = 1;
+			goto next_option;
+		}
+		if (match_option(options_buf, "command=\"") == DROPBEAR_SUCCESS) {
+			int escaped = 0;
+			const unsigned char* command_start = buf_getptr(options_buf, 0);
+			while (options_buf->pos < options_buf->len) {
+				const char c = buf_getbyte(options_buf);
+				if (!escaped && c == '"') {
+					const int command_len = buf_getptr(options_buf, 0) - command_start;
+					ses.authstate.pubkey_options->forced_command = m_malloc(command_len);
+					memcpy(ses.authstate.pubkey_options->forced_command,
+							command_start, command_len-1);
+					ses.authstate.pubkey_options->forced_command[command_len-1] = '\0';
+					dropbear_log(LOG_WARNING, "Forced command '%s'", 
+						ses.authstate.pubkey_options->forced_command);
+					goto next_option;
+				}
+				escaped = (!escaped && c == '\\');
+			}
+			dropbear_log(LOG_WARNING, "Badly formatted command= authorized_keys option");
+			goto bad_option;
+		}
+
+next_option:
+		/*
+		 * Skip the comma, and move to the next option
+		 * (or break out if there are no more).
+		 */
+		if (options_buf->pos < options_buf->len 
+				&& buf_getbyte(options_buf) != ',') {
+			goto bad_option;
+		}
+		/* Process the next option. */
+	}
+	/* parsed all options with no problem */
+	ret = DROPBEAR_SUCCESS;
+	goto end;
+
+bad_option:
+	ret = DROPBEAR_FAILURE;
+	m_free(ses.authstate.pubkey_options);
+	ses.authstate.pubkey_options = NULL;
+	dropbear_log(LOG_WARNING, "Bad public key options at %s:%d", filename, line_num);
+
+end:
+	TRACE(("leave addpubkeyoptions"))
+	return ret;
+}
+
+#endif
--- a/svr-chansession.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-chansession.c	Tue Sep 23 16:05:04 2008 +0000
@@ -37,6 +37,7 @@
 #include "x11fwd.h"
 #include "agentfwd.h"
 #include "runopts.h"
+#include "auth.h"
 
 /* Handles sessions (either shells or programs) requested by the client */
 
@@ -47,7 +48,7 @@
 static int noptycommand(struct Channel *channel, struct ChanSess *chansess);
 static int ptycommand(struct Channel *channel, struct ChanSess *chansess);
 static int sessionwinchange(struct ChanSess *chansess);
-static void execchild(struct ChanSess *chansess);
+static void execchild(void *user_data_chansess);
 static void addchildpid(struct ChanSess *chansess, pid_t pid);
 static void sesssigchild_handler(int val);
 static void closechansess(struct Channel *channel);
@@ -524,8 +525,15 @@
 
 	unsigned int termlen;
 	unsigned char namebuf[65];
+	struct passwd * pw = NULL;
 
 	TRACE(("enter sessionpty"))
+
+	if (!svr_pubkey_allows_pty()) {
+		TRACE(("leave sessionpty : pty forbidden by public key option"))
+		return DROPBEAR_FAILURE;
+	}
+
 	chansess->term = buf_getstring(ses.payload, &termlen);
 	if (termlen > MAX_TERM_LEN) {
 		/* TODO send disconnect ? */
@@ -547,7 +555,10 @@
 		dropbear_exit("out of memory"); /* TODO disconnect */
 	}
 
-	pty_setowner(ses.authstate.pw, chansess->tty);
+	pw = getpwnam(ses.authstate.pw_name);
+	if (!pw)
+		dropbear_exit("getpwnam failed after succeeding previously");
+	pty_setowner(pw, chansess->tty);
 
 	/* Set up the rows/col counts */
 	sessionwinchange(chansess);
@@ -578,14 +589,19 @@
 		return DROPBEAR_FAILURE;
 	}
 
+	/* take public key option 'command' into account */
+	svr_pubkey_set_forced_command(chansess);
+
 	if (iscmd) {
 		/* "exec" */
-		chansess->cmd = buf_getstring(ses.payload, &cmdlen);
+		if (chansess->cmd == NULL) {
+			chansess->cmd = buf_getstring(ses.payload, &cmdlen);
 
-		if (cmdlen > MAX_CMD_LEN) {
-			m_free(chansess->cmd);
-			/* TODO - send error - too long ? */
-			return DROPBEAR_FAILURE;
+			if (cmdlen > MAX_CMD_LEN) {
+				m_free(chansess->cmd);
+				/* TODO - send error - too long ? */
+				return DROPBEAR_FAILURE;
+			}
 		}
 		if (issubsys) {
 #ifdef SFTPSERVER_PATH
@@ -604,10 +620,10 @@
 #ifdef LOG_COMMANDS
 	if (chansess->cmd) {
 		dropbear_log(LOG_INFO, "user %s executing '%s'", 
-						ses.authstate.printableuser, chansess->cmd);
+						ses.authstate.pw_name, chansess->cmd);
 	} else {
 		dropbear_log(LOG_INFO, "user %s executing login shell", 
-						ses.authstate.printableuser);
+						ses.authstate.pw_name);
 	}
 #endif
 
@@ -629,100 +645,37 @@
  * pty.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
 static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
-
-	int infds[2];
-	int outfds[2];
-	int errfds[2];
-	pid_t pid;
-	unsigned int i;
+	int ret;
 
 	TRACE(("enter noptycommand"))
-
-	/* redirect stdin/stdout/stderr */
-	if (pipe(infds) != 0)
-		return DROPBEAR_FAILURE;
-	if (pipe(outfds) != 0)
-		return DROPBEAR_FAILURE;
-	if (pipe(errfds) != 0)
-		return DROPBEAR_FAILURE;
+	ret = spawn_command(execchild, chansess, 
+			&channel->writefd, &channel->readfd, &channel->errfd,
+			&chansess->pid);
 
-#ifdef __uClinux__
-	pid = vfork();
-#else
-	pid = fork();
-#endif
-
-	if (pid < 0)
-		return DROPBEAR_FAILURE;
+	if (ret == DROPBEAR_FAILURE) {
+		return ret;
+	}
 
-	if (!pid) {
-		/* child */
-
-		TRACE(("back to normal sigchld"))
-		/* Revert to normal sigchld handling */
-		if (signal(SIGCHLD, SIG_DFL) == SIG_ERR) {
-			dropbear_exit("signal() error");
-		}
+	ses.maxfd = MAX(ses.maxfd, channel->writefd);
+	ses.maxfd = MAX(ses.maxfd, channel->readfd);
+	ses.maxfd = MAX(ses.maxfd, channel->errfd);
 
-		/* redirect stdin/stdout */
-#define FDIN 0
-#define FDOUT 1
-		if ((dup2(infds[FDIN], STDIN_FILENO) < 0) ||
-			(dup2(outfds[FDOUT], STDOUT_FILENO) < 0) ||
-			(dup2(errfds[FDOUT], STDERR_FILENO) < 0)) {
-			TRACE(("leave noptycommand: error redirecting FDs"))
-			return DROPBEAR_FAILURE;
-		}
-
-		close(infds[FDOUT]);
-		close(infds[FDIN]);
-		close(outfds[FDIN]);
-		close(outfds[FDOUT]);
-		close(errfds[FDIN]);
-		close(errfds[FDOUT]);
-
-		execchild(chansess);
-		/* not reached */
+	addchildpid(chansess, chansess->pid);
 
-	} else {
-		/* parent */
-		TRACE(("continue noptycommand: parent"))
-		chansess->pid = pid;
-		TRACE(("child pid is %d", pid))
-
-		addchildpid(chansess, pid);
-
-		if (svr_ses.lastexit.exitpid != -1) {
-			TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
-			/* The child probably exited and the signal handler triggered
-			 * possibly before we got around to adding the childpid. So we fill
-			 * out its data manually */
-			for (i = 0; i < svr_ses.childpidsize; i++) {
-				if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
-					TRACE(("found match for lastexitpid"))
-					svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
-					svr_ses.lastexit.exitpid = -1;
-				}
+	if (svr_ses.lastexit.exitpid != -1) {
+		TRACE(("parent side: lastexitpid is %d", svr_ses.lastexit.exitpid))
+		/* The child probably exited and the signal handler triggered
+		 * possibly before we got around to adding the childpid. So we fill
+		 * out its data manually */
+		int i;
+		for (i = 0; i < svr_ses.childpidsize; i++) {
+			if (svr_ses.childpids[i].pid == svr_ses.lastexit.exitpid) {
+				TRACE(("found match for lastexitpid"))
+				svr_ses.childpids[i].chansess->exit = svr_ses.lastexit;
+				svr_ses.lastexit.exitpid = -1;
 			}
 		}
-
-		close(infds[FDIN]);
-		close(outfds[FDOUT]);
-		close(errfds[FDOUT]);
-		channel->writefd = infds[FDOUT];
-		channel->readfd = outfds[FDIN];
-		channel->errfd = errfds[FDIN];
-		ses.maxfd = MAX(ses.maxfd, channel->writefd);
-		ses.maxfd = MAX(ses.maxfd, channel->readfd);
-		ses.maxfd = MAX(ses.maxfd, channel->errfd);
-
-		setnonblocking(channel->readfd);
-		setnonblocking(channel->writefd);
-		setnonblocking(channel->errfd);
-
 	}
-#undef FDIN
-#undef FDOUT
 
 	TRACE(("leave noptycommand"))
 	return DROPBEAR_SUCCESS;
@@ -794,11 +747,11 @@
 		if (svr_opts.domotd) {
 			/* don't show the motd if ~/.hushlogin exists */
 
-			/* 11 == strlen("/hushlogin\0") */
-			len = strlen(ses.authstate.pw->pw_dir) + 11; 
+			/* 12 == strlen("/.hushlogin\0") */
+			len = strlen(ses.authstate.pw_dir) + 12; 
 
 			hushpath = m_malloc(len);
-			snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir);
+			snprintf(hushpath, len, "%s/.hushlogin", ses.authstate.pw_dir);
 
 			if (stat(hushpath, &sb) < 0) {
 				/* more than a screenful is stupid IMHO */
@@ -867,12 +820,9 @@
 
 /* Clean up, drop to user privileges, set up the environment and execute
  * the command/shell. This function does not return. */
-static void execchild(struct ChanSess *chansess) {
-
-	char *argv[4];
-	char * usershell = NULL;
-	char * baseshell = NULL;
-	unsigned int i;
+static void execchild(void *user_data) {
+	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 */
@@ -885,12 +835,6 @@
 	reseedrandom();
 #endif
 
-	/* close file descriptors except stdin/stdout/stderr
-	 * Need to be sure FDs are closed here to avoid reading files as root */
-	for (i = 3; i <= (unsigned int)ses.maxfd; i++) {
-		m_close(i);
-	}
-
 	/* clear environment */
 	/* if we're debugging using valgrind etc, we need to keep the LD_PRELOAD
 	 * etc. This is hazardous, so should only be used for debugging. */
@@ -908,12 +852,12 @@
 	/* We can only change uid/gid as root ... */
 	if (getuid() == 0) {
 
-		if ((setgid(ses.authstate.pw->pw_gid) < 0) ||
-			(initgroups(ses.authstate.pw->pw_name, 
-						ses.authstate.pw->pw_gid) < 0)) {
+		if ((setgid(ses.authstate.pw_gid) < 0) ||
+			(initgroups(ses.authstate.pw_name, 
+						ses.authstate.pw_gid) < 0)) {
 			dropbear_exit("error changing user group");
 		}
-		if (setuid(ses.authstate.pw->pw_uid) < 0) {
+		if (setuid(ses.authstate.pw_uid) < 0) {
 			dropbear_exit("error changing user");
 		}
 	} else {
@@ -924,29 +868,22 @@
 		 * usernames with the same uid, but differing groups, then the
 		 * 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->pw_uid) {
+		if (getuid() != ses.authstate.pw_uid) {
 			dropbear_exit("couldn't	change user as non-root");
 		}
 	}
 
-	/* an empty shell should be interpreted as "/bin/sh" */
-	if (ses.authstate.pw->pw_shell[0] == '\0') {
-		usershell = "/bin/sh";
-	} else {
-		usershell = ses.authstate.pw->pw_shell;
-	}
-
 	/* set env vars */
-	addnewvar("USER", ses.authstate.pw->pw_name);
-	addnewvar("LOGNAME", ses.authstate.pw->pw_name);
-	addnewvar("HOME", ses.authstate.pw->pw_dir);
-	addnewvar("SHELL", usershell);
+	addnewvar("USER", ses.authstate.pw_name);
+	addnewvar("LOGNAME", ses.authstate.pw_name);
+	addnewvar("HOME", ses.authstate.pw_dir);
+	addnewvar("SHELL", get_user_shell());
 	if (chansess->term != NULL) {
 		addnewvar("TERM", chansess->term);
 	}
 
 	/* change directory */
-	if (chdir(ses.authstate.pw->pw_dir) < 0) {
+	if (chdir(ses.authstate.pw_dir) < 0) {
 		dropbear_exit("error changing directory");
 	}
 
@@ -959,32 +896,8 @@
 	agentset(chansess);
 #endif
 
-	/* Re-enable SIGPIPE for the executed process */
-	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
-		dropbear_exit("signal() error");
-	}
-
-	baseshell = basename(usershell);
-
-	if (chansess->cmd != NULL) {
-		argv[0] = baseshell;
-	} else {
-		/* a login shell should be "-bash" for "/bin/bash" etc */
-		int len = strlen(baseshell) + 2; /* 2 for "-" */
-		argv[0] = (char*)m_malloc(len);
-		snprintf(argv[0], len, "-%s", baseshell);
-	}
-
-	if (chansess->cmd != NULL) {
-		argv[1] = "-c";
-		argv[2] = chansess->cmd;
-		argv[3] = NULL;
-	} else {
-		/* construct a shell of the form "-bash" etc */
-		argv[1] = NULL;
-	}
-
-	execv(usershell, argv);
+	usershell = m_strdup(get_user_shell());
+	run_shell_command(chansess->cmd, ses.maxfd, usershell);
 
 	/* only reached on error */
 	dropbear_exit("child failed");
--- a/svr-main.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-main.c	Tue Sep 23 16:05:04 2008 +0000
@@ -266,7 +266,11 @@
 				goto out;
 			}
 
+#ifdef DEBUG_NOFORK
+			fork_ret = 0;
+#else
 			fork_ret = fork();
+#endif
 			if (fork_ret < 0) {
 				dropbear_log(LOG_WARNING, "error forking: %s", strerror(errno));
 				goto out;
@@ -292,9 +296,11 @@
 				addrstring = getaddrstring(&remoteaddr, 1);
 				dropbear_log(LOG_INFO, "Child connection from %s", addrstring);
 
+#ifndef DEBUG_NOFORK
 				if (setsid() < 0) {
 					dropbear_exit("setsid: %s", strerror(errno));
 				}
+#endif
 
 				/* make sure we close sockets */
 				for (i = 0; i < listensockcount; i++) {
--- a/svr-runopts.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-runopts.c	Tue Sep 23 16:05:04 2008 +0000
@@ -284,16 +284,13 @@
 	
 	if (recv_window_arg) {
 		opts.recv_window = atol(recv_window_arg);
-		if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW)
-		{
+		if (opts.recv_window == 0 || opts.recv_window > MAX_RECV_WINDOW) {
 			dropbear_exit("Bad recv window '%s'", recv_window_arg);
 		}
 	}
 	
 	if (keepalive_arg) {
-		opts.keepalive_secs = strtoul(keepalive_arg, NULL, 10);
-		if (opts.keepalive_secs == 0 && errno == EINVAL)
-		{
+		if (m_str_to_uint(keepalive_arg, &opts.keepalive_secs) == DROPBEAR_FAILURE) {
 			dropbear_exit("Bad keepalive '%s'", keepalive_arg);
 		}
 	}
--- a/svr-session.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-session.c	Tue Sep 23 16:05:04 2008 +0000
@@ -80,7 +80,7 @@
     reseedrandom();
 
 	crypto_init();
-	common_session_init(sock, remotehost);
+	common_session_init(sock, sock, remotehost);
 
 	/* Initialise server specific parts of the session */
 	svr_ses.childpipe = childpipe;
@@ -130,12 +130,12 @@
 		/* user has authenticated */
 		snprintf(fmtbuf, sizeof(fmtbuf),
 				"exit after auth (%s): %s", 
-				ses.authstate.printableuser, format);
-	} else if (ses.authstate.printableuser) {
+				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",
-				ses.authstate.printableuser, ses.authstate.failcount, format);
+				ses.authstate.pw_name, ses.authstate.failcount, format);
 	} else {
 		/* before userauth */
 		snprintf(fmtbuf, sizeof(fmtbuf), 
@@ -144,6 +144,9 @@
 
 	_dropbear_log(LOG_INFO, fmtbuf, param);
 
+	/* free potential public key options */
+	svr_pubkey_options_cleanup();
+
 	/* must be after we've done with username etc */
 	common_session_cleanup();
 
@@ -183,7 +186,7 @@
 						localtime(&timesec)) == 0)
 		{
 			/* upon failure, just print the epoch-seconds time. */
-			snprintf(datestr, sizeof(datestr), "%d", timesec);
+			snprintf(datestr, sizeof(datestr), "%d", (int)timesec);
 		}
 		fprintf(stderr, "[%d] %s %s\n", getpid(), datestr, printbuf);
 	}
@@ -192,8 +195,10 @@
 /* called when the remote side closes the connection */
 static void svr_remoteclosed() {
 
-	close(ses.sock);
-	ses.sock = -1;
+	m_close(ses.sock_in);
+	m_close(ses.sock_out);
+	ses.sock_in = -1;
+	ses.sock_out = -1;
 	dropbear_close("Exited normally");
 
 }
--- a/svr-tcpfwd.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-tcpfwd.c	Tue Sep 23 16:05:04 2008 +0000
@@ -32,6 +32,7 @@
 #include "packet.h"
 #include "listener.h"
 #include "runopts.h"
+#include "auth.h"
 
 #ifdef ENABLE_SVR_REMOTETCPFWD
 
@@ -72,7 +73,7 @@
 
 	TRACE(("enter recv_msg_global_request_remotetcp"))
 
-	if (svr_opts.noremotetcp) {
+	if (svr_opts.noremotetcp || !svr_pubkey_allows_tcpfwd()) {
 		TRACE(("leave recv_msg_global_request_remotetcp: remote tcp forwarding disabled"))
 		goto out;
 	}
@@ -236,7 +237,7 @@
 	int len;
 	int err = SSH_OPEN_ADMINISTRATIVELY_PROHIBITED;
 
-	if (svr_opts.nolocaltcp) {
+	if (svr_opts.nolocaltcp || !svr_pubkey_allows_tcpfwd()) {
 		TRACE(("leave newtcpdirect: local tcp forwarding disabled"))
 		goto out;
 	}
--- a/svr-x11fwd.c	Thu Nov 29 11:38:06 2007 +0000
+++ b/svr-x11fwd.c	Tue Sep 23 16:05:04 2008 +0000
@@ -33,6 +33,7 @@
 #include "channel.h"
 #include "packet.h"
 #include "buffer.h"
+#include "auth.h"
 
 #define X11BASEPORT 6000
 #define X11BINDBASE 6010
@@ -47,6 +48,10 @@
 
 	int fd;
 
+	if (!svr_pubkey_allows_x11fwd()) {
+		return DROPBEAR_FAILURE;
+	}
+
 	/* we already have an x11 connection */
 	if (chansess->x11listener != NULL) {
 		return DROPBEAR_FAILURE;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sysoptions.h	Tue Sep 23 16:05:04 2008 +0000
@@ -0,0 +1,205 @@
+/*******************************************************************
+ * You shouldn't edit this file unless you know you need to. 
+ * This file is only included from options.h
+ *******************************************************************/
+
+#ifndef DROPBEAR_VERSION
+#define DROPBEAR_VERSION "0.51"
+#endif
+
+#define LOCAL_IDENT "SSH-2.0-dropbear_" DROPBEAR_VERSION
+#define PROGNAME "dropbear"
+
+/* Spec recommends after one hour or 1 gigabyte of data. One hour
+ * is a bit too verbose, so we try 8 hours */
+#ifndef KEX_REKEY_TIMEOUT
+#define KEX_REKEY_TIMEOUT (3600 * 8)
+#endif
+#ifndef KEX_REKEY_DATA
+#define KEX_REKEY_DATA (1<<30) /* 2^30 == 1GB, this value must be < INT_MAX */
+#endif
+/* Close connections to clients which haven't authorised after AUTH_TIMEOUT */
+#ifndef AUTH_TIMEOUT
+#define AUTH_TIMEOUT 300 /* we choose 5 minutes */
+#endif
+
+/* Minimum key sizes for DSS and RSA */
+#ifndef MIN_DSS_KEYLEN
+#define MIN_DSS_KEYLEN 512
+#endif
+#ifndef MIN_RSA_KEYLEN
+#define MIN_RSA_KEYLEN 512
+#endif
+
+#define MAX_BANNER_SIZE 2000 /* this is 25*80 chars, any more is foolish */
+#define MAX_BANNER_LINES 20 /* How many lines the client will display */
+
+/* the number of NAME=VALUE pairs to malloc for environ, if we don't have
+ * the clearenv() function */
+#define ENV_SIZE 100
+
+#define MAX_CMD_LEN 1024 /* max length of a command */
+#define MAX_TERM_LEN 200 /* max length of TERM name */
+
+#define MAX_HOST_LEN 254 /* max hostname len for tcp fwding */
+#define MAX_IP_LEN 15 /* strlen("255.255.255.255") == 15 */
+
+#define DROPBEAR_MAX_PORTS 10 /* max number of ports which can be specified,
+								 ipv4 and ipv6 don't count twice */
+
+/* Each port might have at least a v4 and a v6 address */
+#define MAX_LISTEN_ADDR (DROPBEAR_MAX_PORTS*3)
+
+#define _PATH_TTY "/dev/tty"
+
+#define _PATH_CP "/bin/cp"
+
+/* success/failure defines */
+#define DROPBEAR_SUCCESS 0
+#define DROPBEAR_FAILURE -1
+
+/* various algorithm identifiers */
+#define DROPBEAR_KEX_DH_GROUP1 0
+
+#define DROPBEAR_SIGNKEY_ANY 0
+#define DROPBEAR_SIGNKEY_RSA 1
+#define DROPBEAR_SIGNKEY_DSS 2
+#define DROPBEAR_SIGNKEY_NONE 3
+
+#define DROPBEAR_COMP_NONE 0
+#define DROPBEAR_COMP_ZLIB 1
+
+/* Required for pubkey auth */
+#if defined(ENABLE_SVR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
+#define DROPBEAR_SIGNKEY_VERIFY
+#endif
+
+/* SHA1 is 20 bytes == 160 bits */
+#define SHA1_HASH_SIZE 20
+/* SHA512 is 64 bytes == 512 bits */
+#define SHA512_HASH_SIZE 64
+/* MD5 is 16 bytes = 128 bits */
+#define MD5_HASH_SIZE 16
+
+/* largest of MD5 and SHA1 */
+#define MAX_MAC_LEN SHA1_HASH_SIZE
+
+
+#define MAX_KEY_LEN 32 /* 256 bits for aes256 etc */
+#define MAX_IV_LEN 20 /* must be same as max blocksize, 
+						 and >= SHA1_HASH_SIZE */
+#define MAX_MAC_KEY 20
+
+#define MAX_NAME_LEN 64 /* maximum length of a protocol name, isn't
+						   explicitly specified for all protocols (just
+						   for algos) but seems valid */
+
+#define MAX_PROPOSED_ALGO 20
+
+/* size/count limits */
+#define MIN_PACKET_LEN 16
+
+#define RECV_MAX_PACKET_LEN (MAX(35000, ((RECV_MAX_PAYLOAD_LEN)+100)))
+
+/* for channel code */
+#define TRANS_MAX_WINDOW 500000000 /* 500MB is sufficient, stopping overflow */
+#define TRANS_MAX_WIN_INCR 500000000 /* overflow prevention */
+
+#define RECV_WINDOWEXTEND (opts.recv_window / 3) /* We send a "window extend" every
+								RECV_WINDOWEXTEND bytes */
+#define MAX_RECV_WINDOW (1024*1024) /* 1 MB should be enough */
+
+#define MAX_CHANNELS 100 /* simple mem restriction, includes each tcp/x11
+							connection, so can't be _too_ small */
+
+#define MAX_STRING_LEN 1400 /* ~= MAX_PROPOSED_ALGO * MAX_NAME_LEN, also
+							   is the max length for a password etc */
+
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PUBKEY_SIZE 1700
+/* For a 4096 bit DSS key, empirically determined */
+#define MAX_PRIVKEY_SIZE 1700
+
+/* The maximum size of the bignum portion of the kexhash buffer */
+/* Sect. 8 of the transport draft, K_S + e + f + K */
+#define KEXHASHBUF_MAX_INTS (1700 + 130 + 130 + 130)
+
+#define DROPBEAR_MAX_SOCKS 2 /* IPv4, IPv6 are all we'll get for now. Revisit
+								in a few years time.... */
+
+#define DROPBEAR_MAX_CLI_PASS 1024
+
+#define DROPBEAR_MAX_CLI_INTERACT_PROMPTS 80 /* The number of prompts we'll 
+												accept for keyb-interactive
+												auth */
+
+#if defined(DROPBEAR_AES256_CBC) || defined(DROPBEAR_AES128_CBC)
+#define DROPBEAR_AES_CBC
+#endif
+
+#if defined(DROPBEAR_TWOFISH256_CBC) || defined(DROPBEAR_TWOFISH128_CBC)
+#define DROPBEAR_TWOFISH_CBC
+#endif
+
+#ifndef ENABLE_X11FWD
+#define DISABLE_X11FWD
+#endif
+
+#ifndef ENABLE_AGENTFWD
+#define DISABLE_AGENTFWD
+#endif
+
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD)
+#define ENABLE_CLI_ANYTCPFWD 
+#endif
+
+#if defined(ENABLE_CLI_LOCALTCPFWD) || defined(ENABLE_SVR_REMOTETCPFWD)
+#define DROPBEAR_TCP_ACCEPT
+#endif
+
+#if defined(ENABLE_CLI_REMOTETCPFWD) || defined(ENABLE_CLI_LOCALTCPFWD) || \
+	defined(ENABLE_SVR_REMOTETCPFWD) || defined(ENABLE_SVR_LOCALTCPFWD) || \
+	defined(ENABLE_AGENTFWD) || defined(ENABLE_X11FWD)
+#define USING_LISTENERS
+#endif
+
+#if defined(ENABLE_CLI_NETCAT) && defined(ENABLE_CLI_PROXYCMD)
+#define ENABLE_CLI_MULTIHOP
+#endif
+
+#if defined(DROPBEAR_CLIENT) || defined(ENABLE_SVR_PUBKEY_AUTH)
+#define DROPBEAR_KEY_LINES /* ie we're using authorized_keys or known_hosts */
+#endif
+
+#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
+
+#if defined(DROPBEAR_RANDOM_DEV) && defined(DROPBEAR_PRNGD_SOCKET)
+#error "You can't turn on DROPBEAR_PRNGD_SOCKET and DROPBEAR_RANDOM_DEV at once"
+#endif
+
+#if !defined(DROPBEAR_RANDOM_DEV) && !defined(DROPBEAR_PRNGD_SOCKET)
+#error "You must choose one of DROPBEAR_PRNGD_SOCKET or DROPBEAR_RANDOM_DEV in options.h"
+#endif
+
+/* We use dropbear_client and dropbear_server as shortcuts to avoid redundant
+ * code, if we're just compiling as client or server */
+#if defined(DROPBEAR_SERVER) && defined(DROPBEAR_CLIENT)
+
+#define IS_DROPBEAR_SERVER (ses.isserver == 1)
+#define IS_DROPBEAR_CLIENT (ses.isserver == 0)
+
+#elif defined(DROPBEAR_SERVER)
+
+#define IS_DROPBEAR_SERVER 1
+#define IS_DROPBEAR_CLIENT 0
+
+#elif defined(DROPBEAR_CLIENT)
+
+#define IS_DROPBEAR_SERVER 0
+#define IS_DROPBEAR_CLIENT 1
+
+#else
+#error You must compiled with either DROPBEAR_CLIENT or DROPBEAR_SERVER selected
+#endif
--- a/tcpfwd.h	Thu Nov 29 11:38:06 2007 +0000
+++ b/tcpfwd.h	Tue Sep 23 16:05:04 2008 +0000
@@ -55,6 +55,7 @@
 
 /* Server */
 void recv_msg_global_request_remotetcp();
+
 extern const struct ChanType svr_chan_tcpdirect;
 
 /* Client */