diff --git a/ssh/kex.c b/ssh/kex.c index 593596e..757f621 100644 --- a/ssh/kex.c +++ b/ssh/kex.c @@ -82,7 +82,7 @@ kex_names_valid(const char *names) } /* put algorithm proposal into buffer */ -static void +void kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX]) { u_int i; @@ -101,7 +101,7 @@ kex_prop2buf(Buffer *b, char *proposal[PROPOSAL_MAX]) } /* parse buffer and return algorithm proposal */ -static char ** +char ** kex_buf2prop(Buffer *raw, int *first_kex_follows) { Buffer b; @@ -131,7 +131,7 @@ kex_buf2prop(Buffer *raw, int *first_kex_follows) return proposal; } -static void +void kex_prop_free(char **proposal) { u_int i; @@ -262,6 +262,14 @@ kex_new(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) return kex; } +void +kex_free(Kex *kex) +{ + buffer_free(&kex->peer); + buffer_free(&kex->my); + xfree(kex); +} + Kex * kex_setup(struct ssh *ssh, char *proposal[PROPOSAL_MAX]) { diff --git a/ssh/kex.h b/ssh/kex.h index 6b82c37..0dc8e7f 100644 --- a/ssh/kex.h +++ b/ssh/kex.h @@ -144,6 +144,11 @@ int kex_names_valid(const char *); Kex *kex_new(struct ssh *, char *[PROPOSAL_MAX]); Kex *kex_setup(struct ssh *, char *[PROPOSAL_MAX]); void kex_finish(struct ssh *); +void kex_free(Kex *); + +char **kex_buf2prop(Buffer *, int *); +void kex_prop2buf(Buffer *, char *proposal[PROPOSAL_MAX]); +void kex_prop_free(char **); void kex_send_kexinit(struct ssh *); void kex_input_kexinit(int, u_int32_t, struct ssh *); diff --git a/ssh/ssh-proxy.txt b/ssh/ssh-proxy.txt index e94670b..13ae7e0 100644 --- a/ssh/ssh-proxy.txt +++ b/ssh/ssh-proxy.txt @@ -1,4 +1,9 @@ -% test -f /tmp/hk2 || ssh-keygen -N '' -t ecdsa -f /tmp/hk2 -% ssh-keyscan -t ecdsa 127.0.0.1 | sed 's/^[^ ]* //' > /tmp/hk.pub -% ./ssh-proxy/obj/ssh-proxy -f -S /tmp/hk2 -C /tmp/hk.pub -L 127.0.0.1:12345:127.0.0.1:22 -d -% ssh -o hostkeyalgorithms'='ecdsa-sha2-nistp256 -o hostkeyalias'='egal2 -v 127.0.0.1 -p 12345 +# example usage of proxy: +# generate hostkey +test -f /tmp/hk2 || ssh-keygen -N '' -t ecdsa -f /tmp/hk2 +# get known hostkey for target (127.0.0.1:22) +ssh-keyscan -t ecdsa 127.0.0.1 | sed 's/^[^ ]* //' > /tmp/hk.pub +# start proxy in foreground, enable packet dumping (except auth-requests) +./ssh-proxy/obj/ssh-proxy -S /tmp/hk2 -C /tmp/hk.pub -L 127.0.0.1:12345:127.0.0.1:22 -dDf +# connect +ssh -o hostkeyalias'='egal2 -v 127.0.0.1 -p 12345 diff --git a/ssh/ssh_api.c b/ssh/ssh_api.c index 0349d0f..4e80284 100644 --- a/ssh/ssh_api.c +++ b/ssh/ssh_api.c @@ -9,12 +9,14 @@ #include "ssh2.h" #include "version.h" #include "xmalloc.h" +#include "myproposal.h" #include void _ssh_exchange_banner(struct ssh *); char *_ssh_send_banner(struct ssh *); char *_ssh_read_banner(struct ssh *); +void _ssh_order_hostkeyalgs(struct ssh *); int _ssh_verify_host_key(struct sshkey *, struct ssh *); struct sshkey *_ssh_host_public_key(int, struct ssh *); struct sshkey *_ssh_host_private_key(int, struct ssh *); @@ -47,6 +49,7 @@ struct ssh * ssh_init(int is_server, struct kex_params *kex_params) { struct ssh *ssh; + char **proposal; static int called; if (!called) { @@ -59,7 +62,8 @@ ssh_init(int is_server, struct kex_params *kex_params) ssh_packet_set_server(ssh); /* Initialize key exchange */ - ssh->kex = kex_new(ssh, kex_params->proposal); + proposal = kex_params ? kex_params->proposal : myproposal; + ssh->kex = kex_new(ssh, proposal); ssh->kex->server = is_server; if (is_server) { ssh->kex->kex[KEX_DH_GRP1_SHA1] = kexdh_server; @@ -84,8 +88,8 @@ void ssh_free(struct ssh *ssh) { ssh_packet_close(ssh); - if (ssh->kex) - xfree(ssh->kex); + if (ssh->kex); + kex_free(ssh->kex); xfree(ssh); } @@ -316,8 +320,10 @@ _ssh_exchange_banner(struct ssh *ssh) } /* start initial kex as soon as we have exchanged the banners */ if (ssh->kex->server_version_string != NULL && - ssh->kex->client_version_string != NULL) + ssh->kex->client_version_string != NULL) { + _ssh_order_hostkeyalgs(ssh); kex_send_kexinit(ssh); + } } struct sshkey * @@ -362,3 +368,47 @@ _ssh_verify_host_key(struct sshkey *hostkey, struct ssh *ssh) } return (-1); /* failed */ } + +/* offer hostkey algorithms in kexinit depending on registered keys */ +void +_ssh_order_hostkeyalgs(struct ssh *ssh) +{ + struct key_entry *k; + char *orig, *avail, *oavail,*alg, *replace; + char **proposal; + size_t maxlen; + int ktype; + + /* XXX we de-serialize ssh->kex->my, modify it, and change it */ + proposal = kex_buf2prop(&ssh->kex->my, NULL); + orig = proposal[PROPOSAL_SERVER_HOST_KEY_ALGS]; + oavail = avail = xstrdup(orig); + maxlen = strlen(avail) + 1; + replace = xmalloc(maxlen); + *replace = '\0'; + while ((alg = strsep(&avail, ",")) && *alg != '\0') { + if ((ktype = sshkey_type_from_name(alg)) == KEY_UNSPEC) + fatal("%s: unknown alg %s", __func__, alg); + TAILQ_FOREACH(k, &ssh->public_keys, next) { + if (k->key->type == ktype || + (sshkey_is_cert(k->key) && k->key->type == + sshkey_type_plain(ktype))) { + if (*replace != '\0') + strlcat(replace, ",", maxlen); + strlcat(replace, alg, maxlen); + break; + } + } + } + xfree(oavail); + if (*replace != '\0') { + debug2("%s: orig/%d %s", __func__, ssh->kex->server, orig); + debug2("%s: replace/%d %s", __func__, ssh->kex->server, replace); + xfree(orig); + proposal[PROPOSAL_SERVER_HOST_KEY_ALGS] = replace; + kex_prop2buf(&ssh->kex->my, proposal); + } else { + xfree(replace); + } + kex_prop_free(proposal); +}