- Big OpenBSD CVS update (mainly beginnings of SSH2 infrastructure)
   - [auth.c session.c sshd.c auth.h]
     split sshd.c -> auth.c session.c sshd.c plus cleanup and goto-removal
   - [bufaux.c bufaux.h]
     support ssh2 bignums
   - [channels.c channels.h clientloop.c sshd.c nchan.c nchan.h packet.c]
     [readconf.c ssh.c ssh.h serverloop.c]
     replace big switch() with function tables (prepare for ssh2)
   - [ssh2.h]
     ssh2 message type codes
   - [sshd.8]
     reorder Xr to avoid cutting
   - [serverloop.c]
     close(fdin) if fdin != fdout, shutdown otherwise, ok theo@
   - [channels.c]
     missing close
     allow bigger packets
   - [cipher.c cipher.h]
     support ssh2 ciphers
   - [compress.c]
     cleanup, less code
   - [dispatch.c dispatch.h]
     function tables for different message types
   - [log-server.c]
     do not log() if debuggin to stderr
     rename a cpp symbol, to avoid param.h collision
   - [mpaux.c]
     KNF
   - [nchan.c]
     sync w/ channels.c
diff --git a/ChangeLog b/ChangeLog
index 2462417..585686e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+20000401
+ - Big OpenBSD CVS update (mainly beginnings of SSH2 infrastructure)
+   - [auth.c session.c sshd.c auth.h]
+     split sshd.c -> auth.c session.c sshd.c plus cleanup and goto-removal
+   - [bufaux.c bufaux.h]
+     support ssh2 bignums
+   - [channels.c channels.h clientloop.c sshd.c nchan.c nchan.h packet.c]
+     [readconf.c ssh.c ssh.h serverloop.c]
+     replace big switch() with function tables (prepare for ssh2)
+   - [ssh2.h]
+     ssh2 message type codes
+   - [sshd.8]
+     reorder Xr to avoid cutting
+   - [serverloop.c]
+     close(fdin) if fdin != fdout, shutdown otherwise, ok theo@
+   - [channels.c]
+     missing close
+     allow bigger packets
+   - [cipher.c cipher.h]
+     support ssh2 ciphers
+   - [compress.c]
+     cleanup, less code
+   - [dispatch.c dispatch.h]
+     function tables for different message types
+   - [log-server.c]
+     do not log() if debuggin to stderr
+     rename a cpp symbol, to avoid param.h collision
+   - [mpaux.c]
+     KNF
+   - [nchan.c]
+     sync w/ channels.c
+
 20000326
  - Better tests for OpenSSL w/ RSAref
  - Added replacement setenv() function from OpenBSD libc. Suggested by 
diff --git a/Makefile.in b/Makefile.in
index 6fee608..43870d5 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -31,11 +31,11 @@
 
 TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS)
 
-LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hostfile.o key.o log.o match.o mpaux.o nchan.o packet.o radix.o random.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o 
+LIBOBJS= atomicio.o authfd.o authfile.o bsd-bindresvport.o bsd-daemon.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o fake-getaddrinfo.o fake-getnameinfo.o fingerprint.o hostfile.o key.o log.o match.o mpaux.o nchan.o packet.o radix.o random.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o xmalloc.o 
 
 SSHOBJS= ssh.o sshconnect.o log-client.o readconf.o clientloop.o
 
-SSHDOBJS= sshd.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o
+SSHDOBJS= sshd.o auth-rhosts.o auth-krb4.o auth-pam.o auth-passwd.o auth-rsa.o auth-rh-rsa.o pty.o log-server.o login.o servconf.o serverloop.o bsd-login.o md5crypt.o session.o auth.o
 
 TROFFMAN	= scp.1 ssh-add.1 ssh-agent.1 ssh-keygen.1 ssh.1 sshd.8
 CATMAN		= scp.0 ssh-add.0 ssh-agent.0 ssh-keygen.0 ssh.0 sshd.0
diff --git a/auth.c b/auth.c
new file mode 100644
index 0000000..11b5381
--- /dev/null
+++ b/auth.c
@@ -0,0 +1,606 @@
+/*
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ */
+
+#include "includes.h"
+RCSID("$OpenBSD: auth.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
+
+#include "xmalloc.h"
+#include "rsa.h"
+#include "ssh.h"
+#include "pty.h"
+#include "packet.h"
+#include "buffer.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "servconf.h"
+#include "channels.h"
+#include "match.h"
+
+#include "session.h"
+#include "dispatch.h"
+
+/* import */
+extern ServerOptions options;
+extern char *forced_command;
+
+/*
+ * Check if the user is allowed to log in via ssh. If user is listed in
+ * DenyUsers or user's primary group is listed in DenyGroups, false will
+ * be returned. If AllowUsers isn't empty and user isn't listed there, or
+ * if AllowGroups isn't empty and user isn't listed there, false will be
+ * returned. 
+ * If the user's shell is not executable, false will be returned.
+ * Otherwise true is returned. 
+ */
+static int
+allowed_user(struct passwd * pw)
+{
+	struct stat st;
+	struct group *grp;
+	int i;
+#ifdef WITH_AIXAUTHENTICATE
+	char *loginmsg;
+#endif /* WITH_AIXAUTHENTICATE */
+
+	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
+	if (!pw)
+		return 0;
+
+	/* deny if shell does not exists or is not executable */
+	if (stat(pw->pw_shell, &st) != 0)
+		return 0;
+	if (!((st.st_mode & S_IFREG) && (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP))))
+		return 0;
+
+	/* Return false if user is listed in DenyUsers */
+	if (options.num_deny_users > 0) {
+		if (!pw->pw_name)
+			return 0;
+		for (i = 0; i < options.num_deny_users; i++)
+			if (match_pattern(pw->pw_name, options.deny_users[i]))
+				return 0;
+	}
+	/* Return false if AllowUsers isn't empty and user isn't listed there */
+	if (options.num_allow_users > 0) {
+		if (!pw->pw_name)
+			return 0;
+		for (i = 0; i < options.num_allow_users; i++)
+			if (match_pattern(pw->pw_name, options.allow_users[i]))
+				break;
+		/* i < options.num_allow_users iff we break for loop */
+		if (i >= options.num_allow_users)
+			return 0;
+	}
+	/* Get the primary group name if we need it. Return false if it fails */
+	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
+		grp = getgrgid(pw->pw_gid);
+		if (!grp)
+			return 0;
+
+		/* Return false if user's group is listed in DenyGroups */
+		if (options.num_deny_groups > 0) {
+			if (!grp->gr_name)
+				return 0;
+			for (i = 0; i < options.num_deny_groups; i++)
+				if (match_pattern(grp->gr_name, options.deny_groups[i]))
+					return 0;
+		}
+		/*
+		 * Return false if AllowGroups isn't empty and user's group
+		 * isn't listed there
+		 */
+		if (options.num_allow_groups > 0) {
+			if (!grp->gr_name)
+				return 0;
+			for (i = 0; i < options.num_allow_groups; i++)
+				if (match_pattern(grp->gr_name, options.allow_groups[i]))
+					break;
+			/* i < options.num_allow_groups iff we break for
+			   loop */
+			if (i >= options.num_allow_groups)
+				return 0;
+		}
+	}
+
+#ifdef WITH_AIXAUTHENTICATE
+	if (loginrestrictions(pw->pw_name,S_LOGIN,NULL,&loginmsg) != 0)
+		return 0;
+#endif /* WITH_AIXAUTHENTICATE */
+
+	/* We found no reason not to let this user try to log on... */
+	return 1;
+}
+
+/*
+ * convert ssh auth msg type into description
+ */
+char *
+get_authname(int type)
+{
+	static char buf[1024];
+	switch (type) {
+	case SSH_CMSG_AUTH_PASSWORD:
+		return "password";
+	case SSH_CMSG_AUTH_RSA:
+		return "rsa";
+	case SSH_CMSG_AUTH_RHOSTS_RSA:
+		return "rhosts-rsa";
+	case SSH_CMSG_AUTH_RHOSTS:
+		return "rhosts";
+#ifdef KRB4
+	case SSH_CMSG_AUTH_KERBEROS:
+		return "kerberos";
+#endif
+#ifdef SKEY
+	case SSH_CMSG_AUTH_TIS_RESPONSE:
+		return "s/key";
+#endif
+	}
+	snprintf(buf, sizeof buf, "bad-auth-msg-%d", type);
+	return buf;
+}
+
+#define AUTH_FAIL_MAX 6
+#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2)
+#define AUTH_FAIL_MSG "Too many authentication failures for %.100s"
+
+/*
+ * The user does not exist or access is denied,
+ * but fake indication that authentication is needed.
+ */
+void
+do_fake_authloop1(char *user)
+{
+	int attempt = 0;
+
+	log("Faking authloop for illegal user %.200s from %.200s port %d",
+	    user,
+	    get_remote_ipaddr(),
+	    get_remote_port());
+
+#ifdef WITH_AIXAUTHENTICATE 
+		if (strncmp(get_authname(type),"password",
+		    strlen(get_authname(type))) == 0)
+			loginfailed(pw->pw_name,get_canonical_hostname(),"ssh");
+#endif /* WITH_AIXAUTHENTICATE */
+
+	/* Indicate that authentication is needed. */
+	packet_start(SSH_SMSG_FAILURE);
+	packet_send();
+	packet_write_wait();
+
+	/*
+	 * Keep reading packets, and always respond with a failure.  This is
+	 * to avoid disclosing whether such a user really exists.
+	 */
+	for (attempt = 1;; attempt++) {
+		/* Read a packet.  This will not return if the client disconnects. */
+		int plen;
+#ifndef SKEY
+		(void)packet_read(&plen);
+#else /* SKEY */
+		int type = packet_read(&plen);
+		unsigned int dlen;
+		char *password, *skeyinfo;
+		/* Try to send a fake s/key challenge. */
+		if (options.skey_authentication == 1 &&
+		    (skeyinfo = skey_fake_keyinfo(user)) != NULL) {
+			password = NULL;
+			if (type == SSH_CMSG_AUTH_TIS) {
+				packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
+				packet_put_string(skeyinfo, strlen(skeyinfo));
+				packet_send();
+				packet_write_wait();
+				continue;
+			} else if (type == SSH_CMSG_AUTH_PASSWORD &&
+			           options.password_authentication &&
+			           (password = packet_get_string(&dlen)) != NULL &&
+			           dlen == 5 &&
+			           strncasecmp(password, "s/key", 5) == 0 ) {
+				packet_send_debug(skeyinfo);
+			}
+			if (password != NULL)
+				xfree(password);
+		}
+#endif
+		if (attempt > AUTH_FAIL_MAX)
+			packet_disconnect(AUTH_FAIL_MSG, user);
+
+		/*
+		 * Send failure.  This should be indistinguishable from a
+		 * failed authentication.
+		 */
+		packet_start(SSH_SMSG_FAILURE);
+		packet_send();
+		packet_write_wait();
+	}
+	/* NOTREACHED */
+	abort();
+}
+
+/*
+ * read packets and try to authenticate local user *pw.
+ * return if authentication is successfull
+ */
+void
+do_authloop(struct passwd * pw)
+{
+	int attempt = 0;
+	unsigned int bits;
+	RSA *client_host_key;
+	BIGNUM *n;
+	char *client_user = NULL, *password = NULL;
+	char user[1024];
+	unsigned int dlen;
+	int plen, nlen, elen;
+	unsigned int ulen;
+	int type = 0;
+	void (*authlog) (const char *fmt,...) = verbose;
+
+	/* Indicate that authentication is needed. */
+	packet_start(SSH_SMSG_FAILURE);
+	packet_send();
+	packet_write_wait();
+
+	for (attempt = 1;; attempt++) {
+		int authenticated = 0;
+		strlcpy(user, "", sizeof user);
+
+		/* Get a packet from the client. */
+		type = packet_read(&plen);
+
+		/* Process the packet. */
+		switch (type) {
+#ifdef AFS
+		case SSH_CMSG_HAVE_KERBEROS_TGT:
+			if (!options.kerberos_tgt_passing) {
+				/* packet_get_all(); */
+				verbose("Kerberos tgt passing disabled.");
+				break;
+			} else {
+				/* Accept Kerberos tgt. */
+				char *tgt = packet_get_string(&dlen);
+				packet_integrity_check(plen, 4 + dlen, type);
+				if (!auth_kerberos_tgt(pw, tgt))
+					verbose("Kerberos tgt REFUSED for %s", pw->pw_name);
+				xfree(tgt);
+			}
+			continue;
+
+		case SSH_CMSG_HAVE_AFS_TOKEN:
+			if (!options.afs_token_passing || !k_hasafs()) {
+				/* packet_get_all(); */
+				verbose("AFS token passing disabled.");
+				break;
+			} else {
+				/* Accept AFS token. */
+				char *token_string = packet_get_string(&dlen);
+				packet_integrity_check(plen, 4 + dlen, type);
+				if (!auth_afs_token(pw, token_string))
+					verbose("AFS token REFUSED for %s", pw->pw_name);
+				xfree(token_string);
+			}
+			continue;
+#endif /* AFS */
+#ifdef KRB4
+		case SSH_CMSG_AUTH_KERBEROS:
+			if (!options.kerberos_authentication) {
+				/* packet_get_all(); */
+				verbose("Kerberos authentication disabled.");
+				break;
+			} else {
+				/* Try Kerberos v4 authentication. */
+				KTEXT_ST auth;
+				char *tkt_user = NULL;
+				char *kdata = packet_get_string((unsigned int *) &auth.length);
+				packet_integrity_check(plen, 4 + auth.length, type);
+
+				if (auth.length < MAX_KTXT_LEN)
+					memcpy(auth.dat, kdata, auth.length);
+				xfree(kdata);
+
+				authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user);
+
+				if (authenticated) {
+					snprintf(user, sizeof user, " tktuser %s", tkt_user);
+					xfree(tkt_user);
+				}
+			}
+			break;
+#endif /* KRB4 */
+
+		case SSH_CMSG_AUTH_RHOSTS:
+			if (!options.rhosts_authentication) {
+				verbose("Rhosts authentication disabled.");
+				break;
+			}
+			/*
+			 * Get client user name.  Note that we just have to
+			 * trust the client; this is one reason why rhosts
+			 * authentication is insecure. (Another is
+			 * IP-spoofing on a local network.)
+			 */
+			client_user = packet_get_string(&ulen);
+			packet_integrity_check(plen, 4 + ulen, type);
+
+			/* Try to authenticate using /etc/hosts.equiv and
+			   .rhosts. */
+			authenticated = auth_rhosts(pw, client_user);
+
+			snprintf(user, sizeof user, " ruser %s", client_user);
+			break;
+
+		case SSH_CMSG_AUTH_RHOSTS_RSA:
+			if (!options.rhosts_rsa_authentication) {
+				verbose("Rhosts with RSA authentication disabled.");
+				break;
+			}
+			/*
+			 * Get client user name.  Note that we just have to
+			 * trust the client; root on the client machine can
+			 * claim to be any user.
+			 */
+			client_user = packet_get_string(&ulen);
+
+			/* Get the client host key. */
+			client_host_key = RSA_new();
+			if (client_host_key == NULL)
+				fatal("RSA_new failed");
+			client_host_key->e = BN_new();
+			client_host_key->n = BN_new();
+			if (client_host_key->e == NULL || client_host_key->n == NULL)
+				fatal("BN_new failed");
+			bits = packet_get_int();
+			packet_get_bignum(client_host_key->e, &elen);
+			packet_get_bignum(client_host_key->n, &nlen);
+
+			if (bits != BN_num_bits(client_host_key->n))
+				error("Warning: keysize mismatch for client_host_key: "
+				      "actual %d, announced %d", BN_num_bits(client_host_key->n), bits);
+			packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type);
+
+			authenticated = auth_rhosts_rsa(pw, client_user, client_host_key);
+			RSA_free(client_host_key);
+
+			snprintf(user, sizeof user, " ruser %s", client_user);
+			break;
+
+		case SSH_CMSG_AUTH_RSA:
+			if (!options.rsa_authentication) {
+				verbose("RSA authentication disabled.");
+				break;
+			}
+			/* RSA authentication requested. */
+			n = BN_new();
+			packet_get_bignum(n, &nlen);
+			packet_integrity_check(plen, nlen, type);
+			authenticated = auth_rsa(pw, n);
+			BN_clear_free(n);
+			break;
+
+		case SSH_CMSG_AUTH_PASSWORD:
+			if (!options.password_authentication) {
+				verbose("Password authentication disabled.");
+				break;
+			}
+			/*
+			 * Read user password.  It is in plain text, but was
+			 * transmitted over the encrypted channel so it is
+			 * not visible to an outside observer.
+			 */
+			password = packet_get_string(&dlen);
+			packet_integrity_check(plen, 4 + dlen, type);
+
+#ifdef USE_PAM
+			/* Do PAM auth with password */
+			authenticated = auth_pam_password(pw, password);
+#else /* USE_PAM */
+			/* Try authentication with the password. */
+			authenticated = auth_password(pw, password);
+#endif /* USE_PAM */
+			memset(password, 0, strlen(password));
+			xfree(password);
+			break;
+
+#ifdef SKEY
+		case SSH_CMSG_AUTH_TIS:
+			debug("rcvd SSH_CMSG_AUTH_TIS");
+			if (options.skey_authentication == 1) {
+				char *skeyinfo = skey_keyinfo(pw->pw_name);
+				if (skeyinfo == NULL) {
+					debug("generating fake skeyinfo for %.100s.", pw->pw_name);
+					skeyinfo = skey_fake_keyinfo(pw->pw_name);
+				}
+				if (skeyinfo != NULL) {
+					/* we send our s/key- in tis-challenge messages */
+					debug("sending challenge '%s'", skeyinfo);
+					packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
+					packet_put_string(skeyinfo, strlen(skeyinfo));
+					packet_send();
+					packet_write_wait();
+					continue;
+				}
+			}
+			break;
+		case SSH_CMSG_AUTH_TIS_RESPONSE:
+			debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE");
+			if (options.skey_authentication == 1) {
+				char *response = packet_get_string(&dlen);
+				debug("skey response == '%s'", response);
+				packet_integrity_check(plen, 4 + dlen, type);
+				authenticated = (skey_haskey(pw->pw_name) == 0 &&
+						 skey_passcheck(pw->pw_name, response) != -1);
+				xfree(response);
+			}
+			break;
+#else
+		case SSH_CMSG_AUTH_TIS:
+			/* TIS Authentication is unsupported */
+			log("TIS authentication unsupported.");
+			break;
+#endif
+
+		default:
+			/*
+			 * Any unknown messages will be ignored (and failure
+			 * returned) during authentication.
+			 */
+			log("Unknown message during authentication: type %d", type);
+			break;
+		}
+
+ 		/*
+ 		 * Check if the user is logging in as root and root logins
+ 		 * are disallowed.
+ 		 * Note that root login is allowed for forced commands.
+ 		 */
+ 		if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) {
+ 			if (forced_command) {
+ 				log("Root login accepted for forced command.");
+ 			} else {
+ 				authenticated = 0;
+ 				log("ROOT LOGIN REFUSED FROM %.200s",
+ 				    get_canonical_hostname());
+ 			}
+  		}
+
+		/* Raise logging level */
+		if (authenticated ||
+		    attempt == AUTH_FAIL_LOG ||
+		    type == SSH_CMSG_AUTH_PASSWORD)
+			authlog = log;
+
+		authlog("%s %s for %.200s from %.200s port %d%s",
+			authenticated ? "Accepted" : "Failed",
+			get_authname(type),
+			pw->pw_uid == 0 ? "ROOT" : pw->pw_name,
+			get_remote_ipaddr(),
+			get_remote_port(),
+			user);
+
+#ifdef USE_PAM
+		if (authenticated) {
+			if (!do_pam_account(pw->pw_name, client_user)) {
+				if (client_user != NULL) {
+					xfree(client_user);
+					client_user = NULL;
+				}
+				do_fake_authloop1(pw->pw_name);
+			}
+			return;
+		}
+#else /* USE_PAM */
+		if (authenticated) {
+			return;
+		}
+#endif /* USE_PAM */
+
+		if (client_user != NULL) {
+			xfree(client_user);
+			client_user = NULL;
+		}
+
+		if (attempt > AUTH_FAIL_MAX)
+			packet_disconnect(AUTH_FAIL_MSG, pw->pw_name);
+
+		/* Send a message indicating that the authentication attempt failed. */
+		packet_start(SSH_SMSG_FAILURE);
+		packet_send();
+		packet_write_wait();
+	}
+}
+
+/*
+ * Performs authentication of an incoming connection.  Session key has already
+ * been exchanged and encryption is enabled.
+ */
+void
+do_authentication()
+{
+	struct passwd *pw, pwcopy;
+	int plen;
+	unsigned int ulen;
+	char *user;
+#ifdef WITH_AIXAUTHENTICATE
+	char *loginmsg;
+#endif /* WITH_AIXAUTHENTICATE */
+
+	/* Get the name of the user that we wish to log in as. */
+	packet_read_expect(&plen, SSH_CMSG_USER);
+
+	/* Get the user name. */
+	user = packet_get_string(&ulen);
+	packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
+
+	setproctitle("%s", user);
+
+#ifdef AFS
+	/* If machine has AFS, set process authentication group. */
+	if (k_hasafs()) {
+		k_setpag();
+		k_unlog();
+	}
+#endif /* AFS */
+
+	/* Verify that the user is a valid user. */
+	pw = getpwnam(user);
+	if (!pw || !allowed_user(pw))
+		do_fake_authloop1(user);
+	xfree(user);
+
+	/* Take a copy of the returned structure. */
+	memset(&pwcopy, 0, sizeof(pwcopy));
+	pwcopy.pw_name = xstrdup(pw->pw_name);
+	pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
+	pwcopy.pw_uid = pw->pw_uid;
+	pwcopy.pw_gid = pw->pw_gid;
+	pwcopy.pw_dir = xstrdup(pw->pw_dir);
+	pwcopy.pw_shell = xstrdup(pw->pw_shell);
+	pw = &pwcopy;
+
+#ifdef USE_PAM
+	start_pam(pw);
+#endif
+
+	/*
+	 * If we are not running as root, the user must have the same uid as
+	 * the server.
+	 */
+	if (getuid() != 0 && pw->pw_uid != getuid())
+		packet_disconnect("Cannot change user when server not running as root.");
+
+	debug("Attempting authentication for %.100s.", pw->pw_name);
+
+	/* If the user has no password, accept authentication immediately. */
+	if (options.password_authentication &&
+#ifdef KRB4
+	    (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
+#endif /* KRB4 */
+#ifdef USE_PAM
+	    auth_pam_password(pw, "")) {
+#else /* USE_PAM */
+	    auth_password(pw, "")) {
+#endif /* USE_PAM */
+		/* Authentication with empty password succeeded. */
+		log("Login for user %s from %.100s, accepted without authentication.",
+		    pw->pw_name, get_remote_ipaddr());
+	} else {
+		/* Loop until the user has been authenticated or the
+		   connection is closed, do_authloop() returns only if
+		   authentication is successfull */
+		do_authloop(pw);
+	}
+
+	/* The user has been authenticated and accepted. */
+#ifdef WITH_AIXAUTHENTICATE
+	loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg);
+#endif /* WITH_AIXAUTHENTICATE */
+	packet_start(SSH_SMSG_SUCCESS);
+	packet_send();
+	packet_write_wait();
+
+	/* Perform session preparation. */
+	do_authenticated(pw);
+}
diff --git a/auth.h b/auth.h
new file mode 100644
index 0000000..8051777
--- /dev/null
+++ b/auth.h
@@ -0,0 +1,6 @@
+#ifndef AUTH_H
+#define AUTH_H
+
+void	do_authentication(void);
+
+#endif
diff --git a/bufaux.c b/bufaux.c
index 0c1381e..4ab45a2 100644
--- a/bufaux.c
+++ b/bufaux.c
@@ -12,10 +12,12 @@
  * Auxiliary functions for storing and retrieving various data types to/from
  * Buffers.
  *
+ * SSH2 packet format added by Markus Friedl
+ *
  */
 
 #include "includes.h"
-RCSID("$Id: bufaux.c,v 1.8 2000/03/17 12:40:15 damien Exp $");
+RCSID("$Id: bufaux.c,v 1.9 2000/04/01 01:09:23 damien Exp $");
 
 #include "ssh.h"
 
@@ -83,6 +85,50 @@
 }
 
 /*
+ * Stores an BIGNUM in the buffer in SSH2 format.
+ */
+void
+buffer_put_bignum2(Buffer *buffer, BIGNUM *value)
+{
+	int bytes = BN_num_bytes(value) + 1;
+	unsigned char *buf = xmalloc(bytes);
+	int oi;
+	int hasnohigh = 0;
+	buf[0] = '\0';
+	/* Get the value of in binary */
+	oi = BN_bn2bin(value, buf+1);
+	if (oi != bytes-1)
+		fatal("buffer_put_bignum: BN_bn2bin() failed: oi %d != bin_size %d",
+		      oi, bytes);
+	hasnohigh = (buf[1] & 0x80) ? 0 : 1;
+	if (value->neg) {
+		/**XXX should be two's-complement */
+		int i, carry;
+		unsigned char *uc = buf;
+		log("negativ!");
+		for(i = bytes-1, carry = 1; i>=0; i--) {
+			uc[i] ^= 0xff;
+			if(carry)
+				carry = !++uc[i];
+		}
+	}
+	buffer_put_string(buffer, buf+hasnohigh, bytes-hasnohigh);
+	memset(buf, 0, bytes);
+	xfree(buf);
+}
+
+int
+buffer_get_bignum2(Buffer *buffer, BIGNUM *value)
+{
+	/**XXX should be two's-complement */
+	int len;
+	unsigned char *bin = (unsigned char *)buffer_get_string(buffer, (unsigned int *)&len);
+	BN_bin2bn(bin, len, value);
+	xfree(bin);
+	return len;
+}
+
+/*
  * Returns an integer from the buffer (4 bytes, msb first).
  */
 unsigned int 
@@ -142,6 +188,11 @@
 	buffer_put_int(buffer, len);
 	buffer_append(buffer, buf, len);
 }
+void 
+buffer_put_cstring(Buffer *buffer, const char *s)
+{
+	buffer_put_string(buffer, s, strlen(s));
+}
 
 /*
  * Returns a character from the buffer (0 - 255).
diff --git a/bufaux.h b/bufaux.h
index 8884c17..b22e98b 100644
--- a/bufaux.h
+++ b/bufaux.h
@@ -11,7 +11,7 @@
  * 
  */
 
-/* RCSID("$Id: bufaux.h,v 1.3 1999/11/25 00:54:58 damien Exp $"); */
+/* RCSID("$Id: bufaux.h,v 1.4 2000/04/01 01:09:23 damien Exp $"); */
 
 #ifndef BUFAUX_H
 #define BUFAUX_H
@@ -23,9 +23,11 @@
  * by (bits+7)/8 bytes of binary data, msb first.
  */
 void    buffer_put_bignum(Buffer * buffer, BIGNUM * value);
+void    buffer_put_bignum2(Buffer * buffer, BIGNUM * value);
 
 /* Retrieves an BIGNUM from the buffer. */
 int     buffer_get_bignum(Buffer * buffer, BIGNUM * value);
+int	buffer_get_bignum2(Buffer *buffer, BIGNUM * value);
 
 /* Returns an integer from the buffer (4 bytes, msb first). */
 unsigned int buffer_get_int(Buffer * buffer);
@@ -51,5 +53,6 @@
 
 /* Stores and arbitrary binary string in the buffer. */
 void    buffer_put_string(Buffer * buffer, const void *buf, unsigned int len);
+void	buffer_put_cstring(Buffer *buffer, const char *s);
 
 #endif				/* BUFAUX_H */
diff --git a/channels.c b/channels.c
index e60ecc6..b87ff9f 100644
--- a/channels.c
+++ b/channels.c
@@ -16,7 +16,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: channels.c,v 1.19 2000/03/17 12:40:15 damien Exp $");
+RCSID("$Id: channels.c,v 1.20 2000/04/01 01:09:23 damien Exp $");
 
 #include "ssh.h"
 #include "packet.h"
@@ -37,6 +37,10 @@
 /* Max len of agent socket */
 #define MAX_SOCKET_NAME 100
 
+/* default buffer for tcp-fwd-channel */
+#define CHAN_WINDOW_DEFAULT      (8*1024)
+#define CHAN_PACKET_DEFAULT	 (CHAN_WINDOW_DEFAULT/2)
+
 /*
  * Pointer to an array containing all allocated channels.  The array is
  * dynamically extended as needed.
@@ -81,8 +85,9 @@
  * network (which might be behind a firewall).
  */
 typedef struct {
-	char *host;		/* Host name. */
-	u_short port;		/* Port number. */
+	char *host_to_connect;		/* Connect to 'host'. */
+	u_short port_to_connect;	/* Connect to 'port'. */
+	u_short listen_port;		/* Remote side should listen port number. */
 } ForwardPermission;
 
 /* List of all permitted host/port pairs to connect. */
@@ -119,20 +124,43 @@
 	all_opens_permitted = 1;
 }
 
+/* lookup channel by id */
+
+Channel *
+channel_lookup(int id)
+{
+	Channel *c;
+	if (id < 0 && id > channels_alloc) {
+		log("channel_lookup: %d: bad id", id);
+		return NULL;
+	}
+	c = &channels[id];
+	if (c->type == SSH_CHANNEL_FREE) {
+		log("channel_lookup: %d: bad id: channel free", id);
+		return NULL;
+	}
+	return c;
+}
+
 /*
  * Allocate a new channel object and set its type and socket. This will cause
  * remote_name to be freed.
  */
 
 int 
