- (djm) Pick up LOGIN_PROGRAM from environment or PATH if not set by headers
 - (djm) OpenBSD CVS updates:
   - deraadt@cvs.openbsd.org 2000/08/18 20:07:23
     [ssh.c]
     accept remsh as a valid name as well; roman@buildpoint.com
   - deraadt@cvs.openbsd.org 2000/08/18 20:17:13
     [deattack.c crc32.c packet.c]
     rename crc32() to ssh_crc32() to avoid zlib name clash.  do not move to
     libz crc32 function yet, because it has ugly "long"'s in it;
     oneill@cs.sfu.ca
   - deraadt@cvs.openbsd.org 2000/08/18 20:26:08
     [scp.1 scp.c]
     -S prog support; tv@debian.org
   - deraadt@cvs.openbsd.org 2000/08/18 20:50:07
     [scp.c]
     knf
   - deraadt@cvs.openbsd.org 2000/08/18 20:57:33
     [log-client.c]
     shorten
   - markus@cvs.openbsd.org  2000/08/19 12:48:11
     [channels.c channels.h clientloop.c ssh.c ssh.h]
     support for ~. in ssh2
   - deraadt@cvs.openbsd.org 2000/08/19 15:29:40
     [crc32.h]
     proper prototype
   - markus@cvs.openbsd.org  2000/08/19 15:34:44
     [authfd.c authfd.h key.c key.h ssh-add.1 ssh-add.c ssh-agent.1]
     [ssh-agent.c ssh-keygen.c sshconnect1.c sshconnect2.c Makefile]
     [fingerprint.c fingerprint.h]
     add SSH2/DSA support to the agent and some other DSA related cleanups.
     (note that we cannot talk to ssh.com's ssh2 agents)
   - markus@cvs.openbsd.org  2000/08/19 15:55:52
     [channels.c channels.h clientloop.c]
     more ~ support for ssh2
   - markus@cvs.openbsd.org  2000/08/19 16:21:19
     [clientloop.c]
     oops
   - millert@cvs.openbsd.org 2000/08/20 12:25:53
     [session.c]
     We have to stash the result of get_remote_name_or_ip() before we
     close our socket or getpeername() will get EBADF and the process
     will exit.  Only a problem for "UseLogin yes".
   - millert@cvs.openbsd.org 2000/08/20 12:30:59
     [session.c]
     Only check /etc/nologin if "UseLogin no" since login(1) may have its
     own policy on determining who is allowed to login when /etc/nologin
     is present.  Also use the _PATH_NOLOGIN define.
   - millert@cvs.openbsd.org 2000/08/20 12:42:43
     [auth1.c auth2.c session.c ssh.c]
     Add calls to setusercontext() and login_get*().  We basically call
     setusercontext() in most places where previously we did a setlogin().
     Add default login.conf file and put root in the "daemon" login class.
   - millert@cvs.openbsd.org 2000/08/21 10:23:31
     [session.c]
     Fix incorrect PATH setting; noted by Markus.
diff --git a/ChangeLog b/ChangeLog
index 4463cf2..9d3dd02 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -3,6 +3,61 @@
    Avoids "scp never exits" problem. Reports from Lutz Jaenicke 
    <Lutz.Jaenicke@aet.TU-Cottbus.DE> and Tamito KAJIYAMA 
    <kajiyama@grad.sccs.chukyo-u.ac.jp>
+ - (djm) Pick up LOGIN_PROGRAM from environment or PATH if not set by headers
+ - (djm) OpenBSD CVS updates:
+   - deraadt@cvs.openbsd.org 2000/08/18 20:07:23
+     [ssh.c]
+     accept remsh as a valid name as well; roman@buildpoint.com
+   - deraadt@cvs.openbsd.org 2000/08/18 20:17:13
+     [deattack.c crc32.c packet.c]
+     rename crc32() to ssh_crc32() to avoid zlib name clash.  do not move to
+     libz crc32 function yet, because it has ugly "long"'s in it;
+     oneill@cs.sfu.ca
+   - deraadt@cvs.openbsd.org 2000/08/18 20:26:08
+     [scp.1 scp.c]
+     -S prog support; tv@debian.org
+   - deraadt@cvs.openbsd.org 2000/08/18 20:50:07
+     [scp.c]
+     knf
+   - deraadt@cvs.openbsd.org 2000/08/18 20:57:33
+     [log-client.c]
+     shorten
+   - markus@cvs.openbsd.org  2000/08/19 12:48:11
+     [channels.c channels.h clientloop.c ssh.c ssh.h]
+     support for ~. in ssh2
+   - deraadt@cvs.openbsd.org 2000/08/19 15:29:40
+     [crc32.h]
+     proper prototype
+   - markus@cvs.openbsd.org  2000/08/19 15:34:44
+     [authfd.c authfd.h key.c key.h ssh-add.1 ssh-add.c ssh-agent.1] 
+     [ssh-agent.c ssh-keygen.c sshconnect1.c sshconnect2.c Makefile] 
+     [fingerprint.c fingerprint.h]
+     add SSH2/DSA support to the agent and some other DSA related cleanups.
+     (note that we cannot talk to ssh.com's ssh2 agents)
+   - markus@cvs.openbsd.org  2000/08/19 15:55:52
+     [channels.c channels.h clientloop.c]
+     more ~ support for ssh2
+   - markus@cvs.openbsd.org  2000/08/19 16:21:19
+     [clientloop.c]
+     oops
+   - millert@cvs.openbsd.org 2000/08/20 12:25:53
+     [session.c]
+     We have to stash the result of get_remote_name_or_ip() before we
+     close our socket or getpeername() will get EBADF and the process
+     will exit.  Only a problem for "UseLogin yes".
+   - millert@cvs.openbsd.org 2000/08/20 12:30:59
+     [session.c]
+     Only check /etc/nologin if "UseLogin no" since login(1) may have its
+     own policy on determining who is allowed to login when /etc/nologin
+     is present.  Also use the _PATH_NOLOGIN define.
+   - millert@cvs.openbsd.org 2000/08/20 12:42:43
+     [auth1.c auth2.c session.c ssh.c]
+     Add calls to setusercontext() and login_get*().  We basically call
+     setusercontext() in most places where previously we did a setlogin().
+     Add default login.conf file and put root in the "daemon" login class.
+   - millert@cvs.openbsd.org 2000/08/21 10:23:31
+     [session.c]
+     Fix incorrect PATH setting; noted by Markus.
 
 20000818
  - (djm) OpenBSD CVS changes:
diff --git a/Makefile.in b/Makefile.in
index ff34c49..4ceef70 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -34,7 +34,7 @@
 
 TARGETS=ssh sshd ssh-add ssh-keygen ssh-agent scp $(EXTRA_TARGETS)
 
-LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o fingerprint.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o util.o uuencode.o xmalloc.o 
+LIBSSH_OBJS=atomicio.o authfd.o authfile.o bufaux.o buffer.o canohost.o channels.o cipher.o compat.o compress.o crc32.o deattack.o dispatch.o dsa.o hmac.o hostfile.o key.o kex.o log.o match.o mpaux.o nchan.o packet.o radix.o entropy.o readpass.o rsa.o tildexpand.o ttymodes.o uidswap.o util.o uuencode.o xmalloc.o 
 
 LIBOPENBSD_COMPAT_OBJS=bsd-arc4random.o bsd-base64.o bsd-bindresvport.o bsd-daemon.o bsd-inet_aton.o bsd-inet_ntoa.o bsd-misc.o bsd-mktemp.o bsd-rresvport.o bsd-setenv.o bsd-sigaction.o bsd-snprintf.o bsd-strlcat.o bsd-strlcpy.o bsd-strsep.o fake-getaddrinfo.o fake-getnameinfo.o next-posix.o
 
diff --git a/TODO b/TODO
index 1d46ae1..ecfd21e 100644
--- a/TODO
+++ b/TODO
@@ -14,3 +14,5 @@
   underlying shell.
 
 - utmp/wtmp logging does not work on NeXT
+
+- Complete Tru64 SIA support
diff --git a/acconfig.h b/acconfig.h
index 601dc16..eeee4ea 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -6,6 +6,13 @@
 
 @TOP@
 
+/* If your header files don't define LOGIN_PROGRAM, then use this (detected) */
+/* from environment and PATH */
+#undef LOGIN_PROGRAM_FALLBACK
+
+/* Define if your password has a pw_class field */
+#undef HAVE_PW_CLASS_IN_PASSWD
+
 /* Define if your socketpair() has bugs */
 #undef USE_PIPES
 
diff --git a/auth1.c b/auth1.c
index d8f2652..b043e8a 100644
--- a/auth1.c
+++ b/auth1.c
@@ -4,7 +4,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: auth1.c,v 1.2 2000/04/29 18:11:52 markus Exp $");
+RCSID("$OpenBSD: auth1.c,v 1.3 2000/08/20 18:42:40 millert Exp $");
 
 #include "xmalloc.h"
 #include "rsa.h"
@@ -480,6 +480,9 @@
 	pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
 	pwcopy.pw_uid = pw->pw_uid;
 	pwcopy.pw_gid = pw->pw_gid;
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+	pwcopy.pw_class = xstrdup(pw->pw_class);
+#endif
 	pwcopy.pw_dir = xstrdup(pw->pw_dir);
 	pwcopy.pw_shell = xstrdup(pw->pw_shell);
 	pw = &pwcopy;
diff --git a/auth2.c b/auth2.c
index 4926ff8..7769046 100644
--- a/auth2.c
+++ b/auth2.c
@@ -27,7 +27,7 @@
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 #include "includes.h"
-RCSID("$OpenBSD: auth2.c,v 1.12 2000/07/07 03:55:03 todd Exp $");
+RCSID("$OpenBSD: auth2.c,v 1.13 2000/08/20 18:42:40 millert Exp $");
 
 #include <openssl/dsa.h>
 #include <openssl/rsa.h>
@@ -411,6 +411,9 @@
 		copy->pw_passwd = xstrdup(pw->pw_passwd);
 		copy->pw_uid = pw->pw_uid;
 		copy->pw_gid = pw->pw_gid;
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+		copy->pw_class = xstrdup(pw->pw_class);
+#endif
 		copy->pw_dir = xstrdup(pw->pw_dir);
 		copy->pw_shell = xstrdup(pw->pw_shell);
 		authctxt->valid = 1;
diff --git a/authfd.c b/authfd.c
index a34e111..d9427d3 100644
--- a/authfd.c
+++ b/authfd.c
@@ -11,10 +11,13 @@
  *
  * Functions for connecting the local authentication agent.
  *
+ * SSH2 implementation,
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ *
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: authfd.c,v 1.24 2000/08/15 19:20:46 markus Exp $");
+RCSID("$OpenBSD: authfd.c,v 1.25 2000/08/19 21:34:42 markus Exp $");
 
 #include "ssh.h"
 #include "rsa.h"
@@ -29,6 +32,7 @@
 #include "key.h"
 #include "authfd.h"
 #include "kex.h"
+#include "dsa.h"
 
 /* helper */
 int	decode_reply(int type);
@@ -71,8 +75,7 @@
 }
 
 int
-ssh_request_reply(AuthenticationConnection *auth,
-    Buffer *request, Buffer *reply)
+ssh_request_reply(AuthenticationConnection *auth, Buffer *request, Buffer *reply)
 {
 	int l, len;
 	char buf[1024];
@@ -162,7 +165,6 @@
 
 	auth = xmalloc(sizeof(*auth));
 	auth->fd = sock;
-	buffer_init(&auth->packet);
 	buffer_init(&auth->identities);
 	auth->howmany = 0;
 
@@ -175,46 +177,57 @@
  */
 
 void
-ssh_close_authentication_connection(AuthenticationConnection *ac)
+ssh_close_authentication_connection(AuthenticationConnection *auth)
 {
-	buffer_free(&ac->packet);
-	buffer_free(&ac->identities);
-	close(ac->fd);
-	xfree(ac);
+	buffer_free(&auth->identities);
+	close(auth->fd);
+	xfree(auth);
 }
 
 /*
  * Returns the first authentication identity held by the agent.
- * Returns true if an identity is available, 0 otherwise.
- * The caller must initialize the integers before the call, and free the
- * comment after a successful call (before calling ssh_get_next_identity).
  */
 
-int
-ssh_get_first_identity(AuthenticationConnection *auth,
-    BIGNUM *e, BIGNUM *n, char **comment)
+Key *
+ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version)
 {
+	int type, code1 = 0, code2 = 0;
 	Buffer request;
-	int type;
+
+	switch(version){
+	case 1:
+		code1 = SSH_AGENTC_REQUEST_RSA_IDENTITIES;
+		code2 = SSH_AGENT_RSA_IDENTITIES_ANSWER;
+		break;
+	case 2:
+		code1 = SSH2_AGENTC_REQUEST_IDENTITIES;
+		code2 = SSH2_AGENT_IDENTITIES_ANSWER;
+		break;
+	default:
+		return NULL;
+	}
 
 	/*
 	 * Send a message to the agent requesting for a list of the
 	 * identities it can represent.
 	 */
 	buffer_init(&request);
-	buffer_put_char(&request, SSH_AGENTC_REQUEST_RSA_IDENTITIES);
+	buffer_put_char(&request, code1);
 
 	buffer_clear(&auth->identities);
 	if (ssh_request_reply(auth, &request, &auth->identities) == 0) {
 		buffer_free(&request);
-		return 0;
+		return NULL;
 	}
 	buffer_free(&request);
 
 	/* Get message type, and verify that we got a proper answer. */
 	type = buffer_get_char(&auth->identities);
-	if (type != SSH_AGENT_RSA_IDENTITIES_ANSWER)
+	if (type == SSH_AGENT_FAILURE) {
+		return NULL;
+	} else if (type != code2) {
 		fatal("Bad authentication reply message type: %d", type);
+	}
 
 	/* Get the number of entries in the response and check it for sanity. */
 	auth->howmany = buffer_get_int(&auth->identities);
@@ -223,43 +236,49 @@
 		    auth->howmany);
 
 	/* Return the first entry (if any). */
-	return ssh_get_next_identity(auth, e, n, comment);
+	return ssh_get_next_identity(auth, comment, version);
 }
 
