- markus@cvs.openbsd.org 2002/05/23 19:24:30
     [authfile.c authfile.h pathnames.h ssh.c sshconnect.c sshconnect.h
      sshconnect1.c sshconnect2.c ssh-keysign.8 ssh-keysign.c Makefile.in]
     add /usr/libexec/ssh-keysign: a setuid helper program for hostbased
     authentication in protocol v2 (needs to access the hostkeys).

Note: Makefile.in untested.  Will test after merge is finished.
diff --git a/ChangeLog b/ChangeLog
index ff71091..8ed0d73 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -16,6 +16,11 @@
    - deraadt@cvs.openbsd.org 2002/05/22 23:18:25
      [ssh.c sshd.c]
      spelling; abishoff@arc.nasa.gov
+   - markus@cvs.openbsd.org 2002/05/23 19:24:30
+     [authfile.c authfile.h pathnames.h ssh.c sshconnect.c sshconnect.h 
+      sshconnect1.c sshconnect2.c ssh-keysign.8 ssh-keysign.c Makefile.in]
+     add /usr/libexec/ssh-keysign: a setuid helper program for hostbased 
+     authentication in protocol v2 (needs to access the hostkeys).
 
 20020604
  - (stevesk) [channels.c] bug #164 patch from YOSHIFUJI Hideaki (changed
@@ -700,4 +705,4 @@
  - (stevesk) entropy.c: typo in debug message
  - (djm) ssh-keygen -i needs seeded RNG; report from markus@
 
-$Id: ChangeLog,v 1.2148 2002/06/06 19:51:58 mouring Exp $
+$Id: ChangeLog,v 1.2149 2002/06/06 19:57:33 mouring Exp $
diff --git a/Makefile.in b/Makefile.in
index e3b1451..ec235f8 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.208 2002/05/13 04:12:05 djm Exp $
+# $Id: Makefile.in,v 1.209 2002/06/06 19:57:33 mouring Exp $
 
 # uncomment if you run a non bourne compatable shell. Ie. csh
 #SHELL = @SH@
@@ -125,6 +125,9 @@
 ssh-keygen$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keygen.o
 	$(LD) -o $@ ssh-keygen.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) 
 
+ssh-keysign$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keysign.o
+	$(LD) -o $@ ssh-keysign.o $(LDFLAGS) -lssh -lopenbsd-compat $(LIBS) 
+
 ssh-keyscan$(EXEEXT): $(LIBCOMPAT) libssh.a ssh-keyscan.o
 	$(LD) -o $@ ssh-keyscan.o $(LDFLAGS) -lssh -lopenbsd-compat -lssh $(LIBS) 
 
diff --git a/authfile.c b/authfile.c
index de8b102..6d936de 100644
--- a/authfile.c
+++ b/authfile.c
@@ -36,7 +36,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: authfile.c,v 1.48 2002/02/28 15:46:33 markus Exp $");
+RCSID("$OpenBSD: authfile.c,v 1.49 2002/05/23 19:24:30 markus Exp $");
 
 #include <openssl/err.h>
 #include <openssl/evp.h>
@@ -421,7 +421,7 @@
 	return NULL;
 }
 
