changeset 33:f789045062e6

Progressing client support
author Matt Johnston <matt@ucc.asn.au>
date Tue, 27 Jul 2004 16:30:46 +0000
parents 8fd0cdbb5b1b
children e2a1eaa19f22
files Makefile.in auth.h cli-auth.c cli-authpasswd.c cli-kex.c cli-main.c cli-service.c cli-session.c common-kex.c common-runopts.c common-session.c dbmulti.c debug.h kex.h options.h packet.c runopts.h scp.c service.h session.h svr-agentfwd.c svr-auth.c svr-authpasswd.c svr-authpubkey.c svr-chansession.c svr-runopts.c svr-service.c svr-session.c
diffstat 28 files changed, 600 insertions(+), 177 deletions(-) [+]
line wrap: on
line diff
--- a/Makefile.in	Tue Jul 27 15:12:29 2004 +0000
+++ b/Makefile.in	Tue Jul 27 16:30:46 2004 +0000
@@ -27,7 +27,7 @@
 		svr-chansession.o svr-runopts.o svr-agentfwd.o svr-main.o svr-x11fwd.o
 
 CLIOBJS=cli-algo.o cli-main.o cli-auth.o cli-authpasswd.o cli-kex.o \
-		cli-session.o cli-service.o
+		cli-session.o cli-service.o cli-runopts.o
 
 CLISVROBJS=common-session.o packet.o common-algo.o common-kex.o \
 			common-channel.o common-chansession.o termcodes.o loginrec.o \
@@ -140,11 +140,12 @@
 dropbearkey: $(dropbearkeyobjs)
 dropbearconvert: $(dropbearconvertobjs)
 
-dropbear dbclient dropbearkey dropbearconvert: $(HEADERS)  $(LTC) $(LTM)
+dropbear dbclient dropbearkey dropbearconvert: $(HEADERS)  $(LTC) $(LTM) \
+													Makefile
 	$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $($@objs) $(LIBS)
 
 # scp doesn't use the libs so is special.
-scp: $(SCPOBJS)  $(HEADERS)
+scp: $(SCPOBJS)  $(HEADERS) Makefile
 	$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(SCPOBJS)
 
 
@@ -155,16 +156,16 @@
 	CFLAGS+=$(addprefix -DDBMULTI_, $(PROGRAMS)) -DDROPBEAR_MULTI
 endif
 
-dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM)
+dropbearmulti: $(HEADERS) $(MULTIOBJS) $(LTC) $(LTM) Makefile
 	$(LD) $(LDFLAGS) -o $(SPREFIX)$@$(EXEEXT) $(MULTIOBJS) $(LIBS)
 	@echo
 	@echo "You should now create symlinks to the programs you have included"
 	@echo "ie 'ln -s dropbearmulti dropbear'"
 
-$(LTC): $(HEADERS)
+$(LTC): options.h
 	cd libtomcrypt && $(MAKE) clean && $(MAKE)
 
-$(LTM): $(HEADERS)
+$(LTM): options.h
 	cd libtommath && $(MAKE)
 
 ltc-clean:
--- a/auth.h	Tue Jul 27 15:12:29 2004 +0000
+++ b/auth.h	Tue Jul 27 16:30:46 2004 +0000
@@ -27,12 +27,28 @@
 
 #include "includes.h"
 
-void authinitialise();
+void svr_authinitialise();
+void cli_authinitialise();
 
+void svr_auth_password();
+void svr_auth_pubkey();
+
+int cli_auth_password();
+int cli_auth_pubkey();
+
+/* Server functions */
 void recv_msg_userauth_request();
 void send_msg_userauth_failure(int partial, int incrfail);
 void send_msg_userauth_success();
 
+/* Client functions */
+void recv_msg_userauth_failure();
+void recv_msg_userauth_success();
+void cli_get_user();
+void cli_auth_getmethods();
+void cli_auth_try();
+
+
 #define MAX_USERNAME_LEN 25 /* arbitrary for the moment */
 
 #define AUTH_TYPE_PUBKEY	1 << 0
@@ -46,17 +62,23 @@
 #define AUTH_METHOD_PASSWORD "password"
 #define AUTH_METHOD_PASSWORD_LEN 8
 
+/* This structure is shared between server and client - it contains
+ * relatively little extraneous bits when used for the client rather than the
+ * server */
 struct AuthState {
 
 	char *username; /* This is the username the client presents to check. It
 					   is updated each run through, used for auth checking */
-	char *printableuser; /* stripped of control chars, used for logs etc */
-	struct passwd * pw;
 	unsigned char authtypes; /* Flags indicating which auth types are still 
 								valid */
 	unsigned int failcount; /* Number of (failed) authentication attempts.*/
-	unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have */
+	unsigned authdone : 1; /* 0 if we haven't authed, 1 if we have. Applies for
+							  client and server (though has differing [obvious]
+							  meanings). */
 
+	/* These are only used for the server */
+	char *printableuser; /* stripped of control chars, used for logs etc */
+	struct passwd * pw;
 
 };
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-auth.c	Tue Jul 27 16:30:46 2004 +0000
@@ -0,0 +1,148 @@
+#include "includes.h"
+#include "session.h"
+#include "auth.h"
+#include "dbutil.h"
+#include "buffer.h"
+#include "ssh.h"
+#include "packet.h"
+#include "runopts.h"
+
+void cli_authinitialise() {
+
+	memset(&ses.authstate, 0, sizeof(ses.authstate));
+}
+
+
+void cli_get_user() {
+
+	uid_t uid;
+	struct passwd *pw; 
+
+	TRACE(("enter cli_get_user"));
+	if (cli_opts.username != NULL) {
+		ses.authstate.username = cli_opts.username;
+	} else {
+		uid = getuid();
+		
+		pw = getpwuid(uid);
+		if (pw == NULL || pw->pw_name == NULL) {
+			dropbear_exit("Couldn't find username for current user");
+		}
+
+		ses.authstate.username = m_strdup(pw->pw_name);
+	}
+	TRACE(("leave cli_get_user: %s", cli_ses.username));
+}
+
+/* Send a "none" auth request to get available methods */
+void cli_auth_getmethods() {
+
+	TRACE(("enter cli_auth_getmethods"));
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+	buf_putstring(ses.writepayload, ses.authstate.username,
+			strlen(ses.authstate.username));
+	buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, 
+			SSH_SERVICE_CONNECTION_LEN);
+	buf_putstring(ses.writepayload, "none", 4); /* 'none' method */
+
+	encrypt_packet();
+	cli_ses.state = USERAUTH_METHODS_SENT;
+	TRACE(("leave cli_auth_getmethods"));
+
+}
+
+void recv_msg_userauth_failure() {
+
+	unsigned char * methods = NULL;
+	unsigned char * tok = NULL;
+	unsigned int methlen = 0;
+	unsigned int partial = 0;
+	unsigned int i = 0;
+
+	TRACE(("<- MSG_USERAUTH_FAILURE"));
+	TRACE(("enter recv_msg_userauth_failure"));
+
+	methods = buf_getstring(ses.payload, &methlen);
+
+	partial = buf_getbyte(ses.payload);
+
+	if (partial) {
+		dropbear_log(LOG_INFO, "Authentication partially succeeded, more attempts required");
+	} else {
+		ses.authstate.failcount++;
+	}
+
+	TRACE(("Methods (len %d): '%s'", methlen, methods));
+
+	ses.authstate.authdone=0;
+	ses.authstate.authtypes=0;
+
+	/* Split with nulls rather than commas */
+	for (i = 0; i < methlen; i++) {
+		if (methods[i] == ',') {
+			methods[i] = '\0';
+		}
+	}
+
+	tok = methods; /* tok stores the next method we'll compare */
+	for (i = 0; i <= methlen; i++) {
+		if (methods[i] == '\0') {
+			TRACE(("auth method '%s'\n", tok));
+#ifdef DROPBEAR_PUBKEY_AUTH
+			if (strncmp(AUTH_METHOD_PUBKEY, tok,
+				AUTH_METHOD_PUBKEY_LEN) == 0) {
+				ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
+			}
+#endif
+#ifdef DROPBEAR_PASSWORD_AUTH
+			if (strncmp(AUTH_METHOD_PASSWORD, tok,
+				AUTH_METHOD_PASSWORD_LEN) == 0) {
+				ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
+			}
+#endif
+			tok = &methods[i]; /* Must make sure we don't use it after
+								  the last loop, since it'll point
+								  to something undefined */
+		}
+	}
+
+	cli_ses.state = USERAUTH_FAIL_RCVD;
+		
+	TRACE(("leave recv_msg_userauth_failure"));
+}
+
+void recv_msg_userauth_success() {
+	TRACE(("received msg_userauth_success"));
+	ses.authstate.authdone = 1;
+}
+
+void cli_auth_try() {
+
+	TRACE(("enter cli_auth_try"));
+	int finished = 0;
+
+	CHECKCLEARTOWRITE();
+	
+	/* XXX We hardcode that we try a pubkey first */
+#ifdef DROPBEAR_PUBKEY_AUTH
+	if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
+		finished = cli_auth_pubkey();
+	}
+#endif
+
+#ifdef DROPBEAR_PASSWORD_AUTH
+	if (!finished && ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+		finished = cli_auth_password();
+	}
+#endif
+
+	if (!finished) {
+		dropbear_exit("No auth methods could be used.");
+	}
+
+	cli_ses.state = USERAUTH_REQ_SENT;
+	TRACE(("leave cli_auth_try"));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-authpasswd.c	Tue Jul 27 16:30:46 2004 +0000
@@ -0,0 +1,36 @@
+#include "includes.h"
+#include "buffer.h"
+#include "dbutil.h"
+#include "session.h"
+#include "ssh.h"
+
+int cli_auth_password() {
+
+	char* password = NULL;
+	TRACE(("enter cli_auth_password"));
+
+	CHECKCLEARTOWRITE();
+	password = getpass("Password: ");
+
+	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_REQUEST);
+
+	buf_putstring(ses.writepayload, ses.authstate.username,
+			strlen(ses.authstate.username));
+
+	buf_putstring(ses.writepayload, SSH_SERVICE_CONNECTION, 
+			SSH_SERVICE_CONNECTION_LEN);
+
+	buf_putstring(ses.writepayload, AUTH_METHOD_PASSWORD, 
+			AUTH_METHOD_PASSWORD_LEN);
+
+	buf_putbyte(ses.writepayload, 0); /* FALSE - so says the spec */
+
+	buf_putstring(ses.writepayload, password, strlen(password));
+
+	encrypt_packet();
+	m_burn(password, strlen(password));
+
+	TRACE(("leave cli_auth_password"));
+	return 1; /* Password auth can always be tried */
+
+}
--- a/cli-kex.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/cli-kex.c	Tue Jul 27 16:30:46 2004 +0000
@@ -34,6 +34,7 @@
 #include "bignum.h"
 #include "random.h"
 #include "runopts.h"