-/*
- * Returns the next authentication identity for the agent.  Other functions
- * can be called between this and ssh_get_first_identity or two calls of this
- * function.  This returns 0 if there are no more identities.  The caller
- * must free comment after a successful return.
- */
-
-int
-ssh_get_next_identity(AuthenticationConnection *auth,
-    BIGNUM *e, BIGNUM *n, char **comment)
+Key *
+ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version)
 {
 	unsigned int bits;
+	unsigned char *blob;
+	unsigned int blen;
+	Key *key = NULL;
 
 	/* Return failure if no more entries. */
 	if (auth->howmany <= 0)
-		return 0;
+		return NULL;
 
 	/*
 	 * Get the next entry from the packet.  These will abort with a fatal
 	 * error if the packet is too short or contains corrupt data.
 	 */
-	bits = buffer_get_int(&auth->identities);
-	buffer_get_bignum(&auth->identities, e);
-	buffer_get_bignum(&auth->identities, n);
-	*comment = buffer_get_string(&auth->identities, NULL);
-
-	if (bits != BN_num_bits(n))
-		log("Warning: identity keysize mismatch: actual %d, announced %u",
-		    BN_num_bits(n), bits);
-
+	switch(version){
+	case 1:
+		key = key_new(KEY_RSA);
+		bits = buffer_get_int(&auth->identities);
+		buffer_get_bignum(&auth->identities, key->rsa->e);
+		buffer_get_bignum(&auth->identities, key->rsa->n);
+		*comment = buffer_get_string(&auth->identities, NULL);
+		if (bits != BN_num_bits(key->rsa->n))
+			log("Warning: identity keysize mismatch: actual %d, announced %u",
+			    BN_num_bits(key->rsa->n), bits);
+		break;
+	case 2:
+		blob = buffer_get_string(&auth->identities, &blen);
+		*comment = buffer_get_string(&auth->identities, NULL);
+		key = dsa_key_from_blob(blob, blen);
+		xfree(blob);
+		break;
+	default:
+		return NULL;
+		break;
+	}
 	/* Decrement the number of remaining entries. */
 	auth->howmany--;
-
-	return 1;
+	return key;
 }
 
 /*
@@ -272,7 +291,7 @@
 
 int
 ssh_decrypt_challenge(AuthenticationConnection *auth,
-    BIGNUM* e, BIGNUM *n, BIGNUM *challenge,
+    Key* key, BIGNUM *challenge,
     unsigned char session_id[16],
     unsigned int response_type,
     unsigned char response[16])
@@ -282,15 +301,17 @@
 	int i;
 	int type;
 
-	if (response_type == 0)
-		fatal("Compatibility with ssh protocol version "
-		    "1.0 no longer supported.");
-
+	if (key->type != KEY_RSA)
+		return 0;
+	if (response_type == 0) {
+		log("Compatibility with ssh protocol version 1.0 no longer supported.");
+		return 0;
+	}
 	buffer_init(&buffer);
 	buffer_put_char(&buffer, SSH_AGENTC_RSA_CHALLENGE);
-	buffer_put_int(&buffer, BN_num_bits(n));
-	buffer_put_bignum(&buffer, e);
-	buffer_put_bignum(&buffer, n);
+	buffer_put_int(&buffer, BN_num_bits(key->rsa->n));
+	buffer_put_bignum(&buffer, key->rsa->e);
+	buffer_put_bignum(&buffer, key->rsa->n);
 	buffer_put_bignum(&buffer, challenge);
 	buffer_append(&buffer, (char *) session_id, 16);
 	buffer_put_int(&buffer, response_type);
@@ -318,6 +339,45 @@
 	return success;
 }
 
+/* ask agent to sign data, returns -1 on error, 0 on success */
+int
+ssh_agent_sign(AuthenticationConnection *auth,
+    Key *key,
+    unsigned char **sigp, int *lenp,
+    unsigned char *data, int datalen)
+{
+	Buffer msg;
+	unsigned char *blob;
+	unsigned int blen;
+	int type;
+	int ret = -1;
+
+	if (dsa_make_key_blob(key, &blob, &blen) == 0)
+		return -1;
+
+	buffer_init(&msg);
+	buffer_put_char(&msg, SSH2_AGENTC_SIGN_REQUEST);
+	buffer_put_string(&msg, blob, blen);
+	buffer_put_string(&msg, data, datalen);
+	xfree(blob);
+
+	if (ssh_request_reply(auth, &msg, &msg) == 0) {
+		buffer_free(&msg);
+		return -1;
+	}
+	type = buffer_get_char(&msg);
+	if (type == SSH_AGENT_FAILURE) {
+		log("Agent admitted failure to sign using the key.");
+	} else if (type != SSH2_AGENT_SIGN_RESPONSE) {
+		fatal("Bad authentication response: %d", type);
+	} else {
+		ret = 0;
+		*sigp = buffer_get_string(&msg, lenp);
+	}
+	buffer_free(&msg);
+	return ret;
+}
+
 /* Encode key for a message to the agent. */
 
 void
@@ -358,29 +418,29 @@
 int
 ssh_add_identity(AuthenticationConnection *auth, Key *key, const char *comment)
 {
-	Buffer buffer;
+	Buffer msg;
 	int type;
 
-	buffer_init(&buffer);
+	buffer_init(&msg);
 
 	switch (key->type) {
 	case KEY_RSA:
-		ssh_encode_identity_rsa(&buffer, key->rsa, comment);
+		ssh_encode_identity_rsa(&msg, key->rsa, comment);
 		break;
 	case KEY_DSA:
-		ssh_encode_identity_dsa(&buffer, key->dsa, comment);
+		ssh_encode_identity_dsa(&msg, key->dsa, comment);
 		break;
 	default:
-		buffer_free(&buffer);
+		buffer_free(&msg);
 		return 0;
 		break;
 	}
-	if (ssh_request_reply(auth, &buffer, &buffer) == 0) {
-		buffer_free(&buffer);
+	if (ssh_request_reply(auth, &msg, &msg) == 0) {
+		buffer_free(&msg);
 		return 0;
 	}
-	type = buffer_get_char(&buffer);
-	buffer_free(&buffer);
+	type = buffer_get_char(&msg);
+	buffer_free(&msg);
 	return decode_reply(type);
 }
 
@@ -390,23 +450,35 @@
  */
 
 int
-ssh_remove_identity(AuthenticationConnection *auth, RSA *key)
+ssh_remove_identity(AuthenticationConnection *auth, Key *key)
 {
-	Buffer buffer;
+	Buffer msg;
 	int type;
+	unsigned char *blob;
+	unsigned int blen;
 
-	buffer_init(&buffer);
-	buffer_put_char(&buffer, SSH_AGENTC_REMOVE_RSA_IDENTITY);
-	buffer_put_int(&buffer, BN_num_bits(key->n));
-	buffer_put_bignum(&buffer, key->e);
-	buffer_put_bignum(&buffer, key->n);
+	buffer_init(&msg);
 
-	if (ssh_request_reply(auth, &buffer, &buffer) == 0) {
-		buffer_free(&buffer);
+	if (key->type == KEY_RSA) {
+		buffer_put_char(&msg, SSH_AGENTC_REMOVE_RSA_IDENTITY);
+		buffer_put_int(&msg, BN_num_bits(key->rsa->n));
+		buffer_put_bignum(&msg, key->rsa->e);
+		buffer_put_bignum(&msg, key->rsa->n);
+	} else if (key->type == KEY_DSA) {
+		dsa_make_key_blob(key, &blob, &blen);
+		buffer_put_char(&msg, SSH2_AGENTC_REMOVE_IDENTITY);
+		buffer_put_string(&msg, blob, blen);
+		xfree(blob);
+	} else {
+		buffer_free(&msg);
 		return 0;
 	}
-	type = buffer_get_char(&buffer);
-	buffer_free(&buffer);
+	if (ssh_request_reply(auth, &msg, &msg) == 0) {
+		buffer_free(&msg);
+		return 0;
+	}
+	type = buffer_get_char(&msg);
+	buffer_free(&msg);
 	return decode_reply(type);
 }
 
@@ -416,20 +488,23 @@
  */
 
 int
-ssh_remove_all_identities(AuthenticationConnection *auth)
+ssh_remove_all_identities(AuthenticationConnection *auth, int version)
 {
-	Buffer buffer;
+	Buffer msg;
 	int type;
+	int code = (version==1) ?
+		SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES :
+		SSH2_AGENTC_REMOVE_ALL_IDENTITIES;
 
-	buffer_init(&buffer);
-	buffer_put_char(&buffer, SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES);
+	buffer_init(&msg);
+	buffer_put_char(&msg, code);
 
-	if (ssh_request_reply(auth, &buffer, &buffer) == 0) {
-		buffer_free(&buffer);
+	if (ssh_request_reply(auth, &msg, &msg) == 0) {
+		buffer_free(&msg);
 		return 0;
 	}
-	type = buffer_get_char(&buffer);
-	buffer_free(&buffer);
+	type = buffer_get_char(&msg);
+	buffer_free(&msg);
 	return decode_reply(type);
 }
 
diff --git a/authfd.h b/authfd.h
index 14b9bee..5819b91 100644
--- a/authfd.h
+++ b/authfd.h
@@ -13,7 +13,7 @@
  *
  */
 
-/* RCSID("$OpenBSD: authfd.h,v 1.9 2000/07/16 08:27:21 markus Exp $"); */
+/* RCSID("$OpenBSD: authfd.h,v 1.10 2000/08/19 21:34:43 markus Exp $"); */
 
 #ifndef AUTHFD_H
 #define AUTHFD_H
@@ -35,18 +35,16 @@
 #define SSH2_AGENT_IDENTITIES_ANSWER		12
 #define SSH2_AGENTC_SIGN_REQUEST		13
 #define SSH2_AGENT_SIGN_RESPONSE		14
-#define SSH2_AGENT_FAILURE			SSH_AGENT_FAILURE
-#define SSH2_AGENT_SUCCESS			SSH_AGENT_SUCCESS
 #define SSH2_AGENTC_ADD_IDENTITY		17
 #define SSH2_AGENTC_REMOVE_IDENTITY		18
 #define SSH2_AGENTC_REMOVE_ALL_IDENTITIES	19
 
 typedef struct {
 	int     fd;
-	Buffer  packet;
 	Buffer  identities;
 	int     howmany;
 }       AuthenticationConnection;
+
 /* Returns the number of the authentication fd, or -1 if there is none. */
 int     ssh_get_authentication_socket();
 
@@ -69,44 +67,48 @@
  * Closes the connection to the authentication agent and frees any associated
  * memory.
  */
-void    ssh_close_authentication_connection(AuthenticationConnection * ac);
+void    ssh_close_authentication_connection(AuthenticationConnection *auth);
 
 /*
- * Returns the first authentication identity held by the agent. Returns true
- * if an identity is available, 0 otherwise. The caller must initialize the
- * integers before the call, and free the comment after a successful call
- * (before calling ssh_get_next_identity).
+ * Returns the first authentication identity held by the agent or NULL if
+ * no identies are available. Caller must free comment and key.
+ * Note that you cannot mix calls with different versions.
  */
-int
-ssh_get_first_identity(AuthenticationConnection * connection,
-    BIGNUM * e, BIGNUM * n, char **comment);
+Key	*ssh_get_first_identity(AuthenticationConnection *auth, char **comment, int version);
 
 /*
  * Returns the next authentication identity for the agent.  Other functions
  * can be called between this and ssh_get_first_identity or two calls of this
- * function.  This returns 0 if there are no more identities.  The caller
- * must free comment after a successful return.
+ * function.  This returns NULL if there are no more identities.  The caller
+ * must free key and comment after a successful return.
+ */
+Key	*ssh_get_next_identity(AuthenticationConnection *auth, char **comment, int version);
+
+/*
+ * Requests the agent to decrypt the given challenge.  Returns true if the
+ * agent claims it was able to decrypt it.
  */
 int
-ssh_get_next_identity(AuthenticationConnection * connection,
-    BIGNUM * e, BIGNUM * n, char **comment);
-
-/* Requests the agent to decrypt the given challenge.  Returns true if
-   the agent claims it was able to decrypt it. */
-int
-ssh_decrypt_challenge(AuthenticationConnection * auth,
-    BIGNUM * e, BIGNUM * n, BIGNUM * challenge,
+ssh_decrypt_challenge(AuthenticationConnection *auth,
+    Key *key, BIGNUM * challenge,
     unsigned char session_id[16],
     unsigned int response_type,
     unsigned char response[16]);
 
+/* Requests the agent to sign data using key */
+int
+ssh_agent_sign(AuthenticationConnection *auth,
+    Key *key,
+    unsigned char **sigp, int *lenp,
+    unsigned char *data, int datalen);
+
 /*
  * Adds an identity to the authentication server.  This call is not meant to
  * be used by normal applications.  This returns true if the identity was
  * successfully added.
  */
 int
-ssh_add_identity(AuthenticationConnection * connection, Key *key,
+ssh_add_identity(AuthenticationConnection *auth, Key *key,
     const char *comment);
 
 /*
@@ -114,16 +116,13 @@
  * meant to be used by normal applications.  This returns true if the
  * identity was successfully added.
  */
-int     ssh_remove_identity(AuthenticationConnection * connection, RSA * key);
+int     ssh_remove_identity(AuthenticationConnection *auth, Key *key);
 
 /*
  * Removes all identities from the authentication agent.  This call is not
  * meant to be used by normal applications.  This returns true if the
  * operation was successful.
  */
-int     ssh_remove_all_identities(AuthenticationConnection * connection);
-
-/* Closes the connection to the authentication agent. */
-void    ssh_close_authentication(AuthenticationConnection * connection);
+int     ssh_remove_all_identities(AuthenticationConnection *auth, int version);
 
 #endif				/* AUTHFD_H */
diff --git a/channels.c b/channels.c
index ea39529..c77f6b9 100644
--- a/channels.c
+++ b/channels.c
@@ -17,7 +17,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: channels.c,v 1.64 2000/07/16 08:27:21 markus Exp $");
+RCSID("$OpenBSD: channels.c,v 1.66 2000/08/19 21:55:51 markus Exp $");
 
 #include "ssh.h"
 #include "packet.h"
@@ -244,6 +244,7 @@
 	c->cb_arg = NULL;
 	c->cb_event = 0;
 	c->dettach_user = NULL;
+	c->input_filter = NULL;
 	debug("channel %d: new [%s]", found, remote_name);
 	return found;
 }
@@ -665,7 +666,14 @@
 			}
 			return -1;
 		}
-		buffer_append(&c->input, buf, len);
+		if(c->input_filter != NULL) {
+			if (c->input_filter(c, buf, len) == -1) {
+				debug("filter stops channel %d", c->self);
+				chan_read_failed(c);
+			}
+		} else {
+			buffer_append(&c->input, buf, len);
+		}
 	}
 	return 1;
 }
@@ -2309,6 +2317,16 @@
 	}
 	c->dettach_user = NULL;
 }
+void   
+channel_register_filter(int id, channel_filter_fn *fn)
+{
+	Channel *c = channel_lookup(id);
+	if (c == NULL) {
+		log("channel_register_filter: %d: bad id", id);
+		return;
+	}
+	c->input_filter = fn;
+}
 
 void
 channel_set_fds(int id, int rfd, int wfd, int efd, int extusage)
diff --git a/channels.h b/channels.h
index 9629124..9acdf58 100644
--- a/channels.h
+++ b/channels.h
@@ -1,4 +1,4 @@
-/* RCSID("$OpenBSD: channels.h,v 1.14 2000/06/20 01:39:40 markus Exp $"); */
+/* RCSID("$OpenBSD: channels.h,v 1.16 2000/08/19 21:55:51 markus Exp $"); */
 
 #ifndef CHANNELS_H
 #define CHANNELS_H
@@ -21,9 +21,13 @@
  * 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);
+struct Channel;
+typedef struct Channel Channel;
 
-typedef struct Channel {
+typedef void channel_callback_fn(int id, void *arg);
+typedef int channel_filter_fn(struct Channel *c, char *buf, int len);
+
+struct Channel {
 	int     type;		/* channel type/state */
 	int     self;		/* my own channel identifier */
 	int     remote_id;	/* channel identifier for remote peer */
@@ -61,7 +65,10 @@
 	void	*cb_arg;
 	int	cb_event;
 	channel_callback_fn	*dettach_user;
-}       Channel;
+
+	/* filter */
+	channel_filter_fn	*input_filter;
+};
 
 #define CHAN_EXTENDED_IGNORE		0
 #define CHAN_EXTENDED_READ		1
@@ -73,6 +80,7 @@
 void	channel_request_start(int id, char *service, int wantconfirm);
 void	channel_register_callback(int id, int mtype, channel_callback_fn *fn, void *arg);
 void	channel_register_cleanup(int id, channel_callback_fn *fn);
+void	channel_register_filter(int id, channel_filter_fn *fn);
 void	channel_cancel_cleanup(int id);
 Channel	*channel_lookup(int id);
 