-static Key *
+Key *
 key_load_private_pem(int fd, int type, const char *passphrase,
     char **commentp)
 {
diff --git a/authfile.h b/authfile.h
index c614ca1..7f92701 100644
--- a/authfile.h
+++ b/authfile.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: authfile.h,v 1.9 2002/03/04 17:27:39 stevesk Exp $	*/
+/*	$OpenBSD: authfile.h,v 1.10 2002/05/23 19:24:30 markus Exp $	*/
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -20,5 +20,6 @@
 Key	*key_load_public_type(int, const char *, char **);
 Key	*key_load_private(const char *, const char *, char **);
 Key	*key_load_private_type(int, const char *, const char *, char **);
+Key	*key_load_private_pem(int, int, const char *, char **);
 
 #endif
diff --git a/pathnames.h b/pathnames.h
index 691293c..89e22c7 100644
--- a/pathnames.h
+++ b/pathnames.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: pathnames.h,v 1.12 2002/03/19 03:03:43 stevesk Exp $	*/
+/*	$OpenBSD: pathnames.h,v 1.13 2002/05/23 19:24:30 markus Exp $	*/
 
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -120,6 +120,11 @@
 #define _PATH_SSH_ASKPASS_DEFAULT	"/usr/X11R6/bin/ssh-askpass"
 #endif
 
+/* Location of ssh-keysign for hostbased authentication */
+#ifndef _PATH_SSH_KEY_SIGN
+#define _PATH_SSH_KEY_SIGN            "/usr/libexec/ssh-keysign"
+#endif
+
 /* xauth for X11 forwarding */
 #ifndef _PATH_XAUTH
 #define _PATH_XAUTH			"/usr/X11R6/bin/xauth"
diff --git a/ssh-keysign.8 b/ssh-keysign.8
new file mode 100644
index 0000000..fccbd7c
--- /dev/null
+++ b/ssh-keysign.8
@@ -0,0 +1,58 @@
+.\" $OpenBSD: ssh-keysign.8,v 1.1 2002/05/25 08:16:59 markus Exp $
+.\"
+.\" Copyright (c) 2002 Markus Friedl.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd May 24, 2002
+.Dt SSH-KEYSIGN 8
+.Os
+.Sh NAME
+.Nm ssh-keysign
+.Nd ssh helper program for hostbased authentication
+.Sh SYNOPSIS
+.Nm ssh-keysign
+.Sh DESCRIPTION
+.Nm
+is used by
+.Xr ssh 1
+to access the local host keys during hostbased authentication with
+SSH protocol version 2.
+Since the host keys are readable only by root
+.Nm
+must be setuid root.
+.Nm
+is not intended to be invoked by the user, but from
+.Xr ssh 1 .
+See
+.Xr ssh 1
+and
+.Xr sshd 8
+for more information about hostbased authentication.
+.Sh SEE ALSO
+.Xr ssh 1 ,
+.Xr sshd 8
+.Sh AUTHORS
+Markus Friedl <markus@openbsd.org>
+.Sh HISTORY
+.Nm
+first appeared in
+.Ox 3.2 .
diff --git a/ssh-keysign.c b/ssh-keysign.c
new file mode 100644
index 0000000..da63070
--- /dev/null
+++ b/ssh-keysign.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2002 Markus Friedl.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "includes.h"
+RCSID("$OpenBSD: ssh-keysign.c,v 1.2 2002/05/31 10:30:33 markus Exp $");
+
+#include <openssl/evp.h>
+
+#include "log.h"
+#include "key.h"
+#include "ssh2.h"
+#include "misc.h"
+#include "xmalloc.h"
+#include "buffer.h"
+#include "bufaux.h"
+#include "authfile.h"
+#include "msg.h"
+#include "canohost.h"
+#include "pathnames.h"
+
+static int
+valid_request(struct passwd *pw, char *host, Key **ret, u_char *data,
+    u_int datalen)
+{
+	Buffer b;
+	Key *key;
+	u_char *pkblob;
+	u_int blen, len;
+	char *pkalg, *p;
+	int pktype, fail;
+
+	fail = 0;
+
+	buffer_init(&b);
+	buffer_append(&b, data, datalen);
+ 
+	/* session id */
+	buffer_skip_string(&b);
+	if (buffer_get_char(&b) != SSH2_MSG_USERAUTH_REQUEST)
+		fail++;
+
+	/* server user */
+	buffer_skip_string(&b);
+
+	/* service */
+	p = buffer_get_string(&b, NULL);
+	if (strcmp("ssh-connection", p) != 0)
+		fail++;
+	xfree(p);
+
+	/* method */
+	p = buffer_get_string(&b, NULL);
+	if (strcmp("hostbased", p) != 0)
+		fail++;
+	xfree(p);
+
+	/* pubkey */
+	pkalg = buffer_get_string(&b, NULL);
+	pkblob = buffer_get_string(&b, &blen);
+
+	pktype = key_type_from_name(pkalg);
+	if (pktype == KEY_UNSPEC)
+		fail++;
+	else if ((key = key_from_blob(pkblob, blen)) == NULL)
+		fail++;
+	else if (key->type != pktype)
+		fail++;
+	xfree(pkalg);
+	xfree(pkblob);
+
+	/* client host name, handle trailing dot */
+	p = buffer_get_string(&b, &len);
+	debug2("valid_request: check expect chost %s got %s", host, p);
+	if (strlen(host) != len - 1)
+		fail++;
+	else if (p[len - 1] != '.')
+	      fail++;
+	else if (strncasecmp(host, p, len - 1) != 0)
+	      fail++;
+	xfree(p);
+
+	/* local user */
+	p = buffer_get_string(&b, NULL);
+
+	if (strcmp(pw->pw_name, p) != 0)
+		fail++;
+	xfree(p);
+
+	/* end of message */
+	if (buffer_len(&b) != 0)
+		fail++;
+
+	debug3("valid_request: fail %d", fail);
+
+	if (fail && key != NULL)
+		key_free(key);
+	else
+		*ret = key;
+
+	return (fail ? -1 : 0);
+}
+
+int
+main(int argc, char **argv)
+{
+	Buffer b;
+	Key *keys[2], *key;
+	struct passwd *pw;
+	int key_fd[2], i, found, version = 2, fd;
+	u_char *signature, *data;
+	char *host;
+	u_int slen, dlen;
+
+	key_fd[0] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY);
+	key_fd[1] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY);
+
+	seteuid(getuid());
+	setuid(getuid());
+
+#ifdef DEBUG_SSH_KEYSIGN
+	log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0);
+#endif  
+
+	if (key_fd[0] == -1 && key_fd[1] == -1)
+		fatal("could not open any host key");
+
+	if ((pw = getpwuid(getuid())) == NULL)
+		fatal("getpwuid failed");
+	pw = pwcopy(pw); 
+
+	SSLeay_add_all_algorithms();
+
+	found = 0;
+	for (i = 0; i < 2; i++) {
+		keys[i] = NULL;
+		if (key_fd[i] == -1)
+			continue;
+		keys[i] = key_load_private_pem(key_fd[i], KEY_UNSPEC,
+		    NULL, NULL);
+		close(key_fd[i]);
+		if (keys[i] != NULL)
+			found = 1;
+	}
+	if (!found)
+		fatal("no hostkey found");
+
+	buffer_init(&b);
+	if (msg_recv(STDIN_FILENO, &b) < 0)
+		fatal("msg_recv failed");
+	if (buffer_get_char(&b) != version)
+		fatal("bad version");
+	fd = buffer_get_int(&b);
+	if ((fd == STDIN_FILENO) || (fd == STDOUT_FILENO))
+		fatal("bad fd");
+	if ((host = get_local_name(fd)) == NULL)
+		fatal("cannot get sockname for fd");
+	
+	data = buffer_get_string(&b, &dlen);
+	if (valid_request(pw, host, &key, data, dlen) < 0)
+		fatal("not a valid request");
+	xfree(data);
+	xfree(host);
+
+	found = 0;
+	for (i = 0; i < 2; i++) {
+		if (keys[i] != NULL &&
+		    key_equal(key, keys[i])) {
+			found = 1;
+			break;
+		}
+	}
+	if (!found)
+		fatal("no matching hostkey found");
+
+	if (key_sign(keys[i], &signature, &slen, data, dlen) != 0)
+		fatal("key_sign failed");
+	
+	/* send reply */
+	buffer_clear(&b);
+	buffer_put_string(&b, signature, slen);
+	msg_send(STDOUT_FILENO, version, &b);
+
+	return (0);
+}
diff --git a/ssh.c b/ssh.c
index 4b82d1e..2e479d5 100644
--- a/ssh.c
+++ b/ssh.c
@@ -40,7 +40,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: ssh.c,v 1.172 2002/05/22 23:18:25 deraadt Exp $");
+RCSID("$OpenBSD: ssh.c,v 1.173 2002/05/23 19:24:30 markus Exp $");
 
 #include <openssl/evp.h>
 #include <openssl/err.h>