-channel_allocate(int type, int sock, char *remote_name)
+channel_new(char *ctype, int type, int rfd, int wfd, int efd,
+    int window, int maxpack, int extended_usage, char *remote_name)
 {
 	int i, found;
 	Channel *c;
 
 	/* Update the maximum file descriptor value. */
-	if (sock > channel_max_fd_value)
-		channel_max_fd_value = sock;
+	if (rfd > channel_max_fd_value)
+		channel_max_fd_value = rfd;
+	if (wfd > channel_max_fd_value)
+		channel_max_fd_value = wfd;
+	if (efd > channel_max_fd_value)
+		channel_max_fd_value = efd;
 	/* XXX set close-on-exec -markus */
 
 	/* Do initial allocation if this is the first call. */
@@ -167,360 +195,486 @@
 	c = &channels[found];
 	buffer_init(&c->input);
 	buffer_init(&c->output);
+	buffer_init(&c->extended);
 	chan_init_iostates(c);
 	c->self = found;
 	c->type = type;
-	c->sock = sock;
+	c->ctype = ctype;
+	c->local_window = window;
+	c->local_window_max = window;
+	c->local_consumed = 0;
+	c->local_maxpacket = maxpack;
+	c->remote_window = 0;
+	c->remote_maxpacket = 0;
+	c->rfd = rfd;
+	c->wfd = wfd;
+	c->sock = (rfd == wfd) ? rfd : -1;
+	c->efd = efd;
+	c->extended_usage = extended_usage;
 	c->remote_id = -1;
 	c->remote_name = remote_name;
+	c->remote_window = 0;
+	c->remote_maxpacket = 0;
+	c->cb_fn = NULL;
+	c->cb_arg = NULL;
+	c->cb_event = 0;
+	c->dettach_user = NULL;
 	debug("channel %d: new [%s]", found, remote_name);
 	return found;
 }
+int 
+channel_allocate(int type, int sock, char *remote_name)
+{
+	return channel_new("", type, sock, sock, -1, 0, 0, 0, remote_name);
+}
 
 /* Free the channel and close its socket. */
 
 void 
-channel_free(int channel)
+channel_free(int id)
 {
-	if (channel < 0 || channel >= channels_alloc ||
-	    channels[channel].type == SSH_CHANNEL_FREE)
-		packet_disconnect("channel free: bad local channel %d", channel);
-
-	if (compat13)
-		shutdown(channels[channel].sock, SHUT_RDWR);
-	close(channels[channel].sock);
-	buffer_free(&channels[channel].input);
-	buffer_free(&channels[channel].output);
-	channels[channel].type = SSH_CHANNEL_FREE;
-	if (channels[channel].remote_name) {
-		xfree(channels[channel].remote_name);
-		channels[channel].remote_name = NULL;
+	Channel *c = channel_lookup(id);
+	if (c == NULL)
+		packet_disconnect("channel free: bad local channel %d", id);
+	debug("channel_free: channel %d: status: %s", id, channel_open_message());
+	if (c->sock != -1) {
+		shutdown(c->sock, SHUT_RDWR);
+		close(c->sock);
+	}
+	buffer_free(&c->input);
+	buffer_free(&c->output);
+	buffer_free(&c->extended);
+	c->type = SSH_CHANNEL_FREE;
+	if (c->remote_name) {
+		xfree(c->remote_name);
+		c->remote_name = NULL;
 	}
 }
 
 /*
- * This is called just before select() to add any bits relevant to channels
- * in the select bitmasks.
+ * 'channel_pre*' are called just before select() to add any bits relevant to
+ * channels in the select bitmasks.
  */
+/*
+ * 'channel_post*': perform any appropriate operations for channels which
+ * have events pending.
+ */
+typedef void chan_fn(Channel *c, fd_set * readset, fd_set * writeset);
+chan_fn *channel_pre[SSH_CHANNEL_MAX_TYPE];
+chan_fn *channel_post[SSH_CHANNEL_MAX_TYPE];
+
+void
+channel_pre_listener(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	FD_SET(c->sock, readset);
+}
+
+void
+channel_pre_open_13(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	if (buffer_len(&c->input) < packet_get_maxsize())
+		FD_SET(c->sock, readset);
+	if (buffer_len(&c->output) > 0)
+		FD_SET(c->sock, writeset);
+}
+
+void
+channel_pre_open_15(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	/* test whether sockets are 'alive' for read/write */
+	if (c->istate == CHAN_INPUT_OPEN)
+		if (buffer_len(&c->input) < packet_get_maxsize())
+			FD_SET(c->sock, readset);
+	if (c->ostate == CHAN_OUTPUT_OPEN ||
+	    c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
+		if (buffer_len(&c->output) > 0) {
+			FD_SET(c->sock, writeset);
+		} else if (c->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
+			chan_obuf_empty(c);
+		}
+	}
+}
+
+void
+channel_pre_input_draining(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	if (buffer_len(&c->input) == 0) {
+		packet_start(SSH_MSG_CHANNEL_CLOSE);
+		packet_put_int(c->remote_id);
+		packet_send();
+		c->type = SSH_CHANNEL_CLOSED;
+		debug("Closing channel %d after input drain.", c->self);
+	}
+}
+
+void
+channel_pre_output_draining(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	if (buffer_len(&c->output) == 0)
+		channel_free(c->self);
+	else 
+		FD_SET(c->sock, writeset);
+}
+
+/*
+ * This is a special state for X11 authentication spoofing.  An opened X11
+ * connection (when authentication spoofing is being done) remains in this
+ * state until the first packet has been completely read.  The authentication
+ * data in that packet is then substituted by the real data if it matches the
+ * fake data, and the channel is put into normal mode.
+ */
+int
+x11_open_helper(Channel *c)
+{
+	unsigned char *ucp;
+	unsigned int proto_len, data_len;
+
+	/* Check if the fixed size part of the packet is in buffer. */
+	if (buffer_len(&c->output) < 12)
+		return 0;
+
+	/* Parse the lengths of variable-length fields. */
+	ucp = (unsigned char *) buffer_ptr(&c->output);
+	if (ucp[0] == 0x42) {	/* Byte order MSB first. */
+		proto_len = 256 * ucp[6] + ucp[7];
+		data_len = 256 * ucp[8] + ucp[9];
+	} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
+		proto_len = ucp[6] + 256 * ucp[7];
+		data_len = ucp[8] + 256 * ucp[9];
+	} else {
+		debug("Initial X11 packet contains bad byte order byte: 0x%x",
+		      ucp[0]);
+		return -1;
+	}
+
+	/* Check if the whole packet is in buffer. */
+	if (buffer_len(&c->output) <
+	    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
+		return 0;
+
+	/* Check if authentication protocol matches. */
+	if (proto_len != strlen(x11_saved_proto) ||
+	    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
+		debug("X11 connection uses different authentication protocol.");
+		return -1;
+	}
+	/* Check if authentication data matches our fake data. */
+	if (data_len != x11_fake_data_len ||
+	    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
+		x11_fake_data, x11_fake_data_len) != 0) {
+		debug("X11 auth data does not match fake data.");
+		return -1;
+	}
+	/* Check fake data length */
+	if (x11_fake_data_len != x11_saved_data_len) {
+		error("X11 fake_data_len %d != saved_data_len %d",
+		    x11_fake_data_len, x11_saved_data_len);
+		return -1;
+	}
+	/*
+	 * Received authentication protocol and data match
+	 * our fake data. Substitute the fake data with real
+	 * data.
+	 */
+	memcpy(ucp + 12 + ((proto_len + 3) & ~3),
+	    x11_saved_data, x11_saved_data_len);
+	return 1;
+}
+
+void
+channel_pre_x11_open_13(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	int ret = x11_open_helper(c);
+	if (ret == 1) {
+		/* Start normal processing for the channel. */
+		c->type = SSH_CHANNEL_OPEN;
+	} else if (ret == -1) {
+		/*
+		 * We have received an X11 connection that has bad
+		 * authentication information.
+		 */
+		log("X11 connection rejected because of wrong authentication.\r\n");
+		buffer_clear(&c->input);
+		buffer_clear(&c->output);
+		close(c->sock);
+		c->sock = -1;
+		c->type = SSH_CHANNEL_CLOSED;
+		packet_start(SSH_MSG_CHANNEL_CLOSE);
+		packet_put_int(c->remote_id);
+		packet_send();
+	}
+}
+
+void
+channel_pre_x11_open_15(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	int ret = x11_open_helper(c);
+	if (ret == 1) {
+		c->type = SSH_CHANNEL_OPEN;
+	} else if (ret == -1) {
+		debug("X11 rejected %d i%d/o%d", c->self, c->istate, c->ostate);
+		chan_read_failed(c);
+		chan_write_failed(c);
+		debug("X11 closed %d i%d/o%d", c->self, c->istate, c->ostate);
+	}
+}
+
+/* This is our fake X11 server socket. */
+void
+channel_post_x11_listener(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	struct sockaddr addr;
+	int newsock, newch;
+	socklen_t addrlen;
+	char buf[16384], *remote_hostname;
+
+	if (FD_ISSET(c->sock, readset)) {
+		debug("X11 connection requested.");
+		addrlen = sizeof(addr);
+		newsock = accept(c->sock, &addr, &addrlen);
+		if (newsock < 0) {
+			error("accept: %.100s", strerror(errno));
+			return;
+		}
+		remote_hostname = get_remote_hostname(newsock);
+		snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
+		remote_hostname, get_peer_port(newsock));
+		xfree(remote_hostname);
+		newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
+					 xstrdup(buf));
+		packet_start(SSH_SMSG_X11_OPEN);
+		packet_put_int(newch);
+		if (have_hostname_in_open)
+			packet_put_string(buf, strlen(buf));
+		packet_send();
+	}
+}
+
+/*
+ * This socket is listening for connections to a forwarded TCP/IP port.
+ */
+void
+channel_post_port_listener(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	struct sockaddr addr;
+	int newsock, newch;
+	socklen_t addrlen;
+	char buf[1024], *remote_hostname;
+	int remote_port;
+
+	if (FD_ISSET(c->sock, readset)) {
+		debug("Connection to port %d forwarding "
+		    "to %.100s port %d requested.",
+		    c->listening_port, c->path, c->host_port);
+		addrlen = sizeof(addr);
+		newsock = accept(c->sock, &addr, &addrlen);
+		if (newsock < 0) {
+			error("accept: %.100s", strerror(errno));
+			return;
+		}
+		remote_hostname = get_remote_hostname(newsock);
+		remote_port = get_peer_port(newsock);
+		snprintf(buf, sizeof buf,
+		    "listen port %d for %.100s port %d, "
+		    "connect from %.200s port %d",
+		    c->listening_port, c->path, c->host_port,
+		    remote_hostname, remote_port);
+		newch = channel_new("direct-tcpip",
+		    SSH_CHANNEL_OPENING, newsock, newsock, -1,
+		    c->local_window_max, c->local_maxpacket,
+		    0, xstrdup(buf));
+
+		packet_start(SSH_MSG_PORT_OPEN);
+		packet_put_int(newch);
+		packet_put_string(c->path, strlen(c->path));
+		packet_put_int(c->host_port);
+		if (have_hostname_in_open) {
+			packet_put_string(buf, strlen(buf));
+		}
+		packet_send();
+		xfree(remote_hostname);
+	}
+}
+
+/*
+ * This is the authentication agent socket listening for connections from
+ * clients.
+ */
+void
+channel_post_auth_listener(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	struct sockaddr addr;
+	int newsock, newch;
+	socklen_t addrlen;
+
+	if (FD_ISSET(c->sock, readset)) {
+		addrlen = sizeof(addr);
+		newsock = accept(c->sock, &addr, &addrlen);
+		if (newsock < 0) {
+			error("accept from auth socket: %.100s", strerror(errno));
+			return;
+		}
+		newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
+		    xstrdup("accepted auth socket"));
+		packet_start(SSH_SMSG_AGENT_OPEN);
+		packet_put_int(newch);
+		packet_send();
+	}
+}
+
+int
+channel_handle_rfd(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	char buf[16*1024];
+	int len;
+
+	if (c->rfd != -1 &&
+	    FD_ISSET(c->rfd, readset)) {
+		len = read(c->rfd, buf, sizeof(buf));
+		if (len <= 0) {
+			debug("channel %d: read<0 rfd %d len %d",
+			    c->self, c->rfd, len);
+			if (compat13) {
+				buffer_consume(&c->output, buffer_len(&c->output));
+				c->type = SSH_CHANNEL_INPUT_DRAINING;
+				debug("Channel %d status set to input draining.", c->self);
+			} else {
+				chan_read_failed(c);
+			}
+			return -1;
+		}
+		buffer_append(&c->input, buf, len);
+	}
+	return 1;
+}
+int
+channel_handle_wfd(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	int len;
+
+	/* Send buffered output data to the socket. */
+	if (c->wfd != -1 &&
+	    FD_ISSET(c->wfd, writeset) &&
+	    buffer_len(&c->output) > 0) {
+		len = write(c->wfd, buffer_ptr(&c->output),
+			    buffer_len(&c->output));
+		if (len <= 0) {
+			if (compat13) {
+				buffer_consume(&c->output, buffer_len(&c->output));
+				debug("Channel %d status set to input draining.", c->self);
+				c->type = SSH_CHANNEL_INPUT_DRAINING;
+			} else {
+				chan_write_failed(c);
+			}
+			return -1;
+		}
+		buffer_consume(&c->output, len);
+	}
+	return 1;
+}
+
+void
+channel_post_open_1(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	channel_handle_rfd(c, readset, writeset);
+	channel_handle_wfd(c, readset, writeset);
+}
+
+void
+channel_post_output_drain_13(Channel *c, fd_set * readset, fd_set * writeset)
+{
+	int len;
+	/* Send buffered output data to the socket. */
+	if (FD_ISSET(c->sock, writeset) && buffer_len(&c->output) > 0) {
+		len = write(c->sock, buffer_ptr(&c->output),
+			    buffer_len(&c->output));
+		if (len <= 0)
+			buffer_consume(&c->output, buffer_len(&c->output));
+		else
+			buffer_consume(&c->output, len);
+	}
+}
+
+void
+channel_handler_init_13(void)
+{
+	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_13;
+	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open_13;
+	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
+	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
+	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
+	channel_pre[SSH_CHANNEL_INPUT_DRAINING] =	&channel_pre_input_draining;
+	channel_pre[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_pre_output_draining;
+
+	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open_1;
+	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
+	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
+	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
+	channel_post[SSH_CHANNEL_OUTPUT_DRAINING] =	&channel_post_output_drain_13;
+}
+
+void
+channel_handler_init_15(void)
+{
+	channel_pre[SSH_CHANNEL_OPEN] =			&channel_pre_open_15;
+	channel_pre[SSH_CHANNEL_X11_OPEN] =		&channel_pre_x11_open_15;
+	channel_pre[SSH_CHANNEL_X11_LISTENER] =		&channel_pre_listener;
+	channel_pre[SSH_CHANNEL_PORT_LISTENER] =	&channel_pre_listener;
+	channel_pre[SSH_CHANNEL_AUTH_SOCKET] =		&channel_pre_listener;
+
+	channel_post[SSH_CHANNEL_X11_LISTENER] =	&channel_post_x11_listener;
+	channel_post[SSH_CHANNEL_PORT_LISTENER] =	&channel_post_port_listener;
+	channel_post[SSH_CHANNEL_AUTH_SOCKET] =		&channel_post_auth_listener;
+	channel_post[SSH_CHANNEL_OPEN] =		&channel_post_open_1;
+}
+
+void
+channel_handler_init(void)
+{
+	int i;
+	for(i = 0; i < SSH_CHANNEL_MAX_TYPE; i++) {
+		channel_pre[i] = NULL;
+		channel_post[i] = NULL;
+	}
+	if (compat13)
+		channel_handler_init_13();
+	else
+		channel_handler_init_15();
+}
+
+void 
+channel_handler(chan_fn *ftab[], fd_set * readset, fd_set * writeset)
+{
+	static int did_init = 0;
+	int i;
+	Channel *c;
+
+	if (!did_init) {
+		channel_handler_init();
+		did_init = 1;
+	}
+	for (i = 0; i < channels_alloc; i++) {
+		c = &channels[i];
+		if (c->type == SSH_CHANNEL_FREE)
+			continue;
+		if (ftab[c->type] == NULL)
+			continue;
+		(*ftab[c->type])(c, readset, writeset);
+		if (!compat13)
+			chan_delete_if_full_closed(c);
+	}
+}
 
 void 
 channel_prepare_select(fd_set * readset, fd_set * writeset)
 {
-	int i;
-	Channel *ch;
-	unsigned char *ucp;
-	unsigned int proto_len, data_len;
-
-	for (i = 0; i < channels_alloc; i++) {
-		ch = &channels[i];
-redo:
-		switch (ch->type) {
-		case SSH_CHANNEL_X11_LISTENER:
-		case SSH_CHANNEL_PORT_LISTENER:
-		case SSH_CHANNEL_AUTH_SOCKET:
-			FD_SET(ch->sock, readset);
-			break;
-
-		case SSH_CHANNEL_OPEN:
-			if (compat13) {
-				if (buffer_len(&ch->input) < packet_get_maxsize())
-					FD_SET(ch->sock, readset);
-				if (buffer_len(&ch->output) > 0)
-					FD_SET(ch->sock, writeset);
-				break;
-			}
-			/* test whether sockets are 'alive' for read/write */
-			if (ch->istate == CHAN_INPUT_OPEN)
-				if (buffer_len(&ch->input) < packet_get_maxsize())
-					FD_SET(ch->sock, readset);
-			if (ch->ostate == CHAN_OUTPUT_OPEN ||
-			    ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
-				if (buffer_len(&ch->output) > 0) {
-					FD_SET(ch->sock, writeset);
-				} else if (ch->ostate == CHAN_OUTPUT_WAIT_DRAIN) {
-					chan_obuf_empty(ch);
-				}
-			}
-			break;
-
-		case SSH_CHANNEL_INPUT_DRAINING:
-			if (!compat13)
-				fatal("cannot happen: IN_DRAIN");
-			if (buffer_len(&ch->input) == 0) {
-				packet_start(SSH_MSG_CHANNEL_CLOSE);
-				packet_put_int(ch->remote_id);
-				packet_send();
-				ch->type = SSH_CHANNEL_CLOSED;
-				debug("Closing channel %d after input drain.", ch->self);
-				break;
-			}
-			break;
-
-		case SSH_CHANNEL_OUTPUT_DRAINING:
-			if (!compat13)
-				fatal("cannot happen: OUT_DRAIN");
-			if (buffer_len(&ch->output) == 0) {
-				channel_free(i);
-				break;
-			}
-			FD_SET(ch->sock, writeset);
-			break;
-
-		case SSH_CHANNEL_X11_OPEN:
-			/*
-			 * This is a special state for X11 authentication
-			 * spoofing.  An opened X11 connection (when
-			 * authentication spoofing is being done) remains in
-			 * this state until the first packet has been
-			 * completely read.  The authentication data in that
-			 * packet is then substituted by the real data if it
-			 * matches the fake data, and the channel is put into
-			 * normal mode.
-			 */
-			/* Check if the fixed size part of the packet is in buffer. */
-			if (buffer_len(&ch->output) < 12)
-				break;
-
-			/* Parse the lengths of variable-length fields. */
-			ucp = (unsigned char *) buffer_ptr(&ch->output);
-			if (ucp[0] == 0x42) {	/* Byte order MSB first. */
-				proto_len = 256 * ucp[6] + ucp[7];
-				data_len = 256 * ucp[8] + ucp[9];
-			} else if (ucp[0] == 0x6c) {	/* Byte order LSB first. */
-				proto_len = ucp[6] + 256 * ucp[7];
-				data_len = ucp[8] + 256 * ucp[9];
-			} else {
-				debug("Initial X11 packet contains bad byte order byte: 0x%x",
-				      ucp[0]);
-				ch->type = SSH_CHANNEL_OPEN;
-				goto reject;
-			}
-
-			/* Check if the whole packet is in buffer. */
-			if (buffer_len(&ch->output) <
-			    12 + ((proto_len + 3) & ~3) + ((data_len + 3) & ~3))
-				break;
-
-			/* Check if authentication protocol matches. */
-			if (proto_len != strlen(x11_saved_proto) ||
-			    memcmp(ucp + 12, x11_saved_proto, proto_len) != 0) {
-				debug("X11 connection uses different authentication protocol.");
-				ch->type = SSH_CHANNEL_OPEN;
-				goto reject;
-			}
-			/* Check if authentication data matches our fake data. */
-			if (data_len != x11_fake_data_len ||
-			    memcmp(ucp + 12 + ((proto_len + 3) & ~3),
-				x11_fake_data, x11_fake_data_len) != 0) {
-				debug("X11 auth data does not match fake data.");
-				ch->type = SSH_CHANNEL_OPEN;
-				goto reject;
-			}
-			/* Check fake data length */
-			if (x11_fake_data_len != x11_saved_data_len) {
-				error("X11 fake_data_len %d != saved_data_len %d",
-				  x11_fake_data_len, x11_saved_data_len);
-				ch->type = SSH_CHANNEL_OPEN;
-				goto reject;
-			}
-			/*
-			 * Received authentication protocol and data match
-			 * our fake data. Substitute the fake data with real
-			 * data.
-			 */
-			memcpy(ucp + 12 + ((proto_len + 3) & ~3),
-			       x11_saved_data, x11_saved_data_len);
-
-			/* Start normal processing for the channel. */
-			ch->type = SSH_CHANNEL_OPEN;
-			goto redo;
-
-	reject:
-			/*
-			 * We have received an X11 connection that has bad
-			 * authentication information.
-			 */
-			log("X11 connection rejected because of wrong authentication.\r\n");
-			buffer_clear(&ch->input);
-			buffer_clear(&ch->output);
-			if (compat13) {
-				close(ch->sock);
-				ch->sock = -1;
-				ch->type = SSH_CHANNEL_CLOSED;
-				packet_start(SSH_MSG_CHANNEL_CLOSE);
-				packet_put_int(ch->remote_id);
-				packet_send();
-			} else {
-				debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate);
-				chan_read_failed(ch);
-				chan_write_failed(ch);
-				debug("X11 rejected %d i%d/o%d", ch->self, ch->istate, ch->ostate);
-			}
-			break;
-
-		case SSH_CHANNEL_FREE:
-		default:
-			continue;
-		}
-	}
+	channel_handler(channel_pre, readset, writeset);
 }
 
-/*
- * After select, perform any appropriate operations for channels which have
- * events pending.
- */
-
 void 
 channel_after_select(fd_set * readset, fd_set * writeset)
 {
-	struct sockaddr addr;
-	int newsock, i, newch, len;
-	socklen_t addrlen;
-	Channel *ch;
-	char buf[16384], *remote_hostname;
-
-	/* Loop over all channels... */
-	for (i = 0; i < channels_alloc; i++) {
-		ch = &channels[i];
-		switch (ch->type) {
-		case SSH_CHANNEL_X11_LISTENER:
-			/* This is our fake X11 server socket. */
-			if (FD_ISSET(ch->sock, readset)) {
-				debug("X11 connection requested.");
-				addrlen = sizeof(addr);
-				newsock = accept(ch->sock, &addr, &addrlen);
-				if (newsock < 0) {
-					error("accept: %.100s", strerror(errno));
-					break;
-				}
-				remote_hostname = get_remote_hostname(newsock);
-				snprintf(buf, sizeof buf, "X11 connection from %.200s port %d",
-				remote_hostname, get_peer_port(newsock));
-				xfree(remote_hostname);
-				newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
-							 xstrdup(buf));
-				packet_start(SSH_SMSG_X11_OPEN);
-				packet_put_int(newch);
-				if (have_hostname_in_open)
-					packet_put_string(buf, strlen(buf));
-				packet_send();
-			}
-			break;
-
-		case SSH_CHANNEL_PORT_LISTENER:
-			/*
-			 * This socket is listening for connections to a
-			 * forwarded TCP/IP port.
-			 */
-			if (FD_ISSET(ch->sock, readset)) {
-				debug("Connection to port %d forwarding to %.100s port %d requested.",
-				      ch->listening_port, ch->path, ch->host_port);
-				addrlen = sizeof(addr);
-				newsock = accept(ch->sock, &addr, &addrlen);
-				if (newsock < 0) {
-					error("accept: %.100s", strerror(errno));
-					break;
-				}
-				remote_hostname = get_remote_hostname(newsock);
-				snprintf(buf, sizeof buf, "listen port %d for %.100s port %d, connect from %.200s port %d",
-					 ch->listening_port, ch->path, ch->host_port,
-				remote_hostname, get_peer_port(newsock));
-				xfree(remote_hostname);
-				newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
-							 xstrdup(buf));
-				packet_start(SSH_MSG_PORT_OPEN);
-				packet_put_int(newch);
-				packet_put_string(ch->path, strlen(ch->path));
-				packet_put_int(ch->host_port);
-				if (have_hostname_in_open)
-					packet_put_string(buf, strlen(buf));
-				packet_send();
-			}
-			break;
-
-		case SSH_CHANNEL_AUTH_SOCKET:
-			/*
-			 * This is the authentication agent socket listening
-			 * for connections from clients.
-			 */
-			if (FD_ISSET(ch->sock, readset)) {
-				addrlen = sizeof(addr);
-				newsock = accept(ch->sock, &addr, &addrlen);
-				if (newsock < 0) {
-					error("accept from auth socket: %.100s", strerror(errno));
-					break;
-				}
-				newch = channel_allocate(SSH_CHANNEL_OPENING, newsock,
-					xstrdup("accepted auth socket"));
-				packet_start(SSH_SMSG_AGENT_OPEN);
-				packet_put_int(newch);
-				packet_send();
-			}
-			break;
-
-		case SSH_CHANNEL_OPEN:
-			/*
-			 * This is an open two-way communication channel. It
-			 * is not of interest to us at this point what kind
-			 * of data is being transmitted.
-			 */
-
-			/*
-			 * Read available incoming data and append it to
-			 * buffer; shutdown socket, if read or write failes
-			 */
-			if (FD_ISSET(ch->sock, readset)) {
-				len = read(ch->sock, buf, sizeof(buf));
-				if (len <= 0) {
-					if (compat13) {
-						buffer_consume(&ch->output, buffer_len(&ch->output));
-						ch->type = SSH_CHANNEL_INPUT_DRAINING;
-						debug("Channel %d status set to input draining.", i);
-					} else {
-						chan_read_failed(ch);
-					}
-					break;
-				}
-				buffer_append(&ch->input, buf, len);
-			}
-			/* Send buffered output data to the socket. */
-			if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) {
-				len = write(ch->sock, buffer_ptr(&ch->output),
-					    buffer_len(&ch->output));
-				if (len <= 0) {
-					if (compat13) {
-						buffer_consume(&ch->output, buffer_len(&ch->output));
-						debug("Channel %d status set to input draining.", i);
-						ch->type = SSH_CHANNEL_INPUT_DRAINING;
-					} else {
-						chan_write_failed(ch);
-					}
-					break;
-				}
-				buffer_consume(&ch->output, len);
-			}
-			break;
-
-		case SSH_CHANNEL_OUTPUT_DRAINING:
-			if (!compat13)
-				fatal("cannot happen: OUT_DRAIN");
-			/* Send buffered output data to the socket. */
-			if (FD_ISSET(ch->sock, writeset) && buffer_len(&ch->output) > 0) {
-				len = write(ch->sock, buffer_ptr(&ch->output),
-					    buffer_len(&ch->output));
-				if (len <= 0)
-					buffer_consume(&ch->output, buffer_len(&ch->output));
-				else
-					buffer_consume(&ch->output, len);
-			}
-			break;
-
-		case SSH_CHANNEL_X11_OPEN:
-		case SSH_CHANNEL_FREE:
-		default:
-			continue;
-		}
-	}
+	channel_handler(channel_post, readset, writeset);
 }
 
 /* If there is data to send to the connection, send some of it now. */
@@ -529,26 +683,26 @@
 channel_output_poll()
 {
 	int len, i;
-	Channel *ch;
+	Channel *c;
 
 	for (i = 0; i < channels_alloc; i++) {
-		ch = &channels[i];
+		c = &channels[i];
 
 		/* We are only interested in channels that can have buffered incoming data. */
 		if (compat13) {
-			if (ch->type != SSH_CHANNEL_OPEN &&
-			    ch->type != SSH_CHANNEL_INPUT_DRAINING)
+			if (c->type != SSH_CHANNEL_OPEN &&
+			    c->type != SSH_CHANNEL_INPUT_DRAINING)
 				continue;
 		} else {
-			if (ch->type != SSH_CHANNEL_OPEN)
+			if (c->type != SSH_CHANNEL_OPEN)
 				continue;
-			if (ch->istate != CHAN_INPUT_OPEN &&
-			    ch->istate != CHAN_INPUT_WAIT_DRAIN)
+			if (c->istate != CHAN_INPUT_OPEN &&
+			    c->istate != CHAN_INPUT_WAIT_DRAIN)
 				continue;
 		}
 
 		/* Get the amount of buffered data for this channel. */
-		len = buffer_len(&ch->input);
+		len = buffer_len(&c->input);
 		if (len > 0) {
 			/* Send some data for the other side over the secure connection. */
 			if (packet_is_interactive()) {
@@ -556,22 +710,26 @@
 					len = 512;
 			} else {
 				/* Keep the packets at reasonable size. */
-				if (len > packet_get_maxsize()/2)
+				if (len > packet_get_maxsize())
 					len = packet_get_maxsize()/2;
 			}
-			packet_start(SSH_MSG_CHANNEL_DATA);
-			packet_put_int(ch->remote_id);
-			packet_put_string(buffer_ptr(&ch->input), len);
-			packet_send();
-			buffer_consume(&ch->input, len);
-		} else if (ch->istate == CHAN_INPUT_WAIT_DRAIN) {
+			if (len > 0) {
+				packet_start(SSH_MSG_CHANNEL_DATA);
+				packet_put_int(c->remote_id);
+				packet_put_string(buffer_ptr(&c->input), len);
+				packet_send();
+				buffer_consume(&c->input, len);
+				c->remote_window -= len;
+debug("channel %d: send data len %d", c->self, len);
+			}
+		} else if (c->istate == CHAN_INPUT_WAIT_DRAIN) {
 			if (compat13)
 				fatal("cannot happen: istate == INPUT_WAIT_DRAIN for proto 1.3");
 			/*
 			 * input-buffer is empty and read-socket shutdown:
 			 * tell peer, that we will not send more data: send IEOF
 			 */
-			chan_ibuf_empty(ch);
+			chan_ibuf_empty(c);
 		}
 	}
 }
