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