The unified diff between revisions [833d0ade..] and [6bbab836..] is displayed below. It can also be downloaded as a raw diff.

#
#
# add_file "svr-authpubkeyoptions.c"
#  content [f3a44bf261ca76cf73ba35a1c6eab8c52a88844a]
#
# add_file "sysoptions.h"
#  content [d6c87215a21aed518e5c25a4911e09835093fa10]
#
# patch "CHANGES"
#  from [e725f331510859501b092d9dd9e8d6c258e73e3b]
#    to [fa1aff3c8b4e2bec870d55939ef70ecc207304a6]
#
# patch "Makefile.in"
#  from [b0525e17721c99af6ac2eb24c5f3800ffc01f6e9]
#    to [ff2db8ce4b9d774fdcc300611c5d987ee58dbfac]
#
# patch "auth.h"
#  from [15c3cde23ddce7783b47c81148c4ef89c8e3a5d8]
#    to [404d52a03f577f4c0f41f9fa97bf56eaba044aae]
#
# patch "chansession.h"
#  from [c9c4cc35dbe6c70d3ee6987b1c4dc8a79d5adcae]
#    to [9bb4ad4cd198dbcedf4389aba67135c58acdc0b4]
#
# patch "cli-authinteract.c"
#  from [7f57db7f1f69fa57583d1b29dade1a578105c4b8]
#    to [e89e5e29ab9332f6ce3a091fb3bd629c0fd6469d]
#
# patch "cli-chansession.c"
#  from [8fa645a2a787126dcfe333bc0c5ce185c9b87d74]
#    to [c566ff178f6679a267aec4df92fb2259a1f52434]
#
# patch "cli-main.c"
#  from [ba080ad9e807acfcee3fa0debc7be6c426f22887]
#    to [3b1ab6de581d29ab29e2f7655310bd9ebaba6425]
#
# patch "cli-runopts.c"
#  from [6435ac6046e57fb5d30bdad5297dca84c67f828b]
#    to [a7a8de7ac8a60c743bb5c9bdb4776772853b3454]
#
# patch "cli-session.c"
#  from [7c6dc26c6d23f92785979f9e89da6e42630b15e1]
#    to [32a8c16817cf01a78c969e53d98073352e5692a5]
#
# patch "common-channel.c"
#  from [375ff10b1da7d82baf11af5e032ac82184639ce4]
#    to [b03248e356ab9bef60e8e14bae501f22fe53d2c2]
#
# patch "common-session.c"
#  from [f15be48e3436391db518800b58e0dc7cbcdbfa28]
#    to [2dd840a19e09d0bd69ab9c5353e218d0213398fb]
#
# patch "dbclient.1"
#  from [272e5fd6b8d6dc6756aa1e20b84459269e31eabb]
#    to [d90e1a86f650942d7158ce117e87fb2c19559d03]
#
# patch "dbutil.c"
#  from [f05d525e641f5e2f061ae3ef428e168026de6f70]
#    to [118e377841783badd92191d3666eabee0e4b9dd8]
#
# patch "dbutil.h"
#  from [c85093e85be71cfba82966a2e83d8d8b0a6f8ab1]
#    to [801a91e3fee11c70ea61efbd1d56ebc8b50044fd]
#
# patch "debian/README.runit"
#  from [42d3c81e6fbd458aead257dafdcaf7cee510ecf5]
#    to [8a6be1d836ac5e0997b662368e010939575b7acc]
#
# patch "debian/changelog"
#  from [dea08a9c54f137c7783257b232bca7c71a1119ad]
#    to [1423f6f888a4900b154b73b347933bd7a0823766]
#
# patch "debian/control"
#  from [b5ef433283069be55d16e283c643fc388d473557]
#    to [3ed2e096b863c10fab48c81dba5f0f8f902169db]
#
# patch "debian/dropbear.README.Debian"
#  from [97d45e9ee2e8d974bdfe7f48ae2090475e790f2f]
#    to [17d13ce05a8fb5092891ced2866bc0d3557cff3d]
#
# patch "debian/dropbear.init"
#  from [232b19ca8c51ddb29b9cc0c4bcfb25e071f82127]
#    to [88fc77a4f765fa8e1d73fe8ea1e672139a23bbaa]
#
# patch "debian/dropbear.postinst"
#  from [5665ae46016d735143457d5bf069a1eaee0f21d0]
#    to [3315185791bfd9c2080bb82ddcfb52de4910993d]
#
# patch "debian/rules"
#  from [58521b19dfa543e95e1c580e894b09df24cc514b]
#    to [9b2f3b4d6ca6b86df0b67debfdd349c7f04004f2]
#
# patch "debug.h"
#  from [e7a35458ab4dfd22551fcbb3dd4c95e3040095c8]
#    to [d715d223951e95f3128d23507646626592980ae6]
#
# patch "genrsa.c"
#  from [e6709602978123e50233339e9e68059b8c8f9a61]
#    to [d904ecb1c370837a97739141ba8d7a0f5d26292a]
#
# patch "keyimport.c"
#  from [aed5ec4c19aaab025da56fbb39c241c04219230b]
#    to [bfd8def469b9ded7d36c4097b39b7988a4b1e62d]
#
# patch "options.h"
#  from [e73d26719374d625cd0cbb5c0bd02f9c53e91ba8]
#    to [4f14edfc00a03d193bfd40cbf3860ce4d01c990d]
#
# patch "packet.c"
#  from [2a54e5d8d0f0d9f099b9fe07a52caae5a8d6cd51]
#    to [cf8adfd7c6b39f8633e67bea035bdabb7bcd2479]
#
# patch "runopts.h"
#  from [32d5267b31f12c6d3160512d459367f2eee90ee7]
#    to [c05c00ecf9672692b7e04d143f0f2cb89df35cf4]
#
# patch "session.h"
#  from [698e52464e29293ca989ac2547c438669587337c]
#    to [5cb6c0fbbdea6dd7a58f39ce8518cbe8a6e20aaa]
#
# patch "svr-agentfwd.c"
#  from [4c686d351feecba37348ea3a9e0117a2425ae490]
#    to [792c284ad752241038b893995ff5e8c126709694]
#
# patch "svr-auth.c"
#  from [4d806df1e0e48b101a419330aa8858ddc770ef99]
#    to [26eb88c51e2b4d929bbaa7e4e07d8d435aca3dde]
#
# patch "svr-authpam.c"
#  from [f2afec7b54253236f53a65cd79e302f28a14945c]
#    to [7317b23f5900a7be17f5fbc8221550092663e1f7]
#
# patch "svr-authpasswd.c"
#  from [163ade7f267461bf9e873fb27d4ad2d98a857133]
#    to [597aa1de9b7467b194217ec9f4421de659bcc358]
#
# patch "svr-authpubkey.c"
#  from [396e8bc5105d11dd4695b3abb8ba2dc2f7aea374]
#    to [386fd28cf535d16f5df189932791b240cb22e476]
#
# patch "svr-chansession.c"
#  from [507d7fb29b5b0a8c412603048937d6cf61a54787]
#    to [0ab73b2cd6a67d3c6459491fad8a3e0959a99e30]
#
# patch "svr-main.c"
#  from [ca315d545592e772311d4ce9e2dcf4d7de5fd4d1]
#    to [c780b9e90ca48b31db2341c1ecac552c845ebe8c]
#
# patch "svr-runopts.c"
#  from [4d5f8179cb0c9f5caaae528bc458971259cc8abd]
#    to [e8e74dc0309d7a9773a74e951852bd4a4bfd3dc4]
#
# patch "svr-session.c"
#  from [ce034997dee4480afd43fa50d4cabf228b2a3564]
#    to [d3d822234c194abbd81e6df5279bab564fb77c57]
#
# patch "svr-tcpfwd.c"
#  from [dba94b5f63e6e5cfcc4eb61809f890715c8eacf5]
#    to [b960334e12ddbe24db8743eb5568a6e47cae8bdf]
#
# patch "svr-x11fwd.c"
#  from [79db165bbf9982b3cfee9f616e5626656a35d13e]
#    to [e7fba52fe0081a827faa30ec479b3dfaabb3e926]
#
# patch "tcpfwd.h"
#  from [16c1fbd5251921ecc87e704bc2afe2076dd55a4e]
#    to [087e84191b75b5950efd328d7d9a78d13474ff20]
#
============================================================
--- svr-authpubkeyoptions.c	f3a44bf261ca76cf73ba35a1c6eab8c52a88844a
+++ svr-authpubkeyoptions.c	f3a44bf261ca76cf73ba35a1c6eab8c52a88844a
@@ -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 <ylo@cs.hut.fi>
+ * 	Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, 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
============================================================
--- sysoptions.h	d6c87215a21aed518e5c25a4911e09835093fa10
+++ sysoptions.h	d6c87215a21aed518e5c25a4911e09835093fa10
@@ -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
============================================================
--- CHANGES	e725f331510859501b092d9dd9e8d6c258e73e3b
+++ CHANGES	fa1aff3c8b4e2bec870d55939ef70ecc207304a6
@@ -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
============================================================
--- Makefile.in	b0525e17721c99af6ac2eb24c5f3800ffc01f6e9
+++ Makefile.in	ff2db8ce4b9d774fdcc300611c5d987ee58dbfac
@@ -23,7 +23,7 @@ SVROBJS=svr-kex.o svr-algo.o svr-auth.o
 		atomicio.o compat.o  fake-rfc2553.o

 SVROBJS=svr-kex.o svr-algo.o svr-auth.o sshpty.o \