+#include "signkey.h"
 
 
 
--- a/cli-main.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/cli-main.c	Tue Jul 27 16:30:46 2004 +0000
@@ -1,6 +1,17 @@
-#include <includes.h>
+#include "includes.h"
+#include "dbutil.h"
+#include "runopts.h"
+#include "session.h"
 
+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);
+
+#if defined(DBMULTI_dbclient) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_dbclient) && defined(DROPBEAR_MULTI)
+int cli_main(int argc, char ** argv) {
+#else
 int main(int argc, char ** argv) {
+#endif
 
 	int sock;
 	char* error = NULL;
@@ -12,6 +23,9 @@
 
 	cli_getopts(argc, argv);
 
+	TRACE(("user='%s' host='%s' port='%s'", cli_opts.username,
+				cli_opts.remotehost, cli_opts.remoteport));
+
 	sock = connect_remote(cli_opts.remotehost, cli_opts.remoteport, 
 			0, &error);
 
@@ -23,7 +37,7 @@
 	len = strlen(cli_opts.remotehost);
 	len += 10; /* 16 bit port and leeway*/
 	hostandport = (char*)m_malloc(len);
-	snprintf(hostandport, len, "%s%d", 
+	snprintf(hostandport, len, "%s:%s", 
 			cli_opts.remotehost, cli_opts.remoteport);
 
 	cli_session(sock, hostandport);
@@ -31,3 +45,34 @@
 	/* not reached */
 	return -1;
 }
+#endif /* DBMULTI stuff */
+
+static void cli_dropbear_exit(int exitcode, const char* format, va_list param) {
+
+	char fmtbuf[300];
+
+	if (!sessinitdone) {
+		snprintf(fmtbuf, sizeof(fmtbuf), "exited: %s",
+				format);
+	} else {
+		snprintf(fmtbuf, sizeof(fmtbuf), 
+				"connection to %s@%s:%s exited: %s", 
+				cli_opts.username, cli_opts.remotehost, 
+				cli_opts.remoteport, format);
+	}
+
+	_dropbear_log(LOG_INFO, fmtbuf, param);
+
+	common_session_cleanup();
+	exit(exitcode);
+}
+
+static void cli_dropbear_log(int priority, const char* format, va_list param) {
+
+	char printbuf[1024];
+
+	vsnprintf(printbuf, sizeof(printbuf), format, param);
+
+	fprintf(stderr, "Dropbear: %s\n", printbuf);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cli-service.c	Tue Jul 27 16:30:46 2004 +0000
@@ -0,0 +1,62 @@
+#include "includes.h"
+#include "service.h"
+#include "dbutil.h"
+#include "packet.h"
+#include "buffer.h"
+#include "session.h"
+#include "ssh.h"
+
+void send_msg_service_request(char* servicename) {
+
+	TRACE(("enter send_msg_service_request: servicename='%s'", servicename));
+
+	CHECKCLEARTOWRITE();
+
+	buf_putbyte(ses.payload, SSH_MSG_SERVICE_REQUEST);
+	buf_putstring(ses.payload, servicename, strlen(servicename));
+
+	encrypt_packet();
+	TRACE(("leave send_msg_service_request"));
+}
+
+/* This just sets up the state variables right for the main client session loop
+ * to deal with */
+void recv_msg_service_accept() {
+
+	unsigned char* servicename;
+	unsigned int len;
+
+	TRACE(("enter recv_msg_service_accept"));
+
+	servicename = buf_getstring(ses.payload, &len);
+
+	/* ssh-userauth */
+	if (cli_ses.state = SERVICE_AUTH_REQ_SENT
+			&& len == SSH_SERVICE_USERAUTH_LEN
+			&& strncmp(SSH_SERVICE_USERAUTH, servicename, len) == 0) {
+
+		cli_ses.state = SERVICE_AUTH_ACCEPT_RCVD;
+		m_free(servicename);
+		TRACE(("leave recv_msg_service_accept: done ssh-userauth"));
+		return;
+	}
+
+	/* ssh-connection */
+	if (cli_ses.state = SERVICE_CONN_REQ_SENT
+			&& len == SSH_SERVICE_CONNECTION_LEN 
+			&& strncmp(SSH_SERVICE_CONNECTION, servicename, len) == 0) {
+
+		if (ses.authstate.authdone != 1) {
+			dropbear_exit("request for connection before auth");
+		}
+
+		cli_ses.state = SERVICE_CONN_ACCEPT_RCVD;
+		m_free(servicename);
+		TRACE(("leave recv_msg_service_accept: done ssh-connection"));
+		return;
+	}
+
+	dropbear_exit("unrecognised service accept");
+	/* m_free(servicename); not reached */
+
+}
--- a/cli-session.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/cli-session.c	Tue Jul 27 16:30:46 2004 +0000
@@ -8,9 +8,11 @@
 #include "tcpfwd-remote.h"
 #include "channel.h"
 #include "random.h"
+#include "service.h"
 
 static void cli_remoteclosed();
 static void cli_sessionloop();
+static void cli_session_init();
 
 struct clientsession cli_ses; /* GLOBAL */
 
@@ -28,6 +30,8 @@
 	{SSH_MSG_CHANNEL_CLOSE, recv_msg_channel_close},
 	{SSH_MSG_CHANNEL_OPEN_CONFIRMATION, recv_msg_channel_open_confirmation},
 	{SSH_MSG_CHANNEL_OPEN_FAILURE, recv_msg_channel_open_failure},
+	{SSH_MSG_USERAUTH_FAILURE, recv_msg_userauth_failure},
+	{SSH_MSG_USERAUTH_SUCCESS, recv_msg_userauth_success},
 	{0, 0} /* End */
 };
 
@@ -37,6 +41,7 @@
 	 * that forwarding */
 	NULL /* Null termination */
 };
+
 void cli_session(int sock, char* remotehost) {
 
 	crypto_init();
@@ -44,11 +49,9 @@
 
 	chaninitialise(cli_chantypes);
 
-	/* For printing "remote host closed" for the user */
-	session_remoteclosed = cli_remoteclosed;
 
-	/* packet handlers */
-	ses.packettypes = cli_packettypes;
+	/* Set up cli_ses vars */
+	cli_session_init();
 
 	/* Ready to go */
 	sessinitdone = 1;
@@ -66,27 +69,86 @@
 
 	/* Not reached */
 
+}
 
+static void cli_session_init() {
+
+	cli_ses.state = STATE_NOTHING;
+	cli_ses.kex_state = KEX_NOTHING;
+
+	/* For printing "remote host closed" for the user */
+	ses.remoteclosed = cli_remoteclosed;
+	ses.buf_match_algo = cli_buf_match_algo;
+
+	/* packet handlers */
+	ses.packettypes = cli_packettypes;
 }
 
+/* This function drives the progress of the session - it initiates KEX,
+ * service, userauth and channel requests */
 static void cli_sessionloop() {
 
+	TRACE(("enter cli_sessionloop"));
+
+	if (cli_ses.kex_state == KEX_NOTHING && ses.kexstate.recvkexinit) {
+		cli_ses.state = KEXINIT_RCVD;
+	}
+
+	if (cli_ses.state == KEXINIT_RCVD) {
+
+		/* We initiate the KEXDH. If DH wasn't the correct type, the KEXINIT
+		 * negotiation would have failed. */
+		send_msg_kexdh_init();
+		cli_ses.kex_state = KEXDH_INIT_SENT;
+		TRACE(("leave cli_sessionloop: done with KEXINIT_RCVD"));
+		return;
+	}
+
+	/* A KEX has finished, so we should go back to our KEX_NOTHING state */
+	if (cli_ses.kex_state != KEX_NOTHING && ses.kexstate.recvkexinit == 0
+			&& ses.kexstate.sentkexinit == 0) {
+		cli_ses.kex_state = KEX_NOTHING;
+	}
+
+	/* We shouldn't do anything else if a KEX is in progress */
+	if (cli_ses.kex_state != KEX_NOTHING) {
+		TRACE(("leave cli_sessionloop: kex_state != KEX_NOTHING"));
+		return;
+	}
+
+	/* We should exit if we haven't donefirstkex: we shouldn't reach here
+	 * in normal operation */
+	if (ses.kexstate.donefirstkex == 0) {
+		TRACE(("XXX XXX might be bad! leave cli_sessionloop: haven't donefirstkex"));
+	}
+
 	switch (cli_ses.state) {
 
-		KEXINIT_RCVD:
-			/* We initiate the KEX. If DH wasn't the correct type, the KEXINIT
-			 * negotiation would have failed. */
-			send_msg_kexdh_init();
-			cli_ses.state = KEXDH_INIT_SENT;
-			break;
+		case STATE_NOTHING:
+			/* We've got the transport layer sorted, we now need to request
+			 * userauth */
+			send_msg_service_request(SSH_SERVICE_USERAUTH);
+			cli_ses.state = SERVICE_AUTH_REQ_SENT;
+			return;
 
-		default:
-			break;
+		/* userauth code */
+		case SERVICE_AUTH_ACCEPT_RCVD:
+			cli_get_user();
+			cli_auth_getmethods();
+			cli_ses.state = USERAUTH_METHODS_SENT;
+			return;
+			
+		case USERAUTH_FAIL_RCVD:
+			cli_auth_try();
+			return;
+
+		/* XXX more here needed */
+
+
+	default:
+		break;
 	}
 
-	if (cli_ses.donefirstkex && !cli_ses.authdone) {
-
-
 
 }
 
@@ -97,5 +159,5 @@
 	 * already sent/received disconnect message(s) ??? */
 	close(ses.sock);
 	ses.sock = -1;
-	dropbear_exit("%s closed the connection", ses.remotehost);
+	dropbear_exit("remote closed the connection");
 }
--- a/common-kex.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/common-kex.c	Tue Jul 27 16:30:46 2004 +0000
@@ -5,7 +5,7 @@
  * This code is copied from the larger file "kex.c" 
  * some functions are verbatim, others are generalized --mihnea
  * 
- * Copyright (c) 2002,2003 Matt Johnston
+ * Copyright (c) 2002-2004 Matt Johnston
  * Portions Copyright (c) 2004 by Mihnea Stoenescu
  * All rights reserved.
  * 
@@ -54,10 +54,12 @@
 
 const int DH_G_VAL = 2;
 
+static void kexinitialise();
 static void gen_new_keys();
 #ifndef DISABLE_ZLIB
 static void gen_new_zstreams();
 #endif
+static void read_kex_algos();
 /* helper function for gen_new_keys */
 static void hashkeys(unsigned char *out, int outlen, 
 		const hash_state * hs, unsigned const char X);
@@ -145,6 +147,7 @@
 		kexinitialise(); /* we've finished with this kex */
 		TRACE((" -> DATAALLOWED=1"));
 		ses.dataallowed = 1; /* we can send other packets again now */
+		ses.kexstate.donefirstkex = 1;
 	} else {
 		ses.kexstate.sentnewkeys = 1;
 		TRACE(("SENTNEWKEYS=1"));
@@ -168,6 +171,7 @@
 		kexinitialise(); /* we've finished with this kex */
 	    TRACE((" -> DATAALLOWED=1"));
 	    ses.dataallowed = 1; /* we can send other packets again now */
+		ses.kexstate.donefirstkex = 1;
 	} else {
 		TRACE(("RECVNEWKEYS=1"));
 		ses.kexstate.recvnewkeys = 1;
@@ -177,8 +181,15 @@
 }
 
 
