- markus@cvs.openbsd.org 2013/07/19 07:37:48
     [auth.h kex.h kexdhs.c kexecdhs.c kexgexs.c monitor.c servconf.c]
     [servconf.h session.c sshd.c sshd_config.5]
     add ssh-agent(1) support to sshd(8); allows encrypted hostkeys,
     or hostkeys on smartcards; most of the work by Zev Weiss; bz #1974
     ok djm@
diff --git a/sshd.c b/sshd.c
index 1306a62..174cc7a 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.403 2013/06/05 02:27:50 dtucker Exp $ */
+/* $OpenBSD: sshd.c,v 1.404 2013/07/19 07:37:48 markus Exp $ */
 /*
  * Author: Tatu Ylonen <ylo@cs.hut.fi>
  * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -106,6 +106,7 @@
 #include "canohost.h"
 #include "hostfile.h"
 #include "auth.h"
+#include "authfd.h"
 #include "misc.h"
 #include "msg.h"
 #include "dispatch.h"
@@ -194,6 +195,10 @@
 /* for rekeying XXX fixme */
 Kex *xxx_kex;
 
+/* Daemon's agent connection */
+AuthenticationConnection *auth_conn = NULL;
+int have_agent = 0;
+
 /*
  * Any really sensitive data in the application is contained in this
  * structure. The idea is that this structure could be locked into memory so
@@ -206,6 +211,7 @@
 	Key	*server_key;		/* ephemeral server key */
 	Key	*ssh1_host_key;		/* ssh1 host key */
 	Key	**host_keys;		/* all private host keys */
+	Key	**host_pubkeys;		/* all public host keys */
 	Key	**host_certificates;	/* all public host certificates */
 	int	have_ssh1_key;
 	int	have_ssh2_key;
@@ -653,6 +659,8 @@
 		debug2("Network child is on pid %ld", (long)pid);
 
 		pmonitor->m_pid = pid;
+		if (have_agent)
+			auth_conn = ssh_get_authentication_connection();
 		if (box != NULL)
 			ssh_sandbox_parent_preauth(box, pid);
 		monitor_child_preauth(authctxt, pmonitor);
@@ -767,6 +775,8 @@
 	for (i = 0; i < options.num_host_key_files; i++) {
 		key = sensitive_data.host_keys[i];
 		if (key == NULL)
+			key = sensitive_data.host_pubkeys[i];
+		if (key == NULL)
 			continue;
 		switch (key->type) {
 		case KEY_RSA:
@@ -819,6 +829,8 @@
 			break;
 		default:
 			key = sensitive_data.host_keys[i];
+			if (key == NULL && !need_private)
+				key = sensitive_data.host_pubkeys[i];
 			break;
 		}
 		if (key != NULL && key->type == type)
@@ -848,6 +860,14 @@
 	return (sensitive_data.host_keys[ind]);
 }
 
+Key *
+get_hostkey_public_by_index(int ind)
+{
+	if (ind < 0 || ind >= options.num_host_key_files)
+		return (NULL);
+	return (sensitive_data.host_pubkeys[ind]);
+}
+
 int
 get_hostkey_index(Key *key)
 {
@@ -860,6 +880,8 @@
 		} else {
 			if (key == sensitive_data.host_keys[i])
 				return (i);
+			if (key == sensitive_data.host_pubkeys[i])
+				return (i);
 		}
 	}
 	return (-1);
@@ -1344,6 +1366,8 @@
 	u_int64_t ibytes, obytes;
 	mode_t new_umask;
 	Key *key;
+	Key *pubkey;
+	int keytype;
 	Authctxt *authctxt;
 	struct connection_info *connection_info = get_connection_info(0, 0);
 
@@ -1623,22 +1647,45 @@
 	}
 	endpwent();
 
-	/* load private host keys */
+	/* load host keys */
 	sensitive_data.host_keys = xcalloc(options.num_host_key_files,
 	    sizeof(Key *));
