From 523463a3a2a9bfc6cfc5afa01bae9147f76a37cc Mon Sep 17 00:00:00 2001 From: "djm@openbsd.org" Date: Mon, 16 Feb 2015 22:13:32 +0000 Subject: upstream commit Revise hostkeys@openssh.com hostkey learning extension. The client will not ask the server to prove ownership of the private halves of any hitherto-unseen hostkeys it offers to the client. Allow UpdateHostKeys option to take an 'ask' argument to let the user manually review keys offered. ok markus@ --- monitor.c | 45 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) (limited to 'monitor.c') diff --git a/monitor.c b/monitor.c index e97b20ef0..6e97def1c 100644 --- a/monitor.c +++ b/monitor.c @@ -1,4 +1,4 @@ -/* $OpenBSD: monitor.c,v 1.143 2015/02/13 18:57:00 markus Exp $ */ +/* $OpenBSD: monitor.c,v 1.144 2015/02/16 22:13:32 djm Exp $ */ /* * Copyright 2002 Niels Provos * Copyright 2002 Markus Friedl @@ -685,12 +685,15 @@ mm_answer_moduli(int sock, Buffer *m) int mm_answer_sign(int sock, Buffer *m) { + struct ssh *ssh = active_state; /* XXX */ extern int auth_sock; /* XXX move to state struct? */ struct sshkey *key; + struct sshbuf *sigbuf; u_char *p; u_char *signature; size_t datlen, siglen; - int r, keyid; + int r, keyid, is_proof = 0; + const char proof_req[] = "hostkeys-prove@openssh.com"; debug3("%s", __func__); @@ -701,9 +704,38 @@ mm_answer_sign(int sock, Buffer *m) /* * Supported KEX types use SHA1 (20 bytes), SHA256 (32 bytes), * SHA384 (48 bytes) and SHA512 (64 bytes). + * + * Otherwise, verify the signature request is for a hostkey + * proof. + * + * XXX perform similar check for KEX signature requests too? + * it's not trivial, since what is signed is the hash, rather + * than the full kex structure... */ - if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) - fatal("%s: data length incorrect: %zu", __func__, datlen); + if (datlen != 20 && datlen != 32 && datlen != 48 && datlen != 64) { + /* + * Construct expected hostkey proof and compare it to what + * the client sent us. + */ + if (session_id2_len == 0) /* hostkeys is never first */ + fatal("%s: bad data length: %zu", __func__, datlen); + if ((key = get_hostkey_public_by_index(keyid, ssh)) == NULL) + fatal("%s: no hostkey for index %d", __func__, keyid); + if ((sigbuf = sshbuf_new()) == NULL) + fatal("%s: sshbuf_new", __func__); + if ((r = sshbuf_put_string(sigbuf, session_id2, + session_id2_len) != 0) || + (r = sshbuf_put_cstring(sigbuf, proof_req)) != 0 || + (r = sshkey_puts(key, sigbuf)) != 0) + fatal("%s: couldn't prepare private key " + "proof buffer: %s", __func__, ssh_err(r)); + if (datlen != sshbuf_len(sigbuf) || + memcmp(p, sshbuf_ptr(sigbuf), sshbuf_len(sigbuf)) != 0) + fatal("%s: bad data length: %zu, hostkey proof len %zu", + __func__, datlen, sshbuf_len(sigbuf)); + sshbuf_free(sigbuf); + is_proof = 1; + } /* save session id, it will be passed on the first call */ if (session_id2_len == 0) { @@ -717,7 +749,7 @@ mm_answer_sign(int sock, Buffer *m) datafellows)) != 0) fatal("%s: sshkey_sign failed: %s", __func__, ssh_err(r)); - } else if ((key = get_hostkey_public_by_index(keyid, active_state)) != NULL && + } else if ((key = get_hostkey_public_by_index(keyid, ssh)) != NULL && auth_sock > 0) { if ((r = ssh_agent_sign(auth_sock, key, &signature, &siglen, p, datlen, datafellows)) != 0) { @@ -727,7 +759,8 @@ mm_answer_sign(int sock, Buffer *m) } else fatal("%s: no hostkey from index %d", __func__, keyid); - debug3("%s: signature %p(%zu)", __func__, signature, siglen); + debug3("%s: %s signature %p(%zu)", __func__, + is_proof ? "KEX" : "hostkey proof", signature, siglen); sshbuf_reset(m); if ((r = sshbuf_put_string(m, signature, siglen)) != 0) -- cgit v1.2.3