@@ -583,35 +741,33 @@
  */
 
 void 
-channel_input_data(int payload_len)
+channel_input_data(int type, int plen)
 {
 	int id;
 	char *data;
 	unsigned int data_len;
-	Channel *ch;
+	Channel *c;
 
 	/* Get the channel number and verify it. */
 	id = packet_get_int();
-	if (id < 0 || id >= channels_alloc)
+	c = channel_lookup(id);
+	if (c == NULL)
 		packet_disconnect("Received data for nonexistent channel %d.", id);
-	ch = &channels[id];
-
-	if (ch->type == SSH_CHANNEL_FREE)
-		packet_disconnect("Received data for free channel %d.", ch->self);
 
 	/* Ignore any data for non-open channels (might happen on close) */
-	if (ch->type != SSH_CHANNEL_OPEN &&
-	    ch->type != SSH_CHANNEL_X11_OPEN)
+	if (c->type != SSH_CHANNEL_OPEN &&
+	    c->type != SSH_CHANNEL_X11_OPEN)
 		return;
 
 	/* same for protocol 1.5 if output end is no longer open */
-	if (!compat13 && ch->ostate != CHAN_OUTPUT_OPEN)
+	if (!compat13 && c->ostate != CHAN_OUTPUT_OPEN)
 		return;
 
 	/* Get the data. */
 	data = packet_get_string(&data_len);
-	packet_integrity_check(payload_len, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA);
-	buffer_append(&ch->output, data, data_len);
+
+	packet_integrity_check(plen, 4 + 4 + data_len, SSH_MSG_CHANNEL_DATA);
+	buffer_append(&c->output, data, data_len);
 	xfree(data);
 }
 
@@ -624,45 +780,60 @@
 channel_not_very_much_buffered_data()
 {
 	unsigned int i;
-	Channel *ch;
+	Channel *c;
 
 	for (i = 0; i < channels_alloc; i++) {
-		ch = &channels[i];
-		if (ch->type == SSH_CHANNEL_OPEN) {
-			if (buffer_len(&ch->input) > packet_get_maxsize())
+		c = &channels[i];
+		if (c->type == SSH_CHANNEL_OPEN) {
+			if (buffer_len(&c->input) > packet_get_maxsize()) {
+				debug("channel %d: big input buffer %d",
+				    c->self, buffer_len(&c->input));
 				return 0;
-			if (buffer_len(&ch->output) > packet_get_maxsize())
+			}
+			if (buffer_len(&c->output) > packet_get_maxsize()) {
+				debug("channel %d: big output buffer %d",
+				    c->self, buffer_len(&c->output));
 				return 0;
+			}
 		}
 	}
 	return 1;
 }
 
-/* This is called after receiving CHANNEL_CLOSE/IEOF. */
+void 
+channel_input_ieof(int type, int plen)
+{
+	int id;
+	Channel *c;
+
+	packet_integrity_check(plen, 4, type);
+
+	id = packet_get_int();
+	c = channel_lookup(id);
+	if (c == NULL)
+		packet_disconnect("Received ieof for nonexistent channel %d.", id);
+	chan_rcvd_ieof(c);
+}
 
 void 
-channel_input_close()
+channel_input_close(int type, int plen)
 {
-	int channel;
+	int id;
+	Channel *c;
 
-	/* Get the channel number and verify it. */
-	channel = packet_get_int();
-	if (channel < 0 || channel >= channels_alloc ||
-	    channels[channel].type == SSH_CHANNEL_FREE)
-		packet_disconnect("Received data for nonexistent channel %d.", channel);
+	packet_integrity_check(plen, 4, type);
 
-	if (!compat13) {
-		/* proto version 1.5 overloads CLOSE with IEOF */
-		chan_rcvd_ieof(&channels[channel]);
-		return;
-	}
+	id = packet_get_int();
+	c = channel_lookup(id);
+	if (c == NULL)
+		packet_disconnect("Received close for nonexistent channel %d.", id);
 
 	/*
 	 * Send a confirmation that we have closed the channel and no more
 	 * data is coming for it.
 	 */
 	packet_start(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION);
-	packet_put_int(channels[channel].remote_id);
+	packet_put_int(c->remote_id);
 	packet_send();
 
 	/*
@@ -672,81 +843,80 @@
 	 * no-one to receive the confirmation.  The channel gets freed when
 	 * the confirmation arrives.
 	 */
-	if (channels[channel].type != SSH_CHANNEL_CLOSED) {
+	if (c->type != SSH_CHANNEL_CLOSED) {
 		/*
 		 * Not a closed channel - mark it as draining, which will
 		 * cause it to be freed later.
 		 */
-		buffer_consume(&channels[channel].input,
-			       buffer_len(&channels[channel].input));
-		channels[channel].type = SSH_CHANNEL_OUTPUT_DRAINING;
+		buffer_consume(&c->input, buffer_len(&c->input));
+		c->type = SSH_CHANNEL_OUTPUT_DRAINING;
 	}
 }
 
-/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION/OCLOSE. */
-
+/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
 void 
-channel_input_close_confirmation()
+channel_input_oclose(int type, int plen)
 {
-	int channel;
-
-	/* Get the channel number and verify it. */
-	channel = packet_get_int();
-	if (channel < 0 || channel >= channels_alloc)
-		packet_disconnect("Received close confirmation for out-of-range channel %d.",
-				  channel);
-
-	if (!compat13) {
-		/* proto version 1.5 overloads CLOSE_CONFIRMATION with OCLOSE */
-		chan_rcvd_oclose(&channels[channel]);
-		return;
-	}
-	if (channels[channel].type != SSH_CHANNEL_CLOSED)
-		packet_disconnect("Received close confirmation for non-closed channel %d (type %d).",
-				  channel, channels[channel].type);
-
-	/* Free the channel. */
-	channel_free(channel);
+	int id = packet_get_int();
+	Channel *c = channel_lookup(id);
+	packet_integrity_check(plen, 4, type);
+	if (c == NULL)
+		packet_disconnect("Received oclose for nonexistent channel %d.", id);
+	chan_rcvd_oclose(c);
 }
 
-/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */
+void 
+channel_input_close_confirmation(int type, int plen)
+{
+	int id = packet_get_int();
+	Channel *c = channel_lookup(id);
+
+	if (c == NULL)
+		packet_disconnect("Received close confirmation for "
+		    "out-of-range channel %d.", id);
+	if (c->type != SSH_CHANNEL_CLOSED)
+		packet_disconnect("Received close confirmation for "
+		    "non-closed channel %d (type %d).", id, c->type);
+	channel_free(c->self);
+}
 
 void 
-channel_input_open_confirmation()
+channel_input_open_confirmation(int type, int plen)
 {
-	int channel, remote_channel;
+	int id, remote_id;
+	Channel *c;
 
-	/* Get the channel number and verify it. */
-	channel = packet_get_int();
-	if (channel < 0 || channel >= channels_alloc ||
-	    channels[channel].type != SSH_CHANNEL_OPENING)
-		packet_disconnect("Received open confirmation for non-opening channel %d.",
-				  channel);
+	packet_integrity_check(plen, 4 + 4, type);
 
-	/* Get remote side's id for this channel. */
-	remote_channel = packet_get_int();
+	id = packet_get_int();
+	c = channel_lookup(id);
 
+	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
+		packet_disconnect("Received open confirmation for "
+		    "non-opening channel %d.", id);
+	remote_id = packet_get_int();
 	/* Record the remote channel number and mark that the channel is now open. */
-	channels[channel].remote_id = remote_channel;
-	channels[channel].type = SSH_CHANNEL_OPEN;
+	c->remote_id = remote_id;
+	c->type = SSH_CHANNEL_OPEN;
 }
 
-/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */
-
 void 
-channel_input_open_failure()
+channel_input_open_failure(int type, int plen)
 {
-	int channel;
+	int id;
+	Channel *c;
 
-	/* Get the channel number and verify it. */
-	channel = packet_get_int();
-	if (channel < 0 || channel >= channels_alloc ||
-	    channels[channel].type != SSH_CHANNEL_OPENING)
-		packet_disconnect("Received open failure for non-opening channel %d.",
-				  channel);
+	packet_integrity_check(plen, 4, type);
+
+	id = packet_get_int();
+	c = channel_lookup(id);
+
+	if (c==NULL || c->type != SSH_CHANNEL_OPENING)
+		packet_disconnect("Received open failure for "
+		    "non-opening channel %d.", id);
 
 	/* Free the channel.  This will also close the socket. */
-	channel_free(channel);
+	channel_free(id);
 }
 
 /*
@@ -859,15 +1029,16 @@
 		case SSH_CHANNEL_X11_OPEN:
 		case SSH_CHANNEL_INPUT_DRAINING:
 		case SSH_CHANNEL_OUTPUT_DRAINING:
-			snprintf(buf, sizeof buf, "  #%d %.300s (t%d r%d i%d/%d o%d/%d)\r\n",
+			snprintf(buf, sizeof buf, "  #%d %.300s (t%d r%d i%d/%d o%d/%d fd %d/%d)\r\n",
 			    c->self, c->remote_name,
 			    c->type, c->remote_id,
 			    c->istate, buffer_len(&c->input),
-			    c->ostate, buffer_len(&c->output));
+			    c->ostate, buffer_len(&c->output),
+			    c->rfd, c->wfd);
 			buffer_append(&buffer, buf, strlen(buf));
 			continue;
 		default:
-			fatal("channel_still_open: bad channel type %d", c->type);
+			fatal("channel_open_message: bad channel type %d", c->type);
 			/* NOTREACHED */
 		}
 	}
@@ -950,8 +1121,11 @@
 			continue;
 		}
 		/* Allocate a channel number for the socket. */
-		ch = channel_allocate(SSH_CHANNEL_PORT_LISTENER, sock,
-		    xstrdup("port listener"));
+		ch = channel_new(
+		    "port listener", SSH_CHANNEL_PORT_LISTENER,
+		    sock, sock, -1,
+		    CHAN_WINDOW_DEFAULT, CHAN_PACKET_DEFAULT,
+		    0, xstrdup("port listener"));
 		strlcpy(channels[ch].path, host, sizeof(channels[ch].path));
 		channels[ch].host_port = host_port;
 		channels[ch].listening_port = port;
@@ -968,26 +1142,26 @@
  */
 
 void 
-channel_request_remote_forwarding(u_short port, const char *host,
-				  u_short remote_port)
+channel_request_remote_forwarding(u_short listen_port, const char *host_to_connect,
+				  u_short port_to_connect)
 {
 	int payload_len;
 	/* Record locally that connection to this host/port is permitted. */
 	if (num_permitted_opens >= SSH_MAX_FORWARDS_PER_DIRECTION)
 		fatal("channel_request_remote_forwarding: too many forwards");
 
-	permitted_opens[num_permitted_opens].host = xstrdup(host);
-	permitted_opens[num_permitted_opens].port = remote_port;
+	permitted_opens[num_permitted_opens].host_to_connect = xstrdup(host_to_connect);
+	permitted_opens[num_permitted_opens].port_to_connect = port_to_connect;
+	permitted_opens[num_permitted_opens].listen_port = listen_port;
 	num_permitted_opens++;
 
 	/* Send the forward request to the remote side. */
 	packet_start(SSH_CMSG_PORT_FORWARD_REQUEST);
-	packet_put_int(port);
-	packet_put_string(host, strlen(host));
-	packet_put_int(remote_port);
+	packet_put_int(port_to_connect);
+	packet_put_string(host_to_connect, strlen(host_to_connect));
+	packet_put_int(listen_port);
 	packet_send();
 	packet_write_wait();
-
 	/*
 	 * Wait for response from the remote side.  It will send a disconnect
 	 * message on failure, and we will never see it here.
@@ -1029,63 +1203,14 @@
 	xfree(hostname);
 }
 
-/*
- * This is called after receiving PORT_OPEN message.  This attempts to
- * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION
- * or CHANNEL_OPEN_FAILURE.
- */
-
-void 
-channel_input_port_open(int payload_len)
+/* XXX move to aux.c */
+int
+channel_connect_to(const char *host, u_short host_port)
 {
-	int remote_channel, sock = 0, newch, i;
-	u_short host_port;
-	char *host, *originator_string;
-	unsigned int host_len, originator_len;
 	struct addrinfo hints, *ai, *aitop;
 	char ntop[NI_MAXHOST], strport[NI_MAXSERV];
 	int gaierr;
-
-	/* Get remote channel number. */
-	remote_channel = packet_get_int();
-
-	/* Get host name to connect to. */
-	host = packet_get_string(&host_len);
-
-	/* Get port to connect to. */
-	host_port = packet_get_int();
-
-	/* Get remote originator name. */
-	if (have_hostname_in_open) {
-		originator_string = packet_get_string(&originator_len);
-		originator_len += 4;	/* size of packet_int */
-	} else {
-		originator_string = xstrdup("unknown (remote did not supply name)");
-		originator_len = 0;	/* no originator supplied */
-	}
-
-	packet_integrity_check(payload_len,
-			       4 + 4 + host_len + 4 + originator_len,
-			       SSH_MSG_PORT_OPEN);
-
-	/* Check if opening that port is permitted. */
-	if (!all_opens_permitted) {
-		/* Go trough all permitted ports. */
-		for (i = 0; i < num_permitted_opens; i++)
-			if (permitted_opens[i].port == host_port &&
-			    strcmp(permitted_opens[i].host, host) == 0)
-				break;
-
-		/* Check if we found the requested port among those permitted. */
-		if (i >= num_permitted_opens) {
-			/* The port is not permitted. */
-			log("Received request to connect to %.100s:%d, but the request was denied.",
-			    host, host_port);
-			packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
-			packet_put_int(remote_channel);
-			packet_send();
-		}
-	}
+	int sock = -1;
 
 	memset(&hints, 0, sizeof(hints));
 	hints.ai_family = IPv4or6;
@@ -1093,15 +1218,14 @@
 	snprintf(strport, sizeof strport, "%d", host_port);
 	if ((gaierr = getaddrinfo(host, strport, &hints, &aitop)) != 0) {
 		error("%.100s: unknown host (%s)", host, gai_strerror(gaierr));
-		goto fail;
+		return -1;
 	}
-
 	for (ai = aitop; ai; ai = ai->ai_next) {
 		if (ai->ai_family != AF_INET && ai->ai_family != AF_INET6)
 			continue;
 		if (getnameinfo(ai->ai_addr, ai->ai_addrlen, ntop, sizeof(ntop),
 		    strport, sizeof(strport), NI_NUMERICHOST|NI_NUMERICSERV) != 0) {
-			error("channel_input_port_open: getnameinfo failed");
+			error("channel_connect_to: getnameinfo failed");
 			continue;
 		}
 		/* Create the socket. */
@@ -1121,37 +1245,82 @@
 
 	}
 	freeaddrinfo(aitop);
-
 	if (!ai) {
 		error("connect %.100s port %d: failed.", host, host_port);	
-		goto fail;
+		return -1;
+	}
+	/* success */
+	return sock;
+}
+
+/*
+ * This is called after receiving PORT_OPEN message.  This attempts to
+ * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION
+ * or CHANNEL_OPEN_FAILURE.
+ */
+
+void 
+channel_input_port_open(int type, int plen)
+{
+	u_short host_port;
+	char *host, *originator_string;
+	int remote_channel, sock = -1, newch, i, denied;
+	unsigned int host_len, originator_len;
+
+	/* Get remote channel number. */
+	remote_channel = packet_get_int();
+
+	/* Get host name to connect to. */
+	host = packet_get_string(&host_len);
+
+	/* Get port to connect to. */
+	host_port = packet_get_int();
+
+	/* Get remote originator name. */
+	if (have_hostname_in_open) {
+		originator_string = packet_get_string(&originator_len);
+		originator_len += 4;	/* size of packet_int */
+	} else {
+		originator_string = xstrdup("unknown (remote did not supply name)");
+		originator_len = 0;	/* no originator supplied */
 	}
 
-	/* Successful connection. */
+	packet_integrity_check(plen,
+	    4 + 4 + host_len + 4 + originator_len, SSH_MSG_PORT_OPEN);
 
-	/* Allocate a channel for this connection. */
-	newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string);
-	channels[newch].remote_id = remote_channel;
+	/* Check if opening that port is permitted. */
+	denied = 0;
+	if (!all_opens_permitted) {
+		/* Go trough all permitted ports. */
+		for (i = 0; i < num_permitted_opens; i++)
+			if (permitted_opens[i].port_to_connect == host_port &&
+			    strcmp(permitted_opens[i].host_to_connect, host) == 0)
+				break;
 
-	/* Send a confirmation to the remote host. */
-	packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
-	packet_put_int(remote_channel);
-	packet_put_int(newch);
-	packet_send();
+		/* Check if we found the requested port among those permitted. */
+		if (i >= num_permitted_opens) {
+			/* The port is not permitted. */
+			log("Received request to connect to %.100s:%d, but the request was denied.",
+			    host, host_port);
+			denied = 1;
+		}
+	}
+	sock = denied ? -1 : channel_connect_to(host, host_port);
+	if (sock > 0) {
+		/* Allocate a channel for this connection. */
+		newch = channel_allocate(SSH_CHANNEL_OPEN, sock, originator_string);
+		channels[newch].remote_id = remote_channel;
 
-	/* Free the argument string. */
+		packet_start(SSH_MSG_CHANNEL_OPEN_CONFIRMATION);
+		packet_put_int(remote_channel);
+		packet_put_int(newch);
+		packet_send();
+	} else {
+		packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
+		packet_put_int(remote_channel);
+		packet_send();
+	}
 	xfree(host);
-
-	return;
-
-fail:
-	/* Free the argument string. */
-	xfree(host);
-
-	/* Send refusal to the remote host. */
-	packet_start(SSH_MSG_CHANNEL_OPEN_FAILURE);
-	packet_put_int(remote_channel);
-	packet_send();
 }
 
 /*
@@ -1336,7 +1505,7 @@
  */
 
 void 
-x11_input_open(int payload_len)
+x11_input_open(int type, int plen)
 {
 	int remote_channel, display_number, sock = 0, newch;
 	const char *display;
@@ -1359,7 +1528,7 @@
 	}
 
 	debug("Received X11 open request.");
-	packet_integrity_check(payload_len, 4 + remote_len, SSH_SMSG_X11_OPEN);
+	packet_integrity_check(plen, 4 + remote_len, SSH_SMSG_X11_OPEN);
 
 	/* Try to open a socket for the local X server. */
 	display = getenv("DISPLAY");
@@ -1425,19 +1594,18 @@
 		sock = socket(ai->ai_family, SOCK_STREAM, 0);
 		if (sock < 0) {
 			debug("socket: %.100s", strerror(errno));
-		continue;
+			continue;
+		}
+		/* Connect it to the display. */
+		if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
+			debug("connect %.100s port %d: %.100s", buf,
+			    6000 + display_number, strerror(errno));
+			close(sock);
+			continue;
+		}
+		/* Success */
+		break;
 	}
-	/* Connect it to the display. */
-	if (connect(sock, ai->ai_addr, ai->ai_addrlen) < 0) {
-		debug("connect %.100s port %d: %.100s", buf, 6000 + display_number, 
-		    strerror(errno));
-		close(sock);
-		continue;
-	}
-	/* Success */
-	break;
-
-	} /* (ai = aitop, ai; ai = ai->ai_next) */
 	freeaddrinfo(aitop);
 	if (!ai) {
 		error("connect %.100s port %d: %.100s", buf, 6000 + display_number, 
@@ -1625,11 +1793,13 @@
 /* This is called to process an SSH_SMSG_AGENT_OPEN message. */
 
 void 
-auth_input_open_request()
+auth_input_open_request(int type, int plen)
 {
 	int remch, sock, newch;
 	char *dummyname;
 
+	packet_integrity_check(plen, 4, type);
+
 	/* Read the remote channel number from the message. */
 	remch = packet_get_int();
 
diff --git a/channels.h b/channels.h
index f8bca5c..73ff5a5 100644
--- a/channels.h
+++ b/channels.h
@@ -1,4 +1,4 @@
-/* RCSID("$Id: channels.h,v 1.4 1999/11/25 00:54:58 damien Exp $"); */
+/* RCSID("$Id: channels.h,v 1.5 2000/04/01 01:09:23 damien Exp $"); */
 
 #ifndef CHANNELS_H
 #define CHANNELS_H
@@ -10,17 +10,18 @@
 #define SSH_CHANNEL_OPENING		3	/* waiting for confirmation */
 #define SSH_CHANNEL_OPEN		4	/* normal open two-way channel */
 #define SSH_CHANNEL_CLOSED		5	/* waiting for close confirmation */
-/*	SSH_CHANNEL_AUTH_FD		6    	   authentication fd */
-#define SSH_CHANNEL_AUTH_SOCKET		7	/* authentication socket */
-/*	SSH_CHANNEL_AUTH_SOCKET_FD	8    	   connection to auth socket */
-#define SSH_CHANNEL_X11_OPEN		9	/* reading first X11 packet */
-#define SSH_CHANNEL_INPUT_DRAINING	10	/* sending remaining data to conn */
-#define SSH_CHANNEL_OUTPUT_DRAINING	11	/* sending remaining data to app */
+#define SSH_CHANNEL_AUTH_SOCKET		6	/* authentication socket */
+#define SSH_CHANNEL_X11_OPEN		7	/* reading first X11 packet */
+#define SSH_CHANNEL_INPUT_DRAINING	8	/* sending remaining data to conn */
+#define SSH_CHANNEL_OUTPUT_DRAINING	9	/* sending remaining data to app */
+#define SSH_CHANNEL_LARVAL		10	/* larval session */
+#define SSH_CHANNEL_MAX_TYPE		11
 
 /*
  * Data structure for channel data.  This is iniailized in channel_allocate
  * and cleared in channel_free.
  */
+typedef void channel_callback_fn(int id, void *arg);
 
 typedef struct Channel {
 	int     type;		/* channel type/state */
@@ -29,15 +30,192 @@
 	/* peer can be reached over encrypted connection, via packet-sent */
 	int     istate;		/* input from channel (state of receive half) */
 	int     ostate;		/* output to channel  (state of transmit half) */
-	int     sock;		/* data socket, linked to this channel */
+	int     rfd;		/* read fd */
+	int     wfd;		/* write fd */
+	int     efd;		/* extended fd */
+	int     sock;		/* sock fd */
 	Buffer  input;		/* data read from socket, to be sent over
 				 * encrypted connection */
 	Buffer  output;		/* data received over encrypted connection for
 				 * send on socket */
+	Buffer  extended;
 	char    path[200];	/* path for unix domain sockets, or host name
 				 * for forwards */
 	int     listening_port;	/* port being listened for forwards */
 	int     host_port;	/* remote port to connect for forwards */
 	char   *remote_name;	/* remote hostname */
+
+	int	remote_window;
+	int	remote_maxpacket;
+	int	local_window;
+	int	local_window_max;
+	int	local_consumed;
+	int	local_maxpacket;
+	int     extended_usage;
+
+	char   *ctype;		/* type */
+
+	// callback
+	channel_callback_fn	*cb_fn;
+	void	*cb_arg;
+	int	cb_event;
+	channel_callback_fn	*dettach_user;
 }       Channel;
+
+#define CHAN_EXTENDED_IGNORE		0
+#define CHAN_EXTENDED_READ		1
+#define CHAN_EXTENDED_WRITE		2
+
+void	channel_open(int id);
+Channel	*channel_lookup(int id);
+
+int
+channel_new(char *ctype, int type, int rfd, int wfd, int efd,
+    int window, int maxpack, int extended_usage, char *remote_name);
+
+void	channel_input_close(int type, int plen);
+void	channel_input_close_confirmation(int type, int plen);
+void	channel_input_data(int type, int plen);
+void	channel_input_ieof(int type, int plen);
+void	channel_input_oclose(int type, int plen);
+void	channel_input_open_confirmation(int type, int plen);
+void	channel_input_open_failure(int type, int plen);
+void	channel_input_port_open(int type, int plen);
+void	channel_input_open(int type, int plen);
+
+/* Sets specific protocol options. */
+void    channel_set_options(int hostname_in_open);
+
+/*
+ * Allocate a new channel object and set its type and socket.  Remote_name
+ * must have been allocated with xmalloc; this will free it when the channel
+ * is freed.
+ */
+int     channel_allocate(int type, int sock, char *remote_name);
+
+/* Free the channel and close its socket. */
+void    channel_free(int channel);
+
+/* Add any bits relevant to channels in select bitmasks. */
+void    channel_prepare_select(fd_set * readset, fd_set * writeset);
+
+/*
+ * After select, perform any appropriate operations for channels which have
+ * events pending.
+ */
+void    channel_after_select(fd_set * readset, fd_set * writeset);
+
+/* If there is data to send to the connection, send some of it now. */
+void    channel_output_poll(void);
+
+/* Returns true if no channel has too much buffered data. */
+int     channel_not_very_much_buffered_data(void);
+
+/* This closes any sockets that are listening for connections; this removes
+   any unix domain sockets. */
+void    channel_stop_listening(void);
+
+/*
+ * Closes the sockets of all channels.  This is used to close extra file
+ * descriptors after a fork.
+ */
+void    channel_close_all(void);
+
+/* Returns the maximum file descriptor number used by the channels. */
+int     channel_max_fd(void);
+
+/* Returns true if there is still an open channel over the connection. */
+int     channel_still_open(void);
+
+/*
+ * Returns a string containing a list of all open channels.  The list is
+ * suitable for displaying to the user.  It uses crlf instead of newlines.
+ * The caller should free the string with xfree.
+ */
+char   *channel_open_message(void);
+
+/*
+ * Initiate forwarding of connections to local port "port" through the secure
+ * channel to host:port from remote side.  This never returns if there was an
+ * error.
+ */
+void 
+channel_request_local_forwarding(u_short port, const char *host,
+    u_short remote_port, int gateway_ports);
+
+/*
+ * Initiate forwarding of connections to port "port" on remote host through
+ * the secure channel to host:port from local side.  This never returns if
+ * there was an error.  This registers that open requests for that port are
+ * permitted.
+ */
+void 
+channel_request_remote_forwarding(u_short port, const char *host,
+    u_short remote_port);
+
+/*
+ * Permits opening to any host/port in SSH_MSG_PORT_OPEN.  This is usually
+ * called by the server, because the user could connect to any port anyway,
+ * and the server has no way to know but to trust the client anyway.
+ */
+void    channel_permit_all_opens(void);
+
+/*
+ * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
+ * listening for the port, and sends back a success reply (or disconnect
+ * message if there was an error).  This never returns if there was an error.
+ */
+void    channel_input_port_forward_request(int is_root);
+
+/*
+ * Creates a port for X11 connections, and starts listening for it. Returns
+ * the display name, or NULL if an error was encountered.
+ */
+char   *x11_create_display(int screen);
+
+/*
+ * Creates an internet domain socket for listening for X11 connections.
+ * Returns a suitable value for the DISPLAY variable, or NULL if an error
+ * occurs.
+ */
+char   *x11_create_display_inet(int screen, int x11_display_offset);
+
+/*
+ * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
+ * the remote channel number.  We should do whatever we want, and respond
+ * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
+ */
+void    x11_input_open(int type, int plen);
+
+/*
+ * Requests forwarding of X11 connections.  This should be called on the
+ * client only.
+ */
+void    x11_request_forwarding(void);
+
+/*
+ * Requests forwarding for X11 connections, with authentication spoofing.
+ * This should be called in the client only.
+ */
+void    x11_request_forwarding_with_spoofing(const char *proto, const char *data);
+
+/* Sends a message to the server to request authentication fd forwarding. */
+void    auth_request_forwarding(void);
+
+/*
+ * Returns the name of the forwarded authentication socket.  Returns NULL if
+ * there is no forwarded authentication socket.  The returned value points to
+ * a static buffer.
+ */
+char   *auth_get_socket_name(void);
+
+/*
+ * This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
+ * This starts forwarding authentication requests.
+ */
+void    auth_input_request_forwarding(struct passwd * pw);
+
+/* This is called to process an SSH_SMSG_AGENT_OPEN message. */
+void    auth_input_open_request(int type, int plen);
+
 #endif
diff --git a/cipher.c b/cipher.c
index bf1518d..f7b7b47 100644
--- a/cipher.c
+++ b/cipher.c
@@ -12,7 +12,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: cipher.c,v 1.14 2000/03/26 03:04:52 damien Exp $");
+RCSID("$Id: cipher.c,v 1.15 2000/04/01 01:09:23 damien Exp $");
 
 #include "ssh.h"
 #include "cipher.h"
@@ -122,7 +122,12 @@
 	"3des",
 	"tss",
 	"rc4",
-	"blowfish"
+	"blowfish",
+	"reserved",
+	"blowfish-cbc",
+	"3des-cbc",
+	"arcfour",
+	"cast128-cbc"
 };
 
 /*
@@ -137,6 +142,10 @@
 	unsigned int mask = 0;
 	mask |= 1 << SSH_CIPHER_3DES;		/* Mandatory */
 	mask |= 1 << SSH_CIPHER_BLOWFISH;
+	mask |= 1 << SSH_CIPHER_BLOWFISH_CBC;
+	mask |= 1 << SSH_CIPHER_3DES_CBC;
+	mask |= 1 << SSH_CIPHER_ARCFOUR;
+	mask |= 1 << SSH_CIPHER_CAST128_CBC;
 	return mask;
 }
 
@@ -233,16 +242,84 @@
 		break;
 
 	case SSH_CIPHER_BLOWFISH:
+		if (keylen < 16)
+			error("Key length %d is insufficient for blowfish.", keylen);
 		BF_set_key(&context->u.bf.key, keylen, padded);
 		memset(context->u.bf.iv, 0, 8);
 		break;
 
+	case SSH_CIPHER_3DES_CBC:
+	case SSH_CIPHER_BLOWFISH_CBC:
+	case SSH_CIPHER_ARCFOUR:
+	case SSH_CIPHER_CAST128_CBC:
+		fatal("cipher_set_key: illegal cipher: %s", cipher_name(cipher));
+		break;
+
 	default:
 		fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher));
 	}
 	memset(padded, 0, sizeof(padded));
 }
 