-		svr-authpasswd.o svr-authpubkey.o svr-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

============================================================
--- auth.h	15c3cde23ddce7783b47c81148c4ef89c8e3a5d8
+++ auth.h	404d52a03f577f4c0f41f9fa97bf56eaba044aae
@@ -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_pam();
 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 @@ struct AuthState {
 							   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 @@ struct SignKeyList {

 };

+#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_ */
============================================================
--- chansession.h	c9c4cc35dbe6c70d3ee6987b1c4dc8a79d5adcae
+++ chansession.h	9bb4ad4cd198dbcedf4389aba67135c58acdc0b4
@@ -78,6 +78,9 @@ void cli_chansess_winchange();
 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;
============================================================
--- cli-authinteract.c	7f57db7f1f69fa57583d1b29dade1a578105c4b8
+++ cli-authinteract.c	e89e5e29ab9332f6ce3a091fb3bd629c0fd6469d
@@ -77,6 +77,11 @@ void recv_msg_userauth_info_request() {

 	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);
============================================================
--- cli-chansession.c	8fa645a2a787126dcfe333bc0c5ce185c9b87d74
+++ cli-chansession.c	c566ff178f6679a267aec4df92fb2259a1f52434
@@ -321,7 +321,11 @@ static void send_chansess_shell_req(stru
 	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 @@ static void send_chansess_shell_req(stru
 	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,7 +354,13 @@ static int cli_initchansess(struct Chann
 	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 @@ static int cli_initchansess(struct Chann
 	}

 	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 @@ void cli_send_chansess_request() {
 	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
============================================================
--- cli-main.c	ba080ad9e807acfcee3fa0debc7be6c426f22887
+++ cli-main.c	3b1ab6de581d29ab29e2f7655310bd9ebaba6425
@@ -32,6 +32,8 @@ static void cli_dropbear_log(int priorit
 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) {
 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 @@ int main(int argc, char ** argv) {
 		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 @@ int main(int argc, char ** argv) {
 	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 @@ static void cli_dropbear_log(int UNUSED(
 	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;
+	}
+}
============================================================
--- cli-runopts.c	6435ac6046e57fb5d30bdad5297dca84c67f828b
+++ cli-runopts.c	a7a8de7ac8a60c743bb5c9bdb4776772853b3454
@@ -33,18 +33,23 @@ static void printhelp();
 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 @@ static void printhelp() {
 					"-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 @@ static void printhelp() {
 #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 @@ void cli_getopts(int argc, char ** argv)
 #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 @@ void cli_getopts(int argc, char ** argv)
 	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 @@ void cli_getopts(int argc, char ** argv)
 	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 @@ void cli_getopts(int argc, char ** argv)
 			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 @@ void cli_getopts(int argc, char ** argv)
 				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 @@ void cli_getopts(int argc, char ** argv)
 					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 @@ void cli_getopts(int argc, char ** argv)
 			/* 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 @@ void cli_getopts(int argc, char ** argv)
 		}
 	}

+	/* And now a few sanity checks and setup */
+
 	if (cli_opts.remotehost == NULL) {
 		printhelp();
 		exit(EXIT_FAILURE);
@@ -319,21 +365,23 @@ void cli_getopts(int argc, char ** argv)
 		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 @@ static void loadidentityfile(const char*
 }
 #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;

-	uid_t uid;
-	struct passwd *pw = 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);
+	}
+
+	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 @@ static void parsehostname(char* orighost
 	}

 	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 @@ static void parsehostname(char* orighost
 	}
 }

+#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 @@ static void addforward(char* origstr, st

 	/* 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;
 	}

============================================================
--- cli-session.c	7c6dc26c6d23f92785979f9e89da6e42630b15e1
+++ cli-session.c	32a8c16817cf01a78c969e53d98073352e5692a5
@@ -77,13 +77,13 @@ static const struct ChanType *cli_chanty
 	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,22 +200,8 @@ static void cli_sessionloop() {
 			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) {
 				int devnull;
 				/* keeping stdin open steals input from the terminal and
@@ -238,7 +224,13 @@ static void cli_sessionloop() {
 #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 @@ static void cli_remoteclosed() {

 	/* 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");
 }

============================================================
--- common-channel.c	375ff10b1da7d82baf11af5e032ac82184639ce4
+++ common-channel.c	b03248e356ab9bef60e8e14bae501f22fe53d2c2
@@ -261,6 +261,7 @@ static void check_close(struct Channel *

 /* 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 @@ static void check_close(struct Channel *
 	{
 		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 @@ static void check_close(struct Channel *
 	}

 	/* 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 @@ void recv_msg_channel_request() {

 	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 {
============================================================
--- common-session.c	f15be48e3436391db518800b58e0dc7cbcdbfa28
+++ common-session.c	2dd840a19e09d0bd69ab9c5353e218d0213398fb
@@ -52,14 +52,15 @@ int exitflag = 0; /* GLOBAL */


 /* 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,12 +138,12 @@ void session_loop(void(*loophandler)())
 		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.
 		   SIGCHLD in svr-chansession is the only one currently. */
@@ -183,12 +184,14 @@ void session_loop(void(*loophandler)())
 		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 @@ void session_identification() {
 	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 @@ static long select_timeout() {
 		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);
+}
+
============================================================
--- dbclient.1	272e5fd6b8d6dc6756aa1e20b84459269e31eabb
+++ dbclient.1	d90e1a86f650942d7158ce117e87fb2c19559d03
@@ -86,6 +86,19 @@ if 0 disables keepalives.
 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 (matt@ucc.asn.au).
 .br
============================================================
--- dbutil.c	f05d525e641f5e2f061ae3ef428e168026de6f70
+++ dbutil.c	118e377841783badd92191d3666eabee0e4b9dd8
@@ -146,7 +146,7 @@ void dropbear_trace(const char* format,
 	}

 	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 @@ int connect_remote(const char* remotehos
 	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 @@ int connect_remote(const char* remotehos
 	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 @@ void disallow_core() {
 	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;
+	}
+}
============================================================
--- dbutil.h	c85093e85be71cfba82966a2e83d8d8b0a6f8ab1
+++ dbutil.h	801a91e3fee11c70ea61efbd1d56ebc8b50044fd
@@ -49,6 +49,9 @@ int dropbear_listen(const char* address,
 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 disallow_core();
 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}
============================================================
--- debian/README.runit	42d3c81e6fbd458aead257dafdcaf7cee510ecf5
+++ debian/README.runit	8a6be1d836ac5e0997b662368e010939575b7acc
@@ -31,16 +31,16 @@ run script

  # 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 <pape@smarden.org>, Fri, 02 Mar 2007 20:41:08 +0000
- -- Gerrit Pape <pape@smarden.org>, Sun, 16 May 2004 15:52:34 +0000
============================================================
--- debian/changelog	dea08a9c54f137c7783257b232bca7c71a1119ad
+++ debian/changelog	1423f6f888a4900b154b73b347933bd7a0823766
@@ -1,15 +1,76 @@
-dropbear (0.50-0.1) unstable; urgency=low
+dropbear (0.51-0.1) unstable; urgency=low

   * New upstream release.

- -- Matt Johnston <matt@ucc.asn.au>  Wed, 8 Aug 2007 11:22:33 +0800
+ -- Matt Johnston <matt@ucc.asn.au>  Thu, 27 Mar 2008 19:14:00 +0900

-dropbear (0.49-0.1) unstable; urgency=low
+dropbear (0.50-4) unstable; urgency=low

-  * New upstream release.
+  * 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.

- -- Matt Johnston <matt@ucc.asn.au>  Fri, 23 Feb 2007 00:44:00 +0900
+ -- Gerrit Pape <pape@smarden.org>  Thu, 06 Mar 2008 19:06:58 +0000

+dropbear (0.50-3) 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 <pape@smarden.org>  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 <pape@smarden.org>  Mon, 24 Sep 2007 16:49:17 +0000
+
+dropbear (0.50-1) unstable; urgency=low
+
+  * 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 <pape@smarden.org>  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.
+
+ -- Gerrit Pape <pape@smarden.org>  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 <pape@smarden.org>  Fri,  2 Mar 2007 20:48:18 +0000
+
 dropbear (0.48.1-1) unstable; urgency=medium

   * new upstream point release.
============================================================
--- debian/control	b5ef433283069be55d16e283c643fc388d473557
+++ debian/control	3ed2e096b863c10fab48c81dba5f0f8f902169db
@@ -3,12 +3,12 @@ Build-Depends: libz-dev
 Priority: optional
 Maintainer: Gerrit Pape <pape@smarden.org>
 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
============================================================
--- debian/dropbear.README.Debian	97d45e9ee2e8d974bdfe7f48ae2090475e790f2f
+++ debian/dropbear.README.Debian	17d13ce05a8fb5092891ced2866bc0d3557cff3d
@@ -1,52 +1,19 @@ Dropbear for Debian
 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.
+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 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.
+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.
+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.

-Replacing OpenSSH "sshd" with Dropbear
---------------------------------------
-
-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:
-
-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
-
 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.
============================================================
--- debian/dropbear.init	232b19ca8c51ddb29b9cc0c4bcfb25e071f82127
+++ debian/dropbear.init	88fc77a4f765fa8e1d73fe8ea1e672139a23bbaa
@@ -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 @@ test -x "$DAEMON" || cancel "$DAEMON doe
 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"
============================================================
--- debian/dropbear.postinst	5665ae46016d735143457d5bf069a1eaee0f21d0
+++ debian/dropbear.postinst	3315185791bfd9c2080bb82ddcfb52de4910993d
@@ -69,3 +69,11 @@ fi
     /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
============================================================
--- debian/rules	58521b19dfa543e95e1c580e894b09df24cc514b
+++ debian/rules	9b2f3b4d6ca6b86df0b67debfdd349c7f04004f2
@@ -28,7 +28,7 @@ patch-stamp:
 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 @@ clean: deb-checkdir deb-checkuid
 	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: deb-checkdir deb-checkuid build
 	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 \
============================================================
--- debug.h	e7a35458ab4dfd22551fcbb3dd4c95e3040095c8
+++ debug.h	d715d223951e95f3128d23507646626592980ae6
@@ -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
============================================================
--- genrsa.c	e6709602978123e50233339e9e68059b8c8f9a61
+++ genrsa.c	d904ecb1c370837a97739141ba8d7a0f5d26292a
@@ -62,17 +62,13 @@ rsa_key * gen_rsa_priv_key(unsigned int
 		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) {
============================================================
--- keyimport.c	aed5ec4c19aaab025da56fbb39c241c04219230b
+++ keyimport.c	bfd8def469b9ded7d36c4097b39b7988a4b1e62d
@@ -701,7 +701,6 @@ static int openssh_write(const char *fil
 	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;
============================================================
--- options.h	e73d26719374d625cd0cbb5c0bd02f9c53e91ba8
+++ options.h	4f14edfc00a03d193bfd40cbf3860ce4d01c990d
@@ -60,6 +60,10 @@ etc) slower (perhaps by 50%). Recommende
 #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 @@ etc) slower (perhaps by 50%). Recommende
 #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 @@ etc) slower (perhaps by 50%). Recommende
  * 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 disa
 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.
- *******************************************************************/
+/* Some other defines (that mostly should be left alone) are defined
+ * in sysoptions.h */
+#include "sysoptions.h"

-#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
-
 #endif /* _OPTIONS_H_ */
============================================================
--- packet.c	2a54e5d8d0f0d9f099b9fe07a52caae5a8d6cd51
+++ packet.c	cf8adfd7c6b39f8633e67bea035bdabb7bcd2479
@@ -61,7 +61,7 @@ void write_packet() {
 	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 @@ void read_packet() {
 	 * 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 @@ static void read_packet_init() {
 	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();
============================================================
--- runopts.h	32d5267b31f12c6d3160512d459367f2eee90ee7
+++ runopts.h	c05c00ecf9672692b7e04d143f0f2cb89df35cf4
@@ -37,7 +37,7 @@ typedef struct runopts {
 	int listen_fwd_all;
 #endif
 	unsigned int recv_window;
-	time_t keepalive_secs;
+	unsigned int keepalive_secs;

 } runopts;

@@ -101,6 +101,7 @@ typedef struct cli_runopts {
 	char *remotehost;
 	char *remoteport;

+	char *own_user;
 	char *username;

 	char *cmd;
@@ -108,6 +109,7 @@ typedef struct cli_runopts {
 	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 @@ typedef struct cli_runopts {
 							  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;
============================================================
--- session.h	698e52464e29293ca989ac2547c438669587337c
+++ session.h	5cb6c0fbbdea6dd7a58f39ce8518cbe8a6e20aaa
@@ -41,12 +41,14 @@ extern int exitflag;
 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, cons
 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 @@ struct sshsession {
 							(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 */

============================================================
--- svr-agentfwd.c	4c686d351feecba37348ea3a9e0117a2425ae490
+++ svr-agentfwd.c	792c284ad752241038b893995ff5e8c126709694
@@ -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 agentreq(struct ChanSess * chansess)

 	int fd;

+	if (!svr_pubkey_allows_agentfwd()) {
+		return DROPBEAR_FAILURE;
+	}
+
 	if (chansess->agentlistener != NULL) {
 		return DROPBEAR_FAILURE;
 	}
@@ -150,8 +155,8 @@ void agentcleanup(struct ChanSess * chan
 		 * 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 @@ static int bindagent(int fd, struct Chan
 	/* 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");
 	}

============================================================
--- svr-auth.c	4d806df1e0e48b101a419330aa8858ddc770ef99
+++ svr-auth.c	26eb88c51e2b4d929bbaa7e4e07d8d435aca3dde
@@ -42,6 +42,10 @@ void svr_authinitialise() {
 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 @@ static void authclear() {
 		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 @@ void recv_msg_userauth_request() {

 #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 @@ void recv_msg_userauth_request() {

 #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 @@ out:
 	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 @@ static int checkusername(unsigned char *

 	char* listshell = NULL;
 	char* usershell = NULL;
-
 	TRACE(("enter checkusername"))
 	if (userlen > MAX_USERNAME_LEN) {
 		return DROPBEAR_FAILURE;
@@ -210,13 +226,12 @@ static int checkusername(unsigned char *
 				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 @@ static int checkusername(unsigned char *
 		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 @@ static int checkusername(unsigned char *
 	}

 	/* 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 @@ static int checkusername(unsigned char *
 	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 @@ goodshell:
 	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 @@ void send_msg_userauth_failure(int parti
 		/* 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 @@ void send_msg_userauth_success() {
 	ses.connect_time = 0;


-	if (ses.authstate.pw->pw_uid == 0) {
+	if (ses.authstate.pw_uid == 0) {
 		ses.allowprivport = 1;
 	}

============================================================
--- svr-authpam.c	f2afec7b54253236f53a65cd79e302f28a14945c
+++ svr-authpam.c	7317b23f5900a7be17f5fbc8221550092663e1f7
@@ -195,7 +195,7 @@ void svr_auth_pam() {
 	/* 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 @@ void svr_auth_pam() {
 				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 @@ void svr_auth_pam() {
 				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 @@ void svr_auth_pam() {

 	/* 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();

============================================================
--- svr-authpasswd.c	163ade7f267461bf9e873fb27d4ad2d98a857133
+++ svr-authpasswd.c	597aa1de9b7467b194217ec9f4421de659bcc358
@@ -46,10 +46,10 @@ void svr_auth_password() {

 	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 @@ void svr_auth_password() {
 	 * 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 @@ void svr_auth_password() {
 		/* 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);
 	}
============================================================
--- svr-authpubkey.c	396e8bc5105d11dd4695b3abb8ba2dc2f7aea374
+++ svr-authpubkey.c	386fd28cf535d16f5df189932791b240cb22e476
@@ -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 @@ void svr_auth_pubkey() {
 				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 @@ static int checkpubkey(unsigned char* al
 	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 @@ static int checkpubkey(unsigned char* al

 	/* 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 @@ static int checkpubkey(unsigned char* al
 	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 @@ static int checkpubkey(unsigned char* al
 		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 @@ out:
 		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 @@ static int checkpubkeyperms() {

 	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 @@ static int checkfileperm(char * filename
 		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 @@ static int checkfileperm(char * filename
 	return DROPBEAR_SUCCESS;
 }

+#endif
-
-#endif
============================================================
--- svr-chansession.c	507d7fb29b5b0a8c412603048937d6cf61a54787
+++ svr-chansession.c	0ab73b2cd6a67d3c6459491fad8a3e0959a99e30
@@ -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 sessionwinchange(struct ChanS
 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 @@ static int sessionpty(struct ChanSess *

 	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 @@ static int sessionpty(struct ChanSess *
 		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 @@ static int sessioncommand(struct Channel
 		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 @@ static int sessioncommand(struct Channel
 #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 @@ static int noptycommand(struct Channel *
  * pty.
  * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */
 static int noptycommand(struct Channel *channel, struct ChanSess *chansess) {
+	int ret;

-	int infds[2];
-	int outfds[2];
-	int errfds[2];
-	pid_t pid;
-	unsigned int i;
-
 	TRACE(("enter noptycommand"))
+	ret = spawn_command(execchild, chansess,
+			&channel->writefd, &channel->readfd, &channel->errfd,
+			&chansess->pid);

-	/* 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;
+	if (ret == DROPBEAR_FAILURE) {
+		return ret;
+	}

-#ifdef __uClinux__
-	pid = vfork();
-#else
-	pid = fork();
-#endif
+	ses.maxfd = MAX(ses.maxfd, channel->writefd);
+	ses.maxfd = MAX(ses.maxfd, channel->readfd);
+	ses.maxfd = MAX(ses.maxfd, channel->errfd);

-	if (pid < 0)
-		return DROPBEAR_FAILURE;
+	addchildpid(chansess, chansess->pid);

-	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 */
-#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 */
-
-	} 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 @@ static int ptycommand(struct Channel *ch
 		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,13 +820,10 @@ static void addchildpid(struct ChanSess

 /* 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) {
+static void execchild(void *user_data) {
+	struct ChanSess *chansess = user_data;
+	char *usershell = NULL;

-	char *argv[4];
-	char * usershell = NULL;
-	char * baseshell = NULL;
-	unsigned int i;
-
     /* with uClinux we'll have vfork()ed, so don't want to overwrite the
      * hostkey. can't think of a workaround to clear it */
 #ifndef __uClinux__
@@ -885,12 +835,6 @@ static void execchild(struct ChanSess *c
 	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 @@ static void execchild(struct ChanSess *c
 	/* 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 @@ static void execchild(struct ChanSess *c
 		 * 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,33 +896,9 @@ static void execchild(struct ChanSess *c
 	agentset(chansess);
 #endif

-	/* Re-enable SIGPIPE for the executed process */
-	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR) {
-		dropbear_exit("signal() error");
-	}
+	usershell = m_strdup(get_user_shell());
+	run_shell_command(chansess->cmd, ses.maxfd, usershell);

-	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);
-
 	/* only reached on error */
 	dropbear_exit("child failed");
 }
============================================================
--- svr-main.c	ca315d545592e772311d4ce9e2dcf4d7de5fd4d1
+++ svr-main.c	c780b9e90ca48b31db2341c1ecac552c845ebe8c
@@ -266,7 +266,11 @@ void main_noinetd() {
 				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 @@ void main_noinetd() {
 				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++) {
============================================================
--- svr-runopts.c	4d5f8179cb0c9f5caaae528bc458971259cc8abd
+++ svr-runopts.c	e8e74dc0309d7a9773a74e951852bd4a4bfd3dc4
@@ -284,16 +284,13 @@ void svr_getopts(int argc, char ** argv)

 	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);
 		}
 	}
============================================================
--- svr-session.c	ce034997dee4480afd43fa50d4cabf228b2a3564
+++ svr-session.c	d3d822234c194abbd81e6df5279bab564fb77c57
@@ -80,7 +80,7 @@ void svr_session(int sock, int childpipe
     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 @@ void svr_dropbear_exit(int exitcode, con
 		/* 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 @@ void svr_dropbear_exit(int exitcode, con

 	_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 @@ void svr_dropbear_log(int priority, cons
 						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 @@ static void svr_remoteclosed() {
 /* 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");

 }
============================================================
--- svr-tcpfwd.c	dba94b5f63e6e5cfcc4eb61809f890715c8eacf5
+++ svr-tcpfwd.c	b960334e12ddbe24db8743eb5568a6e47cae8bdf
@@ -32,6 +32,7 @@
 #include "packet.h"
 #include "listener.h"
 #include "runopts.h"
+#include "auth.h"

 #ifdef ENABLE_SVR_REMOTETCPFWD

@@ -72,7 +73,7 @@ void recv_msg_global_request_remotetcp()

 	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 @@ static int newtcpdirect(struct Channel *
 	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;
 	}
============================================================
--- svr-x11fwd.c	79db165bbf9982b3cfee9f616e5626656a35d13e
+++ svr-x11fwd.c	e7fba52fe0081a827faa30ec479b3dfaabb3e926
@@ -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 x11req(struct ChanSess * chansess) {

 	int fd;

+	if (!svr_pubkey_allows_x11fwd()) {
+		return DROPBEAR_FAILURE;
+	}
+
 	/* we already have an x11 connection */
 	if (chansess->x11listener != NULL) {
 		return DROPBEAR_FAILURE;
============================================================
--- tcpfwd.h	16c1fbd5251921ecc87e704bc2afe2076dd55a4e
+++ tcpfwd.h	087e84191b75b5950efd328d7d9a78d13474ff20
@@ -55,6 +55,7 @@ void recv_msg_global_request_remotetcp()

 /* Server */
 void recv_msg_global_request_remotetcp();
+
 extern const struct ChanType svr_chan_tcpdirect;

 /* Client */