diff --git a/clientloop.c b/clientloop.c
index 67fa36d..d339e12 100644
--- a/clientloop.c
+++ b/clientloop.c
@@ -16,7 +16,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: clientloop.c,v 1.29 2000/07/16 08:27:21 markus Exp $");
+RCSID("$OpenBSD: clientloop.c,v 1.32 2000/08/19 22:21:19 markus Exp $");
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -29,6 +29,9 @@
 #include "channels.h"
 #include "dispatch.h"
 
+#include "buffer.h"
+#include "bufaux.h"
+
 /* Flag indicating that stdin should be redirected from /dev/null. */
 extern int stdin_null_flag;
 
@@ -60,6 +63,8 @@
 static int in_non_blocking_mode = 0;
 
 /* Common data for the client loop code. */
+static int quit_pending;	/* Set to non-zero to quit the client loop. */
+static int escape_char;		/* Escape character. */
 static int escape_pending;	/* Last character was the escape character */
 static int last_was_cr;		/* Last character was a newline. */
 static int exit_status;		/* Used to store the exit status of the command. */
@@ -67,13 +72,11 @@
 static Buffer stdin_buffer;	/* Buffer for stdin data. */
 static Buffer stdout_buffer;	/* Buffer for stdout data. */
 static Buffer stderr_buffer;	/* Buffer for stderr data. */
+static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
 static unsigned int buffer_high;/* Soft max buffer size. */
 static int max_fd;		/* Maximum file descriptor number in select(). */
 static int connection_in;	/* Connection to server (input). */
 static int connection_out;	/* Connection to server (output). */
-static unsigned long stdin_bytes, stdout_bytes, stderr_bytes;
-static int quit_pending;	/* Set to non-zero to quit the client loop. */
-static int escape_char;		/* Escape character. */
 
 
 void	client_init_dispatch(void);
@@ -379,17 +382,15 @@
 }
 
 void
-client_suspend_self()
+client_suspend_self(Buffer *bin, Buffer *bout, Buffer *berr)
 {
 	struct winsize oldws, newws;
 
 	/* Flush stdout and stderr buffers. */
-	if (buffer_len(&stdout_buffer) > 0)
-		atomicio(write, fileno(stdout), buffer_ptr(&stdout_buffer),
-		    buffer_len(&stdout_buffer));
-	if (buffer_len(&stderr_buffer) > 0)
-		atomicio(write, fileno(stderr), buffer_ptr(&stderr_buffer),
-		    buffer_len(&stderr_buffer));
+	if (buffer_len(bout) > 0)
+		atomicio(write, fileno(stdout), buffer_ptr(bout), buffer_len(bout));
+	if (buffer_len(berr) > 0)
+		atomicio(write, fileno(stderr), buffer_ptr(berr), buffer_len(berr));
 
 	leave_raw_mode();
 
@@ -397,9 +398,9 @@
 	 * Free (and clear) the buffer to reduce the amount of data that gets
 	 * written to swap.
 	 */
-	buffer_free(&stdin_buffer);
-	buffer_free(&stdout_buffer);
-	buffer_free(&stderr_buffer);
+	buffer_free(bin);
+	buffer_free(bout);
+	buffer_free(berr);
 
 	/* Save old window size. */
 	ioctl(fileno(stdin), TIOCGWINSZ, &oldws);
@@ -416,9 +417,9 @@
 		received_window_change_signal = 1;
 
 	/* OK, we have been continued by the user. Reinitialize buffers. */
-	buffer_init(&stdin_buffer);
-	buffer_init(&stdout_buffer);
-	buffer_init(&stderr_buffer);
+	buffer_init(bin);
+	buffer_init(bout);
+	buffer_init(berr);
 
 	enter_raw_mode();
 }
@@ -466,12 +467,155 @@
 	}
 }
 
+/* process the characters one by one */
+int
+process_escapes(Buffer *bin, Buffer *bout, Buffer *berr, char *buf, int len)
+{
+	char string[1024];
+	pid_t pid;
+	int bytes = 0;
+	unsigned int i;
+	unsigned char ch;
+	char *s;
+
+	for (i = 0; i < len; i++) {
+		/* Get one character at a time. */
+		ch = buf[i];
+
+		if (escape_pending) {
+			/* We have previously seen an escape character. */
+			/* Clear the flag now. */
+			escape_pending = 0;
+
+			/* Process the escaped character. */
+			switch (ch) {
+			case '.':
+				/* Terminate the connection. */
+				snprintf(string, sizeof string, "%c.\r\n", escape_char);
+				buffer_append(berr, string, strlen(string));
+				/*stderr_bytes += strlen(string); XXX*/
+
+				quit_pending = 1;
+				return -1;
+
+			case 'Z' - 64:
+				/* Suspend the program. */
+				/* Print a message to that effect to the user. */
+				snprintf(string, sizeof string, "%c^Z [suspend ssh]\r\n", escape_char);
+				buffer_append(berr, string, strlen(string));
+				/*stderr_bytes += strlen(string); XXX*/
+
+				/* Restore terminal modes and suspend. */
+				client_suspend_self(bin, bout, berr);
+
+				/* We have been continued. */
+				continue;
+
+			case '&':
+				/* XXX does not work yet with proto 2 */
+				if (compat20)
+					continue;
+				/*
+				 * Detach the program (continue to serve connections,
+				 * but put in background and no more new connections).
+				 */
+				if (!stdin_eof) {
+					/*
+					 * Sending SSH_CMSG_EOF alone does not always appear
+					 * to be enough.  So we try to send an EOF character
+					 * first.
+					 */
+					packet_start(SSH_CMSG_STDIN_DATA);
+					packet_put_string("\004", 1);
+					packet_send();
+					/* Close stdin. */
+					stdin_eof = 1;
+					if (buffer_len(bin) == 0) {
+						packet_start(SSH_CMSG_EOF);
+						packet_send();
+					}
+				}
+				/* Restore tty modes. */
+				leave_raw_mode();
+
+				/* Stop listening for new connections. */
+				channel_stop_listening();
+
+				printf("%c& [backgrounded]\n", escape_char);
+
+				/* Fork into background. */
+				pid = fork();
+				if (pid < 0) {
+					error("fork: %.100s", strerror(errno));
+					continue;
+				}
+				if (pid != 0) {	/* This is the parent. */
+					/* The parent just exits. */
+					exit(0);
+				}
+				/* The child continues serving connections. */
+				continue; /*XXX ? */
+
+			case '?':
+				snprintf(string, sizeof string,
+"%c?\r\n\
+Supported escape sequences:\r\n\
+~.  - terminate connection\r\n\
+~^Z - suspend ssh\r\n\
+~#  - list forwarded connections\r\n\
+~&  - background ssh (when waiting for connections to terminate)\r\n\
+~?  - this message\r\n\
+~~  - send the escape character by typing it twice\r\n\
+(Note that escapes are only recognized immediately after newline.)\r\n",
+					 escape_char);
+				buffer_append(berr, string, strlen(string));
+				continue;
+
+			case '#':
+				snprintf(string, sizeof string, "%c#\r\n", escape_char);
+				buffer_append(berr, string, strlen(string));
+				s = channel_open_message();
+				buffer_append(berr, s, strlen(s));
+				xfree(s);
+				continue;
+
+			default:
+				if (ch != escape_char) {
+					buffer_put_char(bin, escape_char);
+					bytes++;
+				}
+				/* Escaped characters fall through here */
+				break;
+			}
+		} else {
+			/*
+			 * The previous character was not an escape char. Check if this
+			 * is an escape.
+			 */
+			if (last_was_cr && ch == escape_char) {
+				/* It is. Set the flag and continue to next character. */
+				escape_pending = 1;
+				continue;
+			}
+		}
+
+		/*
+		 * Normal character.  Record whether it was a newline,
+		 * and append it to the buffer.
+		 */
+		last_was_cr = (ch == '\r' || ch == '\n');
+		buffer_put_char(bin, ch);
+		bytes++;
+	}
+	return bytes;
+}
+
 void
 client_process_input(fd_set * readset)
 {
+	int ret;
 	int len;
-	pid_t pid;
-	char buf[8192], *s;
+	char buf[8192];
 
 	/* Read input from stdin. */
 	if (FD_ISSET(fileno(stdin), readset)) {
@@ -513,145 +657,10 @@
 			 * Normal, successful read.  But we have an escape character
 			 * and have to process the characters one by one.
 			 */
-			unsigned int i;
-			for (i = 0; i < len; i++) {
-				unsigned char ch;
-				/* Get one character at a time. */
-				ch = buf[i];
-
-				if (escape_pending) {
-					/* We have previously seen an escape character. */
-					/* Clear the flag now. */
-					escape_pending = 0;
-					/* Process the escaped character. */
-					switch (ch) {
-					case '.':
-						/* Terminate the connection. */
-						snprintf(buf, sizeof buf, "%c.\r\n", escape_char);
-						buffer_append(&stderr_buffer, buf, strlen(buf));
-						stderr_bytes += strlen(buf);
-						quit_pending = 1;
-						return;
-
-					case 'Z' - 64:
-						/* Suspend the program. */
-						/* Print a message to that effect to the user. */
-						snprintf(buf, sizeof buf, "%c^Z\r\n", escape_char);
-						buffer_append(&stderr_buffer, buf, strlen(buf));
-						stderr_bytes += strlen(buf);
-
-						/* Restore terminal modes and suspend. */
-						client_suspend_self();
-
-						/* We have been continued. */
-						continue;
-
-					case '&':
-						/*
-						 * Detach the program (continue to serve connections,
-						 * but put in background and no more new connections).
-						 */
-						if (!stdin_eof) {
-							/*
-							 * Sending SSH_CMSG_EOF alone does not always appear
-							 * to be enough.  So we try to send an EOF character
-							 * first.
-							 */
-							packet_start(SSH_CMSG_STDIN_DATA);
-							packet_put_string("\004", 1);
-							packet_send();
-							/* Close stdin. */
-							stdin_eof = 1;
-							if (buffer_len(&stdin_buffer) == 0) {
-								packet_start(SSH_CMSG_EOF);
-								packet_send();
-							}
-						}
-						/* Restore tty modes. */
-						leave_raw_mode();
-
-						/* Stop listening for new connections. */
-						channel_stop_listening();
-
-						printf("%c& [backgrounded]\n", escape_char);
-
-						/* Fork into background. */
-						pid = fork();
-						if (pid < 0) {
-							error("fork: %.100s", strerror(errno));
-							continue;
-						}
-						if (pid != 0) {	/* This is the parent. */
-							/* The parent just exits. */
-							exit(0);
-						}
-						/* The child continues serving connections. */
-						continue;
-
-					case '?':
-						snprintf(buf, sizeof buf,
-"%c?\r\n\
-Supported escape sequences:\r\n\
-~.  - terminate connection\r\n\
-~^Z - suspend ssh\r\n\
-~#  - list forwarded connections\r\n\
-~&  - background ssh (when waiting for connections to terminate)\r\n\
-~?  - this message\r\n\
-~~  - send the escape character by typing it twice\r\n\
-(Note that escapes are only recognized immediately after newline.)\r\n",
-							 escape_char);
-						buffer_append(&stderr_buffer, buf, strlen(buf));
-						continue;
-
-					case '#':
-						snprintf(buf, sizeof buf, "%c#\r\n", escape_char);
-						buffer_append(&stderr_buffer, buf, strlen(buf));
-						s = channel_open_message();
-						buffer_append(&stderr_buffer, s, strlen(s));
-						xfree(s);
-						continue;
-
-					default:
-						if (ch != escape_char) {
-							/*
-							 * Escape character followed by non-special character.
-							 * Append both to the input buffer.
-							 */
-							buf[0] = escape_char;
-							buf[1] = ch;
-							buffer_append(&stdin_buffer, buf, 2);
-							stdin_bytes += 2;
-							continue;
-						}
-						/*
-						 * Note that escape character typed twice
-						 * falls through here; the latter gets processed
-						 * as a normal character below.
-						 */
-						break;
-					}
-				} else {
-					/*
-					 * The previous character was not an escape char. Check if this
-					 * is an escape.
-					 */
-					if (last_was_cr && ch == escape_char) {
-						/* It is. Set the flag and continue to next character. */
-						escape_pending = 1;
-						continue;
-					}
-				}
-
-				/*
-				 * Normal character.  Record whether it was a newline,
-				 * and append it to the buffer.
-				 */
-				last_was_cr = (ch == '\r' || ch == '\n');
-				buf[0] = ch;
-				buffer_append(&stdin_buffer, buf, 1);
-				stdin_bytes += 1;
-				continue;
-			}
+			ret = process_escapes(&stdin_buffer, &stdout_buffer, &stderr_buffer, buf, len);
+			if (ret == -1)
+				return;
+			stdout_bytes += ret;
 		}
 	}
 }
@@ -722,6 +731,15 @@
 	dispatch_run(DISPATCH_NONBLOCK, &quit_pending);
 }
 
+/* scan buf[] for '~' before sending data to the peer */
+
+int
+simple_escape_filter(Channel *c, char *buf, int len)
+{
+	/* XXX we assume c->extended is writeable */
+	return process_escapes(&c->input, &c->output, &c->extended, buf, len);
+}
+
 /*
  * Implements the interactive session with the server.  This is called after
  * the user has been authenticated, and a command has been started on the
@@ -730,7 +748,7 @@
  */
 
 int