+
+void 
+cipher_set_key_iv(CipherContext * context, int cipher,
+    const unsigned char *key, int keylen, 
+    const unsigned char *iv, int ivlen)
+{
+	/* Set cipher type. */
+	context->type = cipher;
+
+	/* Initialize the initialization vector. */
+	switch (cipher) {
+	case SSH_CIPHER_NONE:
+		break;
+
+	case SSH_CIPHER_3DES:
+	case SSH_CIPHER_BLOWFISH:
+		fatal("cipher_set_key_iv: illegal cipher: %s", cipher_name(cipher));
+		break;
+
+	case SSH_CIPHER_3DES_CBC:
+		if (keylen < 24)
+			error("Key length %d is insufficient for 3des-cbc.", keylen);
+		des_set_key((void *) key, context->u.des3.key1);
+		des_set_key((void *) (key+8), context->u.des3.key2);
+		des_set_key((void *) (key+16), context->u.des3.key3);
+		if (ivlen < 8)
+			error("IV length %d is insufficient for 3des-cbc.", ivlen);
+		memcpy(context->u.des3.iv3, (char *)iv, 8);
+		break;
+
+	case SSH_CIPHER_BLOWFISH_CBC:
+		if (keylen < 16)
+			error("Key length %d is insufficient for blowfish.", keylen);
+		if (ivlen < 8)
+			error("IV length %d is insufficient for blowfish.", ivlen);
+		BF_set_key(&context->u.bf.key, keylen, (unsigned char *)key);
+		memcpy(context->u.bf.iv, (char *)iv, 8);
+		break;
+
+	case SSH_CIPHER_ARCFOUR:
+		if (keylen < 16)
+			error("Key length %d is insufficient for arcfour.", keylen);
+		RC4_set_key(&context->u.rc4, keylen, (unsigned char *)key);
+		break;
+
+	case SSH_CIPHER_CAST128_CBC:
+		if (keylen < 16)
+			error("Key length %d is insufficient for cast128.", keylen);
+		if (ivlen < 8)
+			error("IV length %d is insufficient for cast128.", ivlen);
+		CAST_set_key(&context->u.cast.key, keylen, (unsigned char *) key);
+		memcpy(context->u.cast.iv, (char *)iv, 8);
+		break;
+
+	default:
+		fatal("cipher_set_key: unknown cipher: %s", cipher_name(cipher));
+	}
+}
+
 /* Encrypts data using the cipher. */
 
 void 
@@ -272,6 +349,27 @@
 		swap_bytes(dest, dest, len);
 		break;
 
+	case SSH_CIPHER_BLOWFISH_CBC:
+		BF_cbc_encrypt((void *)src, dest, len,
+		               &context->u.bf.key, context->u.bf.iv,
+			       BF_ENCRYPT);
+		break;
+
+	case SSH_CIPHER_3DES_CBC:
+		des_ede3_cbc_encrypt(src, dest, len,
+		    context->u.des3.key1, context->u.des3.key2,
+		    context->u.des3.key3, &context->u.des3.iv3, DES_ENCRYPT);
+		break;
+
+	case SSH_CIPHER_ARCFOUR:
+		RC4(&context->u.rc4, len, (unsigned char *)src, dest);
+		break;
+
+	case SSH_CIPHER_CAST128_CBC:
+		CAST_cbc_encrypt(src, dest, len,
+		    &context->u.cast.key, context->u.cast.iv, CAST_ENCRYPT);
+		break;
+
 	default:
 		fatal("cipher_encrypt: unknown cipher: %s", cipher_name(context->type));
 	}
@@ -306,6 +404,27 @@
 		swap_bytes(dest, dest, len);
 		break;
 
+	case SSH_CIPHER_BLOWFISH_CBC:
+		BF_cbc_encrypt((void *) src, dest, len,
+			       &context->u.bf.key, context->u.bf.iv,
+			       BF_DECRYPT);
+		break;
+
+	case SSH_CIPHER_3DES_CBC:
+		des_ede3_cbc_encrypt(src, dest, len,
+		    context->u.des3.key1, context->u.des3.key2,
+		    context->u.des3.key3, &context->u.des3.iv3, DES_DECRYPT);
+		break;
+
+	case SSH_CIPHER_ARCFOUR:
+		RC4(&context->u.rc4, len, (unsigned char *)src, dest);
+		break;
+
+	case SSH_CIPHER_CAST128_CBC:
+		CAST_cbc_encrypt(src, dest, len,
+		    &context->u.cast.key, context->u.cast.iv, CAST_DECRYPT);
+		break;
+
 	default:
 		fatal("cipher_decrypt: unknown cipher: %s", cipher_name(context->type));
 	}
diff --git a/cipher.h b/cipher.h
index c323a6c..6cfeb63 100644
--- a/cipher.h
+++ b/cipher.h
@@ -11,7 +11,7 @@
  * 
  */
 
-/* RCSID("$Id: cipher.h,v 1.6 2000/03/26 03:04:52 damien Exp $"); */
+/* RCSID("$Id: cipher.h,v 1.7 2000/04/01 01:09:23 damien Exp $"); */
 
 #ifndef CIPHER_H
 #define CIPHER_H
@@ -21,10 +21,14 @@
 #ifdef HAVE_OPENSSL
 #include <openssl/des.h>
 #include <openssl/blowfish.h>
+#include <openssl/rc4.h>
+#include <openssl/cast.h>
 #endif
 #ifdef HAVE_SSL
 #include <ssl/des.h>
 #include <ssl/blowfish.h>
+#include <ssl/rc4.h>
+#include <ssl/cast.h>
 #endif
 
 /* Cipher types.  New types can be added, but old types should not be removed
@@ -37,6 +41,13 @@
 #define SSH_CIPHER_BROKEN_TSS	4	/* TRI's Simple Stream encryption CBC */
 #define SSH_CIPHER_BROKEN_RC4	5	/* Alleged RC4 */
 #define SSH_CIPHER_BLOWFISH	6
+#define SSH_CIPHER_RESERVED	7
+
+/* these ciphers are used in SSH2: */
+#define SSH_CIPHER_BLOWFISH_CBC	8
+#define SSH_CIPHER_3DES_CBC	9
+#define SSH_CIPHER_ARCFOUR	10	/* Alleged RC4 */
+#define SSH_CIPHER_CAST128_CBC	11
 
 typedef struct {
 	unsigned int type;
@@ -52,6 +63,11 @@
 			struct bf_key_st key;
 			unsigned char iv[8];
 		}       bf;
+		struct {
+			CAST_KEY key;
+			unsigned char iv[8];
+		} cast;
+		RC4_KEY rc4;
 	}       u;
 }       CipherContext;
 /*
@@ -77,6 +93,10 @@
 void 
 cipher_set_key(CipherContext * context, int cipher,
     const unsigned char *key, int keylen, int for_encryption);
+void 
+cipher_set_key_iv(CipherContext * context, int cipher,
+    const unsigned char *key, int keylen, 
+    const unsigned char *iv, int ivlen);
 
 /*
  * Sets key for the cipher by computing the MD5 checksum of the passphrase,
diff --git a/clientloop.c b/clientloop.c
index bfa3019..1bc6d7e 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -15,7 +15,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: clientloop.c,v 1.7 1999/12/07 04:38:32 damien Exp $");
+RCSID("$Id: clientloop.c,v 1.8 2000/04/01 01:09:23 damien Exp $");
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -24,6 +24,11 @@
 #include "authfd.h"
 #include "readconf.h"
 
+#include "compat.h"
+#include "channels.h"
+#include "dispatch.h"
+
+
 /* Flag indicating that stdin should be redirected from /dev/null. */
 extern int stdin_null_flag;
 
@@ -228,108 +233,6 @@
 	}
 }
 
-/*
- * Get packets from the connection input buffer, and process them as long as
- * there are packets available.
- */
-
-void 
-client_process_buffered_input_packets()
-{
-	int type;
-	char *data;
-	unsigned int data_len;
-	int payload_len;
-
-	/* Process any buffered packets from the server. */
-	while (!quit_pending &&
-	       (type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) {
-		switch (type) {
-
-		case SSH_SMSG_STDOUT_DATA:
-			data = packet_get_string(&data_len);
-			packet_integrity_check(payload_len, 4 + data_len, type);
-			buffer_append(&stdout_buffer, data, data_len);
-			stdout_bytes += data_len;
-			memset(data, 0, data_len);
-			xfree(data);
-			break;
-
-		case SSH_SMSG_STDERR_DATA:
-			data = packet_get_string(&data_len);
-			packet_integrity_check(payload_len, 4 + data_len, type);
-			buffer_append(&stderr_buffer, data, data_len);
-			stdout_bytes += data_len;
-			memset(data, 0, data_len);
-			xfree(data);
-			break;
-
-		case SSH_SMSG_EXITSTATUS:
-			packet_integrity_check(payload_len, 4, type);
-			exit_status = packet_get_int();
-			/* Acknowledge the exit. */
-			packet_start(SSH_CMSG_EXIT_CONFIRMATION);
-			packet_send();
-			/*
-			 * Must wait for packet to be sent since we are
-			 * exiting the loop.
-			 */
-			packet_write_wait();
-			/* Flag that we want to exit. */
-			quit_pending = 1;
-			break;
-
-		case SSH_SMSG_X11_OPEN:
-			x11_input_open(payload_len);
-			break;
-
-		case SSH_MSG_PORT_OPEN:
-			channel_input_port_open(payload_len);
-			break;
-
-		case SSH_SMSG_AGENT_OPEN:
-			packet_integrity_check(payload_len, 4, type);
-			auth_input_open_request();
-			break;
-
-		case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
-			packet_integrity_check(payload_len, 4 + 4, type);
-			channel_input_open_confirmation();
-			break;
-
-		case SSH_MSG_CHANNEL_OPEN_FAILURE:
-			packet_integrity_check(payload_len, 4, type);
-			channel_input_open_failure();
-			break;
-
-		case SSH_MSG_CHANNEL_DATA:
-			channel_input_data(payload_len);
-			break;
-
-		case SSH_MSG_CHANNEL_CLOSE:
-			packet_integrity_check(payload_len, 4, type);
-			channel_input_close();
-			break;
-
-		case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
-			packet_integrity_check(payload_len, 4, type);
-			channel_input_close_confirmation();
-			break;
-
-		default:
-			/*
-			 * Any unknown packets received during the actual
-			 * session cause the session to terminate.  This is
-			 * intended to make debugging easier since no
-			 * confirmations are sent.  Any compatible protocol
-			 * extensions must be negotiated during the
-			 * preparatory phase.
-			 */
-			packet_disconnect("Protocol error during session: type %d",
-					  type);
-		}
-	}
-}
 
 /*
  * Make packets from buffered stdin data, and buffer them for sending to the
@@ -776,12 +679,32 @@
 }
 
 /*
+ * Get packets from the connection input buffer, and process them as long as
+ * there are packets available.
+ *
+ * Any unknown packets received during the actual
+ * session cause the session to terminate.  This is
+ * intended to make debugging easier since no
+ * confirmations are sent.  Any compatible protocol
+ * extensions must be negotiated during the
+ * preparatory phase.
+ */
+
+void 
+client_process_buffered_input_packets()
+{
+	dispatch_run(DISPATCH_NONBLOCK, &quit_pending);
+}
+
+/*
  * Implements the interactive session with the server.  This is called after
  * the user has been authenticated, and a command has been started on the
  * remote host.  If escape_char != -1, it is the character used as an escape
  * character for terminating or suspending the session.
  */
 
+void client_init_dispatch(void);
+
 int 
 client_loop(int have_pty, int escape_char_arg)
 {
@@ -816,6 +739,8 @@
 	buffer_init(&stdout_buffer);
 	buffer_init(&stderr_buffer);
 
+	client_init_dispatch();
+
 	/* Set signal handlers to restore non-blocking mode.  */
 	signal(SIGINT, signal_handler);
 	signal(SIGQUIT, signal_handler);
@@ -950,3 +875,77 @@
 	debug("Exit status %d", exit_status);
 	return exit_status;
 }
+
+/*********/
+
+void
+client_input_stdout_data(int type, int plen)
+{
+	unsigned int data_len;
+	char *data = packet_get_string(&data_len);
+	packet_integrity_check(plen, 4 + data_len, type);
+	buffer_append(&stdout_buffer, data, data_len);
+	stdout_bytes += data_len;
+	memset(data, 0, data_len);
+	xfree(data);
+}
+void
+client_input_stderr_data(int type, int plen)
+{
+	unsigned int data_len;
+	char *data = packet_get_string(&data_len);
+	packet_integrity_check(plen, 4 + data_len, type);
+	buffer_append(&stderr_buffer, data, data_len);
+	stdout_bytes += data_len;
+	memset(data, 0, data_len);
+	xfree(data);
+}
+void
+client_input_exit_status(int type, int plen)
+{
+	packet_integrity_check(plen, 4, type);
+	exit_status = packet_get_int();
+	/* Acknowledge the exit. */
+	packet_start(SSH_CMSG_EXIT_CONFIRMATION);
+	packet_send();
+	/*
+	 * Must wait for packet to be sent since we are
+	 * exiting the loop.
+	 */
+	packet_write_wait();
+	/* Flag that we want to exit. */
+	quit_pending = 1;
+}
+
+void 
+client_init_dispatch_13()
+{
+	dispatch_init(NULL);
+	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
+	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
+	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
+	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
+	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
+	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
+	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
+	dispatch_set(SSH_SMSG_AGENT_OPEN, &auth_input_open_request);
+	dispatch_set(SSH_SMSG_EXITSTATUS, &client_input_exit_status);
+	dispatch_set(SSH_SMSG_STDERR_DATA, &client_input_stderr_data);
+	dispatch_set(SSH_SMSG_STDOUT_DATA, &client_input_stdout_data);
+	dispatch_set(SSH_SMSG_X11_OPEN, &x11_input_open);
+}
+void 
+client_init_dispatch_15()
+{
+	client_init_dispatch_13();
+	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
+	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, & channel_input_oclose);
+}
+void 
+client_init_dispatch()
+{
+	if (compat13)
+		client_init_dispatch_13();
+	else
+		client_init_dispatch_15();
+}
diff --git a/compress.c b/compress.c
index cf15c66..ee5cdcc 100644
--- a/compress.c
+++ b/compress.c
@@ -14,7 +14,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: compress.c,v 1.4 2000/03/17 12:40:16 damien Exp $");
+RCSID("$Id: compress.c,v 1.5 2000/04/01 01:09:24 damien Exp $");
 
 #include "ssh.h"
 #include "buffer.h"
@@ -90,23 +90,13 @@
 		case Z_OK:
 			/* Append compressed data to output_buffer. */
 			buffer_append(output_buffer, buf,
-				      sizeof(buf) - outgoing_stream.avail_out);
+			    sizeof(buf) - outgoing_stream.avail_out);
 			break;
-		case Z_STREAM_END:
-			fatal("buffer_compress: deflate returned Z_STREAM_END");
-			/* NOTREACHED */
-		case Z_STREAM_ERROR:
-			fatal("buffer_compress: deflate returned Z_STREAM_ERROR");
-			/* NOTREACHED */
-		case Z_BUF_ERROR:
-			fatal("buffer_compress: deflate returned Z_BUF_ERROR");
-			/* NOTREACHED */
 		default:
 			fatal("buffer_compress: deflate returned %d", status);
 			/* NOTREACHED */
 		}
-	}
-	while (outgoing_stream.avail_out == 0);
+	} while (outgoing_stream.avail_out == 0);
 }
 
 /*
@@ -127,27 +117,17 @@
 	incoming_stream.next_in = (unsigned char *) buffer_ptr(input_buffer);
 	incoming_stream.avail_in = buffer_len(input_buffer);
 
-	incoming_stream.next_out = (unsigned char *) buf;
-	incoming_stream.avail_out = sizeof(buf);
-
 	for (;;) {
+		/* Set up fixed-size output buffer. */
+		incoming_stream.next_out = (unsigned char *) buf;
+		incoming_stream.avail_out = sizeof(buf);
+
 		status = inflate(&incoming_stream, Z_PARTIAL_FLUSH);
 		switch (status) {
 		case Z_OK:
 			buffer_append(output_buffer, buf,
-				      sizeof(buf) - incoming_stream.avail_out);
-			incoming_stream.next_out = (unsigned char *) buf;
-			incoming_stream.avail_out = sizeof(buf);
+			    sizeof(buf) - incoming_stream.avail_out);
 			break;
-		case Z_STREAM_END:
-			fatal("buffer_uncompress: inflate returned Z_STREAM_END");
-			/* NOTREACHED */
-		case Z_DATA_ERROR:
-			fatal("buffer_uncompress: inflate returned Z_DATA_ERROR");
-			/* NOTREACHED */
-		case Z_STREAM_ERROR:
-			fatal("buffer_uncompress: inflate returned Z_STREAM_ERROR");
-			/* NOTREACHED */
 		case Z_BUF_ERROR:
 			/*
 			 * Comments in zlib.h say that we should keep calling
@@ -155,11 +135,9 @@
 			 * be the error that we get.
 			 */
 			return;
-		case Z_MEM_ERROR:
-			fatal("buffer_uncompress: inflate returned Z_MEM_ERROR");
-			/* NOTREACHED */
 		default:
 			fatal("buffer_uncompress: inflate returned %d", status);
+			/* NOTREACHED */
 		}
 	}
 }
diff --git a/dispatch.c b/dispatch.c
new file mode 100644
index 0000000..8594f98
--- /dev/null
+++ b/dispatch.c
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *      This product includes software developed by Markus Friedl.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+#include "includes.h"
+RCSID("$Id: dispatch.c,v 1.1 2000/04/01 01:09:24 damien Exp $");
+#include "ssh.h"
+#include "dispatch.h"
+#include "packet.h"
+
+#define DISPATCH_MIN	0
+#define DISPATCH_MAX	255
+
+dispatch_fn *dispatch[DISPATCH_MAX];
+
+void
+dispatch_protocol_error(int type, int plen)
+{
+	error("Hm, dispatch protocol error: type %d plen %d", type, plen);
+}
+void 
+dispatch_init(dispatch_fn *dflt)
+{
+	int i;
+	for (i = 0; i < DISPATCH_MAX; i++)
+		dispatch[i] = dflt;
+}
+void
+dispatch_set(int type, dispatch_fn *fn)
+{
+	dispatch[type] = fn;
+}
+void
+dispatch_run(int mode, int *done)
+{
+	for (;;) {
+		int plen;
+		int type;
+
+		if (mode == DISPATCH_BLOCK) {
+			type = packet_read(&plen);
+		} else {
+			type = packet_read_poll(&plen);
+			if (type == SSH_MSG_NONE)
+				return;
+		}
+		if (type > 0 && type < DISPATCH_MAX && dispatch[type] != NULL)
+			(*dispatch[type])(type, plen);
+		else
+			packet_disconnect("protocol error: rcvd type %d", type);	
+		if (done != NULL && *done)
+			return;
+	}
+}
diff --git a/dispatch.h b/dispatch.h
new file mode 100644
index 0000000..12084aa
--- /dev/null
+++ b/dispatch.h
@@ -0,0 +1,11 @@
+enum {
+	DISPATCH_BLOCK,
+	DISPATCH_NONBLOCK
+};
+
+typedef void dispatch_fn(int type, int plen);
+
+void	dispatch_init(dispatch_fn *dflt);
+void	dispatch_set(int type, dispatch_fn *fn);
+void	dispatch_run(int mode, int *done);
+void	dispatch_protocol_error(int type, int plen);
diff --git a/log-server.c b/log-server.c
index 7f732ed..476e49f 100644
--- a/log-server.c
+++ b/log-server.c
@@ -15,7 +15,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: log-server.c,v 1.7 2000/03/09 10:27:50 damien Exp $");
+RCSID("$Id: log-server.c,v 1.8 2000/04/01 01:09:24 damien Exp $");
 
 #include <syslog.h>
 #include "packet.h"
@@ -137,9 +137,11 @@
 	} else {
 		vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
 	}
-	if (log_on_stderr)
+	if (log_on_stderr) {
 		fprintf(stderr, "%s\n", msgbuf);
-	openlog(__progname, LOG_PID, log_facility);
-	syslog(pri, "%.500s", msgbuf);
-	closelog();
+	} else {
+		openlog(__progname, LOG_PID, log_facility);
+		syslog(pri, "%.500s", msgbuf);
+		closelog();
+	}
 }
diff --git a/mpaux.c b/mpaux.c
index 24e9ce1..c105ce0 100644
--- a/mpaux.c
+++ b/mpaux.c
@@ -15,7 +15,7 @@
 */
 
 #include "includes.h"
-RCSID("$Id: mpaux.c,v 1.8 1999/12/13 23:47:16 damien Exp $");
+RCSID("$Id: mpaux.c,v 1.9 2000/04/01 01:09:24 damien Exp $");
 
 #include "getput.h"
 #include "xmalloc.h"
@@ -31,9 +31,9 @@
 
 void
 compute_session_id(unsigned char session_id[16],
-		   unsigned char cookie[8],
-		   BIGNUM* host_key_n,
-		   BIGNUM* session_key_n)
+    unsigned char cookie[8],
+    BIGNUM* host_key_n,
+    BIGNUM* session_key_n)
 {
 	unsigned int host_key_bytes = BN_num_bytes(host_key_n);
 	unsigned int session_key_bytes = BN_num_bytes(session_key_n);
diff --git a/nchan.c b/nchan.c
index 0c8066f..996623f 100644
--- a/nchan.c
+++ b/nchan.c
@@ -28,7 +28,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: nchan.c,v 1.5 2000/01/14 04:45:50 damien Exp $");
+RCSID("$Id: nchan.c,v 1.6 2000/04/01 01:09:24 damien Exp $");
 
 #include "ssh.h"
 
@@ -41,7 +41,6 @@
 static void chan_send_oclose(Channel *c);
 static void chan_shutdown_write(Channel *c);
 static void chan_shutdown_read(Channel *c);
-static void chan_delete_if_full_closed(Channel *c);
 
 /*
  * EVENTS update channel input/output states execute ACTIONS
@@ -73,7 +72,6 @@
 		error("protocol error: chan_rcvd_oclose %d for istate %d", c->self, c->istate);
 		return;
 	}
-	chan_delete_if_full_closed(c);
 }
 void
 chan_read_failed(Channel *c)
@@ -121,7 +119,6 @@
 	case CHAN_OUTPUT_WAIT_IEOF:
 		debug("channel %d: OUTPUT_WAIT_IEOF -> OUTPUT_CLOSED [rvcd IEOF]", c->self);
 		c->ostate = CHAN_OUTPUT_CLOSED;
-		chan_delete_if_full_closed(c);
 		break;
 	default:
 		error("protocol error: chan_rcvd_ieof %d for ostate %d", c->self, c->ostate);
@@ -141,7 +138,6 @@
 		debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [write failed]", c->self);
 		chan_send_oclose(c);
 		c->ostate = CHAN_OUTPUT_CLOSED;
-		chan_delete_if_full_closed(c);
 		break;
 	default:
 		error("internal error: chan_write_failed %d for ostate %d", c->self, c->ostate);
@@ -160,7 +156,6 @@
 		debug("channel %d: OUTPUT_WAIT_DRAIN -> OUTPUT_CLOSED [obuf empty, send OCLOSE]", c->self);
 		chan_send_oclose(c);
 		c->ostate = CHAN_OUTPUT_CLOSED;
-		chan_delete_if_full_closed(c);
 		break;
 	default:
 		error("internal error: chan_obuf_empty %d for ostate %d", c->self, c->ostate);
@@ -222,7 +217,7 @@
 		error("chan_shutdown_read failed for #%d/fd%d [i%d o%d]: %.100s",
 		      c->self, c->sock, c->istate, c->ostate, strerror(errno));
 }
-static void
+void
 chan_delete_if_full_closed(Channel *c)
 {
 	if (c->istate == CHAN_INPUT_CLOSED && c->ostate == CHAN_OUTPUT_CLOSED) {
diff --git a/nchan.h b/nchan.h
index 49f2d06..0ceee1c 100644
--- a/nchan.h
+++ b/nchan.h
@@ -27,7 +27,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-/* RCSID("$Id: nchan.h,v 1.3 1999/11/25 00:54:59 damien Exp $"); */
+/* RCSID("$Id: nchan.h,v 1.4 2000/04/01 01:09:24 damien Exp $"); */
 
 #ifndef NCHAN_H
 #define NCHAN_H
@@ -83,4 +83,6 @@
 void    chan_obuf_empty(Channel * c);
 
 void    chan_init_iostates(Channel * c);
+
+void	chan_delete_if_full_closed(Channel *c);
 #endif
diff --git a/packet.c b/packet.c
index f65ae98..ffce2c7 100644
--- a/packet.c
+++ b/packet.c
@@ -15,7 +15,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: packet.c,v 1.12 2000/03/09 10:27:50 damien Exp $");
+RCSID("$Id: packet.c,v 1.13 2000/04/01 01:09:25 damien Exp $");
 
 #include "xmalloc.h"
 #include "buffer.h"
@@ -28,6 +28,7 @@
 
 #include "compress.h"
 #include "deattack.h"
+#include "channels.h"
 
 /*
  * This variable contains the file descriptors used for communicating with
diff --git a/pty.h b/pty.h
index b97b96d..af6c279 100644
--- a/pty.h
+++ b/pty.h
@@ -13,7 +13,7 @@
  * tty.
  */
 
-/* RCSID("$Id: pty.h,v 1.6 2000/03/09 10:27:51 damien Exp $"); */
+/* RCSID("$Id: pty.h,v 1.7 2000/04/01 01:09:25 damien Exp $"); */
 
 #ifndef PTY_H
 #define PTY_H
@@ -45,6 +45,4 @@
 
 void	pty_setowner(struct passwd *pw, const char *ttyname);
 
-void	pty_setowner(struct passwd *pw, const char *ttyname);
-
 #endif				/* PTY_H */
diff --git a/readconf.c b/readconf.c
index 99023d1..bb420ac 100644
--- a/readconf.c
+++ b/readconf.c
@@ -14,11 +14,12 @@
  */
 
 #include "includes.h"
-RCSID("$Id: readconf.c,v 1.8 2000/03/09 10:27:51 damien Exp $");
+RCSID("$Id: readconf.c,v 1.9 2000/04/01 01:09:25 damien Exp $");
 
 #include "ssh.h"
 #include "cipher.h"
 #include "readconf.h"
+#include "match.h"
 #include "xmalloc.h"
 
 /* Format of the configuration file:
diff --git a/serverloop.c b/serverloop.c
index 2afca76..8bf448c 100644
--- a/serverloop.c
+++ b/serverloop.c
@@ -13,6 +13,10 @@
 #include "buffer.h"
 #include "servconf.h"
 #include "pty.h"
+#include "channels.h"
+
+#include "compat.h"
+#include "dispatch.h"
 
 static Buffer stdin_buffer;	/* Buffer for stdin data. */
 static Buffer stdout_buffer;	/* Buffer for stdout data. */
@@ -47,6 +51,8 @@
 static volatile int child_has_selected; /* Child has had chance to drain. */
 static volatile int child_wait_status;	/* Status from wait(). */
 
+void	server_init_dispatch(void);
+
 void 
 sigchld_handler(int sig)
 {
@@ -68,104 +74,6 @@
 }
 
 /*
- * Process any buffered packets that have been received from the client.
- */
-void 
-process_buffered_input_packets()
-{
-	int type;
-	char *data;
-	unsigned int data_len;
-	int row, col, xpixel, ypixel;
-	int payload_len;
-
-	/* Process buffered packets from the client. */
-	while ((type = packet_read_poll(&payload_len)) != SSH_MSG_NONE) {
-		switch (type) {
-		case SSH_CMSG_STDIN_DATA:
-			/* Stdin data from the client.  Append it to the buffer. */
-			/* Ignore any data if the client has closed stdin. */
-			if (fdin == -1)
-				break;
-			data = packet_get_string(&data_len);
-			packet_integrity_check(payload_len, (4 + data_len), type);
-			buffer_append(&stdin_buffer, data, data_len);
-			memset(data, 0, data_len);
-			xfree(data);
-			break;
-
-		case SSH_CMSG_EOF:
-			/*
-			 * Eof from the client.  The stdin descriptor to the
-			 * program will be closed when all buffered data has
-			 * drained.
-			 */
-			debug("EOF received for stdin.");
-			packet_integrity_check(payload_len, 0, type);
-			stdin_eof = 1;
-			break;
-
-		case SSH_CMSG_WINDOW_SIZE:
-			debug("Window change received.");
-			packet_integrity_check(payload_len, 4 * 4, type);
-			row = packet_get_int();
-			col = packet_get_int();
-			xpixel = packet_get_int();
-			ypixel = packet_get_int();
-			if (fdin != -1)
-				pty_change_window_size(fdin, row, col, xpixel, ypixel);
-			break;
-
-		case SSH_MSG_PORT_OPEN:
-			debug("Received port open request.");
-			channel_input_port_open(payload_len);
-			break;
-
-		case SSH_MSG_CHANNEL_OPEN_CONFIRMATION:
-			debug("Received channel open confirmation.");
-			packet_integrity_check(payload_len, 4 + 4, type);
-			channel_input_open_confirmation();
-			break;
-
-		case SSH_MSG_CHANNEL_OPEN_FAILURE:
-			debug("Received channel open failure.");
-			packet_integrity_check(payload_len, 4, type);
-			channel_input_open_failure();
-			break;
-
-		case SSH_MSG_CHANNEL_DATA:
-			channel_input_data(payload_len);
-			break;
-
-		case SSH_MSG_CHANNEL_CLOSE:
-			debug("Received channel close.");
-			packet_integrity_check(payload_len, 4, type);
-			channel_input_close();
-			break;
-
-		case SSH_MSG_CHANNEL_CLOSE_CONFIRMATION:
-			debug("Received channel close confirmation.");
-			packet_integrity_check(payload_len, 4, type);
-			channel_input_close_confirmation();
-			break;
-
-		default:
-			/*
-			 * In this phase, any unexpected messages cause a
-			 * protocol error.  This is to ease debugging; also,
-			 * since no confirmations are sent messages,
-			 * unprocessed unknown messages could cause strange
-			 * problems.  Any compatible protocol extensions must
-			 * be negotiated before entering the interactive
-			 * session.
-			 */
-			packet_disconnect("Protocol error during session: type %d",
-					  type);
-		}
-	}
-}
-
-/*
  * Make packets from buffered stderr data, and buffer it for sending
  * to the client.
  */
@@ -378,7 +286,7 @@
 #ifdef USE_PIPES
 			close(fdin);
 #else
-			if (fdout == -1)
+			if (fdin != fdout)
 				close(fdin);
 			else
 				shutdown(fdin, SHUT_WR); /* We will no longer send. */
@@ -425,6 +333,12 @@
 	packet_write_wait();
 }
 
