=================================================================== RCS file: /cvsrepo/anoncvs/cvs/src/usr.bin/ssh/monitor.c,v retrieving revision 1.143 retrieving revision 1.144 diff -u -r1.143 -r1.144 --- src/usr.bin/ssh/monitor.c 2015/02/13 18:57:00 1.143 +++ src/usr.bin/ssh/monitor.c 2015/02/16 22:13:32 1.144 @@ -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 @@ -591,12 +591,15 @@ 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__); @@ -607,9 +610,38 @@ /* * 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) { @@ -623,7 +655,7 @@ 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) { @@ -633,7 +665,8 @@ } 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)