-	for (i = 0; i < options.num_host_key_files; i++)
+	sensitive_data.host_pubkeys = xcalloc(options.num_host_key_files,
+	    sizeof(Key *));
+	for (i = 0; i < options.num_host_key_files; i++) {
 		sensitive_data.host_keys[i] = NULL;
+		sensitive_data.host_pubkeys[i] = NULL;
+	}
+
+	if (options.host_key_agent) {
+		if (strcmp(options.host_key_agent, SSH_AUTHSOCKET_ENV_NAME))
+			setenv(SSH_AUTHSOCKET_ENV_NAME,
+			    options.host_key_agent, 1);
+		have_agent = ssh_agent_present();
+	}
 
 	for (i = 0; i < options.num_host_key_files; i++) {
 		key = key_load_private(options.host_key_files[i], "", NULL);
+		pubkey = key_load_public(options.host_key_files[i], NULL);
 		sensitive_data.host_keys[i] = key;
-		if (key == NULL) {
+		sensitive_data.host_pubkeys[i] = pubkey;
+
+		if (key == NULL && pubkey != NULL && pubkey->type != KEY_RSA1 &&
+		    have_agent) {
+			debug("will rely on agent for hostkey %s",
+			    options.host_key_files[i]);
+			keytype = pubkey->type;
+		} else if (key != NULL) {
+			keytype = key->type;
+		} else {
 			error("Could not load host key: %s",
 			    options.host_key_files[i]);
 			sensitive_data.host_keys[i] = NULL;
+			sensitive_data.host_pubkeys[i] = NULL;
 			continue;
 		}
-		switch (key->type) {
+
+		switch (keytype) {
 		case KEY_RSA1:
 			sensitive_data.ssh1_host_key = key;
 			sensitive_data.have_ssh1_key = 1;
@@ -1649,8 +1696,8 @@
 			sensitive_data.have_ssh2_key = 1;
 			break;
 		}
-		debug("private host key: #%d type %d %s", i, key->type,
-		    key_type(key));
+		debug("private host key: #%d type %d %s", i, keytype,
+		    key_type(key ? key : pubkey));
 	}
 	if ((options.protocol & SSH_PROTO_1) && !sensitive_data.have_ssh1_key) {
 		logit("Disabling protocol version 1. Could not load host key");
@@ -2020,9 +2067,11 @@
 	buffer_init(&loginmsg);
 	auth_debug_reset();
 
-	if (use_privsep)
+	if (use_privsep) {
 		if (privsep_preauth(authctxt) == 1)
 			goto authenticated;
+	} else if (compat20 && have_agent)
+		auth_conn = ssh_get_authentication_connection();
 
 	/* perform the key exchange */
 	/* authenticate user and start session */
@@ -2336,6 +2385,23 @@
 	packet_write_wait();
 }
 
+void
+sshd_hostkey_sign(Key *privkey, Key *pubkey, u_char **signature, u_int *slen,
+    u_char *data, u_int dlen)
+{
+	if (privkey) {
+		if (PRIVSEP(key_sign(privkey, signature, slen, data, dlen) < 0))
+			fatal("%s: key_sign failed", __func__);
+	} else if (use_privsep) {
+		if (mm_key_sign(pubkey, signature, slen, data, dlen) < 0)
+			fatal("%s: pubkey_sign failed", __func__);
+	} else {
+		if (ssh_agent_sign(auth_conn, pubkey, signature, slen, data,
+		    dlen))
+			fatal("%s: ssh_agent_sign failed", __func__);
+	}
+}
+
 /*
  * SSH2 key exchange: diffie-hellman-group1-sha1
  */
@@ -2386,6 +2452,7 @@
 	kex->load_host_public_key=&get_hostkey_public_by_type;
 	kex->load_host_private_key=&get_hostkey_private_by_type;
 	kex->host_key_index=&get_hostkey_index;
+	kex->sign = sshd_hostkey_sign;
 
 	xxx_kex = kex;