+void 
+process_buffered_input_packets()
+{
+	dispatch_run(DISPATCH_NONBLOCK, NULL);
+}
+
 /*
  * Performs the interactive session.  This handles data transmission between
  * the client and the program.  Note that the notion of stdin, stdout, and
@@ -490,6 +404,8 @@
 	if (fderr == -1)
 		fderr_eof = 1;
 
+	server_init_dispatch();
+
 	/* Main loop of the server for the interactive session mode. */
 	for (;;) {
 		fd_set readset, writeset;
@@ -505,7 +421,7 @@
 #ifdef USE_PIPES
 			close(fdin);
 #else
-			if (fdout == -1)
+			if (fdin != fdout)
 				close(fdin);
 			else
 				shutdown(fdin, SHUT_WR); /* We will no longer send. */
@@ -549,7 +465,7 @@
 		    (buffer_len(&stdout_buffer) == 0) && 
 			 (buffer_len(&stderr_buffer) == 0)) {
 			if (!channel_still_open())
-				goto quit;
+				break;
 			if (!waiting_termination) {
 				const char *s = "Waiting for forwarded connections to terminate...\r\n";
 				char *cp;
@@ -576,7 +492,6 @@
 		process_output(&writeset);
 	}
 
-quit:
 	/* Cleanup and termination code. */
 
 	/* Wait until all output has been sent to the client. */
@@ -662,3 +577,79 @@
 	packet_disconnect("wait returned status %04x.", wait_status);
 	/* NOTREACHED */
 }
+
+void
+server_input_stdin_data(int type, int plen)
+{
+	char *data;
+	unsigned int data_len;
+
+	/* Stdin data from the client.  Append it to the buffer. */
+	/* Ignore any data if the client has closed stdin. */
+	if (fdin == -1)
+		return;
+	data = packet_get_string(&data_len);
+	packet_integrity_check(plen, (4 + data_len), type);
+	buffer_append(&stdin_buffer, data, data_len);
+	memset(data, 0, data_len);
+	xfree(data);
+}
+
+void
+server_input_eof(int type, int plen)
+{
+	/*
+	 * Eof from the client.  The stdin descriptor to the
+	 * program will be closed when all buffered data has
+	 * drained.
+	 */
+	debug("EOF received for stdin.");
+	packet_integrity_check(plen, 0, type);
+	stdin_eof = 1;
+}
+
+void
+server_input_window_size(int type, int plen)
+{
+	int row = packet_get_int();
+	int col = packet_get_int();
+	int xpixel = packet_get_int();
+	int ypixel = packet_get_int();
+
+	debug("Window change received.");
+	packet_integrity_check(plen, 4 * 4, type);
+	if (fdin != -1)
+		pty_change_window_size(fdin, row, col, xpixel, ypixel);
+}
+
+void 
+server_init_dispatch_13()
+{
+	debug("server_init_dispatch_13");
+	dispatch_init(NULL);
+	dispatch_set(SSH_CMSG_EOF, &server_input_eof);
+	dispatch_set(SSH_CMSG_STDIN_DATA, &server_input_stdin_data);
+	dispatch_set(SSH_CMSG_WINDOW_SIZE, &server_input_window_size);
+	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_close);
+	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_close_confirmation);
+	dispatch_set(SSH_MSG_CHANNEL_DATA, &channel_input_data);
+	dispatch_set(SSH_MSG_CHANNEL_OPEN_CONFIRMATION, &channel_input_open_confirmation);
+	dispatch_set(SSH_MSG_CHANNEL_OPEN_FAILURE, &channel_input_open_failure);
+	dispatch_set(SSH_MSG_PORT_OPEN, &channel_input_port_open);
+}
+void 
+server_init_dispatch_15()
+{
+	server_init_dispatch_13();
+	debug("server_init_dispatch_15");
+	dispatch_set(SSH_MSG_CHANNEL_CLOSE, &channel_input_ieof);
+	dispatch_set(SSH_MSG_CHANNEL_CLOSE_CONFIRMATION, &channel_input_oclose);
+}
+void 
+server_init_dispatch()
+{
+	if (compat13)
+		server_init_dispatch_13();
+	else
+		server_init_dispatch_15();
+}
diff --git a/session.c b/session.c
new file mode 100644
index 0000000..2128fe3
--- /dev/null
+++ b/session.c
@@ -0,0 +1,1153 @@
+/*
+ * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
+ *                    All rights reserved
+ */
+
+#include "includes.h"
+RCSID("$OpenBSD: session.c,v 1.1 2000/03/28 21:15:45 markus Exp $");
+
+#include "xmalloc.h"
+#include "ssh.h"
+#include "pty.h"
+#include "packet.h"
+#include "buffer.h"
+#include "cipher.h"
+#include "mpaux.h"
+#include "servconf.h"
+#include "uidswap.h"
+#include "compat.h"
+#include "channels.h"
+#include "nchan.h"
+
+/* types */
+
+#define TTYSZ 64
+typedef struct Session Session;
+struct Session {
+	int	used;
+	int	self;
+	struct	passwd *pw;
+	pid_t	pid;
+	/* tty */
+	char	*term;
+	int	ptyfd, ttyfd, ptymaster;
+	int	row, col, xpixel, ypixel;
+	char	tty[TTYSZ];
+	/* X11 */
+	char	*display;
+	int	screen;
+	char	*auth_proto;
+	char	*auth_data;
+	/* proto 2 */
+	int	chanid;
+};
+
+/* func */
+
+Session *session_new(void);
+void	session_set_fds(Session *s, int fdin, int fdout, int fderr);
+void	session_pty_cleanup(Session *s);
+void	do_exec_pty(Session *s, const char *command, struct passwd * pw);
+void	do_exec_no_pty(Session *s, const char *command, struct passwd * pw);
+
+void
+do_child(const char *command, struct passwd * pw, const char *term,
+    const char *display, const char *auth_proto,
+    const char *auth_data, const char *ttyname);
+
+/* import */
+extern ServerOptions options;
+extern char *__progname;
+extern int log_stderr;
+extern int debug_flag;
+
+/* Local Xauthority file. */
+static char *xauthfile;
+
+/* data */
+#define MAX_SESSIONS 10
+Session	sessions[MAX_SESSIONS];
+
+/* Flags set in auth-rsa from authorized_keys flags.  These are set in auth-rsa.c. */
+int no_port_forwarding_flag = 0;
+int no_agent_forwarding_flag = 0;
+int no_x11_forwarding_flag = 0;
+int no_pty_flag = 0;
+
+/* RSA authentication "command=" option. */
+char *forced_command = NULL;
+
+/* RSA authentication "environment=" options. */
+struct envstring *custom_environment = NULL;
+
+/*
+ * Remove local Xauthority file.
+ */
+void
+xauthfile_cleanup_proc(void *ignore)
+{
+	debug("xauthfile_cleanup_proc called");
+
+	if (xauthfile != NULL) {
+		char *p;
+		unlink(xauthfile);
+		p = strrchr(xauthfile, '/');
+		if (p != NULL) {
+			*p = '\0';
+			rmdir(xauthfile);
+		}
+		xfree(xauthfile);
+		xauthfile = NULL;
+	}
+}
+
+/*
+ * Function to perform cleanup if we get aborted abnormally (e.g., due to a
+ * dropped connection).
+ */
+void 
+pty_cleanup_proc(void *session)
+{
+	Session *s=session;
+	if (s == NULL)
+		fatal("pty_cleanup_proc: no session");
+	debug("pty_cleanup_proc: %s", s->tty);
+
+	if (s->pid != 0) {
+		/* Record that the user has logged out. */
+		record_logout(s->pid, s->tty);
+	}
+
+	/* Release the pseudo-tty. */
+	pty_release(s->tty);
+}
+
+/*
+ * Prepares for an interactive session.  This is called after the user has
+ * been successfully authenticated.  During this message exchange, pseudo
+ * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
+ * are requested, etc.
+ */
+void 
+do_authenticated(struct passwd * pw)
+{
+	Session *s;
+	int type;
+	int compression_level = 0, enable_compression_after_reply = 0;
+	int have_pty = 0;
+	char *command;
+	int n_bytes;
+	int plen;
+	unsigned int proto_len, data_len, dlen;
+
+	/*
+	 * Cancel the alarm we set to limit the time taken for
+	 * authentication.
+	 */
+	alarm(0);
+
+	/*
+	 * Inform the channel mechanism that we are the server side and that
+	 * the client may request to connect to any port at all. (The user
+	 * could do it anyway, and we wouldn\'t know what is permitted except
+	 * by the client telling us, so we can equally well trust the client
+	 * not to request anything bogus.)
+	 */
+	if (!no_port_forwarding_flag)
+		channel_permit_all_opens();
+
+	s = session_new();
+
+	/*
+	 * We stay in this loop until the client requests to execute a shell
+	 * or a command.
+	 */
+	for (;;) {
+		int success = 0;
+
+		/* Get a packet from the client. */
+		type = packet_read(&plen);
+
+		/* Process the packet. */
+		switch (type) {
+		case SSH_CMSG_REQUEST_COMPRESSION:
+			packet_integrity_check(plen, 4, type);
+			compression_level = packet_get_int();
+			if (compression_level < 1 || compression_level > 9) {
+				packet_send_debug("Received illegal compression level %d.",
+				     compression_level);
+				break;
+			}
+			/* Enable compression after we have responded with SUCCESS. */
+			enable_compression_after_reply = 1;
+			success = 1;
+			break;
+
+		case SSH_CMSG_REQUEST_PTY:
+			if (no_pty_flag) {
+				debug("Allocating a pty not permitted for this authentication.");
+				break;
+			}
+			if (have_pty)
+				packet_disconnect("Protocol error: you already have a pty.");
+
+			debug("Allocating pty.");
+
+			/* Allocate a pty and open it. */
+			if (!pty_allocate(&s->ptyfd, &s->ttyfd, s->tty,
+			    sizeof(s->tty))) {
+				error("Failed to allocate pty.");
+				break;
+			}
+			fatal_add_cleanup(pty_cleanup_proc, (void *)s);
+			pty_setowner(pw, s->tty);
+
+			/* Get TERM from the packet.  Note that the value may be of arbitrary length. */
+			s->term = packet_get_string(&dlen);
+			packet_integrity_check(dlen, strlen(s->term), type);
+			/* packet_integrity_check(plen, 4 + dlen + 4*4 + n_bytes, type); */
+			/* Remaining bytes */
+			n_bytes = plen - (4 + dlen + 4 * 4);
+
+			if (strcmp(s->term, "") == 0) {
+				xfree(s->term);
+				s->term = NULL;
+			}
+			/* Get window size from the packet. */
+			s->row = packet_get_int();
+			s->col = packet_get_int();
+			s->xpixel = packet_get_int();
+			s->ypixel = packet_get_int();
+			pty_change_window_size(s->ptyfd, s->row, s->col, s->xpixel, s->ypixel);
+
+			/* Get tty modes from the packet. */
+			tty_parse_modes(s->ttyfd, &n_bytes);
+			packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
+
+			/* Indicate that we now have a pty. */
+			success = 1;
+			have_pty = 1;
+			break;
+
+		case SSH_CMSG_X11_REQUEST_FORWARDING:
+			if (!options.x11_forwarding) {
+				packet_send_debug("X11 forwarding disabled in server configuration file.");
+				break;
+			}
+#ifdef XAUTH_PATH
+			if (no_x11_forwarding_flag) {
+				packet_send_debug("X11 forwarding not permitted for this authentication.");
+				break;
+			}
+			debug("Received request for X11 forwarding with auth spoofing.");
+			if (s->display != NULL)
+				packet_disconnect("Protocol error: X11 display already set.");
+
+			s->auth_proto = packet_get_string(&proto_len);
+			s->auth_data = packet_get_string(&data_len);
+			packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type);
+
+			if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
+				s->screen = packet_get_int();
+			else
+				s->screen = 0;
+			s->display = x11_create_display_inet(s->screen, options.x11_display_offset);
+
+			if (s->display == NULL)
+				break;
+
+			/* Setup to always have a local .Xauthority. */
+			xauthfile = xmalloc(MAXPATHLEN);
+			strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
+			temporarily_use_uid(pw->pw_uid);
+			if (mkdtemp(xauthfile) == NULL) {
+				restore_uid();
+				error("private X11 dir: mkdtemp %s failed: %s",
+				    xauthfile, strerror(errno));
+				xfree(xauthfile);
+				xauthfile = NULL;
+				break;
+			}
+			strlcat(xauthfile, "/cookies", MAXPATHLEN);
+			open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
+			restore_uid();
+			fatal_add_cleanup(xauthfile_cleanup_proc, NULL);
+			success = 1;
+			break;
+#else /* XAUTH_PATH */
+			packet_send_debug("No xauth program; cannot forward with spoofing.");
+			break;
+#endif /* XAUTH_PATH */
+
+		case SSH_CMSG_AGENT_REQUEST_FORWARDING:
+			if (no_agent_forwarding_flag || compat13) {
+				debug("Authentication agent forwarding not permitted for this authentication.");
+				break;
+			}
+			debug("Received authentication agent forwarding request.");
+			auth_input_request_forwarding(pw);
+			success = 1;
+			break;
+
+		case SSH_CMSG_PORT_FORWARD_REQUEST:
+			if (no_port_forwarding_flag) {
+				debug("Port forwarding not permitted for this authentication.");
+				break;
+			}
+			debug("Received TCP/IP port forwarding request.");
+			channel_input_port_forward_request(pw->pw_uid == 0);
+			success = 1;
+			break;
+
+		case SSH_CMSG_MAX_PACKET_SIZE:
+			if (packet_set_maxsize(packet_get_int()) > 0)
+				success = 1;
+			break;
+
+		case SSH_CMSG_EXEC_SHELL:
+		case SSH_CMSG_EXEC_CMD:
+			/* Set interactive/non-interactive mode. */
+			packet_set_interactive(have_pty || s->display != NULL,
+			    options.keepalives);
+
+			if (type == SSH_CMSG_EXEC_CMD) {
+				command = packet_get_string(&dlen);
+				debug("Exec command '%.500s'", command);
+				packet_integrity_check(plen, 4 + dlen, type);
+			} else {
+				command = NULL;
+				packet_integrity_check(plen, 0, type);
+			}
+			if (forced_command != NULL) {
+				command = forced_command;
+				debug("Forced command '%.500s'", forced_command);
+			}
+			if (have_pty)
+				do_exec_pty(s, command, pw);
+			else
+				do_exec_no_pty(s, command, pw);
+
+			if (command != NULL)
+				xfree(command);
+			/* Cleanup user's local Xauthority file. */
+			if (xauthfile)
+				xauthfile_cleanup_proc(NULL);
+			return;
+
+		default:
+			/*
+			 * Any unknown messages in this phase are ignored,
+			 * and a failure message is returned.
+			 */
+			log("Unknown packet type received after authentication: %d", type);
+		}
+		packet_start(success ? SSH_SMSG_SUCCESS : SSH_SMSG_FAILURE);
+		packet_send();
+		packet_write_wait();
+
+		/* Enable compression now that we have replied if appropriate. */
+		if (enable_compression_after_reply) {
+			enable_compression_after_reply = 0;
+			packet_start_compression(compression_level);
+		}
+	}
+}
+
+/*
+ * This is called to fork and execute a command when we have no tty.  This
+ * will call do_child from the child, and server_loop from the parent after
+ * setting up file descriptors and such.
+ */
+void 
+do_exec_no_pty(Session *s, const char *command, struct passwd * pw)
+{
+	int pid;
+
+#ifdef USE_PIPES
+	int pin[2], pout[2], perr[2];
+	/* Allocate pipes for communicating with the program. */
+	if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
+		packet_disconnect("Could not create pipes: %.100s",
+				  strerror(errno));
+#else /* USE_PIPES */
+	int inout[2], err[2];
+	/* Uses socket pairs to communicate with the program. */
+	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
+	    socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
+		packet_disconnect("Could not create socket pairs: %.100s",
+				  strerror(errno));
+#endif /* USE_PIPES */
+	if (s == NULL)
+		fatal("do_exec_no_pty: no session");
+
+	setproctitle("%s@notty", pw->pw_name);
+
+#ifdef USE_PAM
+			do_pam_setcred();
+#endif /* USE_PAM */
+
+	/* Fork the child. */
+	if ((pid = fork()) == 0) {
+		/* Child.  Reinitialize the log since the pid has changed. */
+		log_init(__progname, options.log_level, options.log_facility, log_stderr);
+
+		/*
+		 * Create a new session and process group since the 4.4BSD
+		 * setlogin() affects the entire process group.
+		 */
+		if (setsid() < 0)
+			error("setsid failed: %.100s", strerror(errno));
+
+#ifdef USE_PIPES
+		/*
+		 * Redirect stdin.  We close the parent side of the socket
+		 * pair, and make the child side the standard input.
+		 */
+		close(pin[1]);
+		if (dup2(pin[0], 0) < 0)
+			perror("dup2 stdin");
+		close(pin[0]);
+
+		/* Redirect stdout. */
+		close(pout[0]);
+		if (dup2(pout[1], 1) < 0)
+			perror("dup2 stdout");
+		close(pout[1]);
+
+		/* Redirect stderr. */
+		close(perr[0]);
+		if (dup2(perr[1], 2) < 0)
+			perror("dup2 stderr");
+		close(perr[1]);
+#else /* USE_PIPES */
+		/*
+		 * Redirect stdin, stdout, and stderr.  Stdin and stdout will
+		 * use the same socket, as some programs (particularly rdist)
+		 * seem to depend on it.
+		 */
+		close(inout[1]);
+		close(err[1]);
+		if (dup2(inout[0], 0) < 0)	/* stdin */
+			perror("dup2 stdin");
+		if (dup2(inout[0], 1) < 0)	/* stdout.  Note: same socket as stdin. */
+			perror("dup2 stdout");
+		if (dup2(err[0], 2) < 0)	/* stderr */
+			perror("dup2 stderr");
+#endif /* USE_PIPES */
+
+		/* Do processing for the child (exec command etc). */
+		do_child(command, pw, NULL, s->display, s->auth_proto, s->auth_data, NULL);
+		/* NOTREACHED */
+	}
+	if (pid < 0)
+		packet_disconnect("fork failed: %.100s", strerror(errno));
+	s->pid = pid;
+#ifdef USE_PIPES
+	/* We are the parent.  Close the child sides of the pipes. */
+	close(pin[0]);
+	close(pout[1]);
+	close(perr[1]);
+
+	/* Enter the interactive session. */
+	server_loop(pid, pin[1], pout[0], perr[0]);
+	/* server_loop has closed pin[1], pout[1], and perr[1]. */
+#else /* USE_PIPES */
+	/* We are the parent.  Close the child sides of the socket pairs. */
+	close(inout[0]);
+	close(err[0]);
+
+	/*
+	 * Enter the interactive session.  Note: server_loop must be able to
+	 * handle the case that fdin and fdout are the same.
+	 */
+	server_loop(pid, inout[1], inout[1], err[1]);
+	/* server_loop has closed inout[1] and err[1]. */
+#endif /* USE_PIPES */
+}
+
+/*
+ * This is called to fork and execute a command when we have a tty.  This
+ * will call do_child from the child, and server_loop from the parent after
+ * setting up file descriptors, controlling tty, updating wtmp, utmp,
+ * lastlog, and other such operations.
+ */
+void 
+do_exec_pty(Session *s, const char *command, struct passwd * pw)
+{
+	FILE *f;
+	char buf[100], *time_string;
+	char line[256];
+	const char *hostname;
+	int fdout, ptyfd, ttyfd, ptymaster;
+	int quiet_login;
+	pid_t pid;
+	socklen_t fromlen;
+	struct sockaddr_storage from;
+	struct stat st;
+	time_t last_login_time;
+
+	if (s == NULL)
+		fatal("do_exec_pty: no session");
+	ptyfd = s->ptyfd;
+	ttyfd = s->ttyfd;
+
+	/* Get remote host name. */
+	hostname = get_canonical_hostname();
+
+	/*
+	 * Get the time when the user last logged in.  Buf will be set to
+	 * contain the hostname the last login was from.
+	 */
+	if (!options.use_login) {
+		last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
+						      buf, sizeof(buf));
+	}
+	setproctitle("%s@%s", pw->pw_name, strrchr(s->tty, '/') + 1);
+
+#ifdef USE_PAM
+			do_pam_session(pw->pw_name, s->tty);
+			do_pam_setcred();
+#endif /* USE_PAM */
+
+	/* Fork the child. */
+	if ((pid = fork()) == 0) {
+		pid = getpid();
+
+		/* Child.  Reinitialize the log because the pid has
+		   changed. */
+		log_init(__progname, options.log_level, options.log_facility, log_stderr);
+
+		/* Close the master side of the pseudo tty. */
+		close(ptyfd);
+
+		/* Make the pseudo tty our controlling tty. */
+		pty_make_controlling_tty(&ttyfd, s->tty);
+
+		/* Redirect stdin from the pseudo tty. */
+		if (dup2(ttyfd, fileno(stdin)) < 0)
+			error("dup2 stdin failed: %.100s", strerror(errno));
+
+		/* Redirect stdout to the pseudo tty. */
+		if (dup2(ttyfd, fileno(stdout)) < 0)
+			error("dup2 stdin failed: %.100s", strerror(errno));
+
+		/* Redirect stderr to the pseudo tty. */
+		if (dup2(ttyfd, fileno(stderr)) < 0)
+			error("dup2 stdin failed: %.100s", strerror(errno));
+
+		/* Close the extra descriptor for the pseudo tty. */
+		close(ttyfd);
+
+///XXXX ? move to do_child() ??
+		/*
+		 * Get IP address of client.  This is needed because we want
+		 * to record where the user logged in from.  If the
+		 * connection is not a socket, let the ip address be 0.0.0.0.
+		 */
+		memset(&from, 0, sizeof(from));
+		if (packet_connection_is_on_socket()) {
+			fromlen = sizeof(from);
+			if (getpeername(packet_get_connection_in(),
+			     (struct sockaddr *) & from, &fromlen) < 0) {
+				debug("getpeername: %.100s", strerror(errno));
+				fatal_cleanup();
+			}
+		}
+		/* Record that there was a login on that terminal. */
+		record_login(pid, s->tty, pw->pw_name, pw->pw_uid, hostname,
+			     (struct sockaddr *)&from);
+
+		/* Check if .hushlogin exists. */
+		snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
+		quiet_login = stat(line, &st) >= 0;
+
+#ifdef USE_PAM
+		if (!quiet_login)
+			print_pam_messages();
+#endif /* USE_PAM */
+
+		/*
+		 * If the user has logged in before, display the time of last
+		 * login. However, don't display anything extra if a command
+		 * has been specified (so that ssh can be used to execute
+		 * commands on a remote machine without users knowing they
+		 * are going to another machine). Login(1) will do this for
+		 * us as well, so check if login(1) is used
+		 */
+		if (command == NULL && last_login_time != 0 && !quiet_login &&
+		    !options.use_login) {
+			/* Convert the date to a string. */
+			time_string = ctime(&last_login_time);
+			/* Remove the trailing newline. */
+			if (strchr(time_string, '\n'))
+				*strchr(time_string, '\n') = 0;
+			/* Display the last login time.  Host if displayed
+			   if known. */
+			if (strcmp(buf, "") == 0)
+				printf("Last login: %s\r\n", time_string);
+			else
+				printf("Last login: %s from %s\r\n", time_string, buf);
+		}
+		/*
+		 * Print /etc/motd unless a command was specified or printing
+		 * it was disabled in server options or login(1) will be
+		 * used.  Note that some machines appear to print it in
+		 * /etc/profile or similar.
+		 */
+		if (command == NULL && options.print_motd && !quiet_login &&
+		    !options.use_login) {
+			/* Print /etc/motd if it exists. */
+			f = fopen("/etc/motd", "r");
+			if (f) {
+				while (fgets(line, sizeof(line), f))
+					fputs(line, stdout);
+				fclose(f);
+			}
+		}
+		/* Do common processing for the child, such as execing the command. */
+		do_child(command, pw, s->term, s->display, s->auth_proto, s->auth_data, s->tty);
+		/* NOTREACHED */
+	}
+	if (pid < 0)
+		packet_disconnect("fork failed: %.100s", strerror(errno));
+	s->pid = pid;
+
+	/* Parent.  Close the slave side of the pseudo tty. */
+	close(ttyfd);
+
+	/*
+	 * Create another descriptor of the pty master side for use as the
+	 * standard input.  We could use the original descriptor, but this
+	 * simplifies code in server_loop.  The descriptor is bidirectional.
+	 */
+	fdout = dup(ptyfd);
+	if (fdout < 0)
+		packet_disconnect("dup #1 failed: %.100s", strerror(errno));
+
+	/* we keep a reference to the pty master */
+	ptymaster = dup(ptyfd);
+	if (ptymaster < 0)
+		packet_disconnect("dup #2 failed: %.100s", strerror(errno));
+	s->ptymaster = ptymaster;
+
+	/* Enter interactive session. */
+	server_loop(pid, ptyfd, fdout, -1);
+	/* server_loop _has_ closed ptyfd and fdout. */
+	session_pty_cleanup(s);
+}
+
+/*
+ * Sets the value of the given variable in the environment.  If the variable
+ * already exists, its value is overriden.
+ */
+void 
+child_set_env(char ***envp, unsigned int *envsizep, const char *name,
+	      const char *value)
+{
+	unsigned int i, namelen;
+	char **env;
+
+	/*
+	 * Find the slot where the value should be stored.  If the variable
+	 * already exists, we reuse the slot; otherwise we append a new slot
+	 * at the end of the array, expanding if necessary.
+	 */
+	env = *envp;
+	namelen = strlen(name);
+	for (i = 0; env[i]; i++)
+		if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
+			break;
+	if (env[i]) {
+		/* Reuse the slot. */
+		xfree(env[i]);
+	} else {
+		/* New variable.  Expand if necessary. */
+		if (i >= (*envsizep) - 1) {
+			(*envsizep) += 50;
+			env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
+		}
+		/* Need to set the NULL pointer at end of array beyond the new slot. */
+		env[i + 1] = NULL;
+	}
+
+	/* Allocate space and format the variable in the appropriate slot. */
+	env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
+	snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
+}
+
+/*
+ * Reads environment variables from the given file and adds/overrides them
+ * into the environment.  If the file does not exist, this does nothing.
+ * Otherwise, it must consist of empty lines, comments (line starts with '#')
+ * and assignments of the form name=value.  No other forms are allowed.
+ */
+void 
+read_environment_file(char ***env, unsigned int *envsize,
+		      const char *filename)
+{
+	FILE *f;
+	char buf[4096];
+	char *cp, *value;
+
+	f = fopen(filename, "r");
+	if (!f)
+		return;
+
+	while (fgets(buf, sizeof(buf), f)) {
+		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
+			;
+		if (!*cp || *cp == '#' || *cp == '\n')
+			continue;
+		if (strchr(cp, '\n'))
+			*strchr(cp, '\n') = '\0';
+		value = strchr(cp, '=');
+		if (value == NULL) {
+			fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
+			continue;
+		}
+		/* Replace the equals sign by nul, and advance value to the value string. */
+		*value = '\0';
+		value++;
+		child_set_env(env, envsize, cp, value);
+	}
+	fclose(f);
+}
+
+#ifdef USE_PAM
+/*
+ * Sets any environment variables which have been specified by PAM
+ */
+void do_pam_environment(char ***env, int *envsize)
+{
+	char *equals, var_name[512], var_val[512];
+	char **pam_env;
+	int i;
+
+	if ((pam_env = fetch_pam_environment()) == NULL)
+		return;
+	
+	for(i = 0; pam_env[i] != NULL; i++) {
+		if ((equals = strstr(pam_env[i], "=")) == NULL)
+			continue;
+			
+		if (strlen(pam_env[i]) < (sizeof(var_name) - 1)) {
+			memset(var_name, '\0', sizeof(var_name));
+			memset(var_val, '\0', sizeof(var_val));
+
+			strncpy(var_name, pam_env[i], equals - pam_env[i]);
+			strcpy(var_val, equals + 1);
+
+			debug("PAM environment: %s=%s", var_name, var_val);
+
+			child_set_env(env, envsize, var_name, var_val);
+		}
+	}
+}
+#endif /* USE_PAM */
+
+/*
+ * Performs common processing for the child, such as setting up the
+ * environment, closing extra file descriptors, setting the user and group
+ * ids, and executing the command or shell.
+ */
+void 
+do_child(const char *command, struct passwd * pw, const char *term,
+	 const char *display, const char *auth_proto,
+	 const char *auth_data, const char *ttyname)
+{
+	const char *shell, *cp = NULL;
+	char buf[256];
+	FILE *f;
+	unsigned int envsize, i;
+	char **env;
+	extern char **environ;
+	struct stat st;
+	char *argv[10];
+
+#ifndef USE_PAM /* pam_nologin handles this */
+	f = fopen("/etc/nologin", "r");
+	if (f) {
+		/* /etc/nologin exists.  Print its contents and exit. */
+		while (fgets(buf, sizeof(buf), f))
+			fputs(buf, stderr);
+		fclose(f);
+		if (pw->pw_uid != 0)
+			exit(254);
+	}
+#endif /* USE_PAM */
+
+	/* Set login name in the kernel. */
+	if (setlogin(pw->pw_name) < 0)
+		error("setlogin failed: %s", strerror(errno));
+
+	/* Set uid, gid, and groups. */
+	/* Login(1) does this as well, and it needs uid 0 for the "-h"
+	   switch, so we let login(1) to this for us. */
+	if (!options.use_login) {
+		if (getuid() == 0 || geteuid() == 0) {
+			if (setgid(pw->pw_gid) < 0) {
+				perror("setgid");
+				exit(1);
+			}
+			/* Initialize the group list. */
+			if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
+				perror("initgroups");
+				exit(1);
+			}
+			endgrent();
+
+			/* Permanently switch to the desired uid. */
+			permanently_set_uid(pw->pw_uid);
+		}
+		if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
+			fatal("Failed to set uids to %d.", (int) pw->pw_uid);
+	}
+	/*
+	 * Get the shell from the password data.  An empty shell field is
+	 * legal, and means /bin/sh.
+	 */
+	shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
+
+#ifdef AFS
+	/* Try to get AFS tokens for the local cell. */
+	if (k_hasafs()) {
+		char cell[64];
+
+		if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
+			krb_afslog(cell, 0);
+
+		krb_afslog(0, 0);
+	}
+#endif /* AFS */
+
+	/* Initialize the environment. */
+	envsize = 100;
+	env = xmalloc(envsize * sizeof(char *));
+	env[0] = NULL;
+
+	if (!options.use_login) {
+		/* Set basic environment. */
+		child_set_env(&env, &envsize, "USER", pw->pw_name);
+		child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
+		child_set_env(&env, &envsize, "HOME", pw->pw_dir);
+		child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
+
+		snprintf(buf, sizeof buf, "%.200s/%.50s",
+			 _PATH_MAILDIR, pw->pw_name);
+		child_set_env(&env, &envsize, "MAIL", buf);
+
+		/* Normal systems set SHELL by default. */
+		child_set_env(&env, &envsize, "SHELL", shell);
+	}
+	if (getenv("TZ"))
+		child_set_env(&env, &envsize, "TZ", getenv("TZ"));
+
+	/* Set custom environment options from RSA authentication. */
+	while (custom_environment) {
+		struct envstring *ce = custom_environment;
+		char *s = ce->s;
+		int i;
+		for (i = 0; s[i] != '=' && s[i]; i++);
+		if (s[i] == '=') {
+			s[i] = 0;
+			child_set_env(&env, &envsize, s, s + i + 1);
+		}
+		custom_environment = ce->next;
+		xfree(ce->s);
+		xfree(ce);
+	}
+
+	snprintf(buf, sizeof buf, "%.50s %d %d",
+		 get_remote_ipaddr(), get_remote_port(), get_local_port());
+	child_set_env(&env, &envsize, "SSH_CLIENT", buf);
+
+	if (ttyname)
+		child_set_env(&env, &envsize, "SSH_TTY", ttyname);
+	if (term)
+		child_set_env(&env, &envsize, "TERM", term);
+	if (display)
+		child_set_env(&env, &envsize, "DISPLAY", display);
+
+#ifdef _AIX
+	{
+           char *authstate,*krb5cc;
+
+	   if ((authstate = getenv("AUTHSTATE")) != NULL)
+		 child_set_env(&env,&envsize,"AUTHSTATE",authstate);
+
+	   if ((krb5cc = getenv("KRB5CCNAME")) != NULL)
+		 child_set_env(&env,&envsize,"KRB5CCNAME",krb5cc);
+	}
+#endif
+
+#ifdef KRB4
+	{
+		extern char *ticket;
+
+		if (ticket)
+			child_set_env(&env, &envsize, "KRBTKFILE", ticket);
+	}
+#endif /* KRB4 */
+
+#ifdef USE_PAM
+	/* Pull in any environment variables that may have been set by PAM. */
+	do_pam_environment(&env, &envsize);
+#endif /* USE_PAM */
+
+	read_environment_file(&env,&envsize,"/etc/environment");
+
+	if (xauthfile)
+		child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
+	if (auth_get_socket_name() != NULL)
+		child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
+			      auth_get_socket_name());
+
+	/* read $HOME/.ssh/environment. */
+	if (!options.use_login) {
+		snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir);
+		read_environment_file(&env, &envsize, buf);
+	}
+	if (debug_flag) {
+		/* dump the environment */
+		fprintf(stderr, "Environment:\n");
+		for (i = 0; env[i]; i++)
+			fprintf(stderr, "  %.200s\n", env[i]);
+	}
+	/*
+	 * Close the connection descriptors; note that this is the child, and
+	 * the server will still have the socket open, and it is important
+	 * that we do not shutdown it.  Note that the descriptors cannot be
+	 * closed before building the environment, as we call
+	 * get_remote_ipaddr there.
+	 */
+	if (packet_get_connection_in() == packet_get_connection_out())
+		close(packet_get_connection_in());
+	else {
+		close(packet_get_connection_in());
+		close(packet_get_connection_out());
+	}
+	/*
+	 * Close all descriptors related to channels.  They will still remain
+	 * open in the parent.
+	 */
+	/* XXX better use close-on-exec? -markus */
+	channel_close_all();
+
+	/*
+	 * Close any extra file descriptors.  Note that there may still be
+	 * descriptors left by system functions.  They will be closed later.
+	 */
+	endpwent();
+
+	/*
+	 * Close any extra open file descriptors so that we don\'t have them
+	 * hanging around in clients.  Note that we want to do this after
+	 * initgroups, because at least on Solaris 2.3 it leaves file
+	 * descriptors open.
+	 */
+	for (i = 3; i < 64; i++)
+		close(i);
+
+	/* Change current directory to the user\'s home directory. */
+	if (chdir(pw->pw_dir) < 0)
+		fprintf(stderr, "Could not chdir to home directory %s: %s\n",
+			pw->pw_dir, strerror(errno));
+
+	/*
+	 * Must take new environment into use so that .ssh/rc, /etc/sshrc and
+	 * xauth are run in the proper environment.
+	 */
+	environ = env;
+
+	/*
+	 * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
+	 * in this order).
+	 */
+	if (!options.use_login) {
+		if (stat(SSH_USER_RC, &st) >= 0) {
+			if (debug_flag)
+				fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC);
+
+			f = popen("/bin/sh " SSH_USER_RC, "w");
+			if (f) {
+				if (auth_proto != NULL && auth_data != NULL)
+					fprintf(f, "%s %s\n", auth_proto, auth_data);
+				pclose(f);
+			} else
+				fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
+		} else if (stat(SSH_SYSTEM_RC, &st) >= 0) {
+			if (debug_flag)
+				fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC);
+
+			f = popen("/bin/sh " SSH_SYSTEM_RC, "w");
+			if (f) {
+				if (auth_proto != NULL && auth_data != NULL)
+					fprintf(f, "%s %s\n", auth_proto, auth_data);
+				pclose(f);
+			} else
+				fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
+		}
+#ifdef XAUTH_PATH
+		else {
+			/* Add authority data to .Xauthority if appropriate. */
+			if (auth_proto != NULL && auth_data != NULL) {
+				if (debug_flag)
+					fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n",
+						XAUTH_PATH, display, auth_proto, auth_data);
+
+				f = popen(XAUTH_PATH " -q -", "w");
+				if (f) {
+					fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data);
+					pclose(f);
+				} else
+					fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH);
+			}
+		}
+#endif /* XAUTH_PATH */
+
+		/* Get the last component of the shell name. */
+		cp = strrchr(shell, '/');
+		if (cp)
+			cp++;
+		else
+			cp = shell;
+	}
+	/*
+	 * If we have no command, execute the shell.  In this case, the shell
+	 * name to be passed in argv[0] is preceded by '-' to indicate that
+	 * this is a login shell.
+	 */
+	if (!command) {
+		if (!options.use_login) {
+			char buf[256];
+
+			/*
+			 * Check for mail if we have a tty and it was enabled
+			 * in server options.
+			 */
+			if (ttyname && options.check_mail) {
+				char *mailbox;
+				struct stat mailstat;
+				mailbox = getenv("MAIL");
+				if (mailbox != NULL) {
+					if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0)
+						printf("No mail.\n");
+					else if (mailstat.st_mtime < mailstat.st_atime)
+						printf("You have mail.\n");
+					else
+						printf("You have new mail.\n");
+				}
+			}
+			/* Start the shell.  Set initial character to '-'. */
+			buf[0] = '-';
+			strncpy(buf + 1, cp, sizeof(buf) - 1);
+			buf[sizeof(buf) - 1] = 0;
+
+			/* Execute the shell. */
+			argv[0] = buf;
+			argv[1] = NULL;
+			execve(shell, argv, env);
+
+			/* Executing the shell failed. */
+			perror(shell);
+			exit(1);
+
+		} else {
+			/* Launch login(1). */
+
+			execl("/usr/bin/login", "login", "-h", get_remote_ipaddr(),
+			      "-p", "-f", "--", pw->pw_name, NULL);
+
+			/* Login couldn't be executed, die. */
+
+			perror("login");
+			exit(1);
+		}
+	}
+	/*
+	 * Execute the command using the user's shell.  This uses the -c
+	 * option to execute the command.
+	 */
+	argv[0] = (char *) cp;
+	argv[1] = "-c";
+	argv[2] = (char *) command;
+	argv[3] = NULL;
+	execve(shell, argv, env);
+	perror(shell);
+	exit(1);
+}
+
+Session *
+session_new(void)
+{
+	int i;
+	static int did_init = 0;
+	if (!did_init) {
+		debug("session_new: init");
+		for(i = 0; i < MAX_SESSIONS; i++) {
+			sessions[i].used = 0;
+			sessions[i].self = i;
+		}
+		did_init = 1;
+	}
+	for(i = 0; i < MAX_SESSIONS; i++) {
+		Session *s = &sessions[i];
+		if (! s->used) {
+			s->pid = 0;
+			s->chanid = -1;
+			s->ptyfd = -1;
+			s->ttyfd = -1;
+			s->term = NULL;
+			s->pw = NULL;
+			s->display = NULL;
+			s->screen = 0;
+			s->auth_data = NULL;
+			s->auth_proto = NULL;
+			s->used = 1;
+			debug("session_new: session %d", i);
+			return s;
+		}
+	}
+	return NULL;
+}
+
+void
+session_dump(void)
+{
+	int i;
+	for(i = 0; i < MAX_SESSIONS; i++) {
+		Session *s = &sessions[i];
+		debug("dump: used %d session %d %p channel %d pid %d",
+		    s->used,
+		    s->self,
+		    s,
+		    s->chanid,
+		    s->pid);
+	}
+}
+
+void
+session_pty_cleanup(Session *s)
+{
+	if (s == NULL || s->ttyfd == -1)
+		return;
+
+	debug("session_pty_cleanup: session %i release %s", s->self, s->tty);
+
+	/* Cancel the cleanup function. */
+	fatal_remove_cleanup(pty_cleanup_proc, (void *)s);
+
+	/* Record that the user has logged out. */
+	record_logout(s->pid, s->tty);
+
+	/* Release the pseudo-tty. */
+	pty_release(s->tty);
+
+	/*
+	 * Close the server side of the socket pairs.  We must do this after
+	 * the pty cleanup, so that another process doesn't get this pty
+	 * while we're still cleaning up.
+	 */
+	if (close(s->ptymaster) < 0)
+		error("close(s->ptymaster): %s", strerror(errno));
+}
diff --git a/session.h b/session.h
new file mode 100644
index 0000000..2051b73
--- /dev/null
+++ b/session.h
@@ -0,0 +1,7 @@
+#ifndef SESSION_H
+#define SESSION_H
+
+/* SSH1 */
+void	do_authenticated(struct passwd * pw);
+
+#endif
diff --git a/ssh.c b/ssh.c
index faba54e..70f9d6a 100644
--- a/ssh.c
+++ b/ssh.c
@@ -11,7 +11,7 @@
  */
 
 #include "includes.h"
