From b494ecfcfc17e26d9a214b7cbd4f943f50fc1aea Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 31 Mar 2019 08:35:31 +0100 Subject: [PATCH] Uppity: allow CLI override of the KEXINIT strings. This is an obviously useful test feature, since if nothing else it will let me exercise every individual crypto primitive, even the ones that the client-side configuration is too coarse-grained to describe in detail (such as the difference between CBC and CTR mode versions of the same cipher). --- ssh.h | 7 +++++++ ssh2transport.c | 33 ++++++++++++++++++++++++++------- ssh2transport.h | 5 ----- sshserver.h | 7 ++++++- unix/uxserver.c | 16 ++++++++++++++++ 5 files changed, 55 insertions(+), 13 deletions(-) diff --git a/ssh.h b/ssh.h index 7e0b3606..f16ff204 100644 --- a/ssh.h +++ b/ssh.h @@ -742,6 +742,13 @@ struct ssh_kexes { const ssh_kex *const *list; }; +/* Indices of the negotiation strings in the KEXINIT packet */ +enum kexlist { + KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER, + KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP, + NKEXLIST +}; + struct ssh_keyalg { /* Constructors that create an ssh_key */ ssh_key *(*new_pub) (const ssh_keyalg *self, ptrlen pub); diff --git a/ssh2transport.c b/ssh2transport.c index d73211ab..ca6ad601 100644 --- a/ssh2transport.c +++ b/ssh2transport.c @@ -9,6 +9,7 @@ #include "sshbpp.h" #include "sshppl.h" #include "sshcr.h" +#include "sshserver.h" #include "storage.h" #include "ssh2transport.h" #include "mpint.h" @@ -417,7 +418,7 @@ PktIn *ssh2_transport_pop(struct ssh2_transport_state *s) static void ssh2_write_kexinit_lists( BinarySink *pktout, struct kexinit_algorithm kexlists[NKEXLIST][MAXKEXLIST], - Conf *conf, int remote_bugs, + Conf *conf, const SshServerConfig *ssc, int remote_bugs, const char *hk_host, int hk_port, const ssh_keyalg *hk_prev, ssh_transient_hostkey_cache *thc, ssh_key *const *our_hostkeys, int our_nhostkeys, @@ -738,9 +739,13 @@ static void ssh2_write_kexinit_lists( */ for (i = 0; i < NKEXLIST; i++) { strbuf *list = strbuf_new(); - for (j = 0; j < MAXKEXLIST; j++) { - if (kexlists[i][j].name == NULL) break; - add_to_commasep(list, kexlists[i][j].name); + if (ssc && ssc->kex_override[i].ptr) { + put_datapl(list, ssc->kex_override[i]); + } else { + for (j = 0; j < MAXKEXLIST; j++) { + if (kexlists[i][j].name == NULL) break; + add_to_commasep(list, kexlists[i][j].name); + } } put_stringsb(pktout, list); } @@ -822,12 +827,26 @@ static bool ssh2_scan_kexinits( selected[i] = NULL; for (j = 0; j < MAXKEXLIST; j++) { - if (ptrlen_eq_string(found, kexlists[i][j].name)) { + if (kexlists[i][j].name && + ptrlen_eq_string(found, kexlists[i][j].name)) { selected[i] = &kexlists[i][j]; break; } } - assert(selected[i]); /* kexlists[] must cover one of the inputs */ + if (!selected[i]) { + /* + * In the client, this should never happen! But in the + * server, where we allow manual override on the command + * line of the exact KEXINIT strings, it can happen + * because the command line contained a typo. So we + * produce a reasonably useful message instead of an + * assertion failure. + */ + ssh_sw_abort(ssh, "Selected %s \"%.*s\" does not correspond to " + "any supported algorithm", + kexlist_descr[i], PTRLEN_PRINTF(found)); + return false; + } /* * If the kex or host key algorithm is not the first one in @@ -1063,7 +1082,7 @@ static void ssh2_transport_process_queue(PacketProtocolLayer *ppl) random_read(strbuf_append(s->outgoing_kexinit, 16), 16); ssh2_write_kexinit_lists( BinarySink_UPCAST(s->outgoing_kexinit), s->kexlists, - s->conf, s->ppl.remote_bugs, + s->conf, s->ssc, s->ppl.remote_bugs, s->savedhost, s->savedport, s->hostkey_alg, s->thc, s->hostkeys, s->nhostkeys, !s->got_session_id, s->can_gssapi_keyex, diff --git a/ssh2transport.h b/ssh2transport.h index ca5236a9..effacd19 100644 --- a/ssh2transport.h +++ b/ssh2transport.h @@ -18,11 +18,6 @@ #define DH_MIN_SIZE 1024 #define DH_MAX_SIZE 8192 -enum kexlist { - KEXLIST_KEX, KEXLIST_HOSTKEY, KEXLIST_CSCIPHER, KEXLIST_SCCIPHER, - KEXLIST_CSMAC, KEXLIST_SCMAC, KEXLIST_CSCOMP, KEXLIST_SCCOMP, - NKEXLIST -}; #define MAXKEXLIST 16 struct kexinit_algorithm { const char *name; diff --git a/sshserver.h b/sshserver.h index 2ed06b9d..3496d6ca 100644 --- a/sshserver.h +++ b/sshserver.h @@ -1,7 +1,12 @@ typedef struct AuthPolicy AuthPolicy; struct SshServerConfig { - ptrlen banner; /* banner.ptr == NULL indicates no banner */ + /* + * In all of these ptrlens, setting the 'ptr' member to NULL means + * that we're not overriding the default configuration. + */ + ptrlen banner; /* default here is 'no banner' */ + ptrlen kex_override[NKEXLIST]; bool exit_signal_numeric; /* mimic an old server bug */ }; diff --git a/unix/uxserver.c b/unix/uxserver.c index b14f93db..a8c72d0c 100644 --- a/unix/uxserver.c +++ b/unix/uxserver.c @@ -542,6 +542,22 @@ int main(int argc, char **argv) ssc.banner = ptrlen_from_strbuf(sb); } else if (longoptarg(arg, "--bannertext", &val, &argc, &argv)) { ssc.banner = ptrlen_from_asciz(val); + } else if (longoptarg(arg, "--kexinit-kex", &val, &argc, &argv)) { + ssc.kex_override[KEXLIST_KEX] = ptrlen_from_asciz(val); + } else if (longoptarg(arg, "--kexinit-hostkey", &val, &argc, &argv)) { + ssc.kex_override[KEXLIST_HOSTKEY] = ptrlen_from_asciz(val); + } else if (longoptarg(arg, "--kexinit-cscipher", &val, &argc, &argv)) { + ssc.kex_override[KEXLIST_CSCIPHER] = ptrlen_from_asciz(val); + } else if (longoptarg(arg, "--kexinit-csmac", &val, &argc, &argv)) { + ssc.kex_override[KEXLIST_CSMAC] = ptrlen_from_asciz(val); + } else if (longoptarg(arg, "--kexinit-cscomp", &val, &argc, &argv)) { + ssc.kex_override[KEXLIST_CSCOMP] = ptrlen_from_asciz(val); + } else if (longoptarg(arg, "--kexinit-sccipher", &val, &argc, &argv)) { + ssc.kex_override[KEXLIST_SCCIPHER] = ptrlen_from_asciz(val); + } else if (longoptarg(arg, "--kexinit-scmac", &val, &argc, &argv)) { + ssc.kex_override[KEXLIST_SCMAC] = ptrlen_from_asciz(val); + } else if (longoptarg(arg, "--kexinit-sccomp", &val, &argc, &argv)) { + ssc.kex_override[KEXLIST_SCCOMP] = ptrlen_from_asciz(val); } else if (longoptnoarg(arg, "--exitsignum")) { ssc.exit_signal_numeric = true; } else if (longoptarg(arg, "--sshlog", &val, &argc, &argv) ||