-client_loop(int have_pty, int escape_char_arg)
+client_loop(int have_pty, int escape_char_arg, int ssh2_chan_id)
 {
 	extern Options options;
 	double start_time, total_time;
@@ -780,6 +798,9 @@
 	if (!compat20)
 		client_check_initial_eof_on_stdin();
 
+	if (compat20 && escape_char != -1)
+		channel_register_filter(ssh2_chan_id, simple_escape_filter);
+
 	/* Main loop of the client for the interactive session mode. */
 	while (!quit_pending) {
 		fd_set readset, writeset;
@@ -989,6 +1010,7 @@
 		/* XXX move to channels.c */
 		sock = x11_connect_display();
 		if (sock >= 0) {
+/*XXX MAXPACK */
 			id = channel_new("x11", SSH_CHANNEL_X11_OPEN,
 			    sock, sock, -1, 4*1024, 32*1024, 0,
 			    xstrdup("x11"));
diff --git a/configure.in b/configure.in
index f048b00..02287b8 100644
--- a/configure.in
+++ b/configure.in
@@ -14,6 +14,17 @@
 AC_PATH_PROG(ENT, ent)
 AC_SUBST(ENT)
 
+# Use LOGIN_PROGRAM from environment if possible
+if test ! -z "$LOGIN_PROGRAM" ; then
+	AC_DEFINE_UNQUOTED(LOGIN_PROGRAM_FALLBACK, "$LOGIN_PROGRAM")
+else
+	# Search for login
+	AC_PATH_PROG(LOGIN_PROGRAM_FALLBACK, login)
+	if test ! -z "$LOGIN_PROGRAM_FALLBACK" ; then
+		AC_DEFINE_UNQUOTED(LOGIN_PROGRAM_FALLBACK, "$LOGIN_PROGRAM_FALLBACK")
+	fi
+fi
+
 if test -z "$LD" ; then
 	LD=$CC
 fi
@@ -225,18 +236,18 @@
 fi
 
 # Checks for header files.
-AC_CHECK_HEADERS(bstring.h endian.h floatingpoint.h lastlog.h limits.h login.h maillock.h netdb.h netgroup.h netinet/in_systm.h paths.h poll.h pty.h shadow.h security/pam_appl.h sys/bitypes.h sys/bsdtty.h sys/cdefs.h sys/poll.h sys/select.h sys/stat.h sys/stropts.h sys/sysmacros.h sys/time.h sys/ttcompat.h stddef.h time.h ttyent.h usersec.h util.h utmp.h utmpx.h)
+AC_CHECK_HEADERS(bstring.h endian.h floatingpoint.h lastlog.h limits.h login.h login_cap.h maillock.h netdb.h netgroup.h netinet/in_systm.h paths.h poll.h pty.h shadow.h security/pam_appl.h sys/bitypes.h sys/bsdtty.h sys/cdefs.h sys/poll.h sys/select.h sys/stat.h sys/stropts.h sys/sysmacros.h sys/time.h sys/ttcompat.h stddef.h time.h ttyent.h usersec.h util.h utmp.h utmpx.h)
 
-# Checks for library functions.
-AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock freeaddrinfo gai_strerror getaddrinfo getnameinfo getrusage getttyent inet_aton inet_ntoa innetgr md5_crypt memmove mkdtemp on_exit openpty rresvport_af setenv seteuid setlogin setproctitle setreuid sigaction sigvec snprintf strerror strlcat strlcpy strsep vsnprintf vhangup _getpty __b64_ntop)
-dnl    checks for time functions
+dnl    Checks for library functions.
+AC_CHECK_FUNCS(arc4random atexit b64_ntop bcopy bindresvport_af clock freeaddrinfo gai_strerror getaddrinfo getnameinfo getrusage getttyent inet_aton inet_ntoa innetgr login_getcapbool md5_crypt memmove mkdtemp on_exit openpty rresvport_af setenv seteuid setlogin setproctitle setreuid sigaction sigvec snprintf strerror strlcat strlcpy strsep vsnprintf vhangup _getpty __b64_ntop)
+dnl    Checks for time functions
 AC_CHECK_FUNCS(gettimeofday time)
-dnl    checks for libutil functions
+dnl    Checks for libutil functions
 AC_CHECK_FUNCS(login logout updwtmp logwtmp)
-dnl    checks for utmp functions
+dnl    Checks for utmp functions
 AC_CHECK_FUNCS(entutent getutent getutid getutline pututline setutent)
 AC_CHECK_FUNCS(utmpname)
-dnl    checks for utmpx functions
+dnl    Checks for utmpx functions
 AC_CHECK_FUNCS(entutxent getutxent getutxid getutxline pututxline )
 AC_CHECK_FUNCS(setutxent utmpxname)
 
@@ -736,6 +747,22 @@
 	AC_DEFINE(HAVE___SS_FAMILY_IN_SS)
 fi
 
+AC_CACHE_CHECK([for pw_class field in struct passwd],
+		ac_cv_have_pw_class_in_struct_passwd, [
+	AC_TRY_COMPILE(
+		[
+#include <sys/types.h>
+#include <pwd.h>
+		],
+		[ struct passwd p s; p.pw_class = NULL; ],
+		[ ac_cv_have_pw_class_in_struct_passwd="yes" ],
+		[ ac_cv_have_pw_class_in_struct_passwd="no" ]
+	)
+])
+if test "x$ac_cv_have_pw_class_in_struct_passwd" = "xyes" ; then
+	AC_DEFINE(HAVE_PW_CLASS_IN_PASSWD)
+fi
+
 
 AC_CACHE_CHECK([if libc defines __progname], ac_cv_libc_defines___progname, [
 	AC_TRY_LINK([], 
diff --git a/crc32.c b/crc32.c
index 05a1af7..eb9b2dc 100644
--- a/crc32.c
+++ b/crc32.c
@@ -6,7 +6,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: crc32.c,v 1.5 2000/06/20 01:39:40 markus Exp $");
+RCSID("$OpenBSD: crc32.c,v 1.6 2000/08/19 02:17:12 deraadt Exp $");
 
 #include "crc32.h"
 
@@ -108,7 +108,7 @@
 /* Return a 32-bit CRC of the contents of the buffer. */
 
 unsigned int
-crc32(const unsigned char *s, unsigned int len)
+ssh_crc32(const unsigned char *s, unsigned int len)
 {
 	unsigned int i;
 	unsigned int crc32val;
diff --git a/crc32.h b/crc32.h
index 45495b4..2c84a80 100644
--- a/crc32.h
+++ b/crc32.h
@@ -13,7 +13,7 @@
  *
  */
 
-/* RCSID("$OpenBSD: crc32.h,v 1.6 2000/06/20 01:39:40 markus Exp $"); */
+/* RCSID("$OpenBSD: crc32.h,v 1.7 2000/08/19 21:29:40 deraadt Exp $"); */
 
 #ifndef CRC32_H
 #define CRC32_H
@@ -22,6 +22,6 @@
  * This computes a 32 bit CRC of the data in the buffer, and returns the CRC.
  * The polynomial used is 0xedb88320.
  */
-unsigned int crc32(const unsigned char *buf, unsigned int len);
+unsigned int ssh_crc32(const unsigned char *buf, unsigned int len);
 
 #endif				/* CRC32_H */
diff --git a/deattack.c b/deattack.c
index 7f95eca..72e7f1e 100644
--- a/deattack.c
+++ b/deattack.c
@@ -1,5 +1,5 @@
 /*
- * $OpenBSD: deattack.c,v 1.7 2000/06/20 01:39:41 markus Exp $
+ * $OpenBSD: deattack.c,v 1.8 2000/08/19 02:17:12 deraadt Exp $
  * Cryptographic attack detector for ssh - source code
  *
  * Copyright (c) 1998 CORE SDI S.A., Buenos Aires, Argentina.
@@ -50,7 +50,7 @@
 crc_update(u_int32_t *a, u_int32_t b)
 {
 	b ^= *a;
-	*a = crc32((unsigned char *) &b, sizeof(b));
+	*a = ssh_crc32((unsigned char *) &b, sizeof(b));
 }
 
 /* detect if a block is used in a particular pattern */
diff --git a/defines.h b/defines.h
index cd322f9..1fe74a9 100644
--- a/defines.h
+++ b/defines.h
@@ -246,8 +246,16 @@
 # endif /* RSH_PATH */
 #endif /* _PATH_RSH */
 
+#ifndef _PATH_NOLOGIN
+# define _PATH_NOLOGIN "/etc/nologin"
+#endif
+
 /* Macros */
 
+#if defined(HAVE_LOGIN_GETCAPBOOL) && defined(HAVE_LOGIN_CAP_H)
+# define HAVE_LOGIN_CAP
+#endif
+
 #ifndef MAX
 # define MAX(a,b) (((a)>(b))?(a):(b))
 # define MIN(a,b) (((a)<(b))?(a):(b))
diff --git a/fingerprint.c b/fingerprint.c
deleted file mode 100644
index 801f6a6..0000000
--- a/fingerprint.c
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (c) 1999 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("$OpenBSD: fingerprint.c,v 1.7 2000/06/20 01:39:41 markus Exp $");
-
-#include "ssh.h"
-#include "xmalloc.h"
-#include <openssl/md5.h>
-
-#define FPRINT "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x"
-
-/*
- * Generate key fingerprint in ascii format.
- * Based on ideas and code from Bjoern Groenvall <bg@sics.se>
- */
-char *
-fingerprint(BIGNUM *e, BIGNUM *n)
-{
-	static char retval[80];
-	MD5_CTX md;
-	unsigned char d[16];
-	unsigned char *buf;
-	int nlen, elen;
-
-	nlen = BN_num_bytes(n);
-	elen = BN_num_bytes(e);
-
-	buf = xmalloc(nlen + elen);
-
-	BN_bn2bin(n, buf);
-	BN_bn2bin(e, buf + nlen);
-
-	MD5_Init(&md);
-	MD5_Update(&md, buf, nlen + elen);
-	MD5_Final(d, &md);
-	snprintf(retval, sizeof(retval), FPRINT,
-	    d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7],
-	    d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]);
-	memset(buf, 0, nlen + elen);
-	xfree(buf);
-	return retval;
-}
diff --git a/fingerprint.h b/fingerprint.h
deleted file mode 100644
index 3d7bcb3..0000000
--- a/fingerprint.h
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (c) 1999 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.
- */
-/* RCSID("$OpenBSD: fingerprint.h,v 1.4 2000/06/20 01:39:41 markus Exp $"); */
-
-#ifndef FINGERPRINT_H
-#define FINGERPRINT_H
-char   *fingerprint(BIGNUM * e, BIGNUM * n);
-#endif
diff --git a/key.c b/key.c
index 764f1f2..f2ce75e 100644
--- a/key.c
+++ b/key.c
@@ -41,7 +41,7 @@
 #include "dsa.h"
 #include "uuencode.h"
 
-RCSID("$OpenBSD: key.c,v 1.9 2000/06/22 23:55:00 djm Exp $");
+RCSID("$OpenBSD: key.c,v 1.10 2000/08/19 21:34:43 markus Exp $");
 
 #define SSH_DSS "ssh-dss"
 
@@ -335,3 +335,15 @@
 	}
 	return "unknown";
 }
+unsigned int
+key_size(Key *k){
+	switch (k->type) {
+	case KEY_RSA:
+		return BN_num_bits(k->rsa->n);
+		break;
+	case KEY_DSA:
+		return BN_num_bits(k->dsa->p);
+		break;
+	}
+	return 0;
+}
diff --git a/log-client.c b/log-client.c
index 7e9fd61..7615a94 100644
--- a/log-client.c
+++ b/log-client.c
@@ -15,7 +15,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: log-client.c,v 1.9 2000/06/20 01:39:42 markus Exp $");
+RCSID("$OpenBSD: log-client.c,v 1.10 2000/08/19 02:57:33 deraadt Exp $");
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -57,6 +57,5 @@
 	if (level == SYSLOG_LEVEL_DEBUG)
 		fprintf(stderr, "debug: ");
 	vsnprintf(msgbuf, sizeof(msgbuf), fmt, args);
-	fprintf(stderr, "%s", msgbuf);
-	fprintf(stderr, "\r\n");
+	fprintf(stderr, "%s\r\n", msgbuf);
 }
diff --git a/packet.c b/packet.c
index 56080cb..8f93110 100644
--- a/packet.c
+++ b/packet.c
@@ -17,7 +17,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: packet.c,v 1.33 2000/06/20 01:39:43 markus Exp $");
+RCSID("$OpenBSD: packet.c,v 1.34 2000/08/19 02:17:12 deraadt Exp $");
 
 #include "xmalloc.h"
 #include "buffer.h"
@@ -479,8 +479,8 @@
 	buffer_consume(&outgoing_packet, 8 - padding);
 
 	/* Add check bytes. */
-	checksum = crc32((unsigned char *) buffer_ptr(&outgoing_packet),
-			 buffer_len(&outgoing_packet));
+	checksum = ssh_crc32((unsigned char *) buffer_ptr(&outgoing_packet),
+	    buffer_len(&outgoing_packet));
 	PUT_32BIT(buf, checksum);
 	buffer_append(&outgoing_packet, buf, 4);
 
@@ -764,7 +764,7 @@
 #endif
 
 	/* Compute packet checksum. */
-	checksum = crc32((unsigned char *) buffer_ptr(&incoming_packet),
+	checksum = ssh_crc32((unsigned char *) buffer_ptr(&incoming_packet),
 	    buffer_len(&incoming_packet) - 4);
 
 	/* Skip padding. */
diff --git a/scp.1 b/scp.1
index d2fa546..b7b2476 100644
--- a/scp.1
+++ b/scp.1
@@ -9,7 +9,7 @@
 .\"
 .\" Created: Sun May  7 00:14:37 1995 ylo
 .\"
-.\" $Id: scp.1,v 1.8 2000/07/11 07:31:38 djm Exp $
+.\" $Id: scp.1,v 1.9 2000/08/23 00:46:24 djm Exp $
 .\"
 .Dd September 25, 1999
 .Dt SCP 1
@@ -68,6 +68,11 @@
 .It Fl p
 Preserves modification times, access times, and modes from the
 original file.
+.It Fl S
+Name of program to use for the encrypted connection.
+The program must understand
+.Xr ssh 1
+options.
 .It Fl r
 Recursively copy entire directories.
 .It Fl v
@@ -98,6 +103,11 @@
 .Fl p
 is already reserved for preserving the times and modes of the file in
 .Xr rcp 1 .
+.It Fl S
+Name of program to use for the encrypted connection. The program must 
+understand
+.Xr ssh 1
+options.
 .It Fl 4
 Forces
 .Nm
diff --git a/scp.c b/scp.c
index 02feba9..d2e4224 100644
--- a/scp.c
+++ b/scp.c
@@ -11,6 +11,8 @@
 */
 
 /*
+ * Parts from:
+ *
  * Copyright (c) 1983, 1990, 1992, 1993, 1995
  *	The Regents of the University of California.  All rights reserved.
  *
@@ -45,7 +47,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: scp.c,v 1.33 2000/07/13 23:19:31 provos Exp $");
+RCSID("$OpenBSD: scp.c,v 1.35 2000/08/19 02:50:07 deraadt Exp $");
 
 #include "ssh.h"
 #include "xmalloc.h"
@@ -69,6 +71,7 @@
 
 /* Returns width of the terminal (for progress meter calculations). */
 int getttywidth(void);
+int do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc);
 
 /* Time a transfer started. */
 static struct timeval start;
@@ -111,6 +114,9 @@
 /* This is the port to use in contacting the remote site (is non-NULL). */
 char *port = NULL;
 
+/* This is the program to execute for the secured connection. ("ssh" or -S) */
+char *ssh_program = SSH_PROGRAM;
+
 /*
  * This function executes the given command as the specified user on the
  * given host.  This returns < 0 if execution fails, and >= 0 otherwise. This
@@ -118,13 +124,13 @@
  */
 
 int
-do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout)
+do_cmd(char *host, char *remuser, char *cmd, int *fdin, int *fdout, int argc)
 {
 	int pin[2], pout[2], reserved[2];
 
 	if (verbose_mode)
 		fprintf(stderr, "Executing: host %s, user %s, command %s\n",
-			host, remuser ? remuser : "(unspecified)", cmd);
+		    host, remuser ? remuser : "(unspecified)", cmd);
 
 	/*
 	 * Reserve two descriptors so that the real pipes won't get
@@ -144,7 +150,7 @@
 
 	/* For a child to execute the command on the remote host using ssh. */
 	if (fork() == 0) {
-		char *args[100];
+		char *args[100];	/* XXX careful */
 		unsigned int i;
 
 		/* Child. */
@@ -156,7 +162,7 @@
 		close(pout[1]);
 
 		i = 0;
-		args[i++] = SSH_PROGRAM;
+		args[i++] = ssh_program;
 		args[i++] = "-x";
 		args[i++] = "-oFallBackToRsh no";
 		if (IPv4)
@@ -189,8 +195,8 @@
 		args[i++] = cmd;
 		args[i++] = NULL;
 
-		execvp(SSH_PROGRAM, args);
-		perror(SSH_PROGRAM);
+		execvp(ssh_program, args);
+		perror(ssh_program);
 		exit(1);
 	}
 	/* Parent.  Close the other side, and return the local side. */
@@ -214,8 +220,6 @@
 	exit(255);
 }
 