-RCSID("$Id: ssh.c,v 1.22 2000/03/26 03:04:54 damien Exp $");
+RCSID("$Id: ssh.c,v 1.23 2000/04/01 01:09:26 damien Exp $");
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -20,6 +20,7 @@
 #include "authfd.h"
 #include "readconf.h"
 #include "uidswap.h"
+#include "channels.h"
 
 #ifdef HAVE___PROGNAME
 extern char *__progname;
diff --git a/ssh.h b/ssh.h
index 8bd7081..cc8940b 100644
--- a/ssh.h
+++ b/ssh.h
@@ -13,7 +13,7 @@
  * 
  */
 
-/* RCSID("$Id: ssh.h,v 1.28 2000/03/26 03:04:54 damien Exp $"); */
+/* RCSID("$Id: ssh.h,v 1.29 2000/04/01 01:09:26 damien Exp $"); */
 
 #ifndef SSH_H
 #define SSH_H
@@ -486,175 +486,6 @@
 /* Removes a cleanup function to be called at fatal(). */
 void    fatal_remove_cleanup(void (*proc) (void *context), void *context);
 
-/*---------------- definitions for channels ------------------*/
-
-/* Sets specific protocol options. */
-void    channel_set_options(int hostname_in_open);
-
-/*
- * Allocate a new channel object and set its type and socket.  Remote_name
- * must have been allocated with xmalloc; this will free it when the channel
- * is freed.
- */
-int     channel_allocate(int type, int sock, char *remote_name);
-
-/* Free the channel and close its socket. */
-void    channel_free(int channel);
-
-/* Add any bits relevant to channels in select bitmasks. */
-void    channel_prepare_select(fd_set * readset, fd_set * writeset);
-
-/*
- * After select, perform any appropriate operations for channels which have
- * events pending.
- */
-void    channel_after_select(fd_set * readset, fd_set * writeset);
-
-/* If there is data to send to the connection, send some of it now. */
-void    channel_output_poll(void);
-
-/*
- * This is called when a packet of type CHANNEL_DATA has just been received.
- * The message type has already been consumed, but channel number and data is
- * still there.
- */
-void    channel_input_data(int payload_len);
-
-/* Returns true if no channel has too much buffered data. */
-int     channel_not_very_much_buffered_data(void);
-
-/* This is called after receiving CHANNEL_CLOSE. */
-void    channel_input_close(void);
-
-/* This is called after receiving CHANNEL_CLOSE_CONFIRMATION. */
-void    channel_input_close_confirmation(void);
-
-/* This is called after receiving CHANNEL_OPEN_CONFIRMATION. */
-void    channel_input_open_confirmation(void);
-
-/* This is called after receiving CHANNEL_OPEN_FAILURE from the other side. */
-void    channel_input_open_failure(void);
-
-/* This closes any sockets that are listening for connections; this removes
-   any unix domain sockets. */
-void    channel_stop_listening(void);
-
-/*
- * Closes the sockets of all channels.  This is used to close extra file
- * descriptors after a fork.
- */
-void    channel_close_all(void);
-
-/* Returns the maximum file descriptor number used by the channels. */
-int     channel_max_fd(void);
-
-/* Returns true if there is still an open channel over the connection. */
-int     channel_still_open(void);
-
-/*
- * Returns a string containing a list of all open channels.  The list is
- * suitable for displaying to the user.  It uses crlf instead of newlines.
- * The caller should free the string with xfree.
- */
-char   *channel_open_message(void);
-
-/*
- * Initiate forwarding of connections to local port "port" through the secure
- * channel to host:port from remote side.  This never returns if there was an
- * error.
- */
-void 
-channel_request_local_forwarding(u_short port, const char *host,
-    u_short remote_port, int gateway_ports);
-
-/*
- * Initiate forwarding of connections to port "port" on remote host through
- * the secure channel to host:port from local side.  This never returns if
- * there was an error.  This registers that open requests for that port are
- * permitted.
- */
-void 
-channel_request_remote_forwarding(u_short port, const char *host,
-    u_short remote_port);
-
-/*
- * Permits opening to any host/port in SSH_MSG_PORT_OPEN.  This is usually
- * called by the server, because the user could connect to any port anyway,
- * and the server has no way to know but to trust the client anyway.
- */
-void    channel_permit_all_opens(void);
-
-/*
- * This is called after receiving CHANNEL_FORWARDING_REQUEST.  This initates
- * listening for the port, and sends back a success reply (or disconnect
- * message if there was an error).  This never returns if there was an error.
- */
-void    channel_input_port_forward_request(int is_root);
-
-/*
- * This is called after receiving PORT_OPEN message.  This attempts to
- * connect to the given host:port, and sends back CHANNEL_OPEN_CONFIRMATION
- * or CHANNEL_OPEN_FAILURE.
- */
-void    channel_input_port_open(int payload_len);
-
-/*
- * Creates a port for X11 connections, and starts listening for it. Returns
- * the display name, or NULL if an error was encountered.
- */
-char   *x11_create_display(int screen);
-
-/*
- * Creates an internet domain socket for listening for X11 connections.
- * Returns a suitable value for the DISPLAY variable, or NULL if an error
- * occurs.
- */
-char   *x11_create_display_inet(int screen, int x11_display_offset);
-
-/*
- * This is called when SSH_SMSG_X11_OPEN is received.  The packet contains
- * the remote channel number.  We should do whatever we want, and respond
- * with either SSH_MSG_OPEN_CONFIRMATION or SSH_MSG_OPEN_FAILURE.
- */
-void    x11_input_open(int payload_len);
-
-/*
- * Requests forwarding of X11 connections.  This should be called on the
- * client only.
- */
-void    x11_request_forwarding(void);
-
-/*
- * Requests forwarding for X11 connections, with authentication spoofing.
- * This should be called in the client only.
- */
-void    x11_request_forwarding_with_spoofing(const char *proto, const char *data);
-
-/* Sends a message to the server to request authentication fd forwarding. */
-void    auth_request_forwarding(void);
-
-/*
- * Returns the name of the forwarded authentication socket.  Returns NULL if
- * there is no forwarded authentication socket.  The returned value points to
- * a static buffer.
- */
-char   *auth_get_socket_name(void);
-
-/*
- * This if called to process SSH_CMSG_AGENT_REQUEST_FORWARDING on the server.
- * This starts forwarding authentication requests.
- */
-void    auth_input_request_forwarding(struct passwd * pw);
-
-/* This is called to process an SSH_SMSG_AGENT_OPEN message. */
-void    auth_input_open_request(void);
-
-/*
- * Returns true if the given string matches the pattern (which may contain ?
- * and * as wildcards), and zero if it does not match.
- */
-int     match_pattern(const char *s, const char *pattern);
-
 /*
  * Expands tildes in the file name.  Returns data allocated by xmalloc.
  * Warning: this calls getpw*.
diff --git a/ssh2.h b/ssh2.h
new file mode 100644
index 0000000..cc659f8
--- /dev/null
+++ b/ssh2.h
@@ -0,0 +1,106 @@
+/*
+ * draft-ietf-secsh-architecture-04.txt 
+ *
+ *   Transport layer protocol:
+ * 
+ *     1-19     Transport layer generic (e.g. disconnect, ignore, debug,
+ *              etc)
+ *     20-29    Algorithm negotiation
+ *     30-49    Key exchange method specific (numbers can be reused for
+ *              different authentication methods)
+ * 
+ *   User authentication protocol:
+ * 
+ *     50-59    User authentication generic
+ *     60-79    User authentication method specific (numbers can be reused
+ *              for different authentication methods)
+ * 
+ *   Connection protocol:
+ * 
+ *     80-89    Connection protocol generic
+ *     90-127   Channel related messages
+ * 
+ *   Reserved for client protocols:
+ * 
+ *     128-191  Reserved
+ * 
+ *   Local extensions:
+ * 
+ *     192-255  Local extensions
+ */
+
+/* transport layer: generic */
+
+#define SSH2_MSG_DISCONNECT				1
+#define SSH2_MSG_IGNORE					2
+#define SSH2_MSG_UNIMPLEMENTED				3
+#define SSH2_MSG_DEBUG					4
+#define SSH2_MSG_SERVICE_REQUEST			5
+#define SSH2_MSG_SERVICE_ACCEPT				6
+
+/* transport layer: alg negotiation */
+
+#define SSH2_MSG_KEXINIT				20
+#define SSH2_MSG_NEWKEYS				21
+
+/* transport layer: kex specific messages, can be reused */
+
+#define SSH2_MSG_KEXDH_INIT				30
+#define SSH2_MSG_KEXDH_REPLY				31
+
+/* user authentication: generic */
+
+#define SSH2_MSG_USERAUTH_REQUEST			50
+#define SSH2_MSG_USERAUTH_FAILURE			51
+#define SSH2_MSG_USERAUTH_SUCCESS			52
+#define SSH2_MSG_USERAUTH_BANNER			53
+
+/* user authentication: method specific, can be reused */
+
+#define SSH2_MSG_USERAUTH_PK_OK				60
+#define SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ		60
+#define SSH2_MSG_USERAUTH_INFO_REQUEST			60
+#define SSH2_MSG_USERAUTH_INFO_RESPONSE			61
+
+/* connection protocol: generic */
+
+#define SSH2_MSG_GLOBAL_REQUEST				80
+#define SSH2_MSG_REQUEST_SUCCESS			81
+#define SSH2_MSG_REQUEST_FAILURE			82
+
+/* channel related messages */
+
+#define SSH2_MSG_CHANNEL_OPEN				90
+#define SSH2_MSG_CHANNEL_OPEN_CONFIRMATION		91
+#define SSH2_MSG_CHANNEL_OPEN_FAILURE			92
+#define SSH2_MSG_CHANNEL_WINDOW_ADJUST			93
+#define SSH2_MSG_CHANNEL_DATA				94
+#define SSH2_MSG_CHANNEL_EXTENDED_DATA			95
+#define SSH2_MSG_CHANNEL_EOF				96
+#define SSH2_MSG_CHANNEL_CLOSE				97
+#define SSH2_MSG_CHANNEL_REQUEST			98
+#define SSH2_MSG_CHANNEL_SUCCESS			99
+#define SSH2_MSG_CHANNEL_FAILURE			100
+
+/* disconnect reason code */
+
+#define SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT	1
+#define SSH2_DISCONNECT_PROTOCOL_ERROR			2
+#define SSH2_DISCONNECT_KEY_EXCHANGE_FAILED		3
+#define SSH2_DISCONNECT_HOST_AUTHENTICATION_FAILED	4
+#define SSH2_DISCONNECT_MAC_ERROR			5
+#define SSH2_DISCONNECT_COMPRESSION_ERROR		6
+#define SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE		7
+#define SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED	8
+#define SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE		9
+#define SSH2_DISCONNECT_CONNECTION_LOST			10
+#define SSH2_DISCONNECT_BY_APPLICATION			11
+
+/* misc */
+
+#define SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED		1
+#define SSH2_OPEN_CONNECT_FAILED			2
+#define SSH2_OPEN_UNKNOWN_CHANNEL_TYPE			3
+#define SSH2_OPEN_RESOURCE_SHORTAGE			4
+
+#define SSH2_EXTENDED_DATA_STDERR			1
diff --git a/sshd.8 b/sshd.8
index 3c24210..0de3cef 100644
--- a/sshd.8
+++ b/sshd.8
@@ -9,7 +9,7 @@
 .\"
 .\" Created: Sat Apr 22 21:55:14 1995 ylo
 .\"
-.\" $Id: sshd.8,v 1.15 2000/03/26 03:04:55 damien Exp $
+.\" $Id: sshd.8,v 1.16 2000/04/01 01:09:27 damien Exp $
 .\"
 .Dd September 25, 1999
 .Dt SSHD 8
@@ -69,7 +69,7 @@
 communications in the session.
 The rest of the session is encrypted
 using a conventional cipher, currently Blowfish and 3DES, with 3DES
-being is used by default.
+being used by default.
 The client selects the encryption algorithm
 to use from those offered by the server.
 .Pp
@@ -877,11 +877,11 @@
 .Xr ssl 8
 are required for proper operation.
 .Sh SEE ALSO
-.Xr rlogin 1 ,
-.Xr rsh 1 ,
 .Xr scp 1 ,
 .Xr ssh 1 ,
 .Xr ssh-add 1 ,
 .Xr ssh-agent 1 ,
 .Xr ssh-keygen 1 ,
-.Xr ssl 8
+.Xr ssl 8 ,
+.Xr rlogin 1 ,
+.Xr rsh 1
diff --git a/sshd.c b/sshd.c
index bf95121..6ec413d 100644
--- a/sshd.c
+++ b/sshd.c
@@ -11,19 +11,37 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshd.c,v 1.94 2000/03/23 22:15:34 markus Exp $");
+RCSID("$OpenBSD: sshd.c,v 1.96 2000/03/28 21:15:45 markus Exp $");
 
 #include "xmalloc.h"
 #include "rsa.h"
 #include "ssh.h"
 #include "pty.h"
 #include "packet.h"
-#include "buffer.h"
 #include "cipher.h"
 #include "mpaux.h"
 #include "servconf.h"
 #include "uidswap.h"
 #include "compat.h"
+#include "buffer.h"
+
+#ifdef HAVE_OPENSSL
+# include <openssl/dh.h>
+# include <openssl/bn.h>
+# include <openssl/hmac.h>
+# include <openssl/dsa.h>
+# include <openssl/rsa.h>
+#endif
+#ifdef HAVE_SSL
+# include <ssl/dh.h>
+# include <ssl/bn.h>
+# include <ssl/hmac.h>
+# include <ssl/dsa.h>
+# include <ssl/rsa.h>
+#endif
+#include "key.h"
+
+#include "auth.h"
 
 #ifdef LIBWRAP
 #include <tcpd.h>
@@ -36,9 +54,6 @@
 #define O_NOCTTY	0
 #endif
 
-/* Local Xauthority file. */
-static char *xauthfile = NULL;
-
 /* Server configuration options. */
 ServerOptions options;
 
@@ -88,21 +103,7 @@
  * sshd will skip the version-number exchange
  */
 char *client_version_string = NULL;
-
-/* Flags set in auth-rsa from authorized_keys flags.  These are set in auth-rsa.c. */
-int no_port_forwarding_flag = 0;
-int no_agent_forwarding_flag = 0;
-int no_x11_forwarding_flag = 0;
-int no_pty_flag = 0;
-
-/* RSA authentication "command=" option. */
-char *forced_command = NULL;
-
-/* RSA authentication "environment=" options. */
-struct envstring *custom_environment = NULL;
-
-/* Session id for the current session. */
-unsigned char session_id[16];
+char *server_version_string = NULL;
 
 /*
  * Any really sensitive data in the application is contained in this
@@ -130,43 +131,11 @@
    the private key. */
 RSA *public_key;
 
+/* session identifier, used by RSA-auth */
+unsigned char session_id[16];
+
 /* Prototypes for various functions defined later in this file. */
-void do_ssh_kex();
-void do_authentication();
-void do_authloop(struct passwd * pw);
-void do_fake_authloop(char *user);
-void do_authenticated(struct passwd * pw);
-void do_exec_pty(const char *command, int ptyfd, int ttyfd,
-	         const char *ttyname, struct passwd * pw, const char *term,
-	         const char *display, const char *auth_proto,
-	         const char *auth_data);
-void do_exec_no_pty(const char *command, struct passwd * pw,
-	            const char *display, const char *auth_proto,
-	            const char *auth_data);
-void do_child(const char *command, struct passwd * pw, const char *term,
-	      const char *display, const char *auth_proto,
-	      const char *auth_data, const char *ttyname);
-
-/*
- * Remove local Xauthority file.
- */
-void
-xauthfile_cleanup_proc(void *ignore)
-{
-	debug("xauthfile_cleanup_proc called");
-
-	if (xauthfile != NULL) {
-		char *p;
-		unlink(xauthfile);
-		p = strrchr(xauthfile, '/');
-		if (p != NULL) {
-			*p = '\0';
-			rmdir(xauthfile);
-		}
-		xfree(xauthfile);
-		xauthfile = NULL;
-	}
-}
+void do_ssh1_kex();
 
 /*
  * Close all listening sockets
@@ -250,35 +219,6 @@
 }
 
 /*
- * convert ssh auth msg type into description
- */
-char *
-get_authname(int type)
-{
-	static char buf[1024];
-	switch (type) {
-	case SSH_CMSG_AUTH_PASSWORD:
-		return "password";
-	case SSH_CMSG_AUTH_RSA:
-		return "rsa";
-	case SSH_CMSG_AUTH_RHOSTS_RSA:
-		return "rhosts-rsa";
-	case SSH_CMSG_AUTH_RHOSTS:
-		return "rhosts";
-#ifdef KRB4
-	case SSH_CMSG_AUTH_KERBEROS:
-		return "kerberos";
-#endif
-#ifdef SKEY
-	case SSH_CMSG_AUTH_TIS_RESPONSE:
-		return "s/key";
-#endif
-	}
-	snprintf(buf, sizeof buf, "bad-auth-msg-%d", type);
-	return buf;
-}
-
-/*
  * Signal handler for the key regeneration alarm.  Note that this
  * alarm only occurs in the daemon waiting for connections, and it does not
  * do anything with the private key or random state before forking.
@@ -315,6 +255,88 @@
 	errno = save_errno;
 }
 
+void
+sshd_exchange_identification(int sock_in, int sock_out)
+{
+	int i;
+	int remote_major, remote_minor;
+	char *s;
+	char buf[256];			/* Must not be larger than remote_version. */
+	char remote_version[256];	/* Must be at least as big as buf. */
+
+	snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
+	    PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
+	server_version_string = xstrdup(buf);
+
+	if (client_version_string == NULL) {
+		/* Send our protocol version identification. */
+		if (atomicio(write, sock_out, server_version_string, strlen(server_version_string))
+		    != strlen(server_version_string)) {
+			log("Could not write ident string to %s.", get_remote_ipaddr());
+			fatal_cleanup();
+		}
+
+		/* Read other side\'s version identification. */
+		for (i = 0; i < sizeof(buf) - 1; i++) {
+			if (read(sock_in, &buf[i], 1) != 1) {
+				log("Did not receive ident string from %s.", get_remote_ipaddr());
+				fatal_cleanup();
+			}
+			if (buf[i] == '\r') {
+				buf[i] = '\n';
+				buf[i + 1] = 0;
+				continue;
+				/*break; XXX eat \r */
+			}
+			if (buf[i] == '\n') {
+				/* buf[i] == '\n' */
+				buf[i + 1] = 0;
+				break;
+			}
+		}
+		buf[sizeof(buf) - 1] = 0;
+		client_version_string = xstrdup(buf);
+	}
+
+	/*
+	 * Check that the versions match.  In future this might accept
+	 * several versions and set appropriate flags to handle them.
+	 */
+	if (sscanf(client_version_string, "SSH-%d.%d-%[^\n]\n",
+	    &remote_major, &remote_minor, remote_version) != 3) {
+ 		s = "Protocol mismatch.\n";
+		(void) atomicio(write, sock_out, s, strlen(s));
+		close(sock_in);
+		close(sock_out);
+		log("Bad protocol version identification '%.100s' from %s",
+		    client_version_string, get_remote_ipaddr());
+		fatal_cleanup();
+	}
+	debug("Client protocol version %d.%d; client software version %.100s",
+	      remote_major, remote_minor, remote_version);
+
+	switch(remote_major) {
+	case 1:
+		if (remote_minor < 3) {
+			packet_disconnect("Your ssh version is too old and"
+			    "is no longer supported.  Please install a newer version.");
+		} else if (remote_minor == 3) {
+			/* note that this disables agent-forwarding */
+			enable_compat13();
+		}
+		break;
+	default: 
+		s = "Protocol major versions differ.\n";
+		(void) atomicio(write, sock_out, s, strlen(s));
+		close(sock_in);
+		close(sock_out);
+		log("Protocol major versions differ for %s: %d vs. %d",
+		    get_remote_ipaddr(), PROTOCOL_MAJOR, remote_major);
+		fatal_cleanup();
+		break;
+	}
+}
+
 /*
  * Main program for the daemon.
  */
