Don't wait for KEX_INIT prior to sending our own packet.
Watch out for bad KEX_INIT guesses and burn packets if necessary.
This commit is contained in:
parent
5f85317efa
commit
adee5e5653
4
README
4
README
@ -10,6 +10,10 @@ Version 0.14
|
||||
|
||||
Swap ordering of packet_add/packet-inspection to avoid inspect after free. (Selcuk)
|
||||
|
||||
Swap KEX_INIT ordering, send our KEX_INIT first.
|
||||
|
||||
Add check for oportunistic KEX_INIT packets. Burn bad guess if necessary.
|
||||
|
||||
Fix OpenSSL detection using pkg-config. (Dan Casey)
|
||||
|
||||
Version 0.13
|
||||
|
@ -199,6 +199,7 @@ struct _LIBSSH2_SESSION {
|
||||
|
||||
/* Agreed Key Exchange Method */
|
||||
LIBSSH2_KEX_METHOD *kex;
|
||||
int burn_optimistic_kexinit:1;
|
||||
|
||||
unsigned char *session_id;
|
||||
unsigned long session_id_len;
|
||||
@ -462,6 +463,7 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_typ
|
||||
int libssh2_packet_requirev_ex(LIBSSH2_SESSION *session, unsigned char *packet_types, unsigned char **data, unsigned long *data_len, unsigned long match_ofs, const unsigned char *match_buf, unsigned long match_len);
|
||||
#define libssh2_packet_requirev(session, packet_types, data, data_len) \
|
||||
libssh2_packet_requirev_ex((session), (packet_types), (data), (data_len), 0, NULL, 0)
|
||||
int libssh2_packet_burn(LIBSSH2_SESSION *session);
|
||||
int libssh2_packet_write(LIBSSH2_SESSION *session, unsigned char *data, unsigned long data_len);
|
||||
int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange);
|
||||
unsigned long libssh2_channel_nextid(LIBSSH2_SESSION *session);
|
||||
|
62
src/kex.c
62
src/kex.c
@ -118,6 +118,26 @@ static int libssh2_kex_method_diffie_hellman_groupGP_sha1_key_exchange(LIBSSH2_S
|
||||
goto clean_exit;
|
||||
}
|
||||
|
||||
if (session->burn_optimistic_kexinit) {
|
||||
/* The first KEX packet to come along will be the guess initially sent by the server
|
||||
* That guess turned out to be wrong so we need to silently ignore it */
|
||||
int burn_type;
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Waiting for badly guessed KEX packet (to be ignored)");
|
||||
#endif
|
||||
burn_type = libssh2_packet_burn(session);
|
||||
if (burn_type <= 0) {
|
||||
/* Failed to receive a packet */
|
||||
ret = -1;
|
||||
goto clean_exit;
|
||||
}
|
||||
session->burn_optimistic_kexinit = 0;
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_KEX
|
||||
_libssh2_debug(session, LIBSSH2_DBG_KEX, "Burnt packet of type: %02x", (unsigned int)burn_type);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Wait for KEX reply */
|
||||
if (libssh2_packet_require(session, packet_type_reply, &s_packet, &s_packet_len)) {
|
||||
libssh2_error(session, LIBSSH2_ERROR_TIMEOUT, "Timed out waiting for KEX reply", 0);
|
||||
@ -967,9 +987,9 @@ static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char
|
||||
s = session->kex_prefs;
|
||||
|
||||
while (s && *s) {
|
||||
unsigned char *p = strchr(s, ',');
|
||||
unsigned char *q, *p = strchr(s, ',');
|
||||
int method_len = (p ? (p - s) : strlen(s));
|
||||
if (libssh2_kex_agree_instr(kex, kex_len, s, method_len)) {
|
||||
if ((q = libssh2_kex_agree_instr(kex, kex_len, s, method_len))) {
|
||||
LIBSSH2_KEX_METHOD *method = (LIBSSH2_KEX_METHOD*)libssh2_get_method_by_name(s, method_len, (LIBSSH2_COMMON_METHOD**)kexp);
|
||||
|
||||
if (!method) {
|
||||
@ -982,6 +1002,12 @@ static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char
|
||||
*/
|
||||
if (libssh2_kex_agree_hostkey(session, method->flags, hostkey, hostkey_len) == 0) {
|
||||
session->kex = method;
|
||||
if (session->burn_optimistic_kexinit && (kex == q)) {
|
||||
/* Server sent an optimistic packet,
|
||||
* and client agrees with preference
|
||||
* cancel burning the first KEX_INIT packet that comes in */
|
||||
session->burn_optimistic_kexinit = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -999,6 +1025,12 @@ static int libssh2_kex_agree_kex_hostkey(LIBSSH2_SESSION *session, unsigned char
|
||||
*/
|
||||
if (libssh2_kex_agree_hostkey(session, (*kexp)->flags, hostkey, hostkey_len) == 0) {
|
||||
session->kex = *kexp;
|
||||
if (session->burn_optimistic_kexinit && (kex == s)) {
|
||||
/* Server sent an optimistic packet,
|
||||
* and client agrees with preference
|
||||
* cancel burning the first KEX_INIT packet that comes in */
|
||||
session->burn_optimistic_kexinit = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -1174,6 +1206,12 @@ static int libssh2_kex_agree_methods(LIBSSH2_SESSION *session, unsigned char *da
|
||||
lang_cs_len = libssh2_ntohu32(s); lang_cs = s + 4; s += 4 + lang_cs_len;
|
||||
lang_sc_len = libssh2_ntohu32(s); lang_sc = s + 4; s += 4 + lang_sc_len;
|
||||
|
||||
/* If the server sent an optimistic packet, assume that it guessed wrong.
|
||||
* If the guess is determined to be right (by libssh2_kex_agree_kex_hostkey)
|
||||
* This flag will be reset to zero so that it's not ignored */
|
||||
session->burn_optimistic_kexinit = *(s++);
|
||||
/* Next uint32 in packet is all zeros (reserved) */
|
||||
|
||||
if (libssh2_kex_agree_kex_hostkey(session, kex, kex_len, hostkey, hostkey_len)) {
|
||||
return -1;
|
||||
}
|
||||
@ -1248,7 +1286,23 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f
|
||||
}
|
||||
|
||||
if (!session->kex || !session->hostkey) {
|
||||
/* Preserve in case of failure */
|
||||
unsigned char *oldlocal = session->local.kexinit;
|
||||
unsigned long oldlocal_len = session->local.kexinit_len;
|
||||
|
||||
session->local.kexinit = NULL;
|
||||
if (libssh2_kexinit(session)) {
|
||||
session->local.kexinit = oldlocal;
|
||||
session->local.kexinit_len = oldlocal_len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (libssh2_packet_require(session, SSH_MSG_KEXINIT, &data, &data_len)) {
|
||||
if (session->local.kexinit) {
|
||||
LIBSSH2_FREE(session, session->local.kexinit);
|
||||
}
|
||||
session->local.kexinit = oldlocal;
|
||||
session->local.kexinit_len = oldlocal_len;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -1258,10 +1312,6 @@ int libssh2_kex_exchange(LIBSSH2_SESSION *session, int reexchange) /* session->f
|
||||
session->remote.kexinit = data;
|
||||
session->remote.kexinit_len = data_len;
|
||||
|
||||
if (libssh2_kexinit(session)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (libssh2_kex_agree_methods(session, data, data_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
42
src/packet.c
42
src/packet.c
@ -1017,6 +1017,48 @@ int libssh2_packet_require_ex(LIBSSH2_SESSION *session, unsigned char packet_typ
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_burn
|
||||
* Loops libssh2_packet_read() until any packet is available and promptly discards it
|
||||
* Used during KEX exchange to discard badly guessed KEX_INIT packets
|
||||
*/
|
||||
int libssh2_packet_burn(LIBSSH2_SESSION *session)
|
||||
{
|
||||
unsigned char *data;
|
||||
unsigned long data_len;
|
||||
char all_packets[255];
|
||||
int i;
|
||||
for(i = 1; i < 256; i++) all_packets[i - 1] = i;
|
||||
|
||||
if (libssh2_packet_askv_ex(session, all_packets, &data, &data_len, 0, NULL, 0, 0) == 0) {
|
||||
i = data[0];
|
||||
/* A packet was available in the packet brigade, burn it */
|
||||
LIBSSH2_FREE(session, data);
|
||||
return i;
|
||||
}
|
||||
|
||||
#ifdef LIBSSH2_DEBUG_TRANSPORT
|
||||
_libssh2_debug(session, LIBSSH2_DBG_TRANS, "Blocking until packet becomes available to burn");
|
||||
#endif
|
||||
while (session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
|
||||
int ret = libssh2_packet_read(session, 1);
|
||||
if (ret < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (ret == 0) continue;
|
||||
|
||||
/* Be lazy, let packet_ask pull it out of the brigade */
|
||||
if (0 == libssh2_packet_ask_ex(session, ret, &data, &data_len, 0, NULL, 0, 0)) {
|
||||
/* Smoke 'em if you got 'em */
|
||||
LIBSSH2_FREE(session, data);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* Only reached if the socket died */
|
||||
return -1;
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* {{{ libssh2_packet_requirev
|
||||
* Loops libssh2_packet_read() until one of a list of packet types requested is available
|
||||
* SSH_DISCONNECT or a SOCKET_DISCONNECTED will cause a bailout
|
||||
|
Loading…
x
Reference in New Issue
Block a user