-/* This stuff used to be in BSD rcp extern.h. */
-
 typedef struct {
 	int cnt;
 	char *buf;
@@ -231,8 +235,6 @@
 void run_err(const char *,...);
 void verifydir(char *);
 
-/* Stuff from BSD rcp.c continues. */
-
 struct passwd *pwd;
 uid_t userid;
 int errs, remin, remout;
@@ -260,7 +262,7 @@
 	extern int optind;
 
 	fflag = tflag = 0;
-	while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46")) != EOF)
+	while ((ch = getopt(argc, argv, "dfprtvBCc:i:P:q46S")) != EOF)
 		switch (ch) {
 		/* User-visible flags. */
 		case '4':
@@ -278,6 +280,10 @@
 		case 'r':
 			iamrecursive = 1;
 			break;
+		case 'S':
+			ssh_program = optarg;
+			break;
+
 		/* Server options. */
 		case 'd':
 			targetshouldbedirectory = 1;
@@ -343,8 +349,8 @@
 	remin = remout = -1;
 	/* Command to be executed on remote system using "ssh". */
 	(void) sprintf(cmd, "scp%s%s%s%s", verbose_mode ? " -v" : "",
-		       iamrecursive ? " -r" : "", pflag ? " -p" : "",
-		       targetshouldbedirectory ? " -d" : "");
+	    iamrecursive ? " -r" : "", pflag ? " -p" : "",
+	    targetshouldbedirectory ? " -d" : "");
 
 	(void) signal(SIGPIPE, lostconn);
 
@@ -401,9 +407,9 @@
 			if (*src == 0)
 				src = ".";
 			host = strchr(argv[i], '@');
-			len = strlen(SSH_PROGRAM) + strlen(argv[i]) +
-				strlen(src) + (tuser ? strlen(tuser) : 0) +
-				strlen(thost) + strlen(targ) + CMDNEEDS + 32;
+			len = strlen(ssh_program) + strlen(argv[i]) +
+			    strlen(src) + (tuser ? strlen(tuser) : 0) +
+			    strlen(thost) + strlen(targ) + CMDNEEDS + 32;
 			bp = xmalloc(len);
 			if (host) {
 				*host++ = 0;
@@ -414,19 +420,19 @@
 				else if (!okname(suser))
 					continue;
 				(void) sprintf(bp,
-					       "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'",
-					       SSH_PROGRAM, verbose_mode ? " -v" : "",
-					       suser, host, cmd, src,
-					       tuser ? tuser : "", tuser ? "@" : "",
-					       thost, targ);
+				    "%s%s -x -o'FallBackToRsh no' -n -l %s %s %s %s '%s%s%s:%s'",
+				     ssh_program, verbose_mode ? " -v" : "",
+				     suser, host, cmd, src,
+				     tuser ? tuser : "", tuser ? "@" : "",
+				     thost, targ);
 			} else {
 				host = cleanhostname(argv[i]);
 				(void) sprintf(bp,
-					       "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'",
-					       SSH_PROGRAM, verbose_mode ? " -v" : "",
-					       host, cmd, src,
-					       tuser ? tuser : "", tuser ? "@" : "",
-					       thost, targ);
+				    "exec %s%s -x -o'FallBackToRsh no' -n %s %s %s '%s%s%s:%s'",
+				     ssh_program, verbose_mode ? " -v" : "",
+				     host, cmd, src,
+				     tuser ? tuser : "", tuser ? "@" : "",
+				     thost, targ);
 			}
 			if (verbose_mode)
 				fprintf(stderr, "Executing: %s\n", bp);
@@ -438,8 +444,8 @@
 				bp = xmalloc(len);
 				(void) sprintf(bp, "%s -t %s", cmd, targ);
 				host = cleanhostname(thost);
-				if (do_cmd(host, tuser,
-					   bp, &remin, &remout) < 0)
+				if (do_cmd(host, tuser, bp, &remin,
+				    &remout, argc) < 0)
 					exit(1);
 				if (response() < 0)
 					exit(1);
@@ -461,11 +467,11 @@
 	for (i = 0; i < argc - 1; i++) {
 		if (!(src = colon(argv[i]))) {	/* Local to local. */
 			len = strlen(_PATH_CP) + strlen(argv[i]) +
-				     strlen(argv[argc - 1]) + 20;
+			    strlen(argv[argc - 1]) + 20;
 			bp = xmalloc(len);
 			(void) sprintf(bp, "exec %s%s%s %s %s", _PATH_CP,
-				       iamrecursive ? " -r" : "", pflag ? " -p" : "",
-				       argv[i], argv[argc - 1]);
+			    iamrecursive ? " -r" : "", pflag ? " -p" : "",
+			    argv[i], argv[argc - 1]);
 			if (verbose_mode)
 				fprintf(stderr, "Executing: %s\n", bp);
 			if (system(bp))
@@ -491,7 +497,7 @@
 		len = strlen(src) + CMDNEEDS + 20;
 		bp = xmalloc(len);
 		(void) sprintf(bp, "%s -f %s", cmd, src);
-		if (do_cmd(host, suser, bp, &remin, &remout) < 0) {
+		if (do_cmd(host, suser, bp, &remin, &remout, argc) < 0) {
 			(void) xfree(bp);
 			++errs;
 			continue;
@@ -548,8 +554,8 @@
 			 * versions expecting microseconds.
 			 */
 			(void) sprintf(buf, "T%lu 0 %lu 0\n",
-				       (unsigned long) stb.st_mtime,
-				       (unsigned long) stb.st_atime);
+			    (unsigned long) stb.st_mtime,
+			    (unsigned long) stb.st_atime);
 			(void) atomicio(write, remout, buf, strlen(buf));
 			if (response() < 0)
 				goto next;
@@ -626,8 +632,8 @@
 		last++;
 	if (pflag) {
 		(void) sprintf(path, "T%lu 0 %lu 0\n",
-			       (unsigned long) statp->st_mtime,
-			       (unsigned long) statp->st_atime);
+		    (unsigned long) statp->st_mtime,
+		    (unsigned long) statp->st_atime);
 		(void) atomicio(write, remout, path, strlen(path));
 		if (response() < 0) {
 			closedir(dirp);
@@ -635,8 +641,7 @@
 		}
 	}
 	(void) sprintf(path, "D%04o %d %.1024s\n",
-		       (unsigned int) (statp->st_mode & FILEMODEMASK),
-		       0, last);
+	    (unsigned int) (statp->st_mode & FILEMODEMASK), 0, last);
 	if (verbose_mode)
 		fprintf(stderr, "Entering directory: %s", path);
 	(void) atomicio(write, remout, path, strlen(path));
@@ -783,7 +788,7 @@
 			if (need > cursize)
 				namebuf = xmalloc(need);
 			(void) sprintf(namebuf, "%s%s%s", targ,
-				       *targ ? "/" : "", cp);
+			    *targ ? "/" : "", cp);
 			np = namebuf;
 		} else
 			np = targ;
@@ -954,8 +959,9 @@
 void
 usage()
 {
-	(void) fprintf(stderr,
-		       "usage: scp [-pqrvC46] [-P port] [-c cipher] [-i identity] f1 f2; or:\n       scp [options] f1 ... fn directory\n");
+	(void) fprintf(stderr, "usage: scp "
+	    "[-pqrvC46] [-S ssh] [-P port] [-c cipher] [-i identity] f1 f2; or:\n"
+	    "       scp [options] f1 ... fn directory\n");
 	exit(1);
 }
 
@@ -984,43 +990,6 @@
 	}
 }
 
-/* Stuff below is from BSD rcp util.c. */
-
-/*-
- * Copyright (c) 1992, 1993
- *	The Regents of the University of California.  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 the University of
- *	California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- *    may be used to endorse or promote products derived from this software
- *    without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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.
- *
- *	$OpenBSD: scp.c,v 1.33 2000/07/13 23:19:31 provos Exp $
- */
-
 char *
 colon(cp)
 	char *cp;
@@ -1097,7 +1066,7 @@
 		size = blksize;
 	else
 		size = blksize + (stb.st_blksize - blksize % stb.st_blksize) %
-			stb.st_blksize;
+		    stb.st_blksize;
 	if (bp->cnt >= size)
 		return (bp);
 	if (bp->buf == NULL)
@@ -1228,14 +1197,14 @@
 		i = remaining / 3600;
 		if (i)
 			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
-				 "%2d:", i);
+			    "%2d:", i);
 		else
 			snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
-				 "   ");
+			    "   ");
 		i = remaining % 3600;
 		snprintf(buf + strlen(buf), sizeof(buf) - strlen(buf),
-			 "%02d:%02d%s", i / 60, i % 60,
-			 (flag != 1) ? " ETA" : "    ");
+		    "%02d:%02d%s", i / 60, i % 60,
+		    (flag != 1) ? " ETA" : "    ");
 	}
 	atomicio(write, fileno(stdout), buf, strlen(buf));
 
diff --git a/session.c b/session.c
index d65b069..82096c3 100644
--- a/session.c
+++ b/session.c
@@ -8,7 +8,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: session.c,v 1.25 2000/08/17 20:06:34 markus Exp $");
+RCSID("$OpenBSD: session.c,v 1.29 2000/08/21 16:23:31 millert Exp $");
 
 #include "xmalloc.h"
 #include "ssh.h"
@@ -52,6 +52,10 @@
 # define S_UNOFILE_HARD	S_UNOFILE "_hard"
 #endif
 
+#ifdef HAVE_LOGIN_CAP
+#include <login_cap.h>
+#endif
+
 /* types */
 
 #define TTYSZ 64
@@ -117,6 +121,10 @@
 char *aixloginmsg;
 #endif /* WITH_AIXAUTHENTICATE */
 
+#ifdef HAVE_LOGIN_CAP
+static login_cap_t *lc;
+#endif
+
 /*
  * Remove local Xauthority file.
  */
@@ -200,6 +208,13 @@
 	s = session_new();
 	s->pw = pw;
 
+#ifdef HAVE_LOGIN_CAP
+	if ((lc = login_getclass(pw->pw_class)) == NULL) {
+		error("unable to get login class");
+		return;
+	}
+#endif
+
 	/*
 	 * We stay in this loop until the client requests to execute a shell
 	 * or a command.
@@ -650,7 +665,11 @@
 
 	/* Done if .hushlogin exists. */
 	snprintf(buf, sizeof(buf), "%.200s/.hushlogin", pw->pw_dir);
+#ifdef HAVE_LOGIN_CAP
+	if (login_getcapbool(lc, "hushlogin", 0) || stat(buf, &st) >= 0)
+#else
 	if (stat(buf, &st) >= 0)
+#endif
 		return;
 
 #ifdef USE_PAM
@@ -677,7 +696,12 @@
 			printf("Last login: %s from %s\r\n", time_string, buf);
 	}
 	if (options.print_motd) {
+#ifdef HAVE_LOGIN_CAP
+		f = fopen(login_getcapstr(lc, "welcome", "/etc/motd",
+		    "/etc/motd"), "r");
+#else
 		f = fopen("/etc/motd", "r");
+#endif
 		if (f) {
 			while (fgets(buf, sizeof(buf), f))
 				fputs(buf, stdout);
@@ -887,10 +911,10 @@
 	 const char *display, const char *auth_proto,
 	 const char *auth_data, const char *ttyname)
 {
-	const char *shell, *cp = NULL;
+	const char *shell, *hostname, *cp = NULL;
 	char buf[256];
 	char cmd[1024];
-	FILE *f;
+	FILE *f = NULL;
 	unsigned int envsize, i;
 	char **env;
 	extern char **environ;
@@ -905,24 +929,26 @@
 		options.use_login = 0;
 
 #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)
+	if (!options.use_login) {
+# ifdef HAVE_LOGIN_CAP
+		if (!login_getcapbool(lc, "ignorenologin", 0) && pw->pw_uid)
+			f = fopen(login_getcapstr(lc, "nologin", _PATH_NOLOGIN,
+			    _PATH_NOLOGIN), "r");
+# else /* HAVE_LOGIN_CAP */
+		if (pw->pw_uid)
+			f = fopen(_PATH_NOLOGIN, "r");
+# endif /* HAVE_LOGIN_CAP */
+		if (f) {
+			/* /etc/nologin exists.  Print its contents and exit. */
+			while (fgets(buf, sizeof(buf), f))
+				fputs(buf, stderr);
+			fclose(f);
 			exit(254);
+		}
 	}
 #endif /* USE_PAM */
 
-#ifndef HAVE_OSF_SIA
-	/* Set login name in the kernel. */
-	if (setlogin(pw->pw_name) < 0)
-		error("setlogin failed: %s", strerror(errno));
-#endif
-
-	/* Set uid, gid, and groups. */
+	/* Set login name, 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) {
@@ -943,10 +969,18 @@
 		}
 #else /* HAVE_OSF_SIA */
 		if (getuid() == 0 || geteuid() == 0) {
-#if defined(HAVE_GETUSERATTR)
+# ifdef HAVE_GETUSERATTR
 			set_limits_from_userattr(pw->pw_name);
-#endif /* defined(HAVE_GETUSERATTR) */
-
+# endif /* HAVE_GETUSERATTR */
+# ifdef HAVE_LOGIN_CAP
+			if (setusercontext(lc, pw, pw->pw_uid,
+			    (LOGIN_SETALL & ~LOGIN_SETPATH)) < 0) {
+				perror("unable to set user context");
+				exit(1);
+			}
+# else /* HAVE_LOGIN_CAP */
+			if (setlogin(pw->pw_name) < 0)
+				error("setlogin failed: %s", strerror(errno));
 			if (setgid(pw->pw_gid) < 0) {
 				perror("setgid");
 				exit(1);
@@ -957,38 +991,39 @@
 				exit(1);
 			}
 			endgrent();
-
-#ifdef WITH_IRIX_ARRAY
+#  ifdef WITH_IRIX_ARRAY
 			/* initialize array session */
 			if (newarraysess() != 0)
 				fatal("Failed to set up new array session: %.100s",
 				      strerror(errno));
-#endif /* WITH_IRIX_ARRAY */
-
-#ifdef WITH_IRIX_PROJECT
+#  endif /* WITH_IRIX_ARRAY */
+#  ifdef WITH_IRIX_PROJECT
 			/* initialize irix project info */
 			if ((projid = getdfltprojuser(pw->pw_name)) == -1) {
 			  debug("Failed to get project id, using projid 0");
 			  projid = 0;
 			}
-			
 			if (setprid(projid))
 			  fatal("Failed to initialize project %d for %s: %.100s",
 				(int)projid, pw->pw_name, strerror(errno));
-#endif /* WITH_IRIX_PROJECT */
-
+#  endif /* WITH_IRIX_PROJECT */
 			/* Permanently switch to the desired uid. */
 			permanently_set_uid(pw->pw_uid);
+# endif /* HAVE_LOGIN_CAP */
 		}
+#endif /* HAVE_OSF_SIA */
+
 		if (getuid() != pw->pw_uid || geteuid() != pw->pw_uid)
 			fatal("Failed to set uids to %d.", (int) pw->pw_uid);
-#endif /* HAVE_OSF_SIA */
 	}
 	/*
 	 * 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 HAVE_LOGIN_CAP
+	shell = login_getcapstr(lc, "shell", (char *)shell, (char *)shell);
+#endif
 
 #ifdef AFS
 	/* Try to get AFS tokens for the local cell. */
@@ -1012,7 +1047,12 @@
 		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);
+#ifdef HAVE_LOGIN_CAP
+		(void) setusercontext(lc, pw, pw->pw_uid, LOGIN_SETPATH);
+		child_set_env(&env, &envsize, "PATH", getenv("PATH"));
+#else
 		child_set_env(&env, &envsize, "PATH", _PATH_STDPATH);
+#endif
 
 		snprintf(buf, sizeof buf, "%.200s/%.50s",
 			 _PATH_MAILDIR, pw->pw_name);
@@ -1096,6 +1136,9 @@
 		for (i = 0; env[i]; i++)
 			fprintf(stderr, "  %.200s\n", env[i]);
 	}
+	/* we have to stash the hostname before we close our socket. */
+	if (options.use_login)
+		hostname = get_remote_name_or_ip();
 	/*
 	 * Close the connection descriptors; note that this is the child, and
 	 * the server will still have the socket open, and it is important
@@ -1132,9 +1175,14 @@
 		close(i);
 
 	/* Change current directory to the user\'s home directory. */