@@ -325,12 +347,9 @@
 	extern int optind;
 	int opt, sock_in = 0, sock_out = 0, newsock, i, fdsetsz, pid, on = 1;
 	socklen_t fromlen;
-	int remote_major, remote_minor;
 	int silentrsa = 0;
 	fd_set *fdset;
 	struct sockaddr_storage from;
-	char buf[100];			/* Must not be larger than remote_version. */
-	char remote_version[100];	/* Must be at least as big as buf. */
 	const char *remote_ip;
 	int remote_port;
 	char *comment;
@@ -794,73 +813,7 @@
 	if (!debug_flag)
 		alarm(options.login_grace_time);
 
-	if (client_version_string != NULL) {
-		/* we are exec'ed by sshd2, so skip exchange of protocol version */
-		strlcpy(buf, client_version_string, sizeof(buf));
-	} else {
-		/* Send our protocol version identification. */
-		snprintf(buf, sizeof buf, "SSH-%d.%d-%.100s\n",
-			 PROTOCOL_MAJOR, PROTOCOL_MINOR, SSH_VERSION);
-		if (atomicio(write, sock_out, buf, strlen(buf)) != strlen(buf)) {
-			log("Could not write ident string to %s.", remote_ip);
-			fatal_cleanup();
-		}
-
-		/* Read other side\'s version identification. */
-		for (i = 0; i < sizeof(buf) - 1; i++) {
-			if (read(sock_in, &buf[i], 1) != 1) {
-				log("Did not receive ident string from %s.", remote_ip);
-				fatal_cleanup();
-			}
-			if (buf[i] == '\r') {
-				buf[i] = '\n';
-				buf[i + 1] = 0;
-				break;
-			}
-			if (buf[i] == '\n') {
-				/* buf[i] == '\n' */
-				buf[i + 1] = 0;
-				break;
-			}
-		}
-		buf[sizeof(buf) - 1] = 0;
-	}
-
-	/*
-	 * Check that the versions match.  In future this might accept
-	 * several versions and set appropriate flags to handle them.
-	 */
-	if (sscanf(buf, "SSH-%d.%d-%[^\n]\n", &remote_major, &remote_minor,
-	    remote_version) != 3) {
-		char *s = "Protocol mismatch.\n";
-
-		(void) atomicio(write, sock_out, s, strlen(s));
-		close(sock_in);
-		close(sock_out);
-		log("Bad protocol version identification '%.100s' from %s",
-		    buf, remote_ip);
-		fatal_cleanup();
-	}
-	debug("Client protocol version %d.%d; client software version %.100s",
-	      remote_major, remote_minor, remote_version);
-	if (remote_major != PROTOCOL_MAJOR) {
-		char *s = "Protocol major versions differ.\n";
-
-		(void) atomicio(write, sock_out, s, strlen(s));
-		close(sock_in);
-		close(sock_out);
-		log("Protocol major versions differ for %s: %d vs. %d",
-		    remote_ip, PROTOCOL_MAJOR, remote_major);
-		fatal_cleanup();
-	}
-	/* Check that the client has sufficiently high software version. */
-	if (remote_major == 1 && remote_minor < 3)
-		packet_disconnect("Your ssh version is too old and is no longer supported.  Please install a newer version.");
-
-	if (remote_major == 1 && remote_minor == 3) {
-		/* note that this disables agent-forwarding */
-		enable_compat13();
-	}
+	sshd_exchange_identification(sock_in, sock_out);
 	/*
 	 * Check that the connection comes from a privileged port.  Rhosts-
 	 * and Rhosts-RSA-Authentication only make sense from priviledged
@@ -884,8 +837,7 @@
 	packet_set_nonblocking();
 
 	/* perform the key exchange */
-	do_ssh_kex();
-
+	do_ssh1_kex();
 	/* authenticate user and start session */
 	do_authentication();
 
@@ -895,10 +847,6 @@
 		(void) dest_tkt();
 #endif /* KRB4 */
 
-	/* Cleanup user's local Xauthority file. */
-	if (xauthfile)
-		xauthfile_cleanup_proc(NULL);
-
 	/* The connection has been terminated. */
 	verbose("Closing connection to %.100s", remote_ip);
 
@@ -914,7 +862,7 @@
  * SSH1 key exchange
  */
 void
-do_ssh_kex()
+do_ssh1_kex()
 {
 	int i, len;
 	int plen, slen;
@@ -1101,1586 +1049,3 @@
 	packet_send();
 	packet_write_wait();
 }