-/* Duplicated verbatim from kex.c --mihnea */
-void kexinitialise() {
+/* Set up the kex for the first time */
+void kexfirstinitialise() {
+
+	ses.kexstate.donefirstkex = 0;
+	kexinitialise();
+}
+
+/* Reset the kex state, ready for a new negotiation */
+static void kexinitialise() {
 
 	struct timeval tv;
 
@@ -404,7 +415,7 @@
 #ifdef DROPBEAR_CLIENT
 
 		/* read the peer's choice of algos */
-		read_kex_algos(cli_buf_match_algo);
+		read_kex_algos();
 
 		/* V_C, the client's version string (CR and NL excluded) */
 	    buf_putstring(ses.kexhashbuf,
@@ -423,14 +434,13 @@
 			buf_getptr(ses.payload, ses.payload->len),
 			ses.payload->len);
 
-		cli_ses.state = KEXINIT_RCVD;
 #endif
 	} else {
 		/* SERVER */
 #ifdef DROPBEAR_SERVER
 
 		/* read the peer's choice of algos */
-		read_kex_algos(svr_buf_match_algo);
+		read_kex_algos();
 		/* V_C, the client's version string (CR and NL excluded) */
 	    buf_putstring(ses.kexhashbuf, 
 			ses.remoteident, strlen((char*)ses.remoteident));
@@ -583,9 +593,7 @@
 
 /* read the other side's algo list. buf_match_algo is a callback to match
  * algos for the client or server. */
-void read_kex_algos(
-		algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[],
-			int *goodguess)) {
+static void read_kex_algos() {
 
 	algo_type * algo;
 	char * erralgo = NULL;
@@ -599,7 +607,7 @@
 	ses.newkeys = (struct key_context*)m_malloc(sizeof(struct key_context));
 
 	/* kex_algorithms */
-	algo = buf_match_algo(ses.payload, sshkex, &goodguess);
+	algo = ses.buf_match_algo(ses.payload, sshkex, &goodguess);
 	allgood &= goodguess;
 	if (algo == NULL) {
 		erralgo = "kex";
@@ -608,7 +616,7 @@
 	ses.newkeys->algo_kex = algo->val;
 
 	/* server_host_key_algorithms */
-	algo = buf_match_algo(ses.payload, sshhostkey, &goodguess);
+	algo = ses.buf_match_algo(ses.payload, sshhostkey, &goodguess);
 	allgood &= goodguess;
 	if (algo == NULL) {
 		erralgo = "hostkey";
@@ -617,7 +625,7 @@
 	ses.newkeys->algo_hostkey = algo->val;
 
 	/* encryption_algorithms_client_to_server */
-	algo = buf_match_algo(ses.payload, sshciphers, &goodguess);
+	algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
 	if (algo == NULL) {
 		erralgo = "enc c->s";
 		goto error;
@@ -625,7 +633,7 @@
 	ses.newkeys->recv_algo_crypt = (struct dropbear_cipher*)algo->data;
 
 	/* encryption_algorithms_server_to_client */
-	algo = buf_match_algo(ses.payload, sshciphers, &goodguess);
+	algo = ses.buf_match_algo(ses.payload, sshciphers, &goodguess);
 	if (algo == NULL) {
 		erralgo = "enc s->c";
 		goto error;
@@ -633,7 +641,7 @@
 	ses.newkeys->trans_algo_crypt = (struct dropbear_cipher*)algo->data;
 
 	/* mac_algorithms_client_to_server */
-	algo = buf_match_algo(ses.payload, sshhashes, &goodguess);
+	algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
 	if (algo == NULL) {
 		erralgo = "mac c->s";
 		goto error;
@@ -641,7 +649,7 @@
 	ses.newkeys->recv_algo_mac = (struct dropbear_hash*)algo->data;
 
 	/* mac_algorithms_server_to_client */
-	algo = buf_match_algo(ses.payload, sshhashes, &goodguess);
+	algo = ses.buf_match_algo(ses.payload, sshhashes, &goodguess);
 	if (algo == NULL) {
 		erralgo = "mac s->c";
 		goto error;
@@ -649,7 +657,7 @@
 	ses.newkeys->trans_algo_mac = (struct dropbear_hash*)algo->data;
 
 	/* compression_algorithms_client_to_server */
-	algo = buf_match_algo(ses.payload, sshcompress, &goodguess);
+	algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
 	if (algo == NULL) {
 		erralgo = "comp c->s";
 		goto error;
@@ -657,7 +665,7 @@
 	ses.newkeys->recv_algo_comp = algo->val;
 
 	/* compression_algorithms_server_to_client */
-	algo = buf_match_algo(ses.payload, sshcompress, &goodguess);
+	algo = ses.buf_match_algo(ses.payload, sshcompress, &goodguess);
 	if (algo == NULL) {
 		erralgo = "comp s->c";
 		goto error;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/common-runopts.c	Tue Jul 27 16:30:46 2004 +0000
@@ -0,0 +1,28 @@
+/*
+ * Dropbear - a SSH2 server
+ * 
+ * Copyright (c) 2002,2003 Matt Johnston
+ * All rights reserved.
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE. */
+
+#include "includes.h"
+#include "runopts.h"
+
+runopts opts; /* GLOBAL */
--- a/common-session.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/common-session.c	Tue Jul 27 16:30:46 2004 +0000
@@ -45,8 +45,6 @@
 /* this is set when we get SIGINT or SIGTERM, the handler is in main.c */
 int exitflag = 0; /* GLOBAL */
 
-void(*session_remoteclosed)() = NULL;
-
 
 static void checktimeouts();
 static int ident_readln(int fd, char* buf, int count);
@@ -63,7 +61,7 @@
 
 	ses.connecttimeout = 0;
 	
-	kexinitialise(); /* initialise the kex state */
+	kexfirstinitialise(); /* initialise the kex state */
 	chaninitialise(); /* initialise the channel state */
 
 	ses.writepayload = buf_new(MAX_TRANS_PAYLOAD_LEN);
@@ -104,8 +102,6 @@
 	ses.dh_K = NULL;
 	ses.remoteident = NULL;
 
-	ses.authdone = 0;
-
 	ses.chantypes = NULL;
 
 	ses.allowprivport = 0;
--- a/dbmulti.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/dbmulti.c	Tue Jul 27 16:30:46 2004 +0000
@@ -19,6 +19,11 @@
 			return dropbear_main(argc, argv);
 		}
 #endif
+#ifdef DBMULTI_dbclient
+		if (strcmp(progname, "dbclient") == 0) {
+			return cli_main(argc, argv);
+		}
+#endif
 #ifdef DBMULTI_dropbearkey
 		if (strcmp(progname, "dropbearkey") == 0) {
 			return dropbearkey_main(argc, argv);
@@ -41,6 +46,9 @@
 #ifdef DBMULTI_dropbear
 			"'dropbear' - the Dropbear server\n"
 #endif
+#ifdef DBMULTI_dbclient
+			"'dbclient' - the Dropbear client\n"
+#endif
 #ifdef DBMULTI_dropbearkey
 			"'dropbearkey' - the key generator\n"
 #endif
--- a/debug.h	Tue Jul 27 15:12:29 2004 +0000
+++ b/debug.h	Tue Jul 27 16:30:46 2004 +0000
@@ -36,7 +36,7 @@
 /* Define this to print trace statements - very verbose */
 /* Caution: Don't use this in an unfriendly environment (ie unfirewalled),
  * since the printing does not sanitise strings etc */
-//#define DEBUG_TRACE
+#define DEBUG_TRACE
 
 /* All functions writing to the cleartext payload buffer call
  * CHECKCLEARTOWRITE() before writing. This is only really useful if you're
--- a/kex.h	Tue Jul 27 15:12:29 2004 +0000
+++ b/kex.h	Tue Jul 27 16:30:46 2004 +0000
@@ -32,15 +32,11 @@
 void recv_msg_kexinit();
 void send_msg_newkeys();
 void recv_msg_newkeys();
-void kexinitialise();
+void kexfirstinitialise();
 void gen_kexdh_vals(mp_int *dh_pub, mp_int *dh_priv);
 void kexdh_comb_key(mp_int *dh_pub_us, mp_int *dh_priv, mp_int *dh_pub_them,
 		sign_key *hostkey);
 
-void read_kex_algos(
-		algo_type*(buf_match_algo)(buffer*buf, algo_type localalgos[],
-			int *goodguess));
-
 void recv_msg_kexdh_init(); // server
 
 void send_msg_kexdh_init(); // client
@@ -59,6 +55,9 @@
 	unsigned sentnewkeys : 1; /* set once we've send/recv'ed MSG_NEWKEYS*/
 	unsigned recvnewkeys : 1;
 
+	unsigned donefirstkex : 1; /* Set to 1 after the first kex has completed,
+								  ie the transport layer has been set up */
+
 	long lastkextime; /* time of the last kex */
 	unsigned int datatrans; /* data transmitted since last kex */
 	unsigned int datarecv; /* data received since last kex */
--- a/options.h	Tue Jul 27 15:12:29 2004 +0000
+++ b/options.h	Tue Jul 27 16:30:46 2004 +0000
@@ -29,8 +29,6 @@
  * Define compile-time options below - the "#ifndef DROPBEAR_XXX .... #endif"
  * parts are to allow for commandline -DDROPBEAR_XXX options etc.
  ******************************************************************/
-#define DROPBEAR_SERVER
-//#define DROPBEAR_CLIENT
 
 #ifndef DROPBEAR_PORT
 #define DROPBEAR_PORT 22
@@ -48,7 +46,6 @@
  * perhaps 20% slower for pubkey operations (it is probably worth experimenting
  * if you want to use this) */
 /*#define NO_FAST_EXPTMOD*/
-#define DROPBEAR_SMALL_CODE
 
 /* Enable X11 Forwarding */
 #define ENABLE_X11FWD
@@ -114,7 +111,7 @@
 /* Authentication types to enable, at least one required.
    RFC Draft requires pubkey auth, and recommends password */
 #define DROPBEAR_PASSWORD_AUTH
-#define DROPBEAR_PUBKEY_AUTH
+//#define DROPBEAR_PUBKEY_AUTH
 
 /* Random device to use - you must specify _one only_.
  * DEV_RANDOM is recommended on hosts with a good /dev/urandom, otherwise use
@@ -162,20 +159,8 @@
 /* This is used by the scp binary when used as a client binary */
 #define _PATH_SSH_PROGRAM "/usr/bin/ssh"
 
-/* Multi-purpose binary  configuration - if you want to make the combined
- * binary, first define DROPBEAR_MULTI, and then define which of the three
- * components you want. You should then compile Dropbear with 
- * "make clean; make dropbearmulti". You'll need to install the binary
- * manually, see MULTI for details */
-
-/* #define DROPBEAR_MULTI */
-
-/* The three multi binaries: dropbear, dropbearkey, dropbearconvert
- * Comment out these if you don't want some of them */
-#define DBMULTI_DROPBEAR
-#define DBMULTI_KEY
-#define DBMULTI_CONVERT
-
+/* Multi-purpose binary configuration has now moved. Look at the top
+ * of the Makefile for instructions, or INSTALL */
 
 /*******************************************************************
  * You shouldn't edit below here unless you know you need to.
@@ -246,7 +231,7 @@
 #define DROPBEAR_COMP_ZLIB 1
 
 /* Required for pubkey auth */
-#ifdef DROPBEAR_PUBKEY_AUTH
+#if defined(DROPBEAR_PUBKEY_AUTH) || defined(DROPBEAR_CLIENT)
 #define DROPBEAR_SIGNKEY_VERIFY
 #endif
 
--- a/packet.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/packet.c	Tue Jul 27 16:30:46 2004 +0000
@@ -73,7 +73,7 @@
 	} 
 
 	if (written == 0) {
-		session_remoteclosed();
+		ses.remoteclosed();
 	}
 
 	if (written == len) {
@@ -122,7 +122,7 @@
 	len = read(ses.sock, buf_getptr(ses.readbuf, maxlen), maxlen);
 
 	if (len == 0) {
-		session_remoteclosed();
+		ses.remoteclosed();
 	}
 
 	if (len < 0) {
@@ -171,7 +171,7 @@
 	len = read(ses.sock, buf_getwriteptr(ses.readbuf, maxlen),
 			maxlen);
 	if (len == 0) {
-		session_remoteclosed();
+		ses.remoteclosed();
 	}
 	if (len < 0) {
 		if (errno == EINTR) {
--- a/runopts.h	Tue Jul 27 15:12:29 2004 +0000
+++ b/runopts.h	Tue Jul 27 16:30:46 2004 +0000
@@ -79,7 +79,11 @@
 /* Uncompleted XXX matt */
 typedef struct cli_runopts {
 
-	int todo;
+	char *remotehost;
+	char *remoteport;
+
+	char *username;
+	/* XXX TODO */
 
 } cli_runopts;
 
--- a/scp.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/scp.c	Tue Jul 27 16:30:46 2004 +0000
@@ -229,8 +229,12 @@
 void toremote(char *, int, char *[]);
 void usage(void);
 
-int
+#if defined(DBMULTI_scp) || !defined(DROPBEAR_MULTI)
+#if defined(DBMULTI_scp) && defined(DROPBEAR_MULTI)
+int scp_main(int argc, char **argv)
+#else
 main(int argc, char **argv)
+#endif
 {
 	int ch, fflag, tflag, status;
 	double speed;
@@ -379,6 +383,7 @@
 	}
 	exit(errs != 0);
 }
+#endif /* DBMULTI stuff */
 
 void
 toremote(char *targ, int argc, char **argv)
--- a/service.h	Tue Jul 27 15:12:29 2004 +0000
+++ b/service.h	Tue Jul 27 16:30:46 2004 +0000
@@ -25,6 +25,7 @@
 #ifndef _SERVICE_H_
 #define _SERVICE_H_
 
-void recv_msg_service_request();
+void recv_msg_service_request(); /* Server */
+void send_msg_service_request(); /* Client */
 
 #endif /* _SERVICE_H_ */
--- a/session.h	Tue Jul 27 15:12:29 2004 +0000
+++ b/session.h	Tue Jul 27 16:30:46 2004 +0000
@@ -45,7 +45,6 @@
 void checktimeouts();
 void session_identification();
 
-extern void(*session_remoteclosed)();
 
 /* Server */
 void svr_session(int sock, int childpipe, char *remotehost);
@@ -135,13 +134,18 @@
 	buffer* transkexinit; /* the kexinit packet we send should be kept so we
 							 can add it to the hash when generating keys */
 
+	algo_type*(*buf_match_algo)(buffer*buf, algo_type localalgos[],
+			int *goodguess); /* The function to use to choose which algorithm
+								to use from the ones presented by the remote
+								side. Is specific to the client/server mode,
+								hence the function-pointer callback.*/
 
-	unsigned char authdone;	/* Indicates when authentication has been
-							   completed. This applies to both client and
-							   server - in the server it gets set to 1 when
-							   authentication is successful, in the client it
-							   is set when the server has told us that auth
-							   succeeded */
+	void(*remoteclosed)(); /* A callback to handle closure of the
+									  remote connection */
+
+
+	struct AuthState authstate; /* Common amongst client and server, since most
+								   struct elements are common */
 
 	/* Channel related */
 	struct Channel ** channels; /* these pointers may be null */
@@ -165,7 +169,6 @@
 	/* Server specific options */
 	int childpipe; /* kept open until we successfully authenticate */
 	/* userauth */
-	struct AuthState authstate;
 
 	struct ChildPid * childpids; /* array of mappings childpid<->channel */
 	unsigned int childpidsize;
@@ -173,17 +176,30 @@
 };
 
 typedef enum {
-	NOTHING,
+	KEX_NOTHING,
 	KEXINIT_RCVD,
 	KEXDH_INIT_SENT,
-	KEXDH_REPLY_RCVD,
+	KEXDONE,
+
+} cli_kex_state;
+
+typedef enum {
+	STATE_NOTHING,
+	SERVICE_AUTH_REQ_SENT,
+	SERVICE_AUTH_ACCEPT_RCVD,
+	SERVICE_CONN_REQ_SENT,
+	SERVICE_CONN_ACCEPT_RCVD,
+	USERAUTH_METHODS_SENT,
+	USERAUTH_REQ_SENT,
+	USERAUTH_FAIL_RCVD,
 
 } cli_state;
 
 struct clientsession {
 
 	mp_int *dh_e, *dh_x; /* Used during KEX */
-	cli_state state; /* Used to progress the KEX/auth/channelsession etc */
+	cli_kex_state kex_state; /* Used for progressing KEX */
+	cli_state state; /* Used to progress auth/channelsession etc */
 	int something; /* XXX */
 	unsigned donefirstkex : 1; /* Set when we set sentnewkeys, never reset */
 
--- a/svr-agentfwd.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/svr-agentfwd.c	Tue Jul 27 16:30:46 2004 +0000
@@ -152,8 +152,8 @@
 		 * for themselves */
 		uid = getuid();
 		gid = getgid();
-		if ((setegid(svr_ses.authstate.pw->pw_gid)) < 0 ||
-			(seteuid(svr_ses.authstate.pw->pw_uid)) < 0) {
+		if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
+			(seteuid(ses.authstate.pw->pw_uid)) < 0) {
 			dropbear_exit("failed to set euid");
 		}
 
@@ -215,8 +215,8 @@
 	/* drop to user privs to make the dir/file */
 	uid = getuid();
 	gid = getgid();
-	if ((setegid(svr_ses.authstate.pw->pw_gid)) < 0 ||
-		(seteuid(svr_ses.authstate.pw->pw_uid)) < 0) {
+	if ((setegid(ses.authstate.pw->pw_gid)) < 0 ||
+		(seteuid(ses.authstate.pw->pw_uid)) < 0) {
 		dropbear_exit("failed to set euid");
 	}
 
--- a/svr-auth.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/svr-auth.c	Tue Jul 27 16:30:46 2004 +0000
@@ -41,9 +41,9 @@
 static void send_msg_userauth_banner();
 
 /* initialise the first time for a session, resetting all parameters */
-void authinitialise() {
+void svr_authinitialise() {
 
-	svr_ses.authstate.failcount = 0;
+	ses.authstate.failcount = 0;
 	authclear();
 	
 }
@@ -53,17 +53,13 @@
  * on initialisation */
 static void authclear() {
 	
-	ses.authdone = 0;
-	svr_ses.authstate.pw = NULL;
-	svr_ses.authstate.username = NULL;
-	svr_ses.authstate.printableuser = NULL;
-	svr_ses.authstate.authtypes = 0;
+	memset(&ses.authstate, 0, sizeof(ses.authstate));
 #ifdef DROPBEAR_PUBKEY_AUTH
-	svr_ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
+	ses.authstate.authtypes |= AUTH_TYPE_PUBKEY;
 #endif
 #ifdef DROPBEAR_PASSWORD_AUTH
 	if (svr_opts.noauthpass) {
-		svr_ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
+		ses.authstate.authtypes |= AUTH_TYPE_PASSWORD;
 	}
 #endif
 
@@ -103,7 +99,7 @@
 	TRACE(("enter recv_msg_userauth_request"));
 
 	/* ignore packets if auth is already done */
-	if (ses.authdone == 1) {
+	if (ses.authstate.authdone == 1) {
 		return;
 	}
 
@@ -147,12 +143,12 @@
 
 #ifdef DROPBEAR_PASSWORD_AUTH
 	if (!svr_opts.noauthpass &&
-			!(svr_opts.norootpass && svr_ses.authstate.pw->pw_uid == 0) ) {
+			!(svr_opts.norootpass && ses.authstate.pw->pw_uid == 0) ) {
 		/* user wants to try password auth */
 		if (methodlen == AUTH_METHOD_PASSWORD_LEN &&
 				strncmp(methodname, AUTH_METHOD_PASSWORD,
 					AUTH_METHOD_PASSWORD_LEN) == 0) {
-			passwordauth();
+			svr_auth_password();
 			goto out;
 		}
 	}
@@ -163,7 +159,7 @@
 	if (methodlen == AUTH_METHOD_PUBKEY_LEN &&
 			strncmp(methodname, AUTH_METHOD_PUBKEY,
 				AUTH_METHOD_PUBKEY_LEN) == 0) {
-		pubkeyauth();
+		svr_auth_pubkey();
 		goto out;
 	}
 #endif
@@ -192,21 +188,21 @@
 	}
 
 	/* new user or username has changed */
-	if (svr_ses.authstate.username == NULL ||
-		strcmp(username, svr_ses.authstate.username) != 0) {
+	if (ses.authstate.username == NULL ||
+		strcmp(username, ses.authstate.username) != 0) {
 			/* the username needs resetting */
-			if (svr_ses.authstate.username != NULL) {
+			if (ses.authstate.username != NULL) {
 				dropbear_log(LOG_WARNING, "client trying multiple usernames");
-				m_free(svr_ses.authstate.username);
+				m_free(ses.authstate.username);
 			}
 			authclear();
-			svr_ses.authstate.pw = getpwnam((char*)username);
-			svr_ses.authstate.username = m_strdup(username);
-			m_free(svr_ses.authstate.printableuser);
+			ses.authstate.pw = getpwnam((char*)username);
+			ses.authstate.username = m_strdup(username);
+			m_free(ses.authstate.printableuser);
 	}
 
 	/* check that user exists */
-	if (svr_ses.authstate.pw == NULL) {
+	if (ses.authstate.pw == NULL) {
 		TRACE(("leave checkusername: user '%s' doesn't exist", username));
 		dropbear_log(LOG_WARNING,
 				"login attempt for nonexistent user");
@@ -215,10 +211,10 @@
 	}
 
 	/* We can set it once we know its a real user */
-	svr_ses.authstate.printableuser = m_strdup(svr_ses.authstate.pw->pw_name);
+	ses.authstate.printableuser = m_strdup(ses.authstate.pw->pw_name);
 
 	/* check for non-root if desired */
-	if (svr_opts.norootlogin && svr_ses.authstate.pw->pw_uid == 0) {
+	if (svr_opts.norootlogin && ses.authstate.pw->pw_uid == 0) {
 		TRACE(("leave checkusername: root login disabled"));
 		dropbear_log(LOG_WARNING, "root login rejected");
 		send_msg_userauth_failure(0, 1);
@@ -226,18 +222,18 @@
 	}
 
 	/* check for an empty password */
-	if (svr_ses.authstate.pw->pw_passwd[0] == '\0') {
+	if (ses.authstate.pw->pw_passwd[0] == '\0') {
 		TRACE(("leave checkusername: empty pword"));
 		dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
-				svr_ses.authstate.printableuser);
+				ses.authstate.printableuser);
 		send_msg_userauth_failure(0, 1);
 		return DROPBEAR_FAILURE;
 	}
 
-	TRACE(("shell is %s", svr_ses.authstate.pw->pw_shell));
+	TRACE(("shell is %s", ses.authstate.pw->pw_shell));
 
 	/* check that the shell is set */
-	usershell = svr_ses.authstate.pw->pw_shell;
+	usershell = ses.authstate.pw->pw_shell;
 	if (usershell[0] == '\0') {
 		/* empty shell in /etc/passwd means /bin/sh according to passwd(5) */
 		usershell = "/bin/sh";
@@ -258,7 +254,7 @@
 	endusershell();
 	TRACE(("no matching shell"));
 	dropbear_log(LOG_WARNING, "user '%s' has invalid shell, rejected",
-				svr_ses.authstate.printableuser);
+				ses.authstate.printableuser);
 	send_msg_userauth_failure(0, 1);
 	return DROPBEAR_FAILURE;
 	
@@ -266,7 +262,7 @@
 	endusershell();
 	TRACE(("matching shell"));
 
-	TRACE(("uid = %d", svr_ses.authstate.pw->pw_uid));
+	TRACE(("uid = %d", ses.authstate.pw->pw_uid));
 	TRACE(("leave checkusername"));
 	return DROPBEAR_SUCCESS;
 
@@ -290,14 +286,14 @@
 	/* put a list of allowed types */
 	typebuf = buf_new(30); /* long enough for PUBKEY and PASSWORD */
 
-	if (svr_ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
+	if (ses.authstate.authtypes & AUTH_TYPE_PUBKEY) {
 		buf_putbytes(typebuf, AUTH_METHOD_PUBKEY, AUTH_METHOD_PUBKEY_LEN);
-		if (svr_ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+		if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
 			buf_putbyte(typebuf, ',');
 		}
 	}
 	
-	if (svr_ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
+	if (ses.authstate.authtypes & AUTH_TYPE_PASSWORD) {
 		buf_putbytes(typebuf, AUTH_METHOD_PASSWORD, AUTH_METHOD_PASSWORD_LEN);
 	}
 
@@ -311,18 +307,18 @@
 
 	if (incrfail) {
 		usleep(300000); /* XXX improve this */
-		svr_ses.authstate.failcount++;
+		ses.authstate.failcount++;
 	}
 
-	if (svr_ses.authstate.failcount >= MAX_AUTH_TRIES) {
+	if (ses.authstate.failcount >= MAX_AUTH_TRIES) {
 		char * userstr;
 		/* XXX - send disconnect ? */
 		TRACE(("Max auth tries reached, exiting"));
 
-		if (svr_ses.authstate.printableuser == NULL) {
+		if (ses.authstate.printableuser == NULL) {
 			userstr = "is invalid";
 		} else {
-			userstr = svr_ses.authstate.printableuser;
+			userstr = ses.authstate.printableuser;
 		}
 		dropbear_exit("Max auth tries reached - user %s", userstr);
 	}
@@ -340,9 +336,9 @@
 	buf_putbyte(ses.writepayload, SSH_MSG_USERAUTH_SUCCESS);
 	encrypt_packet();
 
-	ses.authdone = 1;
+	ses.authstate.authdone = 1;
 
-	if (svr_ses.authstate.pw->pw_uid == 0) {
+	if (ses.authstate.pw->pw_uid == 0) {
 		ses.allowprivport = 1;
 	}
 
--- a/svr-authpasswd.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/svr-authpasswd.c	Tue Jul 27 16:30:46 2004 +0000
@@ -35,7 +35,7 @@
 
 /* Process a password auth request, sending success or failure messages as
  * appropriate */
-void passwordauth() {
+void svr_auth_password() {
 	
 #ifdef HAVE_SHADOW_H
 	struct spwd *spasswd;
@@ -47,10 +47,10 @@
 
 	unsigned char changepw;
 
-	passwdcrypt = svr_ses.authstate.pw->pw_passwd;
+	passwdcrypt = ses.authstate.pw->pw_passwd;
 #ifdef HAVE_SHADOW_H
 	/* get the shadow password if possible */
-	spasswd = getspnam(svr_ses.authstate.pw->pw_name);
+	spasswd = getspnam(ses.authstate.pw->pw_name);
 	if (spasswd != NULL && spasswd->sp_pwdp != NULL) {
 		passwdcrypt = spasswd->sp_pwdp;
 	}
@@ -66,7 +66,7 @@
 	 * in auth.c */
 	if (passwdcrypt[0] == '\0') {
 		dropbear_log(LOG_WARNING, "user '%s' has blank password, rejected",
-				svr_ses.authstate.printableuser);
+				ses.authstate.printableuser);
 		send_msg_userauth_failure(0, 1);
 		return;
 	}
@@ -92,12 +92,12 @@
 		/* successful authentication */
 		dropbear_log(LOG_NOTICE, 
 				"password auth succeeded for '%s'",
-				svr_ses.authstate.printableuser);
+				ses.authstate.printableuser);
 		send_msg_userauth_success();
 	} else {
 		dropbear_log(LOG_WARNING,
 				"bad password attempt for '%s'",
-				svr_ses.authstate.printableuser);
+				ses.authstate.printableuser);
 		send_msg_userauth_failure(0, 1);
 	}
 
--- a/svr-authpubkey.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/svr-authpubkey.c	Tue Jul 27 16:30:46 2004 +0000
@@ -50,7 +50,7 @@
 
 /* process a pubkey auth request, sending success or failure message as
  * appropriate */
-void pubkeyauth() {
+void svr_auth_pubkey() {
 
 	unsigned char testkey; /* whether we're just checking if a key is usable */
 	unsigned char* algo = NULL; /* pubkey algo */
@@ -113,12 +113,12 @@
 				signbuf->len) == DROPBEAR_SUCCESS) {
 		dropbear_log(LOG_NOTICE,
 				"pubkey auth succeeded for '%s' with key %s",
-				svr_ses.authstate.printableuser, fp);
+				ses.authstate.printableuser, fp);
 		send_msg_userauth_success();
 	} else {
 		dropbear_log(LOG_WARNING,
 				"pubkey auth bad signature for '%s' with key %s",
-				svr_ses.authstate.printableuser, fp);
+				ses.authstate.printableuser, fp);
 		send_msg_userauth_failure(0, 1);
 	}
 	m_free(fp);
@@ -178,7 +178,7 @@
 	if (have_algo(algo, algolen, sshhostkey) == DROPBEAR_FAILURE) {
 		dropbear_log(LOG_WARNING,
 				"pubkey auth attempt with unknown algo for '%s'",
-				svr_ses.authstate.printableuser);
+				ses.authstate.printableuser);
 		goto out;
 	}
 
@@ -190,12 +190,12 @@
 
 	/* we don't need to check pw and pw_dir for validity, since
 	 * its been done in checkpubkeyperms. */
-	len = strlen(svr_ses.authstate.pw->pw_dir);
+	len = strlen(ses.authstate.pw->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", 
-				svr_ses.authstate.pw->pw_dir);
+				ses.authstate.pw->pw_dir);
 
 	/* open the file */
 	authfile = fopen(filename, "r");
@@ -352,19 +352,19 @@
 
 	TRACE(("enter checkpubkeyperms"));
 
-	assert(svr_ses.authstate.pw);
-	if (svr_ses.authstate.pw->pw_dir == NULL) {
+	assert(ses.authstate.pw);
+	if (ses.authstate.pw->pw_dir == NULL) {
 		goto out;
 	}
 
-	if ((len = strlen(svr_ses.authstate.pw->pw_dir)) == 0) {
+	if ((len = strlen(ses.authstate.pw->pw_dir)) == 0) {
 		goto out;
 	}
 
 	/* allocate max required pathname storage,
 	 * = path + "/.ssh/authorized_keys" + '\0' = pathlen + 22 */
 	filename = m_malloc(len + 22);
-	strncpy(filename, svr_ses.authstate.pw->pw_dir, len+1);
+	strncpy(filename, ses.authstate.pw->pw_dir, len+1);
 
 	/* check ~ */
 	if (checkfileperm(filename) != DROPBEAR_SUCCESS) {
@@ -406,7 +406,7 @@
 		return DROPBEAR_FAILURE;
 	}
 	/* check ownership - user or root only*/
-	if (filestat.st_uid != svr_ses.authstate.pw->pw_uid
+	if (filestat.st_uid != ses.authstate.pw->pw_uid
 			&& filestat.st_uid != 0) {
 		TRACE(("leave checkfileperm: wrong ownership"));
 		return DROPBEAR_FAILURE;
--- a/svr-chansession.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/svr-chansession.c	Tue Jul 27 16:30:46 2004 +0000
@@ -239,7 +239,7 @@
 
 	if (chansess->tty) {
 		/* write the utmp/wtmp login record */
-		li = login_alloc_entry(chansess->pid, svr_ses.authstate.username,
+		li = login_alloc_entry(chansess->pid, ses.authstate.username,
 				ses.remotehost, chansess->tty);
 		login_logout(li);
 		login_free_entry(li);
@@ -425,7 +425,7 @@
 		dropbear_exit("out of memory"); /* TODO disconnect */
 	}
 
-	pty_setowner(svr_ses.authstate.pw, chansess->tty);
+	pty_setowner(ses.authstate.pw, chansess->tty);
 	pty_change_window_size(chansess->master, chansess->termr, chansess->termc,
 			chansess->termw, chansess->termh);
 
@@ -683,7 +683,7 @@
 
 		/* write the utmp/wtmp login record - must be after changing the
 		 * terminal used for stdout with the dup2 above */
-		li= login_alloc_entry(getpid(), svr_ses.authstate.username,
+		li= login_alloc_entry(getpid(), ses.authstate.username,
 				ses.remotehost, chansess->tty);
 		login_login(li);
 		login_free_entry(li);
@@ -695,10 +695,10 @@
 			/* don't show the motd if ~/.hushlogin exists */
 
 			/* 11 == strlen("/hushlogin\0") */
-			len = strlen(svr_ses.authstate.pw->pw_dir) + 11; 
+			len = strlen(ses.authstate.pw->pw_dir) + 11; 
 
 			hushpath = m_malloc(len);
-			snprintf(hushpath, len, "%s/hushlogin", svr_ses.authstate.pw->pw_dir);
+			snprintf(hushpath, len, "%s/hushlogin", ses.authstate.pw->pw_dir);
 
 			if (stat(hushpath, &sb) < 0) {
 				/* more than a screenful is stupid IMHO */
@@ -808,10 +808,10 @@
 	/* We can only change uid/gid as root ... */
 	if (getuid() == 0) {
 
-		if ((setgid(svr_ses.authstate.pw->pw_gid) < 0) ||
-			(initgroups(svr_ses.authstate.pw->pw_name, 
-						svr_ses.authstate.pw->pw_gid) < 0) ||
-			(setuid(svr_ses.authstate.pw->pw_uid) < 0)) {
+		if ((setgid(ses.authstate.pw->pw_gid) < 0) ||
+			(initgroups(ses.authstate.pw->pw_name, 
+						ses.authstate.pw->pw_gid) < 0) ||
+			(setuid(ses.authstate.pw->pw_uid) < 0)) {
 			dropbear_exit("error changing user");
 		}
 	} else {
@@ -822,29 +822,29 @@
 		 * 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() != svr_ses.authstate.pw->pw_uid) {
+		if (getuid() != ses.authstate.pw->pw_uid) {
 			dropbear_exit("couldn't	change user as non-root");
 		}
 	}
 
 	/* an empty shell should be interpreted as "/bin/sh" */
-	if (svr_ses.authstate.pw->pw_shell[0] == '\0') {
+	if (ses.authstate.pw->pw_shell[0] == '\0') {
 		usershell = "/bin/sh";
 	} else {
-		usershell = svr_ses.authstate.pw->pw_shell;
+		usershell = ses.authstate.pw->pw_shell;
 	}
 
 	/* set env vars */
-	addnewvar("USER", svr_ses.authstate.pw->pw_name);
-	addnewvar("LOGNAME", svr_ses.authstate.pw->pw_name);
-	addnewvar("HOME", svr_ses.authstate.pw->pw_dir);
+	addnewvar("USER", ses.authstate.pw->pw_name);
+	addnewvar("LOGNAME", ses.authstate.pw->pw_name);
+	addnewvar("HOME", ses.authstate.pw->pw_dir);
 	addnewvar("SHELL", usershell);
 	if (chansess->term != NULL) {
 		addnewvar("TERM", chansess->term);
 	}
 
 	/* change directory */
-	if (chdir(svr_ses.authstate.pw->pw_dir) < 0) {
+	if (chdir(ses.authstate.pw->pw_dir) < 0) {
 		dropbear_exit("error changing directory");
 	}
 
--- a/svr-runopts.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/svr-runopts.c	Tue Jul 27 16:30:46 2004 +0000
@@ -106,8 +106,8 @@
 	opts.nolocaltcp = 0;
 	opts.noremotetcp = 0;
 	/* not yet
-	svr_opts.ipv4 = 1;
-	svr_opts.ipv6 = 1;
+	opts.ipv4 = 1;
+	opts.ipv6 = 1;
 	*/
 #ifdef DO_MOTD
 	svr_opts.domotd = 1;
--- a/svr-service.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/svr-service.c	Tue Jul 27 16:30:46 2004 +0000
@@ -56,7 +56,7 @@
 	/* ssh-connection */
 	if (len == SSH_SERVICE_CONNECTION_LEN &&
 			(strncmp(SSH_SERVICE_CONNECTION, name, len) == 0)) {
-		if (ses.authdone != 1) {
+		if (ses.authstate.authdone != 1) {
 			dropbear_exit("request for connection before auth");
 		}
 
@@ -70,7 +70,6 @@
 	/* TODO this should be a MSG_DISCONNECT */
 	dropbear_exit("unrecognised SSH_MSG_SERVICE_REQUEST");
 
-	TRACE(("leave recv_msg_service_request"));
 
 }
 
--- a/svr-session.c	Tue Jul 27 15:12:29 2004 +0000
+++ b/svr-session.c	Tue Jul 27 16:30:46 2004 +0000
@@ -79,7 +79,7 @@
 
 	/* Initialise server specific parts of the session */
 	svr_ses.childpipe = childpipe;
-	authinitialise();
+	svr_authinitialise();
 	chaninitialise(svr_chantypes);
 	svr_chansessinitialise();
 
@@ -90,10 +90,11 @@
 	ses.connecttimeout = timeout.tv_sec + AUTH_TIMEOUT;
 
 	/* set up messages etc */
-	session_remoteclosed = svr_remoteclosed;
+	ses.remoteclosed = svr_remoteclosed;
 
 	/* packet handlers */
 	ses.packettypes = svr_packettypes;
+	ses.buf_match_algo = svr_buf_match_algo;
 
 	/* We're ready to go now */
 	sessinitdone = 1;
@@ -123,16 +124,16 @@
 		/* before session init */
 		snprintf(fmtbuf, sizeof(fmtbuf), 
 				"premature exit: %s", format);
-	} else if (svr_ses.authstate.authdone) {
+	} else if (ses.authstate.authdone) {
 		/* user has authenticated */
 		snprintf(fmtbuf, sizeof(fmtbuf),
 				"exit after auth (%s): %s", 
-				svr_ses.authstate.printableuser, format);
-	} else if (svr_ses.authstate.printableuser) {
+				ses.authstate.printableuser, format);
+	} else if (ses.authstate.printableuser) {
 		/* we have a potential user */
 		snprintf(fmtbuf, sizeof(fmtbuf), 
 				"exit before auth (user '%s', %d fails): %s",
-				svr_ses.authstate.printableuser, svr_ses.authstate.failcount, format);
+				ses.authstate.printableuser, ses.authstate.failcount, format);
 	} else {
 		/* before userauth */
 		snprintf(fmtbuf, sizeof(fmtbuf),