@@ -132,10 +132,7 @@
 struct sockaddr_storage hostaddr;
 
 /* Private host keys. */
-struct {
-	Key     **keys;
-	int	nkeys;
-} sensitive_data;
+Sensitive sensitive_data;
 
 /* Original real UID. */
 uid_t original_real_uid;
@@ -689,6 +686,7 @@
 	 */
 	sensitive_data.nkeys = 0;
 	sensitive_data.keys = NULL;
+	sensitive_data.external_keysign = 0;
 	if (!cerr && (options.rhosts_rsa_authentication ||
 	    options.hostbased_authentication)) {
 		sensitive_data.nkeys = 3;
@@ -699,6 +697,16 @@
 		    _PATH_HOST_DSA_KEY_FILE, "", NULL);
 		sensitive_data.keys[2] = key_load_private_type(KEY_RSA,
 		    _PATH_HOST_RSA_KEY_FILE, "", NULL);
+
+		if (sensitive_data.keys[0] == NULL &&
+		    sensitive_data.keys[1] == NULL &&
+		    sensitive_data.keys[2] == NULL) {
+			sensitive_data.keys[1] = key_load_public(
+			    _PATH_HOST_DSA_KEY_FILE, NULL);
+			sensitive_data.keys[2] = key_load_public(
+			    _PATH_HOST_RSA_KEY_FILE, NULL);
+			sensitive_data.external_keysign = 1;
+		}
 	}
 	/*
 	 * Get rid of any extra privileges that we may have.  We will no
@@ -758,8 +766,7 @@
 	signal(SIGPIPE, SIG_IGN); /* ignore SIGPIPE early */
 
 	/* Log into the remote system.  This never returns if the login fails. */
