diff --git a/authfd.c b/authfd.c index 9f092f7cf..df5533ac5 100644 --- a/authfd.c +++ b/authfd.c @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.c,v 1.127 2021/01/26 00:46:17 djm Exp $ */ +/* $OpenBSD: authfd.c,v 1.128 2021/12/19 22:08:48 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -650,3 +650,32 @@ ssh_remove_all_identities(int sock, int version) sshbuf_free(msg); return r; } + +/* Binds a session ID to a hostkey via the initial KEX signature. */ +int +ssh_agent_bind_hostkey(int sock, const struct sshkey *key, + const struct sshbuf *session_id, const struct sshbuf *signature, + int forwarding) +{ + struct sshbuf *msg; + int r; + + if (key == NULL || session_id == NULL || signature == NULL) + return SSH_ERR_INVALID_ARGUMENT; + if ((msg = sshbuf_new()) == NULL) + return SSH_ERR_ALLOC_FAIL; + if ((r = sshbuf_put_u8(msg, SSH_AGENTC_EXTENSION)) != 0 || + (r = sshbuf_put_cstring(msg, "session-bind@openssh.com")) != 0 || + (r = sshkey_puts(key, msg)) != 0 || + (r = sshbuf_put_stringb(msg, session_id)) != 0 || + (r = sshbuf_put_stringb(msg, signature)) != 0 || + (r = sshbuf_put_u8(msg, forwarding ? 1 : 0)) != 0) + goto out; + if ((r = ssh_request_reply_decode(sock, msg)) != 0) + goto out; + /* success */ + r = 0; + out: + sshbuf_free(msg); + return r; +} diff --git a/authfd.h b/authfd.h index 4fbf82f8c..fe1ed8a1a 100644 --- a/authfd.h +++ b/authfd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: authfd.h,v 1.49 2020/06/26 05:03:36 djm Exp $ */ +/* $OpenBSD: authfd.h,v 1.50 2021/12/19 22:08:48 djm Exp $ */ /* * Author: Tatu Ylonen @@ -16,6 +16,8 @@ #ifndef AUTHFD_H #define AUTHFD_H +struct sshbuf; + /* List of identities returned by ssh_fetch_identitylist() */ struct ssh_identitylist { size_t nkeys; @@ -43,6 +45,10 @@ int ssh_agent_sign(int sock, const struct sshkey *key, u_char **sigp, size_t *lenp, const u_char *data, size_t datalen, const char *alg, u_int compat); +int ssh_agent_bind_hostkey(int sock, const struct sshkey *key, + const struct sshbuf *session_id, const struct sshbuf *signature, + int forwarding); + /* Messages for the authentication agent connection. */ #define SSH_AGENTC_REQUEST_RSA_IDENTITIES 1 #define SSH_AGENT_RSA_IDENTITIES_ANSWER 2 @@ -76,6 +82,9 @@ int ssh_agent_sign(int sock, const struct sshkey *key, #define SSH2_AGENTC_ADD_ID_CONSTRAINED 25 #define SSH_AGENTC_ADD_SMARTCARD_KEY_CONSTRAINED 26 +/* generic extension mechanism */ +#define SSH_AGENTC_EXTENSION 27 + #define SSH_AGENT_CONSTRAIN_LIFETIME 1 #define SSH_AGENT_CONSTRAIN_CONFIRM 2 #define SSH_AGENT_CONSTRAIN_MAXSIGN 3 diff --git a/clientloop.c b/clientloop.c index 147dfd21e..7652b35aa 100644 --- a/clientloop.c +++ b/clientloop.c @@ -1,4 +1,4 @@ -/* $OpenBSD: clientloop.c,v 1.371 2021/11/18 21:32:11 djm Exp $ */ +/* $OpenBSD: clientloop.c,v 1.372 2021/12/19 22:08:48 djm Exp $ */ /* * Author: Tatu Ylonen * Copyright (c) 1995 Tatu Ylonen , Espoo, Finland @@ -1597,6 +1597,12 @@ client_request_agent(struct ssh *ssh, const char *request_type, int rchan) debug_fr(r, "ssh_get_authentication_socket"); return NULL; } + if ((r = ssh_agent_bind_hostkey(sock, ssh->kex->initial_hostkey, + ssh->kex->session_id, ssh->kex->initial_sig, 1)) == 0) + debug_f("bound agent to hostkey"); + else + debug2_fr(r, "ssh_agent_bind_hostkey"); + c = channel_new(ssh, "authentication agent connection", SSH_CHANNEL_OPEN, sock, sock, -1, CHAN_X11_WINDOW_DEFAULT, CHAN_TCP_PACKET_DEFAULT, 0, diff --git a/sshconnect2.c b/sshconnect2.c index fea50fab6..672938a31 100644 --- a/sshconnect2.c +++ b/sshconnect2.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sshconnect2.c,v 1.351 2021/07/23 05:24:02 djm Exp $ */ +/* $OpenBSD: sshconnect2.c,v 1.352 2021/12/19 22:08:48 djm Exp $ */ /* * Copyright (c) 2000 Markus Friedl. All rights reserved. * Copyright (c) 2008 Damien Miller. All rights reserved. @@ -391,7 +391,7 @@ void userauth(struct ssh *, char *); static void pubkey_cleanup(struct ssh *); static int sign_and_send_pubkey(struct ssh *ssh, Identity *); -static void pubkey_prepare(Authctxt *); +static void pubkey_prepare(struct ssh *, Authctxt *); static void pubkey_reset(Authctxt *); static struct sshkey *load_identity_file(Identity *); @@ -465,7 +465,7 @@ ssh_userauth2(struct ssh *ssh, const char *local_user, authctxt.mech_tried = 0; #endif authctxt.agent_fd = -1; - pubkey_prepare(&authctxt); + pubkey_prepare(ssh, &authctxt); if (authctxt.method == NULL) { fatal_f("internal error: cannot send userauth none request"); } @@ -1631,6 +1631,36 @@ key_type_allowed_by_config(struct sshkey *key) return 0; } +/* obtain a list of keys from the agent */ +static int +get_agent_identities(struct ssh *ssh, int *agent_fdp, + struct ssh_identitylist **idlistp) +{ + int r, agent_fd; + struct ssh_identitylist *idlist; + + if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { + if (r != SSH_ERR_AGENT_NOT_PRESENT) + debug_fr(r, "ssh_get_authentication_socket"); + return r; + } + if ((r = ssh_agent_bind_hostkey(agent_fd, ssh->kex->initial_hostkey, + ssh->kex->session_id, ssh->kex->initial_sig, 0)) == 0) + debug_f("bound agent to hostkey"); + else + debug2_fr(r, "ssh_agent_bind_hostkey"); + + if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { + debug_fr(r, "ssh_fetch_identitylist"); + close(agent_fd); + return r; + } + /* success */ + *agent_fdp = agent_fd; + *idlistp = idlist; + debug_f("agent returned %zu keys", idlist->nkeys); + return 0; +} /* * try keys in the following order: @@ -1641,7 +1671,7 @@ key_type_allowed_by_config(struct sshkey *key) * 5. keys that are only listed in the config file */ static void -pubkey_prepare(Authctxt *authctxt) +pubkey_prepare(struct ssh *ssh, Authctxt *authctxt) { struct identity *id, *id2, *tmp; struct idlist agent, files, *preferred; @@ -1703,14 +1733,7 @@ pubkey_prepare(Authctxt *authctxt) TAILQ_INSERT_TAIL(preferred, id, next); } /* list of keys supported by the agent */ - if ((r = ssh_get_authentication_socket(&agent_fd)) != 0) { - if (r != SSH_ERR_AGENT_NOT_PRESENT) - debug_fr(r, "ssh_get_authentication_socket"); - } else if ((r = ssh_fetch_identitylist(agent_fd, &idlist)) != 0) { - if (r != SSH_ERR_AGENT_NO_IDENTITIES) - debug_fr(r, "ssh_fetch_identitylist"); - close(agent_fd); - } else { + if ((r = get_agent_identities(ssh, &agent_fd, &idlist)) == 0) { for (j = 0; j < idlist->nkeys; j++) { found = 0; TAILQ_FOREACH(id, &files, next) {