-	if (chdir(pw->pw_dir) < 0)
+	if (chdir(pw->pw_dir) < 0) {
 		fprintf(stderr, "Could not chdir to home directory %s: %s\n",
 			pw->pw_dir, strerror(errno));
+#ifdef HAVE_LOGIN_CAP
+		if (login_getcapbool(lc, "requirehome", 0))
+			exit(1);
+#endif
+	}
 
 	/*
 	 * Must take new environment into use so that .ssh/rc, /etc/sshrc and
@@ -1252,8 +1300,7 @@
 		} else {
 			/* Launch login(1). */
 
-			execl(LOGIN_PROGRAM, "login",
-			     "-h", get_remote_name_or_ip(),
+			execl(LOGIN_PROGRAM, "login", "-h", hostname,
 			     "-p", "-f", "--", pw->pw_name, NULL);
 
 			/* Login couldn't be executed, die. */
@@ -1790,6 +1837,8 @@
 void
 do_authenticated2(void)
 {
+	struct passwd *pw;
+
 	/*
 	 * Cancel the alarm we set to limit the time taken for
 	 * authentication.
@@ -1799,6 +1848,13 @@
 		close(startup_pipe);
 		startup_pipe = -1;
 	}
+#ifdef HAVE_LOGIN_CAP
+	pw = auth_get_user();
+	if ((lc = login_getclass(pw->pw_class)) == NULL) {
+		error("unable to get login class");
+		return;
+	}
+#endif
 	server_loop2();
 	if (xauthfile)
 		xauthfile_cleanup_proc(NULL);
diff --git a/ssh-add.1 b/ssh-add.1
index 5d2f249..d2d0c20 100644
--- a/ssh-add.1
+++ b/ssh-add.1
@@ -9,21 +9,21 @@
 .\"
 .\" Created: Sat Apr 22 23:55:14 1995 ylo
 .\"
-.\" $Id: ssh-add.1,v 1.13 2000/05/07 02:03:18 damien Exp $
+.\" $Id: ssh-add.1,v 1.14 2000/08/23 00:46:24 djm Exp $
 .\"
 .Dd September 25, 1999
 .Dt SSH-ADD 1
 .Os
 .Sh NAME
 .Nm ssh-add
-.Nd adds RSA identities for the authentication agent
+.Nd adds RSA or DSA identities for the authentication agent
 .Sh SYNOPSIS
 .Nm ssh-add
 .Op Fl lLdD
 .Op Ar
 .Sh DESCRIPTION
 .Nm
-adds RSA identities to the authentication agent,
+adds RSA or DSA identities to the authentication agent,
 .Xr ssh-agent 1 .
 When run without arguments, it adds the file
 .Pa $HOME/.ssh/identity .
@@ -63,6 +63,8 @@
 This is the default file added by
 .Nm
 when no other files have been specified.
+.It Pa $HOME/.ssh/id_dsa
+Contains the DSA authentication identity of the user.
 .Pp
 .Sh ENVIRONMENT
 .Bl -tag -width Ds
diff --git a/ssh-add.c b/ssh-add.c
index 482229c..a51f477 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -4,18 +4,21 @@
  *                    All rights reserved
  * Created: Thu Apr  6 00:52:24 1995 ylo
  * Adds an identity to the authentication server, or removes an identity.
+ *
+ * SSH2 implementation,
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh-add.c,v 1.18 2000/07/16 08:27:21 markus Exp $");
+RCSID("$OpenBSD: ssh-add.c,v 1.19 2000/08/19 21:34:43 markus Exp $");
 
+#include <openssl/evp.h>
 #include <openssl/rsa.h>
 #include <openssl/dsa.h>
 
 #include "rsa.h"
 #include "ssh.h"
 #include "xmalloc.h"
-#include "fingerprint.h"
 #include "key.h"
 #include "authfd.h"
 #include "authfile.h"
@@ -37,7 +40,7 @@
 		printf("Bad key file %s: %s\n", filename, strerror(errno));
 		return;
 	}
-	if (ssh_remove_identity(ac, public->rsa))
+	if (ssh_remove_identity(ac, public))
 		fprintf(stderr, "Identity removed: %s (%s)\n", filename, comment);
 	else
 		fprintf(stderr, "Could not remove identity: %s\n", filename);
@@ -45,11 +48,18 @@
 	xfree(comment);
 }
 
+/* Send a request to remove all identities. */
 void
 delete_all(AuthenticationConnection *ac)
 {
-	/* Send a request to remove all identities. */
-	if (ssh_remove_all_identities(ac))
+	int success = 1;
+
+	if (!ssh_remove_all_identities(ac, 1))
+		success = 0;
+	/* ignore error-code for ssh2 */
+	ssh_remove_all_identities(ac, 2);
+
+	if (success)
 		fprintf(stderr, "All identities removed.\n");
 	else
 		fprintf(stderr, "Failed to remove all identitities.\n");
@@ -96,6 +106,7 @@
 void
 add_file(AuthenticationConnection *ac, const char *filename)
 {
+	struct stat st;
 	Key *public;
 	Key *private;
 	char *saved_comment, *comment, *askpass = NULL;
@@ -104,6 +115,10 @@
 	int interactive = isatty(STDIN_FILENO);
 	int type = KEY_RSA;
 
+	if (stat(filename, &st) < 0) {
+		perror(filename);
+		exit(1);
+	}
 	/*
 	 * try to load the public key. right now this only works for RSA,
 	 * since DSA keys are fully encrypted
@@ -154,54 +169,40 @@
 			strlcpy(msg, "Bad passphrase, try again", sizeof msg);
 		}
 	}
-	xfree(saved_comment);
-
-	if (ssh_add_identity(ac, private, comment))
-		fprintf(stderr, "Identity added: %s (%s)\n", filename, comment);
+	xfree(comment);
+	if (ssh_add_identity(ac, private, saved_comment))
+		fprintf(stderr, "Identity added: %s (%s)\n", filename, saved_comment);
 	else
 		fprintf(stderr, "Could not add identity: %s\n", filename);
 	key_free(private);
-	xfree(comment);
+	xfree(saved_comment);
 }
 
 void
 list_identities(AuthenticationConnection *ac, int fp)
 {
-	BIGNUM *e, *n;
-	int status;
+	Key *key;
 	char *comment;
-	int had_identities;
+	int had_identities = 0;
+	int version;
 
-	e = BN_new();
-	n = BN_new();
-	had_identities = 0;
-	for (status = ssh_get_first_identity(ac, e, n, &comment);
-	     status;
-	     status = ssh_get_next_identity(ac, e, n, &comment)) {
-		unsigned int bits = BN_num_bits(n);
-		had_identities = 1;
-		if (fp) {
-			printf("%d %s %s\n", bits, fingerprint(e, n), comment);
-		} else {
-			char *ebuf, *nbuf;
-			ebuf = BN_bn2dec(e);
-			if (ebuf == NULL) {
-				error("list_identities: BN_bn2dec(e) failed.");
+	for (version = 1; version <= 2; version++) {
+		for (key = ssh_get_first_identity(ac, &comment, version);
+		     key != NULL;
+		     key = ssh_get_next_identity(ac, &comment, version)) {
+			had_identities = 1;
+			if (fp) {
+				printf("%d %s %s\n",
+				    key_size(key), key_fingerprint(key), comment);
 			} else {
-				nbuf = BN_bn2dec(n);
-				if (nbuf == NULL) {
-					error("list_identities: BN_bn2dec(n) failed.");
-				} else {
-					printf("%d %s %s %s\n", bits, ebuf, nbuf, comment);
-					free(nbuf);
-				}
-				free(ebuf);
+				if (!key_write(key, stdout))
+					fprintf(stderr, "key_write failed");
+				fprintf(stdout, " %s\n", comment);
 			}
+			key_free(key);
+			xfree(comment);
 		}
-		xfree(comment);
 	}
-	BN_clear_free(e);
-	BN_clear_free(n);
 	if (!had_identities)
 		printf("The agent has no identities.\n");
 }
@@ -225,6 +226,8 @@
 			__progname);
 		exit(1);
 	}
+        SSLeay_add_all_algorithms();
+
 	/* At first, get a connection to the authentication agent. */
 	ac = ssh_get_authentication_connection();
 	if (ac == NULL) {
diff --git a/ssh-agent.1 b/ssh-agent.1
index 47b1e5c..0aa1ecf 100644
--- a/ssh-agent.1
+++ b/ssh-agent.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: ssh-agent.1,v 1.13 2000/07/06 04:06:56 aaron Exp $
+.\" $OpenBSD: ssh-agent.1,v 1.14 2000/08/19 21:34:43 markus Exp $
 .\"
 .\"  -*- nroff -*-
 .\"
@@ -27,14 +27,15 @@
 .Oc
 .Sh DESCRIPTION
 .Nm
-is a program to hold private keys used for RSA authentication.
+is a program to hold private keys used for public key authentication
+(RSA, DSA).
 The idea is that
 .Nm
 is started in the beginning of an X-session or a login session, and
 all other windows or programs are started as clients to the ssh-agent
 program.
 Through use of environment variables the agent can be located
-and automatically used for RSA authentication when logging in to other
+and automatically used for authentication when logging in to other
 machines using
 .Xr ssh 1 .
 .Pp
@@ -128,7 +129,9 @@
 but is normally added to the agent using
 .Xr ssh-add 1
 at login time.
-.It Pa /tmp/ssh-XXXX/agent.<pid> ,
+.It Pa $HOME/.ssh/id_dsa
+Contains the DSA authentication identity of the user.
+.Pq Pa /tmp/ssh-XXXXXXXX/agent.<pid> ,
 Unix-domain sockets used to contain the connection to the
 authentication agent.
 These sockets should only be readable by the owner.
diff --git a/ssh-agent.c b/ssh-agent.c
index e8383b5..56b81a7 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: ssh-agent.c,v 1.32 2000/07/16 08:27:21 markus Exp $	*/
+/*	$OpenBSD: ssh-agent.c,v 1.33 2000/08/19 21:34:43 markus Exp $	*/
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -6,10 +6,13 @@
  *                    All rights reserved
  * Created: Wed Mar 29 03:46:59 1995 ylo
  * The authentication agent program.
+ *
+ * SSH2 implementation,
+ * Copyright (c) 2000 Markus Friedl. All rights reserved.
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh-agent.c,v 1.32 2000/07/16 08:27:21 markus Exp $");
+RCSID("$OpenBSD: ssh-agent.c,v 1.33 2000/08/19 21:34:43 markus Exp $");
 
 #include "ssh.h"
 #include "rsa.h"
@@ -20,11 +23,14 @@
 #include "getput.h"
 #include "mpaux.h"
 
+#include <openssl/evp.h>
 #include <openssl/md5.h>
 #include <openssl/dsa.h>
 #include <openssl/rsa.h>
 #include "key.h"
 #include "authfd.h"
+#include "dsa.h"
+#include "kex.h"
 
 typedef struct {
 	int fd;
@@ -39,12 +45,17 @@
 SocketEntry *sockets = NULL;
 
 typedef struct {
-	RSA *key;
+	Key *key;
 	char *comment;
 } Identity;
 
-unsigned int num_identities = 0;
-Identity *identities = NULL;
+typedef struct {
+	int nentries;
+	Identity *identities;
+} Idtab;
+
+/* private key table, one per protocol version */
+Idtab idtable[3];
 
 int max_fd = 0;
 
@@ -62,175 +73,243 @@
 #endif /* HAVE___PROGNAME */
 
 void
-process_request_identity(SocketEntry *e)
+idtab_init(void)
 {
+	int i;
+	for (i = 0; i <=2; i++){
+		idtable[i].identities = NULL;
+		idtable[i].nentries = 0;
+	}
+}
+
+/* return private key table for requested protocol version */
+Idtab *
+idtab_lookup(int version)
+{
+	if (version < 1 || version > 2)
+		fatal("internal error, bad protocol version %d", version);
+	return &idtable[version];
+}
+
+/* return matching private key for given public key */
+Key *
+lookup_private_key(Key *key, int *idx, int version)
+{
+	int i;
+	Idtab *tab = idtab_lookup(version);
+	for (i = 0; i < tab->nentries; i++) {
+		if (key_equal(key, tab->identities[i].key)) {
+			if (idx != NULL)
+				*idx = i;
+			return tab->identities[i].key;
+		}
+	}
+	return NULL;
+}
+
+/* send list of supported public keys to 'client' */
+void
+process_request_identities(SocketEntry *e, int version)
+{
+	Idtab *tab = idtab_lookup(version);
 	Buffer msg;
 	int i;
 
 	buffer_init(&msg);
-	buffer_put_char(&msg, SSH_AGENT_RSA_IDENTITIES_ANSWER);
-	buffer_put_int(&msg, num_identities);
-	for (i = 0; i < num_identities; i++) {
-		buffer_put_int(&msg, BN_num_bits(identities[i].key->n));
-		buffer_put_bignum(&msg, identities[i].key->e);
-		buffer_put_bignum(&msg, identities[i].key->n);
-		buffer_put_string(&msg, identities[i].comment,
-				  strlen(identities[i].comment));
+	buffer_put_char(&msg, (version == 1) ?
+	    SSH_AGENT_RSA_IDENTITIES_ANSWER : SSH2_AGENT_IDENTITIES_ANSWER);
+	buffer_put_int(&msg, tab->nentries);
+	for (i = 0; i < tab->nentries; i++) {
+		Identity *id = &tab->identities[i];
+		if (id->key->type == KEY_RSA) {
+			buffer_put_int(&msg, BN_num_bits(id->key->rsa->n));
+			buffer_put_bignum(&msg, id->key->rsa->e);
+			buffer_put_bignum(&msg, id->key->rsa->n);
+		} else {
+			unsigned char *blob;
+			unsigned int blen;
+			dsa_make_key_blob(id->key, &blob, &blen);
+			buffer_put_string(&msg, blob, blen);
+			xfree(blob);
+		}
+		buffer_put_cstring(&msg, id->comment);
 	}
 	buffer_put_int(&e->output, buffer_len(&msg));
 	buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
 	buffer_free(&msg);
 }
 
+/* ssh1 only */
 void
-process_authentication_challenge(SocketEntry *e)
+process_authentication_challenge1(SocketEntry *e)
 {
-	int i, pub_bits, len;
-	BIGNUM *pub_e, *pub_n, *challenge;
+	Key *key, *private;
+	BIGNUM *challenge;
+	int i, len;
 	Buffer msg;
 	MD5_CTX md;
 	unsigned char buf[32], mdbuf[16], session_id[16];
 	unsigned int response_type;
 
 	buffer_init(&msg);
-	pub_e = BN_new();
-	pub_n = BN_new();
+	key = key_new(KEY_RSA);
 	challenge = BN_new();
-	pub_bits = buffer_get_int(&e->input);
-	buffer_get_bignum(&e->input, pub_e);
-	buffer_get_bignum(&e->input, pub_n);
+
+	buffer_get_int(&e->input);				/* ignored */
+	buffer_get_bignum(&e->input, key->rsa->e);
+	buffer_get_bignum(&e->input, key->rsa->n);
 	buffer_get_bignum(&e->input, challenge);
-	if (buffer_len(&e->input) == 0) {
-		/* Compatibility code for old servers. */
-		memset(session_id, 0, 16);
-		response_type = 0;
-	} else {
-		/* New code. */
-		buffer_get(&e->input, (char *) session_id, 16);
-		response_type = buffer_get_int(&e->input);
-	}
-	for (i = 0; i < num_identities; i++)
-		if (pub_bits == BN_num_bits(identities[i].key->n) &&
-		    BN_cmp(pub_e, identities[i].key->e) == 0 &&
-		    BN_cmp(pub_n, identities[i].key->n) == 0) {
-			/* Decrypt the challenge using the private key. */
-			rsa_private_decrypt(challenge, challenge, identities[i].key);
 
-			/* Compute the desired response. */
-			switch (response_type) {
-			case 0:/* As of protocol 1.0 */
-				/* This response type is no longer supported. */
-				log("Compatibility with ssh protocol 1.0 no longer supported.");
-				buffer_put_char(&msg, SSH_AGENT_FAILURE);
-				goto send;
+	/* Only protocol 1.1 is supported */
+	if (buffer_len(&e->input) == 0)
+		goto failure;
+	buffer_get(&e->input, (char *) session_id, 16);
+	response_type = buffer_get_int(&e->input);
+	if (response_type != 1)
+		goto failure;
 
-			case 1:/* As of protocol 1.1 */
-				/* The response is MD5 of decrypted challenge plus session id. */
-				len = BN_num_bytes(challenge);
+	private = lookup_private_key(key, NULL, 1);
+	if (private != NULL) {
+		/* Decrypt the challenge using the private key. */
+		rsa_private_decrypt(challenge, challenge, private->rsa);
 
-				if (len <= 0 || len > 32) {
-					fatal("process_authentication_challenge: "
-					 "bad challenge length %d", len);
-				}
-				memset(buf, 0, 32);
-				BN_bn2bin(challenge, buf + 32 - len);
-				MD5_Init(&md);
-				MD5_Update(&md, buf, 32);
-				MD5_Update(&md, session_id, 16);
-				MD5_Final(mdbuf, &md);
-				break;
-
-			default:
-				fatal("process_authentication_challenge: bad response_type %d",
-				      response_type);
-				break;
-			}
-
-			/* Send the response. */
-			buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
-			for (i = 0; i < 16; i++)
-				buffer_put_char(&msg, mdbuf[i]);
-
-			goto send;
+		/* The response is MD5 of decrypted challenge plus session id. */
+		len = BN_num_bytes(challenge);
+		if (len <= 0 || len > 32) {
+			log("process_authentication_challenge: bad challenge length %d", len);
+			goto failure;
 		}
-	/* Unknown identity.  Send failure. */
+		memset(buf, 0, 32);
+		BN_bn2bin(challenge, buf + 32 - len);
+		MD5_Init(&md);
+		MD5_Update(&md, buf, 32);
+		MD5_Update(&md, session_id, 16);
+		MD5_Final(mdbuf, &md);
+
+		/* Send the response. */
+		buffer_put_char(&msg, SSH_AGENT_RSA_RESPONSE);
+		for (i = 0; i < 16; i++)
+			buffer_put_char(&msg, mdbuf[i]);
+		goto send;
+	}
+
+failure:
+	/* Unknown identity or protocol error.  Send failure. */
 	buffer_put_char(&msg, SSH_AGENT_FAILURE);
 send:
 	buffer_put_int(&e->output, buffer_len(&msg));
-	buffer_append(&e->output, buffer_ptr(&msg),
-		      buffer_len(&msg));
-	buffer_free(&msg);
-	BN_clear_free(pub_e);
-	BN_clear_free(pub_n);
+	buffer_append(&e->output, buffer_ptr(&msg), buffer_len(&msg));
+	key_free(key);
 	BN_clear_free(challenge);
+	buffer_free(&msg);
 }
 
+/* ssh2 only */
 void
-process_remove_identity(SocketEntry *e)
+process_sign_request2(SocketEntry *e)
 {
+	extern int datafellows;
+	Key *key, *private;
+	unsigned char *blob, *data, *signature = NULL;
+	unsigned int blen, dlen, slen = 0;
+	Buffer msg;
+	int ok = -1;
+
+	datafellows = 0;
+	
+	blob = buffer_get_string(&e->input, &blen);
+	data = buffer_get_string(&e->input, &dlen);
+
+	key = dsa_key_from_blob(blob, blen);
+	if (key != NULL) {
+		private = lookup_private_key(key, NULL, 2);
+		if (private != NULL)
+			ok = dsa_sign(private, &signature, &slen, data, dlen);
+	}
+	key_free(key);
+	buffer_init(&msg);
+	if (ok == 0) {
+		buffer_put_char(&msg, SSH2_AGENT_SIGN_RESPONSE);
+		buffer_put_string(&msg, signature, slen);
+	} else {
+		buffer_put_char(&msg, SSH_AGENT_FAILURE);
+	}
+	buffer_put_int(&e->output, buffer_len(&msg));
+	buffer_append(&e->output, buffer_ptr(&msg),
+	    buffer_len(&msg));
+	buffer_free(&msg);
+	xfree(data);
+	xfree(blob);
+	if (signature != NULL)
+		xfree(signature);
+}
+
+/* shared */
+void
+process_remove_identity(SocketEntry *e, int version)
+{
+	Key *key = NULL, *private;
+	unsigned char *blob;
+	unsigned int blen;
 	unsigned int bits;
-	unsigned int i;
-	BIGNUM *dummy, *n;
+	int success = 0;
 
-	dummy = BN_new();
-	n = BN_new();
+	switch(version){
+	case 1:
+		key = key_new(KEY_RSA);
+		bits = buffer_get_int(&e->input);
+		buffer_get_bignum(&e->input, key->rsa->e);
+		buffer_get_bignum(&e->input, key->rsa->n);
 
-	/* Get the key from the packet. */
-	bits = buffer_get_int(&e->input);
-	buffer_get_bignum(&e->input, dummy);
-	buffer_get_bignum(&e->input, n);
-
-	if (bits != BN_num_bits(n))
-		log("Warning: identity keysize mismatch: actual %d, announced %d",
-		      BN_num_bits(n), bits);
-
-	/* Check if we have the key. */
-	for (i = 0; i < num_identities; i++)
-		if (BN_cmp(identities[i].key->n, n) == 0) {
+		if (bits != key_size(key))
+			log("Warning: identity keysize mismatch: actual %d, announced %d",
+			      key_size(key), bits);
+		break;
+	case 2:
+		blob = buffer_get_string(&e->input, &blen);
+		key = dsa_key_from_blob(blob, blen);
+		xfree(blob);
+		break;
+	}
+	if (key != NULL) {
+		int idx;
+		private = lookup_private_key(key, &idx, version);
+		if (private != NULL) {
 			/*
 			 * We have this key.  Free the old key.  Since we
 			 * don\'t want to leave empty slots in the middle of
 			 * the array, we actually free the key there and copy
 			 * data from the last entry.
 			 */
-			RSA_free(identities[i].key);
-			xfree(identities[i].comment);
-			if (i < num_identities - 1)
-				identities[i] = identities[num_identities - 1];
-			num_identities--;
-			BN_clear_free(dummy);
-			BN_clear_free(n);
-
-			/* Send success. */
-			buffer_put_int(&e->output, 1);
-			buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
-			return;
+			Idtab *tab = idtab_lookup(version);
+			key_free(tab->identities[idx].key);
+			xfree(tab->identities[idx].comment);
+			if (idx != tab->nentries)
+				tab->identities[idx] = tab->identities[tab->nentries];
+			tab->nentries--;
+			success = 1;
 		}
-	/* We did not have the key. */
-	BN_clear(dummy);
-	BN_clear(n);
-
-	/* Send failure. */
+		key_free(key);
+	}
 	buffer_put_int(&e->output, 1);
-	buffer_put_char(&e->output, SSH_AGENT_FAILURE);
+	buffer_put_char(&e->output,
+	    success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
 }
 
-/*
- * Removes all identities from the agent.
- */
 void
-process_remove_all_identities(SocketEntry *e)
+process_remove_all_identities(SocketEntry *e, int version)
 {
 	unsigned int i;
+	Idtab *tab = idtab_lookup(version);
 
 	/* Loop over all identities and clear the keys. */
-	for (i = 0; i < num_identities; i++) {
-		RSA_free(identities[i].key);
-		xfree(identities[i].comment);
+	for (i = 0; i < tab->nentries; i++) {
+		key_free(tab->identities[i].key);
+		xfree(tab->identities[i].comment);
 	}
 
 	/* Mark that there are no identities. */
-	num_identities = 0;
+	tab->nentries = 0;
 
 	/* Send success. */
 	buffer_put_int(&e->output, 1);
@@ -238,79 +317,108 @@
 	return;
 }
 
-/*
- * Adds an identity to the agent.
- */
 void
-process_add_identity(SocketEntry *e)
+process_add_identity(SocketEntry *e, int version)
 {
-	RSA *k;
-	int i;
+	Key *k = NULL;
+	RSA *rsa;
 	BIGNUM *aux;
 	BN_CTX *ctx;
+	char *type;
+	char *comment;
+	int success = 0;
+	Idtab *tab = idtab_lookup(version);
 
-	if (num_identities == 0)
-		identities = xmalloc(sizeof(Identity));
-	else
-		identities = xrealloc(identities, (num_identities + 1) * sizeof(Identity));
+	switch (version) {
+	case 1:
+		k = key_new(KEY_RSA);
+		rsa = k->rsa;
 
-	identities[num_identities].key = RSA_new();
-	k = identities[num_identities].key;
-	buffer_get_int(&e->input);	/* bits */
-	k->n = BN_new();
-	buffer_get_bignum(&e->input, k->n);
-	k->e = BN_new();
-	buffer_get_bignum(&e->input, k->e);
-	k->d = BN_new();
-	buffer_get_bignum(&e->input, k->d);
-	k->iqmp = BN_new();
-	buffer_get_bignum(&e->input, k->iqmp);
-	/* SSH and SSL have p and q swapped */
-	k->q = BN_new();
-	buffer_get_bignum(&e->input, k->q);	/* p */
-	k->p = BN_new();
-	buffer_get_bignum(&e->input, k->p);	/* q */
+		/* allocate mem for private key */
+		/* XXX rsa->n and rsa->e are already allocated */
+		rsa->d = BN_new();
+		rsa->iqmp = BN_new();
+		rsa->q = BN_new();
+		rsa->p = BN_new();
+		rsa->dmq1 = BN_new();
+		rsa->dmp1 = BN_new();
 
-	/* Generate additional parameters */
-	aux = BN_new();
-	ctx = BN_CTX_new();
+		buffer_get_int(&e->input);		 /* ignored */
 
-	BN_sub(aux, k->q, BN_value_one());
-	k->dmq1 = BN_new();
-	BN_mod(k->dmq1, k->d, aux, ctx);
+		buffer_get_bignum(&e->input, rsa->n);
+		buffer_get_bignum(&e->input, rsa->e);
+		buffer_get_bignum(&e->input, rsa->d);
+		buffer_get_bignum(&e->input, rsa->iqmp);
 
-	BN_sub(aux, k->p, BN_value_one());
-	k->dmp1 = BN_new();
-	BN_mod(k->dmp1, k->d, aux, ctx);
+		/* SSH and SSL have p and q swapped */
+		buffer_get_bignum(&e->input, rsa->q);	/* p */
+		buffer_get_bignum(&e->input, rsa->p);	/* q */
 
-	BN_clear_free(aux);
-	BN_CTX_free(ctx);
+		/* Generate additional parameters */
+		aux = BN_new();
+		ctx = BN_CTX_new();
 
-	identities[num_identities].comment = buffer_get_string(&e->input, NULL);
+		BN_sub(aux, rsa->q, BN_value_one());
+		BN_mod(rsa->dmq1, rsa->d, aux, ctx);
 
-	/* Check if we already have the key. */
-	for (i = 0; i < num_identities; i++)
-		if (BN_cmp(identities[i].key->n, k->n) == 0) {
-			/*
-			 * We already have this key.  Clear and free the new
-			 * data and return success.
-			 */
-			RSA_free(k);
-			xfree(identities[num_identities].comment);
+		BN_sub(aux, rsa->p, BN_value_one());
+		BN_mod(rsa->dmp1, rsa->d, aux, ctx);
 
-			/* Send success. */
-			buffer_put_int(&e->output, 1);
-			buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
-			return;
+		BN_clear_free(aux);
+		BN_CTX_free(ctx);
+
+		break;
+	case 2:
+		type = buffer_get_string(&e->input, NULL);
+		if (strcmp(type, KEX_DSS)) {
+			buffer_clear(&e->input);
+			xfree(type);
+			goto send;
 		}
-	/* Increment the number of identities. */
-	num_identities++;
+		xfree(type);
 
-	/* Send a success message. */
+		k = key_new(KEY_DSA);
+
+		/* allocate mem for private key */
+		k->dsa->priv_key = BN_new();
+
+		buffer_get_bignum2(&e->input, k->dsa->p);
+		buffer_get_bignum2(&e->input, k->dsa->q);
+		buffer_get_bignum2(&e->input, k->dsa->g);
+		buffer_get_bignum2(&e->input, k->dsa->pub_key);
+		buffer_get_bignum2(&e->input, k->dsa->priv_key);
+
+		break;
+	}
+
+	comment = buffer_get_string(&e->input, NULL);
+	if (k == NULL) {
+		xfree(comment);
+		goto send;
+	}
+	success = 1;
+	if (lookup_private_key(k, NULL, version) == NULL) {
+		if (tab->nentries == 0)
+			tab->identities = xmalloc(sizeof(Identity));
+		else
+			tab->identities = xrealloc(tab->identities,
+			    (tab->nentries + 1) * sizeof(Identity));
+		tab->identities[tab->nentries].key = k;
+		tab->identities[tab->nentries].comment = comment;
+		/* Increment the number of identities. */
+		tab->nentries++;
+	} else {
+		key_free(k);
+		xfree(comment);
+	}
+send:
 	buffer_put_int(&e->output, 1);
-	buffer_put_char(&e->output, SSH_AGENT_SUCCESS);
+	buffer_put_char(&e->output,
+	    success ? SSH_AGENT_SUCCESS : SSH_AGENT_FAILURE);
 }
 
+/* dispatch incoming messages */
+
 void
 process_message(SocketEntry *e)
 {
@@ -333,20 +441,37 @@
 	type = buffer_get_char(&e->input);
 
 	switch (type) {
-	case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
-		process_request_identity(e);
-		break;
+	/* ssh1 */
 	case SSH_AGENTC_RSA_CHALLENGE:
-		process_authentication_challenge(e);
+		process_authentication_challenge1(e);
+		break;
+	case SSH_AGENTC_REQUEST_RSA_IDENTITIES:
+		process_request_identities(e, 1);
 		break;
 	case SSH_AGENTC_ADD_RSA_IDENTITY:
-		process_add_identity(e);
+		process_add_identity(e, 1);
 		break;
 	case SSH_AGENTC_REMOVE_RSA_IDENTITY:
-		process_remove_identity(e);
+		process_remove_identity(e, 1);
 		break;
 	case SSH_AGENTC_REMOVE_ALL_RSA_IDENTITIES:
-		process_remove_all_identities(e);
+		process_remove_all_identities(e, 1);
+		break;
+	/* ssh2 */
+	case SSH2_AGENTC_SIGN_REQUEST:
+		process_sign_request2(e);
+		break;
+	case SSH2_AGENTC_REQUEST_IDENTITIES:
+		process_request_identities(e, 2);
+		break;
+	case SSH2_AGENTC_ADD_IDENTITY:
+		process_add_identity(e, 2);
+		break;
+	case SSH2_AGENTC_REMOVE_IDENTITY:
+		process_remove_identity(e, 2);
+		break;
+	case SSH2_AGENTC_REMOVE_ALL_IDENTITIES:
+		process_remove_all_identities(e, 2);
 		break;
 	default:
 		/* Unknown message.  Respond with failure. */
@@ -511,9 +636,9 @@
 	pid_t pid;
 	char *shell, *format, *pidstr, pidstrbuf[1 + 3 * sizeof pid];
 	extern int optind;
-
+	
 	init_rng();
-
+	
 	/* check if RSA support exists */
 	if (rsa_alive() == 0) {
 		fprintf(stderr,
@@ -654,6 +779,7 @@
 		signal(SIGALRM, check_parent_exists);
 		alarm(10);
 	}
+	idtab_init();
 	signal(SIGINT, SIG_IGN);
 	signal(SIGPIPE, SIG_IGN);
 	signal(SIGHUP, cleanup_exit);
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 8a03f0d..83450fe 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -7,7 +7,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh-keygen.c,v 1.29 2000/07/15 04:01:37 djm Exp $");
+RCSID("$OpenBSD: ssh-keygen.c,v 1.30 2000/08/19 21:34:43 markus Exp $");
 
 #include <openssl/evp.h>
 #include <openssl/pem.h>
@@ -16,7 +16,6 @@
 
 #include "ssh.h"
 #include "xmalloc.h"
-#include "fingerprint.h"
 #include "key.h"
 #include "rsa.h"
 #include "dsa.h"
@@ -228,8 +227,9 @@
 void
 do_fingerprint(struct passwd *pw)
 {
+	/* XXX RSA1 only */
+
 	FILE *f;
-	BIGNUM *e, *n;
 	Key *public;
 	char *comment = NULL, *cp, *ep, line[16*1024];
 	int i, skip = 0, num = 1, invalid = 1;
@@ -249,13 +249,9 @@
 		key_free(public);
 		exit(0);
 	}
-	key_free(public);
 
-	/* XXX */
 	f = fopen(identity_file, "r");
 	if (f != NULL) {
-		n = BN_new();
-		e = BN_new();
 		while (fgets(line, sizeof(line), f)) {
 			i = strlen(line) - 1;
 			if (line[i] != '\n') {
@@ -290,18 +286,17 @@
 				*cp++ = '\0';
 			}
 			ep = cp;
-			if (auth_rsa_read_key(&cp, &ignore, e, n)) {
+			if (auth_rsa_read_key(&cp, &ignore, public->rsa->e, public->rsa->n)) {
 				invalid = 0;
 				comment = *cp ? cp : comment;
-				printf("%d %s %s\n", BN_num_bits(n),
-				    fingerprint(e, n),
+				printf("%d %s %s\n", key_size(public),
+				    key_fingerprint(public),
 				    comment ? comment : "no comment");
 			}
 		}
-		BN_free(e);
-		BN_free(n);
 		fclose(f);
 	}
+	key_free(public);
 	if (invalid) {
 		printf("%s is not a valid key file.\n", identity_file);
 		exit(1);
diff --git a/ssh.c b/ssh.c
index 58e4d7b..39512c5 100644
--- a/ssh.c
+++ b/ssh.c
@@ -11,7 +11,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh.c,v 1.58 2000/07/16 08:27:22 markus Exp $");
+RCSID("$OpenBSD: ssh.c,v 1.61 2000/08/20 18:42:40 millert Exp $");
 
 #include <openssl/evp.h>
 #include <openssl/dsa.h>
@@ -253,8 +253,8 @@
 		cp = strrchr(av0, '/') + 1;
 	else
 		cp = av0;
-	if (strcmp(cp, "rsh") != 0 && strcmp(cp, "ssh") != 0 &&
-	    strcmp(cp, "rlogin") != 0 && strcmp(cp, "slogin") != 0)
+	if (strcmp(cp, "rsh") && strcmp(cp, "ssh") && strcmp(cp, "rlogin") &&
+	    strcmp(cp, "slogin") && strcmp(cp, "remsh"))
 		host = cp;
 
 	for (optind = 1; optind < ac; optind++) {
@@ -490,6 +490,9 @@
 	pwcopy.pw_passwd = xstrdup(pw->pw_passwd);
 	pwcopy.pw_uid = pw->pw_uid;
 	pwcopy.pw_gid = pw->pw_gid;
+#ifdef HAVE_PW_CLASS_IN_PASSWD
+	pwcopy.pw_class = xstrdup(pw->pw_class);
+#endif
 	pwcopy.pw_dir = xstrdup(pw->pw_dir);
 	pwcopy.pw_shell = xstrdup(pw->pw_shell);
 	pw = &pwcopy;
@@ -871,7 +874,7 @@
 	}
 
 	/* Enter the interactive session. */
-	return client_loop(have_tty, tty_flag ? options.escape_char : -1);
+	return client_loop(have_tty, tty_flag ? options.escape_char : -1, 0);
 }
 
 void
@@ -954,9 +957,16 @@
 ssh_session2(void)
 {
 	int window, packetmax, id;
-	int in  = dup(STDIN_FILENO);
-	int out = dup(STDOUT_FILENO);
-	int err = dup(STDERR_FILENO);
+	int in, out, err;
+
+	/* If requested, let ssh continue in the background. */
+	if (fork_after_authentication_flag)
+		if (daemon(1, 1) < 0)
+			fatal("daemon() failed: %.200s", strerror(errno));
+
+	in  = dup(STDIN_FILENO);
+	out = dup(STDOUT_FILENO);
+	err = dup(STDERR_FILENO);
 
 	if (in < 0 || out < 0 || err < 0)
 		fatal("dump in/out/err failed");
@@ -972,13 +982,13 @@
 		packetmax = window/2;
 	}
 
+/*XXX MAXPACK */
 	id = channel_new(
 	    "session", SSH_CHANNEL_OPENING, in, out, err,
 	    window, packetmax, CHAN_EXTENDED_WRITE, xstrdup("client-session"));
 
-
 	channel_open(id);
 	channel_register_callback(id, SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, client_init, (void *)0);
 
-	return client_loop(tty_flag, tty_flag ? options.escape_char : -1);
+	return client_loop(tty_flag, tty_flag ? options.escape_char : -1, id);
 }
diff --git a/ssh.h b/ssh.h
index f3f049f..f21dee4 100644
--- a/ssh.h
+++ b/ssh.h
@@ -13,7 +13,7 @@
  *
  */
 
-/* RCSID("$OpenBSD: ssh.h,v 1.48 2000/07/13 22:53:21 provos Exp $"); */
+/* RCSID("$OpenBSD: ssh.h,v 1.49 2000/08/19 18:48:11 markus Exp $"); */
 
 #ifndef SSH_H
 #define SSH_H
@@ -105,7 +105,11 @@
 #endif /* SSH_PROGRAM */
 
 #ifndef LOGIN_PROGRAM
-#define LOGIN_PROGRAM		"/usr/bin/login"
+# ifdef LOGIN_PROGRAM_FALLBACK
+#  define LOGIN_PROGRAM		LOGIN_PROGRAM_FALLBACK
+# else
+#  define LOGIN_PROGRAM		"/usr/bin/login"
+# endif
 #endif /* LOGIN_PROGRAM */
 
 #ifndef ASKPASS_PROGRAM
@@ -506,7 +510,7 @@
 void    server_loop2(void);
 
 /* Client side main loop for the interactive session. */
-int     client_loop(int have_pty, int escape_char);
+int     client_loop(int have_pty, int escape_char, int id);
 
 /* Linked list of custom environment strings (see auth-rsa.c). */
 struct envstring {
diff --git a/sshconnect1.c b/sshconnect1.c
index aaebf17..7b60d62 100644
--- a/sshconnect1.c
+++ b/sshconnect1.c
@@ -9,7 +9,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect1.c,v 1.4 2000/07/16 08:27:22 markus Exp $");
+RCSID("$OpenBSD: sshconnect1.c,v 1.5 2000/08/19 21:34:44 markus Exp $");
 
 #include <openssl/bn.h>
 #include <openssl/dsa.h>
@@ -44,27 +44,27 @@
 int
 try_agent_authentication()
 {
-	int status, type;
+	int type;
 	char *comment;
 	AuthenticationConnection *auth;
 	unsigned char response[16];
 	unsigned int i;
-	BIGNUM *e, *n, *challenge;
+	int plen, clen;
+	Key *key;
+	BIGNUM *challenge;
 
 	/* Get connection to the agent. */
 	auth = ssh_get_authentication_connection();
 	if (!auth)
 		return 0;
 
-	e = BN_new();
-	n = BN_new();
 	challenge = BN_new();
+	key = key_new(KEY_RSA);
 
 	/* Loop through identities served by the agent. */
-	for (status = ssh_get_first_identity(auth, e, n, &comment);
-	     status;
-	     status = ssh_get_next_identity(auth, e, n, &comment)) {
-		int plen, clen;
+	for (key = ssh_get_first_identity(auth, &comment, 1);
+	     key != NULL;
+	     key = ssh_get_next_identity(auth, &comment, 1)) {
 
 		/* Try this identity. */
 		debug("Trying RSA authentication via agent with '%.100s'", comment);
@@ -72,7 +72,7 @@
 
 		/* Tell the server that we are willing to authenticate using this key. */
 		packet_start(SSH_CMSG_AUTH_RSA);
-		packet_put_bignum(n);
+		packet_put_bignum(key->rsa->n);
 		packet_send();
 		packet_write_wait();
 
@@ -83,6 +83,7 @@
 		   does not support RSA authentication. */
 		if (type == SSH_SMSG_FAILURE) {
 			debug("Server refused our key.");
+			key_free(key);
 			continue;
 		}
 		/* Otherwise it should have sent a challenge. */
@@ -97,13 +98,16 @@
 		debug("Received RSA challenge from server.");
 
 		/* Ask the agent to decrypt the challenge. */
-		if (!ssh_decrypt_challenge(auth, e, n, challenge,
-					   session_id, 1, response)) {
-			/* The agent failed to authenticate this identifier although it
-			   advertised it supports this.  Just return a wrong value. */
+		if (!ssh_decrypt_challenge(auth, key, challenge, session_id, 1, response)) {
+			/*
+			 * The agent failed to authenticate this identifier
+			 * although it advertised it supports this.  Just
+			 * return a wrong value.
+			 */
 			log("Authentication agent failed to decrypt challenge.");
 			memset(response, 0, sizeof(response));
 		}
+		key_free(key);
 		debug("Sending response to RSA challenge.");
 
 		/* Send the decrypted challenge back to the server. */
@@ -118,10 +122,8 @@
 
 		/* The server returns success if it accepted the authentication. */
 		if (type == SSH_SMSG_SUCCESS) {
-			debug("RSA authentication accepted by server.");
-			BN_clear_free(e);
-			BN_clear_free(n);
 			BN_clear_free(challenge);
+			debug("RSA authentication accepted by server.");
 			return 1;
 		}
 		/* Otherwise it should return failure. */
@@ -129,11 +131,7 @@
 			packet_disconnect("Protocol error waiting RSA auth response: %d",
 					  type);
 	}
-
-	BN_clear_free(e);
-	BN_clear_free(n);
 	BN_clear_free(challenge);
-
 	debug("RSA authentication using agent refused.");
 	return 0;
 }
diff --git a/sshconnect2.c b/sshconnect2.c
index 22ad39e..1f49067 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -28,7 +28,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect2.c,v 1.16 2000/07/16 08:27:22 markus Exp $");
+RCSID("$OpenBSD: sshconnect2.c,v 1.17 2000/08/19 21:34:44 markus Exp $");
 
 #include <openssl/bn.h>
 #include <openssl/rsa.h>
@@ -54,6 +54,7 @@
 #include "dsa.h"
 #include "sshconnect.h"
 #include "authfile.h"
+#include "authfd.h"
 
 /* import */
 extern char *client_version_string;
@@ -291,7 +292,7 @@
     unsigned char **sigp, int *lenp,
     unsigned char *data, int datalen);
 
-void
+int
 ssh2_sign_and_send_pubkey(Key *k, sign_fn *do_sign,
     const char *server_user, const char *host, const char *service)
 {
@@ -299,6 +300,7 @@
 	unsigned char *blob, *signature;
 	int bloblen, slen;
 	int skip = 0;
+	int ret = -1;
 
 	dsa_make_key_blob(k, &blob, &bloblen);
 
@@ -323,8 +325,12 @@
 	buffer_put_string(&b, blob, bloblen);
 
 	/* generate signature */
-	do_sign(k, &signature, &slen, buffer_ptr(&b), buffer_len(&b));
-	key_free(k); /* XXX */
+	ret = do_sign(k, &signature, &slen, buffer_ptr(&b), buffer_len(&b));
+	if (ret == -1) {
+		xfree(blob);
+		buffer_free(&b);
+		return 0;
+	}
 #ifdef DEBUG_DSS
 	buffer_dump(&b);
 #endif
@@ -357,6 +363,8 @@
 	/* send */
 	packet_send();
 	packet_write_wait();
+
+	return 1;
 }
 
 int
@@ -364,6 +372,7 @@
     const char *server_user, const char *host, const char *service)
 {
 	Key *k;
+	int ret = 0;
 	struct stat st;
 
 	if (stat(filename, &st) != 0) {
@@ -389,13 +398,53 @@
 			return 0;
 		}
 	}
-	ssh2_sign_and_send_pubkey(k, dsa_sign, server_user, host, service);
-	return 1;
+	ret = ssh2_sign_and_send_pubkey(k, dsa_sign, server_user, host, service);
+	key_free(k);
+	return ret;
+}
+
+int agent_sign(
+    Key *key,
+    unsigned char **sigp, int *lenp,
+    unsigned char *data, int datalen)
+{
+	int ret = -1;
+	AuthenticationConnection *ac = ssh_get_authentication_connection();
+	if (ac != NULL) {
+		ret = ssh_agent_sign(ac, key, sigp, lenp, data, datalen);
+		ssh_close_authentication_connection(ac);
+	}
+	return ret;
+}
+
+int
+ssh2_try_agent(AuthenticationConnection *ac,
+    const char *server_user, const char *host, const char *service)
+{
+	static int called = 0;
+	char *comment;
+	Key *k;
+	int ret;
+
+	if (called == 0) {
+		k = ssh_get_first_identity(ac, &comment, 2);
+		called ++;
+	} else {
+		k = ssh_get_next_identity(ac, &comment, 2);
+	}
+	if (k == NULL)
+		return 0;
+	debug("trying DSA agent key %s", comment);
+	xfree(comment);
+	ret = ssh2_sign_and_send_pubkey(k, agent_sign, server_user, host, service);
+	key_free(k);
+	return ret;
 }
 
 void
 ssh_userauth2(const char *server_user, char *host)
 {
+	AuthenticationConnection *ac = ssh_get_authentication_connection();
 	int type;
 	int plen;
 	int sent;
@@ -450,12 +499,17 @@
 			debug("partial success");
 		if (options.dsa_authentication &&
 		    strstr(auths, "publickey") != NULL) {
-			while (i < options.num_identity_files2) {
-				sent = ssh2_try_pubkey(
-				    options.identity_files2[i++],
+			if (ac != NULL)
+				sent = ssh2_try_agent(ac,
 				    server_user, host, service);
-				if (sent)
-					break;
+			if (!sent) {
+				while (i < options.num_identity_files2) {
+					sent = ssh2_try_pubkey(
+					    options.identity_files2[i++],
+					    server_user, host, service);
+					if (sent)
+						break;
+				}
 			}
 		}
 		if (!sent) {
@@ -469,6 +523,8 @@
 			fatal("Permission denied (%s).", auths);
 		xfree(auths);
 	}
+	if (ac != NULL)
+		ssh_close_authentication_connection(ac);
 	packet_done();
 	debug("ssh-userauth2 successfull");
 }