-	ssh_login(sensitive_data.keys, sensitive_data.nkeys,
-	    host, (struct sockaddr *)&hostaddr, pw);
+	ssh_login(&sensitive_data, host, (struct sockaddr *)&hostaddr, pw);
 
 	/* We no longer need the private host keys.  Clear them now. */
 	if (sensitive_data.nkeys != 0) {
diff --git a/sshconnect.c b/sshconnect.c
index 5bb50e0..7af0b9a 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -13,7 +13,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect.c,v 1.119 2002/01/21 15:13:51 markus Exp $");
+RCSID("$OpenBSD: sshconnect.c,v 1.120 2002/05/23 19:24:30 markus Exp $");
 
 #include <openssl/bn.h>
 
@@ -844,7 +844,7 @@
  * This function does not require super-user privileges.
  */
 void
-ssh_login(Key **keys, int nkeys, const char *orighost,
+ssh_login(Sensitive *sensitive, const char *orighost,
     struct sockaddr *hostaddr, struct passwd *pw)
 {
 	char *host, *cp;
@@ -869,10 +869,10 @@
 	/* authenticate user */
 	if (compat20) {
 		ssh_kex2(host, hostaddr);
-		ssh_userauth2(local_user, server_user, host, keys, nkeys);
+		ssh_userauth2(local_user, server_user, host, sensitive);
 	} else {
 		ssh_kex(host, hostaddr);
-		ssh_userauth1(local_user, server_user, host, keys, nkeys);
+		ssh_userauth1(local_user, server_user, host, sensitive);
 	}
 }
 
diff --git a/sshconnect.h b/sshconnect.h
index b475add..ad34ee6 100644
--- a/sshconnect.h
+++ b/sshconnect.h
@@ -1,4 +1,4 @@
-/*	$OpenBSD: sshconnect.h,v 1.13 2001/10/08 19:05:05 markus Exp $	*/
+/*	$OpenBSD: sshconnect.h,v 1.14 2002/05/23 19:24:30 markus Exp $	*/
 
 /*
  * Copyright (c) 2000 Markus Friedl.  All rights reserved.
@@ -26,20 +26,27 @@
 #ifndef SSHCONNECT_H
 #define SSHCONNECT_H
 
+typedef struct Sensitive Sensitive;
+struct Sensitive {
+        Key     **keys;
+        int     nkeys;
+        int     external_keysign;
+};
+
 int
 ssh_connect(const char *, struct sockaddr_storage *, u_short, int, int,
     int, struct passwd *, const char *);
 
 void
-ssh_login(Key **, int, const char *, struct sockaddr *, struct passwd *);
+ssh_login(Sensitive *, const char *, struct sockaddr *, struct passwd *);
 
 int	 verify_host_key(char *, struct sockaddr *, Key *);
 
 void	 ssh_kex(char *, struct sockaddr *);
 void	 ssh_kex2(char *, struct sockaddr *);
 
-void	 ssh_userauth1(const char *, const char *, char *, Key **, int);
-void	 ssh_userauth2(const char *, const char *, char *, Key **, int);
+void	 ssh_userauth1(const char *, const char *, char *, Sensitive *);
+void	 ssh_userauth2(const char *, const char *, char *, Sensitive *);
 
 void	 ssh_put_password(char *);
 
diff --git a/sshconnect1.c b/sshconnect1.c
index d2024a2..e28b7fc 100644
--- a/sshconnect1.c
+++ b/sshconnect1.c
@@ -13,7 +13,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect1.c,v 1.50 2002/04/21 16:25:06 stevesk Exp $");
+RCSID("$OpenBSD: sshconnect1.c,v 1.51 2002/05/23 19:24:30 markus Exp $");
 
 #include <openssl/bn.h>
 #include <openssl/md5.h>
@@ -1138,7 +1138,7 @@
  */
 void
 ssh_userauth1(const char *local_user, const char *server_user, char *host,
-    Key **keys, int nkeys)
+    Sensitive *sensitive)
 {
 #ifdef KRB5
 	krb5_context context = NULL;
@@ -1224,9 +1224,11 @@
 	 */
 	if ((supported_authentications & (1 << SSH_AUTH_RHOSTS_RSA)) &&
 	    options.rhosts_rsa_authentication) {
-		for (i = 0; i < nkeys; i++) {
-			if (keys[i] != NULL && keys[i]->type == KEY_RSA1 &&
-			    try_rhosts_rsa_authentication(local_user, keys[i]))
+		for (i = 0; i < sensitive->nkeys; i++) {
+			if (sensitive->keys[i] != NULL &&
+			    sensitive->keys[i]->type == KEY_RSA1 &&
+			    try_rhosts_rsa_authentication(local_user,
+			    sensitive->keys[i]))
 				goto success;
 		}
 	}
diff --git a/sshconnect2.c b/sshconnect2.c
index 1ee92ab..2736856 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -23,7 +23,7 @@
  */
 
 #include "includes.h"
-RCSID("$OpenBSD: sshconnect2.c,v 1.99 2002/03/26 15:58:46 markus Exp $");
+RCSID("$OpenBSD: sshconnect2.c,v 1.100 2002/05/23 19:24:30 markus Exp $");
 
 #include "ssh.h"
 #include "ssh2.h"
@@ -45,6 +45,8 @@
 #include "match.h"
 #include "dispatch.h"
 #include "canohost.h"
+#include "msg.h"
+#include "pathnames.h"
 
 /* import */
 extern char *client_version_string;
@@ -154,8 +156,7 @@
 	int last_key_hint;
 	AuthenticationConnection *agent;
 	/* hostbased */
-	Key **keys;
-	int nkeys;
+	Sensitive *sensitive;
 	/* kbd-interactive */
 	int info_req_seen;
 };
@@ -215,7 +216,7 @@
 
 void
 ssh_userauth2(const char *local_user, const char *server_user, char *host,
-    Key **keys, int nkeys)
+    Sensitive *sensitive)
 {
 	Authctxt authctxt;
 	int type;
@@ -255,8 +256,7 @@
 	authctxt.success = 0;
 	authctxt.method = authmethod_lookup("none");
 	authctxt.authlist = NULL;
-	authctxt.keys = keys;
-	authctxt.nkeys = nkeys;
+	authctxt.sensitive = sensitive;
 	authctxt.info_req_seen = 0;
 	if (authctxt.method == NULL)
 		fatal("ssh_userauth2: internal error: cannot send userauth none request");
@@ -893,6 +893,75 @@
 	packet_send();
 }
 
+static int
+ssh_keysign(
+    Key *key,
+    u_char **sigp, u_int *lenp,
+    u_char *data, u_int datalen)
+{
+	Buffer b;
+	pid_t pid;
+	int to[2], from[2], status, version = 1;
+
+	debug("ssh_keysign called");
+
+	if (fflush(stdout) != 0)
+		error("ssh_keysign: fflush: %s", strerror(errno));
+	if (pipe(to) < 0) {
+		error("ssh_keysign: pipe: %s", strerror(errno));
+		return -1;
+	}
+	if (pipe(from) < 0) {
+		error("ssh_keysign: pipe: %s", strerror(errno));
+		return -1;
+	}
+	if ((pid = fork()) < 0) {
+		error("ssh_keysign: fork: %s", strerror(errno));
+		return -1;
+	}
+	if (pid == 0) {
+		seteuid(getuid());
+		setuid(getuid());
+		close(from[0]);
+		if (dup2(from[1], STDOUT_FILENO) < 0)
+			fatal("ssh_keysign: dup2: %s", strerror(errno));
+		close(to[1]);
+		if (dup2(to[0], STDIN_FILENO) < 0)
+			fatal("ssh_keysign: dup2: %s", strerror(errno));
+		execlp(_PATH_SSH_KEY_SIGN, _PATH_SSH_KEY_SIGN, (char *) 0);
+		fatal("ssh_keysign: exec(%s): %s", _PATH_SSH_KEY_SIGN,
+		    strerror(errno));
+	}
+	close(from[1]);
+	close(to[0]);
+
+	buffer_init(&b);
+	buffer_put_string(&b, data, datalen);
+	msg_send(to[1], version, &b);
+
+	if (msg_recv(from[0], &b) < 0) {
+		debug("ssh_keysign: no reply");
+		buffer_clear(&b);
+		return -1;
+	}
+	if (buffer_get_char(&b) != version) {
+		debug("ssh_keysign: bad version");
+		buffer_clear(&b);
+		return -1;
+	}
+	*sigp = buffer_get_string(&b, lenp);
+	buffer_clear(&b);
+
+	close(from[0]);
+	close(to[1]);
+
+        while (waitpid(pid, &status, 0) < 0)
+                if (errno != EINTR)
+                        break;
+
+	return 0;
+}
+
 /*
  * this will be move to an external program (ssh-keysign) ASAP. ssh-keysign
  * will be setuid-root and the sbit can be removed from /usr/bin/ssh.
@@ -901,6 +970,7 @@
 userauth_hostbased(Authctxt *authctxt)
 {
 	Key *private = NULL;
+	Sensitive *sensitive = authctxt->sensitive;
 	Buffer b;
 	u_char *signature, *blob;
 	char *chost, *pkalg, *p;
@@ -909,12 +979,12 @@
 	int ok, i, len, found = 0;
 
 	/* check for a useful key */
-	for (i = 0; i < authctxt->nkeys; i++) {
-		private = authctxt->keys[i];
+	for (i = 0; i < sensitive->nkeys; i++) {
+		private = sensitive->keys[i];
 		if (private && private->type != KEY_RSA1) {
 			found = 1;
 			/* we take and free the key */
-			authctxt->keys[i] = NULL;
+			sensitive->keys[i] = NULL;
 			break;
 		}
 	}
@@ -956,7 +1026,12 @@
 #ifdef DEBUG_PK
 	buffer_dump(&b);
 #endif
-	ok = key_sign(private, &signature, &slen, buffer_ptr(&b), buffer_len(&b));
+	if (sensitive->external_keysign)
+		ok = ssh_keysign(private, &signature, &slen,
+		    buffer_ptr(&b), buffer_len(&b));
+	else
+		ok = key_sign(private, &signature, &slen,
+		    buffer_ptr(&b), buffer_len(&b));
 	key_free(private);
 	buffer_free(&b);
 	if (ok != 0) {