[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] agent: fall back to SHA1-based RSA signatures.
[Thread Prev] | [Thread Next]
- Subject: [PATCH] agent: fall back to SHA1-based RSA signatures.
- From: Mihai Moldovan <ionic@xxxxxxxx>
- Reply-to: libssh@xxxxxxxxxx
- Date: Sun, 31 Mar 2019 03:55:25 +0200
- To: libssh@xxxxxxxxxx
We automatically request a SHA2-based signature if the server supports it. Older versions of ssh-agent do not honor such flags and also don't fail, but instead return a legacy (SHA1-based) signature. Check the type of signature that was returned (first nested SSH String in the response), fail if it didn't match what was expected and re-execute ssh_userauth_agent_publickey() in fallback mode on signing errors with RSA-based keys, disabling usage of the SHA2 extensions. We will also always re-enable SHA2-based signatures (both in success and error cases), since the agent is only queried once for initial authentication. After that, we'll be able to use the stronger SHA2-based variants as usual. Signed-off-by: Mihai Moldovan <ionic@xxxxxxxx> --- src/agent.c | 32 ++++++++++++++++++++++++ src/auth.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/src/agent.c b/src/agent.c index 78be33e6..ddaf3447 100644 --- a/src/agent.c +++ b/src/agent.c @@ -479,10 +479,13 @@ ssh_string ssh_agent_sign_data(ssh_session session, ssh_buffer reply; ssh_string key_blob; ssh_string sig_blob; + ssh_string sig_type; unsigned int type = 0; unsigned int flags = 0; uint32_t dlen; int rc; + int memcmp_result = 0; + const char *expected = NULL; request = ssh_buffer_new(); if (request == NULL) { @@ -587,6 +590,35 @@ ssh_string ssh_agent_sign_data(ssh_session session, sig_blob = ssh_buffer_get_ssh_string(reply); ssh_buffer_free(reply); + /* Check signature type for RSA keys. */ + if ((pubkey->type == SSH_KEYTYPE_RSA) && (sig_blob)) { + expected = "ssh-rsa"; + if (session->extensions & SSH_EXT_SIG_RSA_SHA512) { + expected = "rsa-sha2-512"; + } else if (session->extensions & SSH_EXT_SIG_RSA_SHA256) { + expected = "rsa-sha2-256"; + } + + /* + * Do *not* free sig_type! + * It's a reference to the nested ssh_string inside of sig_blob. + */ + sig_type = ssh_string_data(sig_blob); + + if (sig_type == NULL) { + SSH_STRING_FREE(sig_blob); + return NULL; + } + + memcmp_result = memcmp(expected, ssh_string_data(sig_type), MIN(strlen(expected), ssh_string_len(sig_type))); + + if (memcmp_result != 0) { + SSH_LOG(SSH_LOG_DEBUG, "expected signature type '%s', got '%s' from agent", expected, ssh_string_data(sig_type)); + SSH_STRING_FREE(sig_blob); + return NULL; + } + } + return sig_blob; } diff --git a/src/auth.c b/src/auth.c index 2e26d97f..f32dee3d 100644 --- a/src/auth.c +++ b/src/auth.c @@ -746,12 +746,15 @@ fail: #ifndef _WIN32 static int ssh_userauth_agent_publickey(ssh_session session, const char *username, - ssh_key pubkey) + ssh_key pubkey, + int fallback) { ssh_string pubkey_s = NULL; ssh_string sig_blob = NULL; const char *sig_type_c = NULL; int rc; + int disabled_flags = 0; + int rsa_ext_flags = 0; switch(session->pending_call_state) { case SSH_PENDING_CALL_NONE: @@ -777,6 +780,19 @@ static int ssh_userauth_agent_publickey(ssh_session session, if (rc < 0) { goto fail; } + rsa_ext_flags |= session->extensions & SSH_EXT_SIG_RSA_SHA512; + rsa_ext_flags |= session->extensions & SSH_EXT_SIG_RSA_SHA256; + if (fallback) { + /* + * Agent only provides SHA1 signatures, so disable the SHA2-based variants. + * Keep track of the current/old state and reapply it later if needed, though. + * + * N.B.: fallback being set implicitly means that the key type must be RSA. + */ + disabled_flags = rsa_ext_flags; + session->extensions &= ~SSH_EXT_SIG_RSA_SHA512; + session->extensions &= ~SSH_EXT_SIG_RSA_SHA256; + } sig_type_c = ssh_key_get_signature_algorithm(session, pubkey->type); /* Check if the given public key algorithm is allowed */ @@ -786,6 +802,7 @@ static int ssh_userauth_agent_publickey(ssh_session session, " PUBLICKEY_ACCEPTED_TYPES configuration option", sig_type_c); SSH_STRING_FREE(pubkey_s); + session->extensions |= disabled_flags; return SSH_AUTH_DENIED; } @@ -807,7 +824,12 @@ static int ssh_userauth_agent_publickey(ssh_session session, /* sign the buffer with the private key */ sig_blob = ssh_pki_do_sign_agent(session, session->out_buffer, pubkey); if (sig_blob == NULL) { - goto fail; + if (!fallback) { + goto retry_if_rsa; + } + else { + goto fail; + } } rc = ssh_buffer_add_ssh_string(session->out_buffer, sig_blob); @@ -821,6 +843,7 @@ static int ssh_userauth_agent_publickey(ssh_session session, session->pending_call_state = SSH_PENDING_CALL_AUTH_AGENT; rc = ssh_packet_send(session); if (rc == SSH_ERROR) { + session->extensions |= disabled_flags; return SSH_AUTH_ERROR; } @@ -830,11 +853,50 @@ pending: session->pending_call_state = SSH_PENDING_CALL_NONE; } + /* Reactivate SHA2-based signatures if needed. + * + * We're only ever using the agent once (for each key) while authenticating, + * so don't disable stronger algorithms after the initial auth is done + * (using a weaker, but at least supported one). + */ + session->extensions |= disabled_flags; + return rc; + +retry_if_rsa: + ssh_buffer_reinit(session->out_buffer); + /* + * Double-free of pubkey_s in fall-through mode to fail, but this is not + * a problem, since the second free operation will just be a no-op thanks + * to SSH_STRING_FREE(). + */ + SSH_STRING_FREE(pubkey_s); + /* + * We're explicitly checking if rsa_ext_flags is non-zero because + * retrying doesn't make sense if there are no algos to disable in the + * first place. + * + * In such a case, the code will use the implicit fall-through to fail. + */ + if ((pubkey->type == SSH_KEYTYPE_RSA) && (rsa_ext_flags)) { + if (!fallback) { + /* + * Signature request failed, it might have been due to an unexpected signature. + * Let's retry with SHA2-based signatures disabled. + */ + return ssh_userauth_agent_publickey(session, username, pubkey, 1); + } + } + /* + * Note the implicit fall-through to fail for non-RSA key types or + * retry-in-fallback-mode situations. + */ + fail: ssh_set_error_oom(session); ssh_buffer_reinit(session->out_buffer); SSH_STRING_FREE(pubkey_s); + session->extensions |= disabled_flags; return SSH_AUTH_ERROR; } @@ -947,7 +1009,7 @@ int ssh_userauth_agent(ssh_session session, state->state = SSH_AGENT_STATE_AUTH; } if (state->state == SSH_AGENT_STATE_AUTH) { - rc = ssh_userauth_agent_publickey(session, username, state->pubkey); + rc = ssh_userauth_agent_publickey(session, username, state->pubkey, 0); if (rc == SSH_AUTH_AGAIN) return rc; ssh_string_free_char(state->comment); @@ -1322,7 +1384,7 @@ int ssh_userauth_agent_pubkey(ssh_session session, key->dsa = publickey->dsa_pub; key->rsa = publickey->rsa_pub; - rc = ssh_userauth_agent_publickey(session, username, key); + rc = ssh_userauth_agent_publickey(session, username, key, 0); key->dsa = NULL; key->rsa = NULL; -- 2.21.0
Re: [PATCH] agent: fall back to SHA1-based RSA signatures. | Mihai Moldovan <ionic@xxxxxxxx> |