diff options
-rw-r--r-- | src/dh-gex.c | 107 |
1 files changed, 97 insertions, 10 deletions
diff --git a/src/dh-gex.c b/src/dh-gex.c index 1bd1b49a..18e1b477 100644 --- a/src/dh-gex.c +++ b/src/dh-gex.c @@ -32,6 +32,8 @@ #include "libssh/buffer.h" #include "libssh/session.h" +#define PMIN 1534 + static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group); static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply); @@ -59,9 +61,15 @@ int ssh_client_dhgex_init(ssh_session session){ goto error; } + session->next_crypto->dh_pmin = PMIN; + session->next_crypto->dh_pn = 2048; + session->next_crypto->dh_pmax = 8192; /* Minimum group size, preferred group size, maximum group size */ rc = ssh_buffer_pack(session->out_buffer, "bddd", - SSH2_MSG_KEX_DH_GEX_REQUEST, 1534, 2048, 8192); + SSH2_MSG_KEX_DH_GEX_REQUEST, + session->next_crypto->dh_pmin, + session->next_crypto->dh_pn, + session->next_crypto->dh_pmax); if (rc != SSH_OK){ goto error; } @@ -75,7 +83,7 @@ int ssh_client_dhgex_init(ssh_session session){ } return rc; error: - ssh_dh_cleanup(session); + ssh_dh_cleanup(session->next_crypto); return SSH_ERROR; } @@ -89,6 +97,9 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group){ bignum pmin1=NULL, one=NULL; bignum_CTX ctx = bignum_ctx_new(); + (void) type; + (void) user; + SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_KEX_DH_GEX_GROUP received"); if (bignum_ctx_invalid(ctx)) { @@ -114,8 +125,8 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group){ /* basic checks */ bignum_set_word(one, 1); blen = bignum_num_bits(session->next_crypto->p); - if (blen < 1534 || blen > 8192){ - ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p: %d not in [1534:8192]", blen); + if (blen < PMIN || blen > 8192){ + ssh_set_error(session, SSH_FATAL, "Invalid dh group parameter p: %d not in [%d:8192]", blen, PMIN); goto error; } if (bignum_cmp(session->next_crypto->p, one) <= 0){ @@ -139,6 +150,11 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group){ if (rc == SSH_ERROR){ goto error; } + session->next_crypto->e = bignum_new(); + if (session->next_crypto->e == NULL){ + ssh_set_error_oom(session); + goto error; + } bignum_mod_exp(session->next_crypto->e, session->next_crypto->g, session->next_crypto->x, session->next_crypto->p, ctx); @@ -159,7 +175,7 @@ SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_group){ error: bignum_safe_free(one); bignum_safe_free(pmin1); - ssh_dh_cleanup(session); + ssh_dh_cleanup(session->next_crypto); session->session_state=SSH_SESSION_STATE_ERROR; return SSH_PACKET_USED; } @@ -197,10 +213,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_dhgex_reply) { } SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - return SSH_PACKET_USED; error: - ssh_dh_cleanup(session); + ssh_dh_cleanup(session->next_crypto); session->session_state=SSH_SESSION_STATE_ERROR; return SSH_PACKET_USED; } @@ -358,6 +373,8 @@ static int ssh_retrieve_dhgroup(int pmin, int pn, int pmax, int *size, bignum *p if (*size == 0){ return SSH_ERROR; } + *p=NULL; + *g=NULL; rc = bignum_hex2bn(generator, g); if (rc == 0){ goto error; @@ -379,13 +396,14 @@ error: } static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request); +static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_init); static ssh_packet_callback dhgex_server_callbacks[]= { NULL, /* SSH_MSG_KEX_DH_GEX_REQUEST_OLD */ NULL, /* SSH_MSG_KEX_DH_GEX_GROUP */ - NULL, /* SSH_MSG_KEX_DH_GEX_INIT */ - NULL, /* SSH_MSG_KEX_DH_GEX_REPLY */ - ssh_packet_server_dhgex_request + ssh_packet_server_dhgex_init, /* SSH_MSG_KEX_DH_GEX_INIT */ + NULL, /* SSH_MSG_KEX_DH_GEX_REPLY */ + ssh_packet_server_dhgex_request /* SSH_MSG_GEX_DH_GEX_REQUEST */ }; static struct ssh_packet_callbacks_struct ssh_dhgex_server_callbacks = { @@ -402,9 +420,78 @@ void ssh_server_dhgex_init(ssh_session session){ /* register the packet callbacks */ ssh_packet_set_callbacks(session, &ssh_dhgex_server_callbacks); ssh_dh_init_common(session); + session->dh_handshake_state = DH_STATE_INIT; } static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_request){ + int pmin, pn, pmax; + int size = 0; + int rc; + + (void) type; + (void) user; + if (session->dh_handshake_state != DH_STATE_INIT){ + ssh_set_error(session, SSH_FATAL, "Received DH_GEX_REQUEST in invalid state"); + goto error; + } + + rc = ssh_dh_init_common(session); + if (rc != SSH_OK){ + goto error; + } + + /* Minimum group size, preferred group size, maximum group size */ + rc = ssh_buffer_unpack(packet, "ddd", &pmin, &pn, &pmax); + if (rc != SSH_OK){ + goto error; + } + SSH_LOG(SSH_LOG_INFO, "dh-gex: DHGEX_REQUEST[%d:%d:%d]", pmin, pn, pmax); + if (pmin > pn || pn > pmax || pn > pmax || pmax < PMIN){ + ssh_set_error(session, SSH_FATAL, "Invalid dh-gex arguments [%d:%d:%d]", pmin, pn, pmax); + goto error; + } + session->next_crypto->dh_pmin = pmin; + session->next_crypto->dh_pn = pn; + session->next_crypto->dh_pmax = pmax; + + /* ensure safe parameters */ + if (pmin < PMIN){ + pmin = PMIN; + if (pn < pmin){ + pn = pmin; + } + } + rc = ssh_retrieve_dhgroup(pmin, pn, pmax, &size, &session->next_crypto->p, &session->next_crypto->g); + if (rc == SSH_ERROR){ + ssh_set_error(session, SSH_FATAL, "Couldn't find DH group for [%d:%d:%d]", pmin, pn, pmax); + goto error; + } + session->next_crypto->dh_group_is_mutable = 1; + rc = ssh_buffer_pack(session->out_buffer, "bBB", SSH2_MSG_KEX_DH_GEX_GROUP, + session->next_crypto->p, session->next_crypto->g); + if (rc != SSH_OK) { + goto error; + } + + session->dh_handshake_state = DH_STATE_GROUP_SENT; + + rc = ssh_packet_send(session); +error: + + return SSH_PACKET_USED; +} + +/** @internal + * @brief parse an incoming SSH_MSG_KEX_DH_GEX_INIT packet and complete + * Diffie-Hellman key exchange + **/ +static SSH_PACKET_CALLBACK(ssh_packet_server_dhgex_init){ + + (void) type; + (void) user; + SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_KEX_DHGEX_INIT"); + ssh_packet_remove_callbacks(session, &ssh_dhgex_server_callbacks); + ssh_server_dh_process_init(session, packet); return SSH_PACKET_USED; } |