-
-
-/*
- * Check if the user is allowed to log in via ssh. If user is listed in
- * DenyUsers or user's primary group is listed in DenyGroups, false will
- * be returned. If AllowUsers isn't empty and user isn't listed there, or
- * if AllowGroups isn't empty and user isn't listed there, false will be
- * returned. 
- * If the user's shell is not executable, false will be returned.
- * Otherwise true is returned. 
- */
-static int
-allowed_user(struct passwd * pw)
-{
-	struct stat st;
-	struct group *grp;
-	int i;
-#ifdef WITH_AIXAUTHENTICATE
-	char *loginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
-
-	/* Shouldn't be called if pw is NULL, but better safe than sorry... */
-	if (!pw)
-		return 0;
-
-	/* deny if shell does not exists or is not executable */
-	if (stat(pw->pw_shell, &st) != 0)
-		return 0;
-	if (!((st.st_mode & S_IFREG) && (st.st_mode & (S_IXOTH|S_IXUSR|S_IXGRP))))
-		return 0;
-
-	/* Return false if user is listed in DenyUsers */
-	if (options.num_deny_users > 0) {
-		if (!pw->pw_name)
-			return 0;
-		for (i = 0; i < options.num_deny_users; i++)
-			if (match_pattern(pw->pw_name, options.deny_users[i]))
-				return 0;
-	}
-	/* Return false if AllowUsers isn't empty and user isn't listed there */
-	if (options.num_allow_users > 0) {
-		if (!pw->pw_name)
-			return 0;
-		for (i = 0; i < options.num_allow_users; i++)
-			if (match_pattern(pw->pw_name, options.allow_users[i]))
-				break;
-		/* i < options.num_allow_users iff we break for loop */
-		if (i >= options.num_allow_users)
-			return 0;
-	}
-	/* Get the primary group name if we need it. Return false if it fails */
-	if (options.num_deny_groups > 0 || options.num_allow_groups > 0) {
-		grp = getgrgid(pw->pw_gid);
-		if (!grp)
-			return 0;
-
-		/* Return false if user's group is listed in DenyGroups */
-		if (options.num_deny_groups > 0) {
-			if (!grp->gr_name)
-				return 0;
-			for (i = 0; i < options.num_deny_groups; i++)
-				if (match_pattern(grp->gr_name, options.deny_groups[i]))
-					return 0;
-		}
-		/*
-		 * Return false if AllowGroups isn't empty and user's group
-		 * isn't listed there
-		 */
-		if (options.num_allow_groups > 0) {
-			if (!grp->gr_name)
-				return 0;
-			for (i = 0; i < options.num_allow_groups; i++)
-				if (match_pattern(grp->gr_name, options.allow_groups[i]))
-					break;
-			/* i < options.num_allow_groups iff we break for
-			   loop */
-			if (i >= options.num_allow_groups)
-				return 0;
-		}
-	}
-
-#ifdef WITH_AIXAUTHENTICATE
-	if (loginrestrictions(pw->pw_name,S_LOGIN,NULL,&loginmsg) != 0)
-		return 0;
-#endif /* WITH_AIXAUTHENTICATE */
-
-	/* We found no reason not to let this user try to log on... */
-	return 1;
-}
-
-/*
- * Performs authentication of an incoming connection.  Session key has already
- * been exchanged and encryption is enabled.
- */
-void
-do_authentication()
-{
-	struct passwd *pw, pwcopy;
-	int plen;
-	unsigned int ulen;
-	char *user;
-
-	/* Get the name of the user that we wish to log in as. */
-	packet_read_expect(&plen, SSH_CMSG_USER);
-
-	/* Get the user name. */
-	user = packet_get_string(&ulen);
-	packet_integrity_check(plen, (4 + ulen), SSH_CMSG_USER);
-
-	setproctitle("%s", user);
-
-#ifdef WITH_AIXAUTHENTICATE
-	char *loginmsg;
-#endif /* WITH_AIXAUTHENTICATE */
-
-#ifdef AFS
-	/* If machine has AFS, set process authentication group. */
-	if (k_hasafs()) {
-		k_setpag();
-		k_unlog();
-	}
-#endif /* AFS */
-
-	/* Verify that the user is a valid user. */
-	pw = getpwnam(user);
-	if (!pw || !allowed_user(pw))
-		do_fake_authloop(user);
-	xfree(user);
-
-	/* Take a copy of the returned structure. */
-	memset(&pwcopy, 0, sizeof(pwcopy));
-	pwcopy.pw_name = xstrdup(pw->pw_name);
-	pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
-	pwcopy.pw_uid = pw->pw_uid;
-	pwcopy.pw_gid = pw->pw_gid;
-	pwcopy.pw_dir = xstrdup(pw->pw_dir);
-	pwcopy.pw_shell = xstrdup(pw->pw_shell);
-	pw = &pwcopy;
-
-#ifdef USE_PAM
-	start_pam(pw);
-#endif
-
-	/*
-	 * If we are not running as root, the user must have the same uid as
-	 * the server.
-	 */
-	if (getuid() != 0 && pw->pw_uid != getuid())
-		packet_disconnect("Cannot change user when server not running as root.");
-
-	debug("Attempting authentication for %.100s.", pw->pw_name);
-
-	/* If the user has no password, accept authentication immediately. */
-	if (options.password_authentication &&
-#ifdef KRB4
-	    (!options.kerberos_authentication || options.kerberos_or_local_passwd) &&
-#endif /* KRB4 */
-#ifdef USE_PAM
-	    auth_pam_password(pw, "")) {
-#else /* USE_PAM */
-	    auth_password(pw, "")) {
-#endif /* USE_PAM */
-		/* Authentication with empty password succeeded. */
-		log("Login for user %s from %.100s, accepted without authentication.",
-		    pw->pw_name, get_remote_ipaddr());
-	} else {
-		/* Loop until the user has been authenticated or the
-		   connection is closed, do_authloop() returns only if
-		   authentication is successfull */
-		do_authloop(pw);
-	}
-
-	/* The user has been authenticated and accepted. */
-#ifdef WITH_AIXAUTHENTICATE
-	loginsuccess(user,get_canonical_hostname(),"ssh",&loginmsg);
-#endif /* WITH_AIXAUTHENTICATE */
-	packet_start(SSH_SMSG_SUCCESS);
-	packet_send();
-	packet_write_wait();
-
-	/* Perform session preparation. */
-	do_authenticated(pw);
-}
-
-#define AUTH_FAIL_MAX 6
-#define AUTH_FAIL_LOG (AUTH_FAIL_MAX/2)
-#define AUTH_FAIL_MSG "Too many authentication failures for %.100s"
-
-/*
- * read packets and try to authenticate local user *pw.
- * return if authentication is successfull
- */
-void
-do_authloop(struct passwd * pw)
-{
-	int attempt = 0;
-	unsigned int bits;
-	RSA *client_host_key;
-	BIGNUM *n;
-	char *client_user = NULL, *password = NULL;
-	char user[1024];
-	unsigned int dlen;
-	int plen, nlen, elen;
-	unsigned int ulen;
-	int type = 0;
-	void (*authlog) (const char *fmt,...) = verbose;
-
-	/* Indicate that authentication is needed. */
-	packet_start(SSH_SMSG_FAILURE);
-	packet_send();
-	packet_write_wait();
-
-	for (attempt = 1;; attempt++) {
-		int authenticated = 0;
-		strlcpy(user, "", sizeof user);
-
-		/* Get a packet from the client. */
-		type = packet_read(&plen);
-
-		/* Process the packet. */
-		switch (type) {
-#ifdef AFS
-		case SSH_CMSG_HAVE_KERBEROS_TGT:
-			if (!options.kerberos_tgt_passing) {
-				/* packet_get_all(); */
-				verbose("Kerberos tgt passing disabled.");
-				break;
-			} else {
-				/* Accept Kerberos tgt. */
-				char *tgt = packet_get_string(&dlen);
-				packet_integrity_check(plen, 4 + dlen, type);
-				if (!auth_kerberos_tgt(pw, tgt))
-					verbose("Kerberos tgt REFUSED for %s", pw->pw_name);
-				xfree(tgt);
-			}
-			continue;
-
-		case SSH_CMSG_HAVE_AFS_TOKEN:
-			if (!options.afs_token_passing || !k_hasafs()) {
-				/* packet_get_all(); */
-				verbose("AFS token passing disabled.");
-				break;
-			} else {
-				/* Accept AFS token. */
-				char *token_string = packet_get_string(&dlen);
-				packet_integrity_check(plen, 4 + dlen, type);
-				if (!auth_afs_token(pw, token_string))
-					verbose("AFS token REFUSED for %s", pw->pw_name);
-				xfree(token_string);
-			}
-			continue;
-#endif /* AFS */
-#ifdef KRB4
-		case SSH_CMSG_AUTH_KERBEROS:
-			if (!options.kerberos_authentication) {
-				/* packet_get_all(); */
-				verbose("Kerberos authentication disabled.");
-				break;
-			} else {
-				/* Try Kerberos v4 authentication. */
-				KTEXT_ST auth;
-				char *tkt_user = NULL;
-				char *kdata = packet_get_string((unsigned int *) &auth.length);
-				packet_integrity_check(plen, 4 + auth.length, type);
-
-				if (auth.length < MAX_KTXT_LEN)
-					memcpy(auth.dat, kdata, auth.length);
-				xfree(kdata);
-
-				authenticated = auth_krb4(pw->pw_name, &auth, &tkt_user);
-
-				if (authenticated) {
-					snprintf(user, sizeof user, " tktuser %s", tkt_user);
-					xfree(tkt_user);
-				}
-			}
-			break;
-#endif /* KRB4 */
-
-		case SSH_CMSG_AUTH_RHOSTS:
-			if (!options.rhosts_authentication) {
-				verbose("Rhosts authentication disabled.");
-				break;
-			}
-			/*
-			 * Get client user name.  Note that we just have to
-			 * trust the client; this is one reason why rhosts
-			 * authentication is insecure. (Another is
-			 * IP-spoofing on a local network.)
-			 */
-			client_user = packet_get_string(&ulen);
-			packet_integrity_check(plen, 4 + ulen, type);
-
-			/* Try to authenticate using /etc/hosts.equiv and
-			   .rhosts. */
-			authenticated = auth_rhosts(pw, client_user);
-
-			snprintf(user, sizeof user, " ruser %s", client_user);
-			break;
-
-		case SSH_CMSG_AUTH_RHOSTS_RSA:
-			if (!options.rhosts_rsa_authentication) {
-				verbose("Rhosts with RSA authentication disabled.");
-				break;
-			}
-			/*
-			 * Get client user name.  Note that we just have to
-			 * trust the client; root on the client machine can
-			 * claim to be any user.
-			 */
-			client_user = packet_get_string(&ulen);
-
-			/* Get the client host key. */
-			client_host_key = RSA_new();
-			if (client_host_key == NULL)
-				fatal("RSA_new failed");
-			client_host_key->e = BN_new();
-			client_host_key->n = BN_new();
-			if (client_host_key->e == NULL || client_host_key->n == NULL)
-				fatal("BN_new failed");
-			bits = packet_get_int();
-			packet_get_bignum(client_host_key->e, &elen);
-			packet_get_bignum(client_host_key->n, &nlen);
-
-			if (bits != BN_num_bits(client_host_key->n))
-				error("Warning: keysize mismatch for client_host_key: "
-				      "actual %d, announced %d", BN_num_bits(client_host_key->n), bits);
-			packet_integrity_check(plen, (4 + ulen) + 4 + elen + nlen, type);
-
-			authenticated = auth_rhosts_rsa(pw, client_user, client_host_key);
-			RSA_free(client_host_key);
-
-			snprintf(user, sizeof user, " ruser %s", client_user);
-			break;
-
-		case SSH_CMSG_AUTH_RSA:
-			if (!options.rsa_authentication) {
-				verbose("RSA authentication disabled.");
-				break;
-			}
-			/* RSA authentication requested. */
-			n = BN_new();
-			packet_get_bignum(n, &nlen);
-			packet_integrity_check(plen, nlen, type);
-			authenticated = auth_rsa(pw, n);
-			BN_clear_free(n);
-			break;
-
-		case SSH_CMSG_AUTH_PASSWORD:
-			if (!options.password_authentication) {
-				verbose("Password authentication disabled.");
-				break;
-			}
-			/*
-			 * Read user password.  It is in plain text, but was
-			 * transmitted over the encrypted channel so it is
-			 * not visible to an outside observer.
-			 */
-			password = packet_get_string(&dlen);
-			packet_integrity_check(plen, 4 + dlen, type);
-
-#ifdef USE_PAM
-			/* Do PAM auth with password */
-			authenticated = auth_pam_password(pw, password);
-#else /* USE_PAM */
-			/* Try authentication with the password. */
-			authenticated = auth_password(pw, password);
-#endif /* USE_PAM */
-			memset(password, 0, strlen(password));
-			xfree(password);
-			break;
-
-#ifdef SKEY
-		case SSH_CMSG_AUTH_TIS:
-			debug("rcvd SSH_CMSG_AUTH_TIS");
-			if (options.skey_authentication == 1) {
-				char *skeyinfo = skey_keyinfo(pw->pw_name);
-				if (skeyinfo == NULL) {
-					debug("generating fake skeyinfo for %.100s.", pw->pw_name);
-					skeyinfo = skey_fake_keyinfo(pw->pw_name);
-				}
-				if (skeyinfo != NULL) {
-					/* we send our s/key- in tis-challenge messages */
-					debug("sending challenge '%s'", skeyinfo);
-					packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
-					packet_put_string(skeyinfo, strlen(skeyinfo));
-					packet_send();
-					packet_write_wait();
-					continue;
-				}
-			}
-			break;
-		case SSH_CMSG_AUTH_TIS_RESPONSE:
-			debug("rcvd SSH_CMSG_AUTH_TIS_RESPONSE");
-			if (options.skey_authentication == 1) {
-				char *response = packet_get_string(&dlen);
-				debug("skey response == '%s'", response);
-				packet_integrity_check(plen, 4 + dlen, type);
-				authenticated = (skey_haskey(pw->pw_name) == 0 &&
-						 skey_passcheck(pw->pw_name, response) != -1);
-				xfree(response);
-			}
-			break;
-#else
-		case SSH_CMSG_AUTH_TIS:
-			/* TIS Authentication is unsupported */
-			log("TIS authentication unsupported.");
-			break;
-#endif
-
-		default:
-			/*
-			 * Any unknown messages will be ignored (and failure
-			 * returned) during authentication.
-			 */
-			log("Unknown message during authentication: type %d", type);
-			break;
-		}
-
-		/*
-		 * Check if the user is logging in as root and root logins
-		 * are disallowed.
-		 * Note that root login is allowed for forced commands.
-		 */
-		if (authenticated && pw->pw_uid == 0 && !options.permit_root_login) {
-			if (forced_command) {
-				log("Root login accepted for forced command.");
-			} else {
-				authenticated = 0;
-				log("ROOT LOGIN REFUSED FROM %.200s",
-				    get_canonical_hostname());
-			}
-		}
-
-		/* Raise logging level */
-		if (authenticated ||
-		    attempt == AUTH_FAIL_LOG ||
-		    type == SSH_CMSG_AUTH_PASSWORD)
-			authlog = log;
-
-		authlog("%s %s for %.200s from %.200s port %d%s",
-			authenticated ? "Accepted" : "Failed",
-			get_authname(type),
-			pw->pw_uid == 0 ? "ROOT" : pw->pw_name,
-			get_remote_ipaddr(),
-			get_remote_port(),
-			user);
-
-#ifdef USE_PAM
-		if (authenticated) {
-			if (!do_pam_account(pw->pw_name, client_user)) {
-				if (client_user != NULL) {
-					xfree(client_user);
-					client_user = NULL;
-				}
-				do_fake_authloop(pw->pw_name);
-			}
-			return;
-		}
-#else /* USE_PAM */
-		if (authenticated) {
-			return;
-		}
-#endif /* USE_PAM */
-
-		if (client_user != NULL) {
-			xfree(client_user);
-			client_user = NULL;
-		}
-
-		if (attempt > AUTH_FAIL_MAX)
-			packet_disconnect(AUTH_FAIL_MSG, pw->pw_name);
-
-		/* Send a message indicating that the authentication attempt failed. */
-		packet_start(SSH_SMSG_FAILURE);
-		packet_send();
-		packet_write_wait();
-	}
-}
-
-/*
- * The user does not exist or access is denied,
- * but fake indication that authentication is needed.
- */
-void
-do_fake_authloop(char *user)
-{
-	int attempt = 0;
-
-	log("Faking authloop for illegal user %.200s from %.200s port %d",
-	    user,
-	    get_remote_ipaddr(),
-	    get_remote_port());
-
-	/* Indicate that authentication is needed. */
-	packet_start(SSH_SMSG_FAILURE);
-	packet_send();
-	packet_write_wait();
-
-	/*
-	 * Keep reading packets, and always respond with a failure.  This is
-	 * to avoid disclosing whether such a user really exists.
-	 */
-	for (attempt = 1;; attempt++) {
-		/* Read a packet.  This will not return if the client disconnects. */
-		int plen;
-#ifndef SKEY
-		(void)packet_read(&plen);
-#else /* SKEY */
-		int type = packet_read(&plen);
-		unsigned int dlen;
-		char *password, *skeyinfo;
-		/* Try to send a fake s/key challenge. */
-		if (options.skey_authentication == 1 &&
-		    (skeyinfo = skey_fake_keyinfo(user)) != NULL) {
-			password = NULL;
-			if (type == SSH_CMSG_AUTH_TIS) {
-				packet_start(SSH_SMSG_AUTH_TIS_CHALLENGE);
-				packet_put_string(skeyinfo, strlen(skeyinfo));
-				packet_send();
-				packet_write_wait();
-				continue;
-			} else if (type == SSH_CMSG_AUTH_PASSWORD &&
-			           options.password_authentication &&
-			           (password = packet_get_string(&dlen)) != NULL &&
-			           dlen == 5 &&
-			           strncasecmp(password, "s/key", 5) == 0 ) {
-				packet_send_debug(skeyinfo);
-			}
-			if (password != NULL)
-				xfree(password);
-		}
-#endif
-		if (attempt > AUTH_FAIL_MAX)
-			packet_disconnect(AUTH_FAIL_MSG, user);
-
-		/*
-		 * Send failure.  This should be indistinguishable from a
-		 * failed authentication.
-		 */
-		packet_start(SSH_SMSG_FAILURE);
-		packet_send();
-		packet_write_wait();
-#ifdef WITH_AIXAUTHENTICATE 
-		if (strncmp(get_authname(type),"password",
-		    strlen(get_authname(type))) == 0)
-			loginfailed(pw->pw_name,get_canonical_hostname(),"ssh");
-#endif /* WITH_AIXAUTHENTICATE */
-	}
-	/* NOTREACHED */
-	abort();
-}
-
-struct pty_cleanup_context {
-	const char *ttyname;
-	int pid;
-};
-
-/*
- * Function to perform cleanup if we get aborted abnormally (e.g., due to a
- * dropped connection).
- */
-void 
-pty_cleanup_proc(void *context)
-{
-	struct pty_cleanup_context *cu = context;
-
-	debug("pty_cleanup_proc called");
-
-	/* Record that the user has logged out. */
-	record_logout(cu->pid, cu->ttyname);
-
-	/* Release the pseudo-tty. */
-	pty_release(cu->ttyname);
-}
-
-/* simple cleanup: chown tty slave back to root */
-static void
-pty_release_proc(void *tty)
-{
-	char *ttyname = tty;
-	pty_release(ttyname);
-}
-
-/*
- * Prepares for an interactive session.  This is called after the user has
- * been successfully authenticated.  During this message exchange, pseudo
- * terminals are allocated, X11, TCP/IP, and authentication agent forwardings
- * are requested, etc.
- */
-void 
-do_authenticated(struct passwd * pw)
-{
-	int type;
-	int compression_level = 0, enable_compression_after_reply = 0;
-	int have_pty = 0, ptyfd = -1, ttyfd = -1;
-	int row, col, xpixel, ypixel, screen;
-	char ttyname[64];
-	char *command, *term = NULL, *display = NULL, *proto = NULL, *data = NULL;
-	int plen;
-	unsigned int dlen;
-	int n_bytes;
-
-	/*
-	 * Cancel the alarm we set to limit the time taken for
-	 * authentication.
-	 */
-	alarm(0);
-
-	/*
-	 * Inform the channel mechanism that we are the server side and that
-	 * the client may request to connect to any port at all. (The user
-	 * could do it anyway, and we wouldn\'t know what is permitted except
-	 * by the client telling us, so we can equally well trust the client
-	 * not to request anything bogus.)
-	 */
-	if (!no_port_forwarding_flag)
-		channel_permit_all_opens();
-
-	/*
-	 * We stay in this loop until the client requests to execute a shell
-	 * or a command.
-	 */
-	while (1) {
-
-		/* Get a packet from the client. */
-		type = packet_read(&plen);
-
-		/* Process the packet. */
-		switch (type) {
-		case SSH_CMSG_REQUEST_COMPRESSION:
-			packet_integrity_check(plen, 4, type);
-			compression_level = packet_get_int();
-			if (compression_level < 1 || compression_level > 9) {
-				packet_send_debug("Received illegal compression level %d.",
-						  compression_level);
-				goto fail;
-			}
-			/* Enable compression after we have responded with SUCCESS. */
-			enable_compression_after_reply = 1;
-			break;
-
-		case SSH_CMSG_REQUEST_PTY:
-			if (no_pty_flag) {
-				debug("Allocating a pty not permitted for this authentication.");
-				goto fail;
-			}
-			if (have_pty)
-				packet_disconnect("Protocol error: you already have a pty.");
-
-			debug("Allocating pty.");
-
-			/* Allocate a pty and open it. */
-			if (!pty_allocate(&ptyfd, &ttyfd, ttyname,
-			    sizeof(ttyname))) {
-				error("Failed to allocate pty.");
-				goto fail;
-			}
-			fatal_add_cleanup(pty_release_proc, (void *)ttyname);
-			pty_setowner(pw, ttyname);
-
-			/* Get TERM from the packet.  Note that the value may be of arbitrary length. */
-			term = packet_get_string(&dlen);
-			packet_integrity_check(dlen, strlen(term), type);
-
-			/* Remaining bytes */
-			n_bytes = plen - (4 + dlen + 4 * 4);
-
-			if (strcmp(term, "") == 0) {
-				xfree(term);
-				term = NULL;
-			}
-
-			/* Get window size from the packet. */
-			row = packet_get_int();
-			col = packet_get_int();
-			xpixel = packet_get_int();
-			ypixel = packet_get_int();
-			pty_change_window_size(ptyfd, row, col, xpixel, ypixel);
-
-			/* Get tty modes from the packet. */
-			tty_parse_modes(ttyfd, &n_bytes);
-			packet_integrity_check(plen, 4 + dlen + 4 * 4 + n_bytes, type);
-
-			/* Indicate that we now have a pty. */
-			have_pty = 1;
-			break;
-
-		case SSH_CMSG_X11_REQUEST_FORWARDING:
-			if (!options.x11_forwarding) {
-				packet_send_debug("X11 forwarding disabled in server configuration file.");
-				goto fail;
-			}
-#ifdef XAUTH_PATH
-			if (no_x11_forwarding_flag) {
-				packet_send_debug("X11 forwarding not permitted for this authentication.");
-				goto fail;
-			}
-			debug("Received request for X11 forwarding with auth spoofing.");
-			if (display)
-				packet_disconnect("Protocol error: X11 display already set.");
-			{
-				unsigned int proto_len, data_len;
-				proto = packet_get_string(&proto_len);
-				data = packet_get_string(&data_len);
-				packet_integrity_check(plen, 4 + proto_len + 4 + data_len + 4, type);
-			}
-			if (packet_get_protocol_flags() & SSH_PROTOFLAG_SCREEN_NUMBER)
-				screen = packet_get_int();
-			else
-				screen = 0;
-			display = x11_create_display_inet(screen, options.x11_display_offset);
-			if (!display)
-				goto fail;
-
-			/* Setup to always have a local .Xauthority. */
-			xauthfile = xmalloc(MAXPATHLEN);
-			strlcpy(xauthfile, "/tmp/ssh-XXXXXXXX", MAXPATHLEN);
-			temporarily_use_uid(pw->pw_uid);
-			if (mkdtemp(xauthfile) == NULL) {
-				restore_uid();
-				error("private X11 dir: mkdtemp %s failed: %s",
-				    xauthfile, strerror(errno));
-				xfree(xauthfile);
-				xauthfile = NULL;
-				goto fail;
-			}
-			strlcat(xauthfile, "/cookies", MAXPATHLEN);
-			open(xauthfile, O_RDWR|O_CREAT|O_EXCL, 0600);
-			restore_uid();
-			fatal_add_cleanup(xauthfile_cleanup_proc, NULL);
-			break;
-#else /* XAUTH_PATH */
-			packet_send_debug("No xauth program; cannot forward with spoofing.");
-			goto fail;
-#endif /* XAUTH_PATH */
-
-		case SSH_CMSG_AGENT_REQUEST_FORWARDING:
-			if (no_agent_forwarding_flag || compat13) {
-				debug("Authentication agent forwarding not permitted for this authentication.");
-				goto fail;
-			}
-			debug("Received authentication agent forwarding request.");
-			auth_input_request_forwarding(pw);
-			break;
-
-		case SSH_CMSG_PORT_FORWARD_REQUEST:
-			if (no_port_forwarding_flag) {
-				debug("Port forwarding not permitted for this authentication.");
-				goto fail;
-			}
-			debug("Received TCP/IP port forwarding request.");
-			channel_input_port_forward_request(pw->pw_uid == 0);
-			break;
-
-		case SSH_CMSG_MAX_PACKET_SIZE:
-			if (packet_set_maxsize(packet_get_int()) < 0)
-				goto fail;
-			break;
-
-		case SSH_CMSG_EXEC_SHELL:
-			/* Set interactive/non-interactive mode. */
-			packet_set_interactive(have_pty || display != NULL,
-					       options.keepalives);
-
-			if (forced_command != NULL)
-				goto do_forced_command;
-			debug("Forking shell.");
-			packet_integrity_check(plen, 0, type);
-			if (have_pty)
-				do_exec_pty(NULL, ptyfd, ttyfd, ttyname, pw, term, display, proto, data);
-			else
-				do_exec_no_pty(NULL, pw, display, proto, data);
-			return;
-
-		case SSH_CMSG_EXEC_CMD:
-			/* Set interactive/non-interactive mode. */
-			packet_set_interactive(have_pty || display != NULL,
-					       options.keepalives);
-
-			if (forced_command != NULL)
-				goto do_forced_command;
-			/* Get command from the packet. */
-			{
-				unsigned int dlen;
-				command = packet_get_string(&dlen);
-				debug("Executing command '%.500s'", command);
-				packet_integrity_check(plen, 4 + dlen, type);
-			}
-			if (have_pty)
-				do_exec_pty(command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data);
-			else
-				do_exec_no_pty(command, pw, display, proto, data);
-			xfree(command);
-			return;
-
-		default:
-			/*
-			 * Any unknown messages in this phase are ignored,
-			 * and a failure message is returned.
-			 */
-			log("Unknown packet type received after authentication: %d", type);
-			goto fail;
-		}
-
-		/* The request was successfully processed. */
-		packet_start(SSH_SMSG_SUCCESS);
-		packet_send();
-		packet_write_wait();
-
-		/* Enable compression now that we have replied if appropriate. */
-		if (enable_compression_after_reply) {
-			enable_compression_after_reply = 0;
-			packet_start_compression(compression_level);
-		}
-		continue;
-
-fail:
-		/* The request failed. */
-		packet_start(SSH_SMSG_FAILURE);
-		packet_send();
-		packet_write_wait();
-		continue;
-
-do_forced_command:
-		/*
-		 * There is a forced command specified for this login.
-		 * Execute it.
-		 */
-		debug("Executing forced command: %.900s", forced_command);
-		if (have_pty)
-			do_exec_pty(forced_command, ptyfd, ttyfd, ttyname, pw, term, display, proto, data);
-		else
-			do_exec_no_pty(forced_command, pw, display, proto, data);
-		return;
-	}
-}
-
-/*
- * This is called to fork and execute a command when we have no tty.  This
- * will call do_child from the child, and server_loop from the parent after
- * setting up file descriptors and such.
- */
-void 
-do_exec_no_pty(const char *command, struct passwd * pw,
-	       const char *display, const char *auth_proto,
-	       const char *auth_data)
-{
-	int pid;
-
-#ifdef USE_PIPES
-	int pin[2], pout[2], perr[2];
-	/* Allocate pipes for communicating with the program. */
-	if (pipe(pin) < 0 || pipe(pout) < 0 || pipe(perr) < 0)
-		packet_disconnect("Could not create pipes: %.100s",
-				  strerror(errno));
-#else /* USE_PIPES */
-	int inout[2], err[2];
-	/* Uses socket pairs to communicate with the program. */
-	if (socketpair(AF_UNIX, SOCK_STREAM, 0, inout) < 0 ||
-	    socketpair(AF_UNIX, SOCK_STREAM, 0, err) < 0)
-		packet_disconnect("Could not create socket pairs: %.100s",
-				  strerror(errno));
-#endif /* USE_PIPES */
-
-	setproctitle("%s@notty", pw->pw_name);
-
-#ifdef USE_PAM
-			do_pam_setcred();
-#endif /* USE_PAM */
-
-	/* Fork the child. */
-	if ((pid = fork()) == 0) {
-		/* Child.  Reinitialize the log since the pid has changed. */
-		log_init(av0, options.log_level, options.log_facility, log_stderr);
-
-		/*
-		 * Create a new session and process group since the 4.4BSD
-		 * setlogin() affects the entire process group.
-		 */
-		if (setsid() < 0)
-			error("setsid failed: %.100s", strerror(errno));
-
-#ifdef USE_PIPES
-		/*
-		 * Redirect stdin.  We close the parent side of the socket
-		 * pair, and make the child side the standard input.
-		 */
-		close(pin[1]);
-		if (dup2(pin[0], 0) < 0)
-			perror("dup2 stdin");
-		close(pin[0]);
-
-		/* Redirect stdout. */
-		close(pout[0]);
-		if (dup2(pout[1], 1) < 0)
-			perror("dup2 stdout");
-		close(pout[1]);
-
-		/* Redirect stderr. */
-		close(perr[0]);
-		if (dup2(perr[1], 2) < 0)
-			perror("dup2 stderr");
-		close(perr[1]);
-#else /* USE_PIPES */
-		/*
-		 * Redirect stdin, stdout, and stderr.  Stdin and stdout will
-		 * use the same socket, as some programs (particularly rdist)
-		 * seem to depend on it.
-		 */
-		close(inout[1]);
-		close(err[1]);
-		if (dup2(inout[0], 0) < 0)	/* stdin */
-			perror("dup2 stdin");
-		if (dup2(inout[0], 1) < 0)	/* stdout.  Note: same socket as stdin. */
-			perror("dup2 stdout");
-		if (dup2(err[0], 2) < 0)	/* stderr */
-			perror("dup2 stderr");
-#endif /* USE_PIPES */
-
-		/* Do processing for the child (exec command etc). */
-		do_child(command, pw, NULL, display, auth_proto, auth_data, NULL);
-		/* NOTREACHED */
-	}
-	if (pid < 0)
-		packet_disconnect("fork failed: %.100s", strerror(errno));
-#ifdef USE_PIPES
-	/* We are the parent.  Close the child sides of the pipes. */
-	close(pin[0]);
-	close(pout[1]);
-	close(perr[1]);
-
-	/* Enter the interactive session. */
-	server_loop(pid, pin[1], pout[0], perr[0]);
-	/* server_loop has closed pin[1], pout[1], and perr[1]. */
-#else /* USE_PIPES */
-	/* We are the parent.  Close the child sides of the socket pairs. */
-	close(inout[0]);
-	close(err[0]);
-
-	/*
-	 * Enter the interactive session.  Note: server_loop must be able to
-	 * handle the case that fdin and fdout are the same.
-	 */
-	server_loop(pid, inout[1], inout[1], err[1]);
-	/* server_loop has closed inout[1] and err[1]. */
-#endif /* USE_PIPES */
-}
-
-/*
- * This is called to fork and execute a command when we have a tty.  This
- * will call do_child from the child, and server_loop from the parent after
- * setting up file descriptors, controlling tty, updating wtmp, utmp,
- * lastlog, and other such operations.
- */
-void 
-do_exec_pty(const char *command, int ptyfd, int ttyfd,
-	    const char *ttyname, struct passwd * pw, const char *term,
-	    const char *display, const char *auth_proto,
-	    const char *auth_data)
-{
-	int pid, fdout;
-	int ptymaster;
-	const char *hostname;
-	time_t last_login_time;
-	char buf[100], *time_string;
-	FILE *f;
-	char line[256];
-	struct stat st;
-	int quiet_login;
-	struct sockaddr_storage from;
-	socklen_t fromlen;
-	struct pty_cleanup_context cleanup_context;
-
-	/* Get remote host name. */
-	hostname = get_canonical_hostname();
-
-	/*
-	 * Get the time when the user last logged in.  Buf will be set to
-	 * contain the hostname the last login was from.
-	 */
-	if (!options.use_login) {
-		last_login_time = get_last_login_time(pw->pw_uid, pw->pw_name,
-						      buf, sizeof(buf));
-	}
-	setproctitle("%s@%s", pw->pw_name, strrchr(ttyname, '/') + 1);
-
-#ifdef USE_PAM
-			do_pam_session(pw->pw_name, ttyname);
-			do_pam_setcred();
-#endif /* USE_PAM */
-
-	/* Fork the child. */
-	if ((pid = fork()) == 0) {
-		pid = getpid();
-
-		/* Child.  Reinitialize the log because the pid has
-		   changed. */
-		log_init(av0, options.log_level, options.log_facility, log_stderr);
-
-		/* Close the master side of the pseudo tty. */
-		close(ptyfd);
-
-		/* Make the pseudo tty our controlling tty. */
-		pty_make_controlling_tty(&ttyfd, ttyname);
-
-		/* Redirect stdin from the pseudo tty. */
-		if (dup2(ttyfd, fileno(stdin)) < 0)
-			error("dup2 stdin failed: %.100s", strerror(errno));
-
-		/* Redirect stdout to the pseudo tty. */
-		if (dup2(ttyfd, fileno(stdout)) < 0)
-			error("dup2 stdin failed: %.100s", strerror(errno));
-
-		/* Redirect stderr to the pseudo tty. */
-		if (dup2(ttyfd, fileno(stderr)) < 0)
-			error("dup2 stdin failed: %.100s", strerror(errno));
-
-		/* Close the extra descriptor for the pseudo tty. */
-		close(ttyfd);
-
-		/*
-		 * Get IP address of client.  This is needed because we want
-		 * to record where the user logged in from.  If the
-		 * connection is not a socket, let the ip address be 0.0.0.0.
-		 */
-		memset(&from, 0, sizeof(from));
-		if (packet_get_connection_in() == packet_get_connection_out()) {
-			fromlen = sizeof(from);
-			if (getpeername(packet_get_connection_in(),
-			     (struct sockaddr *) & from, &fromlen) < 0) {
-				debug("getpeername: %.100s", strerror(errno));
-				fatal_cleanup();
-			}
-		}
-		/* Record that there was a login on that terminal. */
-		record_login(pid, ttyname, pw->pw_name, pw->pw_uid, hostname,
-			     (struct sockaddr *)&from);
-
-		/* Check if .hushlogin exists. */
-		snprintf(line, sizeof line, "%.200s/.hushlogin", pw->pw_dir);
-		quiet_login = stat(line, &st) >= 0;
-
-#ifdef USE_PAM
-		if (!quiet_login)
-			print_pam_messages();
-#endif /* USE_PAM */
-
-		/*
-		 * If the user has logged in before, display the time of last
-		 * login. However, don't display anything extra if a command
-		 * has been specified (so that ssh can be used to execute
-		 * commands on a remote machine without users knowing they
-		 * are going to another machine). Login(1) will do this for
-		 * us as well, so check if login(1) is used
-		 */
-		if (command == NULL && last_login_time != 0 && !quiet_login &&
-		    !options.use_login) {
-			/* Convert the date to a string. */
-			time_string = ctime(&last_login_time);
-			/* Remove the trailing newline. */
-			if (strchr(time_string, '\n'))
-				*strchr(time_string, '\n') = 0;
-			/* Display the last login time.  Host if displayed
-			   if known. */
-			if (strcmp(buf, "") == 0)
-				printf("Last login: %s\r\n", time_string);
-			else
-				printf("Last login: %s from %s\r\n", time_string, buf);
-		}
-		/*
-		 * Print /etc/motd unless a command was specified or printing
-		 * it was disabled in server options or login(1) will be
-		 * used.  Note that some machines appear to print it in
-		 * /etc/profile or similar.
-		 */
-		if (command == NULL && options.print_motd && !quiet_login &&
-		    !options.use_login) {
-			/* Print /etc/motd if it exists. */
-			f = fopen("/etc/motd", "r");
-			if (f) {
-				while (fgets(line, sizeof(line), f))
-					fputs(line, stdout);
-				fclose(f);
-			}
-		}
-		/* Do common processing for the child, such as execing the command. */
-		do_child(command, pw, term, display, auth_proto, auth_data, ttyname);
-		/* NOTREACHED */
-	}
-	if (pid < 0)
-		packet_disconnect("fork failed: %.100s", strerror(errno));
-	/* Parent.  Close the slave side of the pseudo tty. */
-	close(ttyfd);
-
-	/*
-	 * Add a cleanup function to clear the utmp entry and record logout
-	 * time in case we call fatal() (e.g., the connection gets closed).
-	 */
-	cleanup_context.pid = pid;
-	cleanup_context.ttyname = ttyname;
-	fatal_add_cleanup(pty_cleanup_proc, (void *) &cleanup_context);
-	fatal_remove_cleanup(pty_release_proc, (void *) ttyname);
-
-	/*
-	 * Create another descriptor of the pty master side for use as the
-	 * standard input.  We could use the original descriptor, but this
-	 * simplifies code in server_loop.  The descriptor is bidirectional.
-	 */
-	fdout = dup(ptyfd);
-	if (fdout < 0)
-		packet_disconnect("dup #1 failed: %.100s", strerror(errno));
-
-	/* we keep a reference to the pty master */
-	ptymaster = dup(ptyfd);
-	if (ptymaster < 0)
-		packet_disconnect("dup #2 failed: %.100s", strerror(errno));
-
-	/* Enter interactive session. */
-	server_loop(pid, ptyfd, fdout, -1);
-	/* server_loop _has_ closed ptyfd and fdout. */
-
-	/* Cancel the cleanup function. */
-	fatal_remove_cleanup(pty_cleanup_proc, (void *) &cleanup_context);
-
-	/* Record that the user has logged out. */
-	record_logout(pid, ttyname);
-
-	/* Release the pseudo-tty. */
-	pty_release(ttyname);
-
-	/*
-	 * Close the server side of the socket pairs.  We must do this after
-	 * the pty cleanup, so that another process doesn't get this pty
-	 * while we're still cleaning up.
-	 */
-	if (close(ptymaster) < 0)
-		error("close(ptymaster): %s", strerror(errno));
-}
-
-/*
- * Sets the value of the given variable in the environment.  If the variable
- * already exists, its value is overriden.
- */
-void 
-child_set_env(char ***envp, unsigned int *envsizep, const char *name,
-	      const char *value)
-{
-	unsigned int i, namelen;
-	char **env;
-
-	/*
-	 * Find the slot where the value should be stored.  If the variable
-	 * already exists, we reuse the slot; otherwise we append a new slot
-	 * at the end of the array, expanding if necessary.
-	 */
-	env = *envp;
-	namelen = strlen(name);
-	for (i = 0; env[i]; i++)
-		if (strncmp(env[i], name, namelen) == 0 && env[i][namelen] == '=')
-			break;
-	if (env[i]) {
-		/* Reuse the slot. */
-		xfree(env[i]);
-	} else {
-		/* New variable.  Expand if necessary. */
-		if (i >= (*envsizep) - 1) {
-			(*envsizep) += 50;
-			env = (*envp) = xrealloc(env, (*envsizep) * sizeof(char *));
-		}
-		/* Need to set the NULL pointer at end of array beyond the new slot. */
-		env[i + 1] = NULL;
-	}
-
-	/* Allocate space and format the variable in the appropriate slot. */
-	env[i] = xmalloc(strlen(name) + 1 + strlen(value) + 1);
-	snprintf(env[i], strlen(name) + 1 + strlen(value) + 1, "%s=%s", name, value);
-}
-
-/*
- * Reads environment variables from the given file and adds/overrides them
- * into the environment.  If the file does not exist, this does nothing.
- * Otherwise, it must consist of empty lines, comments (line starts with '#')
- * and assignments of the form name=value.  No other forms are allowed.
- */
-void 
-read_environment_file(char ***env, unsigned int *envsize,
-		      const char *filename)
-{
-	FILE *f;
-	char buf[4096];
-	char *cp, *value;
-
-	f = fopen(filename, "r");
-	if (!f)
-		return;
-
-	while (fgets(buf, sizeof(buf), f)) {
-		for (cp = buf; *cp == ' ' || *cp == '\t'; cp++)
-			;
-		if (!*cp || *cp == '#' || *cp == '\n')
-			continue;
-		if (strchr(cp, '\n'))
-			*strchr(cp, '\n') = '\0';
-		value = strchr(cp, '=');
-		if (value == NULL) {
-			fprintf(stderr, "Bad line in %.100s: %.200s\n", filename, buf);
-			continue;
-		}
-		/* Replace the equals sign by nul, and advance value to the value string. */
-		*value = '\0';
-		value++;
-		child_set_env(env, envsize, cp, value);
-	}
-	fclose(f);
-}
-
-#ifdef USE_PAM
-/*
- * Sets any environment variables which have been specified by PAM
- */
-void do_pam_environment(char ***env, int *envsize)
-{
-	char *equals, var_name[512], var_val[512];
-	char **pam_env;
-	int i;
-
-	if ((pam_env = fetch_pam_environment()) == NULL)
-		return;
-	
-	for(i = 0; pam_env[i] != NULL; i++) {
-		if ((equals = strstr(pam_env[i], "=")) == NULL)
-			continue;
-			
-		if (strlen(pam_env[i]) < (sizeof(var_name) - 1))
-		{
-			memset(var_name, '\0', sizeof(var_name));
-			memset(var_val, '\0', sizeof(var_val));
-
-			strncpy(var_name, pam_env[i], equals - pam_env[i]);
-			strcpy(var_val, equals + 1);
-
-			debug("PAM environment: %s=%s", var_name, var_val);
-
-			child_set_env(env, envsize, var_name, var_val);
-		}
-	}
-}
-#endif /* USE_PAM */
-
-/*
- * Performs common processing for the child, such as setting up the
- * environment, closing extra file descriptors, setting the user and group
- * ids, and executing the command or shell.
- */
-void 
-do_child(const char *command, struct passwd * pw, const char *term,
-	 const char *display, const char *auth_proto,
-	 const char *auth_data, const char *ttyname)
-{
-	const char *shell, *cp = NULL;
-	char buf[256];
-	FILE *f;
-	unsigned int envsize, i;
-	char **env;
-	extern char **environ;
-	struct stat st;
-	char *argv[10];
-
-#ifndef USE_PAM /* pam_nologin handles this */
-	/* Check /etc/nologin. */
-	f = fopen("/etc/nologin", "r");
-	if (f) {
-		/* /etc/nologin exists.  Print its contents and exit. */
-		while (fgets(buf, sizeof(buf), f))
-			fputs(buf, stderr);
-		fclose(f);
-		if (pw->pw_uid != 0)
-			exit(254);
-	}
-#endif /* USE_PAM */
-
-	/* Set login name in the kernel. */
-	if (setlogin(pw->pw_name) < 0)
-		error("setlogin failed: %s", strerror(errno));
-
-	/* Set uid, gid, and groups. */
-	/* Login(1) does this as well, and it needs uid 0 for the "-h"
-	   switch, so we let login(1) to this for us. */
-	if (!options.use_login) {
-		if (getuid() == 0 || geteuid() == 0) {
-			if (setgid(pw->pw_gid) < 0) {
-				perror("setgid");
-				exit(1);
-			}
-			/* Initialize the group list. */
-			if (initgroups(pw->pw_name, pw->pw_gid) < 0) {
-				perror("initgroups");
-				exit(1);
-			}
-			endgrent();
-
-			/* Permanently switch to the desired uid. */
-			permanently_set_uid(pw->pw_uid);
-		}
-		if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
-			fatal("Failed to set uids to %d.", (int) pw->pw_uid);
-	}
-	/*
-	 * Get the shell from the password data.  An empty shell field is
-	 * legal, and means /bin/sh.
-	 */
-	shell = (pw->pw_shell[0] == '\0') ? _PATH_BSHELL : pw->pw_shell;
-
-#ifdef AFS
-	/* Try to get AFS tokens for the local cell. */
-	if (k_hasafs()) {
-		char cell[64];
-
-		if (k_afs_cell_of_file(pw->pw_dir, cell, sizeof(cell)) == 0)
-			krb_afslog(cell, 0);
-
-		krb_afslog(0, 0);
-	}
-#endif /* AFS */
-
-	/* Initialize the environment. */
-	envsize = 100;
-	env = xmalloc(envsize * sizeof(char *));
-	env[0] = NULL;
-
-	if (!options.use_login) {
-		/* Set basic environment. */
-		child_set_env(&env, &envsize, "USER", pw->pw_name);
-		child_set_env(&env, &envsize, "LOGNAME", pw->pw_name);
-		child_set_env(&env, &envsize, "HOME", pw->pw_dir);
-		child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
-
-		snprintf(buf, sizeof buf, "%.200s/%.50s",
-			 _PATH_MAILDIR, pw->pw_name);
-		child_set_env(&env, &envsize, "MAIL", buf);
-
-		/* Normal systems set SHELL by default. */
-		child_set_env(&env, &envsize, "SHELL", shell);
-	}
-	if (getenv("TZ"))
-		child_set_env(&env, &envsize, "TZ", getenv("TZ"));
-
-	/* Set custom environment options from RSA authentication. */
-	while (custom_environment) {
-		struct envstring *ce = custom_environment;
-		char *s = ce->s;
-		int i;
-		for (i = 0; s[i] != '=' && s[i]; i++);
-		if (s[i] == '=') {
-			s[i] = 0;
-			child_set_env(&env, &envsize, s, s + i + 1);
-		}
-		custom_environment = ce->next;
-		xfree(ce->s);
-		xfree(ce);
-	}
-
-	snprintf(buf, sizeof buf, "%.50s %d %d",
-		 get_remote_ipaddr(), get_remote_port(), get_local_port());
-	child_set_env(&env, &envsize, "SSH_CLIENT", buf);
-
-	if (ttyname)
-		child_set_env(&env, &envsize, "SSH_TTY", ttyname);
-	if (term)
-		child_set_env(&env, &envsize, "TERM", term);
-	if (display)
-		child_set_env(&env, &envsize, "DISPLAY", display);
-
-#ifdef _AIX
-	{
-           char *authstate,*krb5cc;
-
-	   if ((authstate = getenv("AUTHSTATE")) != NULL)
-		 child_set_env(&env,&envsize,"AUTHSTATE",authstate);
-
-	   if ((krb5cc = getenv("KRB5CCNAME")) != NULL)
-		 child_set_env(&env,&envsize,"KRB5CCNAME",krb5cc);
-	}
-#endif
-
-#ifdef KRB4
-	{
-		extern char *ticket;
-
-		if (ticket)
-			child_set_env(&env, &envsize, "KRBTKFILE", ticket);
-	}
-#endif /* KRB4 */
-
-#ifdef USE_PAM
-	/* Pull in any environment variables that may have been set by PAM. */
-	do_pam_environment(&env, &envsize);
-#endif /* USE_PAM */
-
-	if (xauthfile)
-		child_set_env(&env, &envsize, "XAUTHORITY", xauthfile);
-
-	if (auth_get_socket_name() != NULL)
-		child_set_env(&env, &envsize, SSH_AUTHSOCKET_ENV_NAME,
-			      auth_get_socket_name());
-
-	read_environment_file(&env,&envsize,"/etc/environment");
-
-	/* read $HOME/.ssh/environment. */
-	if (!options.use_login) {
-		snprintf(buf, sizeof buf, "%.200s/.ssh/environment", pw->pw_dir);
-		read_environment_file(&env, &envsize, buf);
-	}
-	if (debug_flag) {
-		/* dump the environment */
-		fprintf(stderr, "Environment:\n");
-		for (i = 0; env[i]; i++)
-			fprintf(stderr, "  %.200s\n", env[i]);
-	}
-	/*
-	 * Close the connection descriptors; note that this is the child, and
-	 * the server will still have the socket open, and it is important
-	 * that we do not shutdown it.  Note that the descriptors cannot be
-	 * closed before building the environment, as we call
-	 * get_remote_ipaddr there.
-	 */
-	if (packet_get_connection_in() == packet_get_connection_out())
-		close(packet_get_connection_in());
-	else {
-		close(packet_get_connection_in());
-		close(packet_get_connection_out());
-	}
-	/*
-	 * Close all descriptors related to channels.  They will still remain
-	 * open in the parent.
-	 */
-	/* XXX better use close-on-exec? -markus */
-	channel_close_all();
-
-	/*
-	 * Close any extra file descriptors.  Note that there may still be
-	 * descriptors left by system functions.  They will be closed later.
-	 */
-	endpwent();
-
-	/*
-	 * Close any extra open file descriptors so that we don\'t have them
-	 * hanging around in clients.  Note that we want to do this after
-	 * initgroups, because at least on Solaris 2.3 it leaves file
-	 * descriptors open.
-	 */
-	for (i = 3; i < 64; i++)
-		close(i);
-
-	/* Change current directory to the user\'s home directory. */
-	if (chdir(pw->pw_dir) < 0)
-		fprintf(stderr, "Could not chdir to home directory %s: %s\n",
-			pw->pw_dir, strerror(errno));
-
-	/*
-	 * Must take new environment into use so that .ssh/rc, /etc/sshrc and
-	 * xauth are run in the proper environment.
-	 */
-	environ = env;
-
-	/*
-	 * Run $HOME/.ssh/rc, /etc/sshrc, or xauth (whichever is found first
-	 * in this order).
-	 */
-	if (!options.use_login) {
-		if (stat(SSH_USER_RC, &st) >= 0) {
-			if (debug_flag)
-				fprintf(stderr, "Running /bin/sh %s\n", SSH_USER_RC);
-
-			f = popen("/bin/sh " SSH_USER_RC, "w");
-			if (f) {
-				if (auth_proto != NULL && auth_data != NULL)
-					fprintf(f, "%s %s\n", auth_proto, auth_data);
-				pclose(f);
-			} else
-				fprintf(stderr, "Could not run %s\n", SSH_USER_RC);
-		} else if (stat(SSH_SYSTEM_RC, &st) >= 0) {
-			if (debug_flag)
-				fprintf(stderr, "Running /bin/sh %s\n", SSH_SYSTEM_RC);
-
-			f = popen("/bin/sh " SSH_SYSTEM_RC, "w");
-			if (f) {
-				if (auth_proto != NULL && auth_data != NULL)
-					fprintf(f, "%s %s\n", auth_proto, auth_data);
-				pclose(f);
-			} else
-				fprintf(stderr, "Could not run %s\n", SSH_SYSTEM_RC);
-		}
-#ifdef XAUTH_PATH
-		else {
-			/* Add authority data to .Xauthority if appropriate. */
-			if (auth_proto != NULL && auth_data != NULL) {
-				if (debug_flag)
-					fprintf(stderr, "Running %.100s add %.100s %.100s %.100s\n",
-						XAUTH_PATH, display, auth_proto, auth_data);
-
-				f = popen(XAUTH_PATH " -q -", "w");
-				if (f) {
-					fprintf(f, "add %s %s %s\n", display, auth_proto, auth_data);
-					pclose(f);
-				} else
-					fprintf(stderr, "Could not run %s -q -\n", XAUTH_PATH);
-			}
-		}
-#endif /* XAUTH_PATH */
-
-		/* Get the last component of the shell name. */
-		cp = strrchr(shell, '/');
-		if (cp)
-			cp++;
-		else
-			cp = shell;
-	}
-	/*
-	 * If we have no command, execute the shell.  In this case, the shell
-	 * name to be passed in argv[0] is preceded by '-' to indicate that
-	 * this is a login shell.
-	 */
-	if (!command) {
-		if (!options.use_login) {
-			char buf[256];
-
-			/*
-			 * Check for mail if we have a tty and it was enabled
-			 * in server options.
-			 */
-			if (ttyname && options.check_mail) {
-				char *mailbox;
-				struct stat mailstat;
-				mailbox = getenv("MAIL");
-				if (mailbox != NULL) {
-					if (stat(mailbox, &mailstat) != 0 || mailstat.st_size == 0)
-						printf("No mail.\n");
-					else if (mailstat.st_mtime < mailstat.st_atime)
-						printf("You have mail.\n");
-					else
-						printf("You have new mail.\n");
-				}
-			}
-			/* Start the shell.  Set initial character to '-'. */
-			buf[0] = '-';
-			strncpy(buf + 1, cp, sizeof(buf) - 1);
-			buf[sizeof(buf) - 1] = 0;
-
-			/* Execute the shell. */
-			argv[0] = buf;
-			argv[1] = NULL;
-			execve(shell, argv, env);
-
-			/* Executing the shell failed. */
-			perror(shell);
-			exit(1);
-
-		} else {
-			/* Launch login(1). */
-
-			execl(LOGIN_PROGRAM, "login", "-h", get_remote_ipaddr(),
-			      "-p", "-f", "--", pw->pw_name, NULL);
-
-			/* Login couldn't be executed, die. */
-
-			perror("login");
-			exit(1);
-		}
-	}
-	/*
-	 * Execute the command using the user's shell.  This uses the -c
-	 * option to execute the command.
-	 */
-	argv[0] = (char *) cp;
-	argv[1] = "-c";
-	argv[2] = (char *) command;
-	argv[3] = NULL;
-	execve(shell, argv, env);
-	perror(shell);
-	exit(1);
-}