From 6d7a6d47e68e8368216f3ab1a0d071db32d20a11 Mon Sep 17 00:00:00 2001 From: Simon Tatham Date: Sun, 31 Mar 2019 21:08:55 +0100 Subject: [PATCH] Uppity: option to use a pregenerated key for RSA kex. As and when I make this SSH server into a test suite, I'm not going to want to wait for a gratuitous RSA key generation in every test run. So now you can provide one in advance. It has to be in SSH-1 format, because that's the format for which I happen to already have internal API routines that return an RSAKey instead of an opaque ssh_key. But since you also have to store it without a passphrase, that doesn't really matter anyway. --- ssh2kex-client.c | 2 ++ ssh2kex-server.c | 27 +++++++++++++++++++++++---- ssh2transport.c | 4 +++- ssh2transport.h | 1 + sshserver.h | 2 ++ unix/uxserver.c | 29 +++++++++++++++++++++++++++++ 6 files changed, 60 insertions(+), 5 deletions(-) diff --git a/ssh2kex-client.c b/ssh2kex-client.c index 4ed074f1..1192a26a 100644 --- a/ssh2kex-client.c +++ b/ssh2kex-client.c @@ -552,6 +552,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) *aborted = true; return; } + s->rsa_kex_key_needs_freeing = true; put_stringpl(s->exhash, rsakeydata); @@ -611,6 +612,7 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) ssh_rsakex_freekey(s->rsa_kex_key); s->rsa_kex_key = NULL; + s->rsa_kex_key_needs_freeing = false; crMaybeWaitUntilV((pktin = ssh2_transport_pop(s)) != NULL); if (pktin->type != SSH2_MSG_KEXRSA_DONE) { diff --git a/ssh2kex-server.c b/ssh2kex-server.c index d86a59f6..d83d2336 100644 --- a/ssh2kex-server.c +++ b/ssh2kex-server.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" @@ -243,13 +244,27 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) ssh_hash_alg(s->exhash)->text_name); s->ppl.bpp->pls->kctx = SSH2_PKTCTX_RSAKEX; - { - const struct ssh_rsa_kex_extra *extra = - (const struct ssh_rsa_kex_extra *)s->kex_alg->extra; + const struct ssh_rsa_kex_extra *extra = + (const struct ssh_rsa_kex_extra *)s->kex_alg->extra; + + if (s->ssc && s->ssc->rsa_kex_key) { + int klen = ssh_rsakex_klen(s->ssc->rsa_kex_key); + if (klen >= extra->minklen) { + ppl_logevent("Using configured %d-bit RSA key", klen); + s->rsa_kex_key = s->ssc->rsa_kex_key; + } else { + ppl_logevent("Configured %d-bit RSA key is too short (min %d)", + klen, extra->minklen); + } + } + + if (!s->rsa_kex_key) { + ppl_logevent("Generating a %d-bit RSA key", extra->minklen); s->rsa_kex_key = snew(RSAKey); rsa_generate(s->rsa_kex_key, extra->minklen, no_progress, NULL); s->rsa_kex_key->comment = NULL; + s->rsa_kex_key_needs_freeing = true; } pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXRSA_PUBKEY); @@ -288,8 +303,12 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) return; } - ssh_rsakex_freekey(s->rsa_kex_key); + if (s->rsa_kex_key_needs_freeing) { + ssh_rsakex_freekey(s->rsa_kex_key); + sfree(s->rsa_kex_key); + } s->rsa_kex_key = NULL; + s->rsa_kex_key_needs_freeing = false; pktout = ssh_bpp_new_pktout(s->ppl.bpp, SSH2_MSG_KEXRSA_DONE); put_stringsb(pktout, finalise_and_sign_exhash(s)); diff --git a/ssh2transport.c b/ssh2transport.c index efd948f0..4a6b8c7b 100644 --- a/ssh2transport.c +++ b/ssh2transport.c @@ -220,8 +220,10 @@ static void ssh2_transport_free(PacketProtocolLayer *ppl) if (s->K) mp_free(s->K); if (s->dh_ctx) dh_cleanup(s->dh_ctx); - if (s->rsa_kex_key) + if (s->rsa_kex_key_needs_freeing) { ssh_rsakex_freekey(s->rsa_kex_key); + sfree(s->rsa_kex_key); + } if (s->ecdh_key) ssh_ecdhkex_freekey(s->ecdh_key); if (s->exhash) diff --git a/ssh2transport.h b/ssh2transport.h index 3b2955cd..8dc23304 100644 --- a/ssh2transport.h +++ b/ssh2transport.h @@ -173,6 +173,7 @@ struct ssh2_transport_state { char *keystr, *fingerprint; ssh_key *hkey; /* actual host key */ RSAKey *rsa_kex_key; /* for RSA kex */ + bool rsa_kex_key_needs_freeing; ecdh_key *ecdh_key; /* for ECDH kex */ unsigned char exchange_hash[MAX_HASH_LEN]; bool can_gssapi_keyex; diff --git a/sshserver.h b/sshserver.h index 9a7696af..6d6d14ef 100644 --- a/sshserver.h +++ b/sshserver.h @@ -1,6 +1,8 @@ typedef struct AuthPolicy AuthPolicy; struct SshServerConfig { + RSAKey *rsa_kex_key; + /* * In all of these ptrlens, setting the 'ptr' member to NULL means * that we're not overriding the default configuration. diff --git a/unix/uxserver.c b/unix/uxserver.c index 524adef8..2110976c 100644 --- a/unix/uxserver.c +++ b/unix/uxserver.c @@ -579,6 +579,35 @@ int main(int argc, char **argv) key_type_to_str(keytype)); exit(1); } + } else if (longoptarg(arg, "--rsakexkey", &val, &argc, &argv)) { + Filename *keyfile; + int keytype; + const char *error; + + keyfile = filename_from_str(val); + keytype = key_type(keyfile); + + if (keytype != SSH_KEYTYPE_SSH1) { + fprintf(stderr, "%s: '%s' is not loadable as an SSH-1 format " + "private key (%s)", appname, val, + key_type_to_str(keytype)); + exit(1); + } + + if (ssc.rsa_kex_key) { + freersakey(ssc.rsa_kex_key); + } else { + ssc.rsa_kex_key = snew(RSAKey); + } + + if (!rsa_ssh1_loadkey(keyfile, ssc.rsa_kex_key, + NULL, &error)) { + fprintf(stderr, "%s: unable to load RSA kex key '%s': " + "%s\n", appname, val, error); + exit(1); + } + + ssc.rsa_kex_key->sshk.vt = &ssh_rsa; } else if (longoptarg(arg, "--userkey", &val, &argc, &argv)) { Filename *keyfile; int keytype;