0002-src-add-strict-KEX-to-fix-CVE-2023-48795-Terrapin-Attack.patch 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. From d34d9258b8420b19ec3f97b4cc5bf7aa7d98e35a Mon Sep 17 00:00:00 2001
  2. From: Michael Buckley <michael@buckleyisms.com>
  3. Date: Thu, 30 Nov 2023 15:08:02 -0800
  4. Subject: [PATCH] src: add 'strict KEX' to fix CVE-2023-48795 "Terrapin Attack"
  5. Refs:
  6. https://terrapin-attack.com/
  7. https://seclists.org/oss-sec/2023/q4/292
  8. https://osv.dev/list?ecosystem=&q=CVE-2023-48795
  9. https://github.com/advisories/GHSA-45x7-px36-x8w8
  10. https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-48795
  11. Fixes #1290
  12. Closes #1291
  13. Upstream: https://github.com/libssh2/libssh2/commit/d34d9258b8420b19ec3f97b4cc5bf7aa7d98e35a
  14. Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
  15. ---
  16. src/kex.c | 63 +++++++++++++++++++++++------------
  17. src/libssh2_priv.h | 18 +++++++---
  18. src/packet.c | 83 +++++++++++++++++++++++++++++++++++++++++++---
  19. src/packet.h | 2 +-
  20. src/session.c | 3 ++
  21. src/transport.c | 12 ++++++-
  22. 6 files changed, 149 insertions(+), 32 deletions(-)
  23. diff --git a/src/kex.c b/src/kex.c
  24. index 8e7b7f0af3..a7b301e157 100644
  25. --- a/src/kex.c
  26. +++ b/src/kex.c
  27. @@ -3032,6 +3032,13 @@ kex_method_extension_negotiation = {
  28. 0,
  29. };
  30. +static const LIBSSH2_KEX_METHOD
  31. +kex_method_strict_client_extension = {
  32. + "kex-strict-c-v00@openssh.com",
  33. + NULL,
  34. + 0,
  35. +};
  36. +
  37. static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
  38. #if LIBSSH2_ED25519
  39. &kex_method_ssh_curve25519_sha256,
  40. @@ -3050,6 +3057,7 @@ static const LIBSSH2_KEX_METHOD *libssh2_kex_methods[] = {
  41. &kex_method_diffie_helman_group1_sha1,
  42. &kex_method_diffie_helman_group_exchange_sha1,
  43. &kex_method_extension_negotiation,
  44. + &kex_method_strict_client_extension,
  45. NULL
  46. };
  47. @@ -3302,13 +3310,13 @@ static int kexinit(LIBSSH2_SESSION * session)
  48. return 0;
  49. }
  50. -/* kex_agree_instr
  51. +/* _libssh2_kex_agree_instr
  52. * Kex specific variant of strstr()
  53. * Needle must be preceded by BOL or ',', and followed by ',' or EOL
  54. */
  55. -static unsigned char *
  56. -kex_agree_instr(unsigned char *haystack, size_t haystack_len,
  57. - const unsigned char *needle, size_t needle_len)
  58. +unsigned char *
  59. +_libssh2_kex_agree_instr(unsigned char *haystack, size_t haystack_len,
  60. + const unsigned char *needle, size_t needle_len)
  61. {
  62. unsigned char *s;
  63. unsigned char *end_haystack;
  64. @@ -3393,7 +3401,7 @@ static int kex_agree_hostkey(LIBSSH2_SESSION * session,
  65. while(s && *s) {
  66. unsigned char *p = (unsigned char *) strchr((char *) s, ',');
  67. size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
  68. - if(kex_agree_instr(hostkey, hostkey_len, s, method_len)) {
  69. + if(_libssh2_kex_agree_instr(hostkey, hostkey_len, s, method_len)) {
  70. const LIBSSH2_HOSTKEY_METHOD *method =
  71. (const LIBSSH2_HOSTKEY_METHOD *)
  72. kex_get_method_by_name((char *) s, method_len,
  73. @@ -3427,9 +3435,9 @@ static int kex_agree_hostkey(LIBSSH2_SESSION * session,
  74. }
  75. while(hostkeyp && (*hostkeyp) && (*hostkeyp)->name) {
  76. - s = kex_agree_instr(hostkey, hostkey_len,
  77. - (unsigned char *) (*hostkeyp)->name,
  78. - strlen((*hostkeyp)->name));
  79. + s = _libssh2_kex_agree_instr(hostkey, hostkey_len,
  80. + (unsigned char *) (*hostkeyp)->name,
  81. + strlen((*hostkeyp)->name));
  82. if(s) {
  83. /* So far so good, but does it suit our purposes? (Encrypting vs
  84. Signing) */
  85. @@ -3463,6 +3471,12 @@ static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex,
  86. {
  87. const LIBSSH2_KEX_METHOD **kexp = libssh2_kex_methods;
  88. unsigned char *s;
  89. + const unsigned char *strict =
  90. + (unsigned char *)"kex-strict-s-v00@openssh.com";
  91. +
  92. + if(_libssh2_kex_agree_instr(kex, kex_len, strict, 28)) {
  93. + session->kex_strict = 1;
  94. + }
  95. if(session->kex_prefs) {
  96. s = (unsigned char *) session->kex_prefs;
  97. @@ -3470,7 +3484,7 @@ static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex,
  98. while(s && *s) {
  99. unsigned char *q, *p = (unsigned char *) strchr((char *) s, ',');
  100. size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
  101. - q = kex_agree_instr(kex, kex_len, s, method_len);
  102. + q = _libssh2_kex_agree_instr(kex, kex_len, s, method_len);
  103. if(q) {
  104. const LIBSSH2_KEX_METHOD *method = (const LIBSSH2_KEX_METHOD *)
  105. kex_get_method_by_name((char *) s, method_len,
  106. @@ -3504,9 +3518,9 @@ static int kex_agree_kex_hostkey(LIBSSH2_SESSION * session, unsigned char *kex,
  107. }
  108. while(*kexp && (*kexp)->name) {
  109. - s = kex_agree_instr(kex, kex_len,
  110. - (unsigned char *) (*kexp)->name,
  111. - strlen((*kexp)->name));
  112. + s = _libssh2_kex_agree_instr(kex, kex_len,
  113. + (unsigned char *) (*kexp)->name,
  114. + strlen((*kexp)->name));
  115. if(s) {
  116. /* We've agreed on a key exchange method,
  117. * Can we agree on a hostkey that works with this kex?
  118. @@ -3550,7 +3564,7 @@ static int kex_agree_crypt(LIBSSH2_SESSION * session,
  119. unsigned char *p = (unsigned char *) strchr((char *) s, ',');
  120. size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
  121. - if(kex_agree_instr(crypt, crypt_len, s, method_len)) {
  122. + if(_libssh2_kex_agree_instr(crypt, crypt_len, s, method_len)) {
  123. const LIBSSH2_CRYPT_METHOD *method =
  124. (const LIBSSH2_CRYPT_METHOD *)
  125. kex_get_method_by_name((char *) s, method_len,
  126. @@ -3572,9 +3586,9 @@ static int kex_agree_crypt(LIBSSH2_SESSION * session,
  127. }
  128. while(*cryptp && (*cryptp)->name) {
  129. - s = kex_agree_instr(crypt, crypt_len,
  130. - (unsigned char *) (*cryptp)->name,
  131. - strlen((*cryptp)->name));
  132. + s = _libssh2_kex_agree_instr(crypt, crypt_len,
  133. + (unsigned char *) (*cryptp)->name,
  134. + strlen((*cryptp)->name));
  135. if(s) {
  136. endpoint->crypt = *cryptp;
  137. return 0;
  138. @@ -3614,7 +3628,7 @@ static int kex_agree_mac(LIBSSH2_SESSION * session,
  139. unsigned char *p = (unsigned char *) strchr((char *) s, ',');
  140. size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
  141. - if(kex_agree_instr(mac, mac_len, s, method_len)) {
  142. + if(_libssh2_kex_agree_instr(mac, mac_len, s, method_len)) {
  143. const LIBSSH2_MAC_METHOD *method = (const LIBSSH2_MAC_METHOD *)
  144. kex_get_method_by_name((char *) s, method_len,
  145. (const LIBSSH2_COMMON_METHOD **)
  146. @@ -3635,8 +3649,9 @@ static int kex_agree_mac(LIBSSH2_SESSION * session,
  147. }
  148. while(*macp && (*macp)->name) {
  149. - s = kex_agree_instr(mac, mac_len, (unsigned char *) (*macp)->name,
  150. - strlen((*macp)->name));
  151. + s = _libssh2_kex_agree_instr(mac, mac_len,
  152. + (unsigned char *) (*macp)->name,
  153. + strlen((*macp)->name));
  154. if(s) {
  155. endpoint->mac = *macp;
  156. return 0;
  157. @@ -3667,7 +3682,7 @@ static int kex_agree_comp(LIBSSH2_SESSION *session,
  158. unsigned char *p = (unsigned char *) strchr((char *) s, ',');
  159. size_t method_len = (p ? (size_t)(p - s) : strlen((char *) s));
  160. - if(kex_agree_instr(comp, comp_len, s, method_len)) {
  161. + if(_libssh2_kex_agree_instr(comp, comp_len, s, method_len)) {
  162. const LIBSSH2_COMP_METHOD *method =
  163. (const LIBSSH2_COMP_METHOD *)
  164. kex_get_method_by_name((char *) s, method_len,
  165. @@ -3689,8 +3704,9 @@ static int kex_agree_comp(LIBSSH2_SESSION *session,
  166. }
  167. while(*compp && (*compp)->name) {
  168. - s = kex_agree_instr(comp, comp_len, (unsigned char *) (*compp)->name,
  169. - strlen((*compp)->name));
  170. + s = _libssh2_kex_agree_instr(comp, comp_len,
  171. + (unsigned char *) (*compp)->name,
  172. + strlen((*compp)->name));
  173. if(s) {
  174. endpoint->comp = *compp;
  175. return 0;
  176. @@ -3871,6 +3887,7 @@ _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
  177. session->local.kexinit = key_state->oldlocal;
  178. session->local.kexinit_len = key_state->oldlocal_len;
  179. key_state->state = libssh2_NB_state_idle;
  180. + session->state &= ~LIBSSH2_STATE_INITIAL_KEX;
  181. session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
  182. session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
  183. return -1;
  184. @@ -3896,6 +3913,7 @@ _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
  185. session->local.kexinit = key_state->oldlocal;
  186. session->local.kexinit_len = key_state->oldlocal_len;
  187. key_state->state = libssh2_NB_state_idle;
  188. + session->state &= ~LIBSSH2_STATE_INITIAL_KEX;
  189. session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
  190. session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
  191. return -1;
  192. @@ -3944,6 +3962,7 @@ _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
  193. session->remote.kexinit = NULL;
  194. }
  195. + session->state &= ~LIBSSH2_STATE_INITIAL_KEX;
  196. session->state &= ~LIBSSH2_STATE_KEX_ACTIVE;
  197. session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
  198. diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h
  199. index 7660366954..18d9ab2130 100644
  200. --- a/src/libssh2_priv.h
  201. +++ b/src/libssh2_priv.h
  202. @@ -736,6 +736,9 @@ struct _LIBSSH2_SESSION
  203. /* key signing algorithm preferences -- NULL yields server order */
  204. char *sign_algo_prefs;
  205. + /* Whether to use the OpenSSH Strict KEX extension */
  206. + int kex_strict;
  207. +
  208. /* (remote as source of data -- packet_read ) */
  209. libssh2_endpoint_data remote;
  210. @@ -908,6 +911,7 @@ struct _LIBSSH2_SESSION
  211. int fullpacket_macstate;
  212. size_t fullpacket_payload_len;
  213. int fullpacket_packet_type;
  214. + uint32_t fullpacket_required_type;
  215. /* State variables used in libssh2_sftp_init() */
  216. libssh2_nonblocking_states sftpInit_state;
  217. @@ -948,10 +952,11 @@ struct _LIBSSH2_SESSION
  218. };
  219. /* session.state bits */
  220. -#define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000001
  221. -#define LIBSSH2_STATE_NEWKEYS 0x00000002
  222. -#define LIBSSH2_STATE_AUTHENTICATED 0x00000004
  223. -#define LIBSSH2_STATE_KEX_ACTIVE 0x00000008
  224. +#define LIBSSH2_STATE_INITIAL_KEX 0x00000001
  225. +#define LIBSSH2_STATE_EXCHANGING_KEYS 0x00000002
  226. +#define LIBSSH2_STATE_NEWKEYS 0x00000004
  227. +#define LIBSSH2_STATE_AUTHENTICATED 0x00000008
  228. +#define LIBSSH2_STATE_KEX_ACTIVE 0x00000010
  229. /* session.flag helpers */
  230. #ifdef MSG_NOSIGNAL
  231. @@ -1182,6 +1187,11 @@ ssize_t _libssh2_send(libssh2_socket_t socket, const void *buffer,
  232. int _libssh2_kex_exchange(LIBSSH2_SESSION * session, int reexchange,
  233. key_exchange_state_t * state);
  234. +unsigned char *_libssh2_kex_agree_instr(unsigned char *haystack,
  235. + size_t haystack_len,
  236. + const unsigned char *needle,
  237. + size_t needle_len);
  238. +
  239. /* Let crypt.c/hostkey.c expose their method structs */
  240. const LIBSSH2_CRYPT_METHOD **libssh2_crypt_methods(void);
  241. const LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void);
  242. diff --git a/src/packet.c b/src/packet.c
  243. index eccb8c56a8..6da14e9fa1 100644
  244. --- a/src/packet.c
  245. +++ b/src/packet.c
  246. @@ -624,14 +624,13 @@ packet_authagent_open(LIBSSH2_SESSION * session,
  247. * layer when it has received a packet.
  248. *
  249. * The input pointer 'data' is pointing to allocated data that this function
  250. - * is asked to deal with so on failure OR success, it must be freed fine.
  251. - * The only exception is when the return code is LIBSSH2_ERROR_EAGAIN.
  252. + * will be freed unless return the code is LIBSSH2_ERROR_EAGAIN.
  253. *
  254. * This function will always be called with 'datalen' greater than zero.
  255. */
  256. int
  257. _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
  258. - size_t datalen, int macstate)
  259. + size_t datalen, int macstate, uint32_t seq)
  260. {
  261. int rc = 0;
  262. unsigned char *message = NULL;
  263. @@ -676,6 +675,70 @@ _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
  264. break;
  265. }
  266. + if(session->state & LIBSSH2_STATE_INITIAL_KEX) {
  267. + if(msg == SSH_MSG_KEXINIT) {
  268. + if(!session->kex_strict) {
  269. + if(datalen < 17) {
  270. + LIBSSH2_FREE(session, data);
  271. + session->packAdd_state = libssh2_NB_state_idle;
  272. + return _libssh2_error(session,
  273. + LIBSSH2_ERROR_BUFFER_TOO_SMALL,
  274. + "Data too short extracting kex");
  275. + }
  276. + else {
  277. + const unsigned char *strict =
  278. + (unsigned char *)"kex-strict-s-v00@openssh.com";
  279. + struct string_buf buf;
  280. + unsigned char *algs = NULL;
  281. + size_t algs_len = 0;
  282. +
  283. + buf.data = (unsigned char *)data;
  284. + buf.dataptr = buf.data;
  285. + buf.len = datalen;
  286. + buf.dataptr += 17; /* advance past type and cookie */
  287. +
  288. + if(_libssh2_get_string(&buf, &algs, &algs_len)) {
  289. + LIBSSH2_FREE(session, data);
  290. + session->packAdd_state = libssh2_NB_state_idle;
  291. + return _libssh2_error(session,
  292. + LIBSSH2_ERROR_BUFFER_TOO_SMALL,
  293. + "Algs too short");
  294. + }
  295. +
  296. + if(algs_len == 0 ||
  297. + _libssh2_kex_agree_instr(algs, algs_len, strict, 28)) {
  298. + session->kex_strict = 1;
  299. + }
  300. + }
  301. + }
  302. +
  303. + if(session->kex_strict && seq) {
  304. + LIBSSH2_FREE(session, data);
  305. + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
  306. + session->packAdd_state = libssh2_NB_state_idle;
  307. + libssh2_session_disconnect(session, "strict KEX violation: "
  308. + "KEXINIT was not the first packet");
  309. +
  310. + return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
  311. + "strict KEX violation: "
  312. + "KEXINIT was not the first packet");
  313. + }
  314. + }
  315. +
  316. + if(session->kex_strict && session->fullpacket_required_type &&
  317. + session->fullpacket_required_type != msg) {
  318. + LIBSSH2_FREE(session, data);
  319. + session->socket_state = LIBSSH2_SOCKET_DISCONNECTED;
  320. + session->packAdd_state = libssh2_NB_state_idle;
  321. + libssh2_session_disconnect(session, "strict KEX violation: "
  322. + "unexpected packet type");
  323. +
  324. + return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
  325. + "strict KEX violation: "
  326. + "unexpected packet type");
  327. + }
  328. + }
  329. +
  330. if(session->packAdd_state == libssh2_NB_state_allocated) {
  331. /* A couple exceptions to the packet adding rule: */
  332. switch(msg) {
  333. @@ -1364,6 +1427,15 @@ _libssh2_packet_ask(LIBSSH2_SESSION * session, unsigned char packet_type,
  334. return 0;
  335. }
  336. + else if(session->kex_strict &&
  337. + (session->state & LIBSSH2_STATE_INITIAL_KEX)) {
  338. + libssh2_session_disconnect(session, "strict KEX violation: "
  339. + "unexpected packet type");
  340. +
  341. + return _libssh2_error(session, LIBSSH2_ERROR_SOCKET_DISCONNECT,
  342. + "strict KEX violation: "
  343. + "unexpected packet type");
  344. + }
  345. packet = _libssh2_list_next(&packet->node);
  346. }
  347. return -1;
  348. @@ -1425,7 +1497,10 @@ _libssh2_packet_require(LIBSSH2_SESSION * session, unsigned char packet_type,
  349. }
  350. while(session->socket_state == LIBSSH2_SOCKET_CONNECTED) {
  351. - int ret = _libssh2_transport_read(session);
  352. + int ret;
  353. + session->fullpacket_required_type = packet_type;
  354. + ret = _libssh2_transport_read(session);
  355. + session->fullpacket_required_type = 0;
  356. if(ret == LIBSSH2_ERROR_EAGAIN)
  357. return ret;
  358. else if(ret < 0) {
  359. diff --git a/src/packet.h b/src/packet.h
  360. index 1d90b8af12..955351e5f6 100644
  361. --- a/src/packet.h
  362. +++ b/src/packet.h
  363. @@ -72,6 +72,6 @@ int _libssh2_packet_burn(LIBSSH2_SESSION * session,
  364. int _libssh2_packet_write(LIBSSH2_SESSION * session, unsigned char *data,
  365. unsigned long data_len);
  366. int _libssh2_packet_add(LIBSSH2_SESSION * session, unsigned char *data,
  367. - size_t datalen, int macstate);
  368. + size_t datalen, int macstate, uint32_t seq);
  369. #endif /* LIBSSH2_PACKET_H */
  370. diff --git a/src/session.c b/src/session.c
  371. index 35e7929fe7..9d89ade8ec 100644
  372. --- a/src/session.c
  373. +++ b/src/session.c
  374. @@ -469,6 +469,8 @@ libssh2_session_init_ex(LIBSSH2_ALLOC_FUNC((*my_alloc)),
  375. session->abstract = abstract;
  376. session->api_timeout = 0; /* timeout-free API by default */
  377. session->api_block_mode = 1; /* blocking API by default */
  378. + session->state = LIBSSH2_STATE_INITIAL_KEX;
  379. + session->fullpacket_required_type = 0;
  380. session->packet_read_timeout = LIBSSH2_DEFAULT_READ_TIMEOUT;
  381. session->flag.quote_paths = 1; /* default behavior is to quote paths
  382. for the scp subsystem */
  383. @@ -1223,6 +1225,7 @@ libssh2_session_disconnect_ex(LIBSSH2_SESSION *session, int reason,
  384. const char *desc, const char *lang)
  385. {
  386. int rc;
  387. + session->state &= ~LIBSSH2_STATE_INITIAL_KEX;
  388. session->state &= ~LIBSSH2_STATE_EXCHANGING_KEYS;
  389. BLOCK_ADJUST(rc, session,
  390. session_disconnect(session, reason, desc, lang));
  391. diff --git a/src/transport.c b/src/transport.c
  392. index 21be9d2b80..a8bb588a4b 100644
  393. --- a/src/transport.c
  394. +++ b/src/transport.c
  395. @@ -186,6 +186,7 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
  396. struct transportpacket *p = &session->packet;
  397. int rc;
  398. int compressed;
  399. + uint32_t seq = session->remote.seqno;
  400. if(session->fullpacket_state == libssh2_NB_state_idle) {
  401. session->fullpacket_macstate = LIBSSH2_MAC_CONFIRMED;
  402. @@ -317,7 +318,7 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
  403. if(session->fullpacket_state == libssh2_NB_state_created) {
  404. rc = _libssh2_packet_add(session, p->payload,
  405. session->fullpacket_payload_len,
  406. - session->fullpacket_macstate);
  407. + session->fullpacket_macstate, seq);
  408. if(rc == LIBSSH2_ERROR_EAGAIN)
  409. return rc;
  410. if(rc) {
  411. @@ -328,6 +329,11 @@ fullpacket(LIBSSH2_SESSION * session, int encrypted /* 1 or 0 */ )
  412. session->fullpacket_state = libssh2_NB_state_idle;
  413. + if(session->kex_strict &&
  414. + session->fullpacket_packet_type == SSH_MSG_NEWKEYS) {
  415. + session->remote.seqno = 0;
  416. + }
  417. +
  418. return session->fullpacket_packet_type;
  419. }
  420. @@ -1093,6 +1099,10 @@ int _libssh2_transport_send(LIBSSH2_SESSION *session,
  421. session->local.seqno++;
  422. + if(session->kex_strict && data[0] == SSH_MSG_NEWKEYS) {
  423. + session->local.seqno = 0;
  424. + }
  425. +
  426. ret = LIBSSH2_SEND(session, p->outbuf, total_length,
  427. LIBSSH2_SOCKET_SEND_FLAGS(session));
  428. if(ret < 0)