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