Line | Count | Source |
1 | | /* |
2 | | * kex.c - key exchange |
3 | | * |
4 | | * This file is part of the SSH Library |
5 | | * |
6 | | * Copyright (c) 2003-2008 by Aris Adamantiadis |
7 | | * |
8 | | * The SSH Library is free software; you can redistribute it and/or modify |
9 | | * it under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or (at your |
11 | | * option) any later version. |
12 | | * |
13 | | * The SSH Library is distributed in the hope that it will be useful, but |
14 | | * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY |
15 | | * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public |
16 | | * License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with the SSH Library; see the file COPYING. If not, write to |
20 | | * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, |
21 | | * MA 02111-1307, USA. |
22 | | */ |
23 | | |
24 | | #include "config.h" |
25 | | |
26 | | #include <string.h> |
27 | | #include <stdlib.h> |
28 | | #include <stdio.h> |
29 | | #include <stdbool.h> |
30 | | |
31 | | #include "libssh/libssh.h" |
32 | | #include "libssh/priv.h" |
33 | | #include "libssh/buffer.h" |
34 | | #include "libssh/dh.h" |
35 | | #ifdef WITH_GEX |
36 | | #include "libssh/dh-gex.h" |
37 | | #endif /* WITH_GEX */ |
38 | | #include "libssh/kex.h" |
39 | | #include "libssh/session.h" |
40 | | #include "libssh/ssh2.h" |
41 | | #include "libssh/string.h" |
42 | | #include "libssh/curve25519.h" |
43 | | #include "libssh/sntrup761.h" |
44 | | #include "libssh/hybrid_mlkem.h" |
45 | | #include "libssh/kex-gss.h" |
46 | | #include "libssh/knownhosts.h" |
47 | | #include "libssh/misc.h" |
48 | | #include "libssh/pki.h" |
49 | | #include "libssh/bignum.h" |
50 | | #include "libssh/token.h" |
51 | | #include "libssh/gssapi.h" |
52 | | |
53 | | #ifdef HAVE_BLOWFISH |
54 | | # define BLOWFISH ",blowfish-cbc" |
55 | | #else |
56 | | # define BLOWFISH "" |
57 | | #endif |
58 | | |
59 | | #ifdef HAVE_LIBGCRYPT |
60 | | # define AES "aes256-gcm@openssh.com,aes128-gcm@openssh.com," \ |
61 | | "aes256-ctr,aes192-ctr,aes128-ctr" |
62 | | # define AES_CBC ",aes256-cbc,aes192-cbc,aes128-cbc" |
63 | | # define DES_SUPPORTED ",3des-cbc" |
64 | | |
65 | | #elif defined(HAVE_LIBMBEDCRYPTO) |
66 | | # ifdef MBEDTLS_GCM_C |
67 | | # define GCM "aes256-gcm@openssh.com,aes128-gcm@openssh.com," |
68 | | # else |
69 | | # define GCM "" |
70 | | # endif /* MBEDTLS_GCM_C */ |
71 | | # define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr" |
72 | | # define AES_CBC ",aes256-cbc,aes192-cbc,aes128-cbc" |
73 | | # define DES_SUPPORTED ",3des-cbc" |
74 | | |
75 | | #elif defined(HAVE_LIBCRYPTO) |
76 | | # ifdef HAVE_OPENSSL_AES_H |
77 | | # define GCM "aes256-gcm@openssh.com,aes128-gcm@openssh.com," |
78 | | # define AES GCM "aes256-ctr,aes192-ctr,aes128-ctr" |
79 | | # define AES_CBC ",aes256-cbc,aes192-cbc,aes128-cbc" |
80 | | # else /* HAVE_OPENSSL_AES_H */ |
81 | | # define AES "" |
82 | | # define AES_CBC "" |
83 | | # endif /* HAVE_OPENSSL_AES_H */ |
84 | | |
85 | | # define DES_SUPPORTED ",3des-cbc" |
86 | | #endif /* HAVE_LIBCRYPTO */ |
87 | | |
88 | | #ifdef WITH_ZLIB |
89 | | #define ZLIB "none,zlib@openssh.com,zlib" |
90 | | #define ZLIB_DEFAULT "none,zlib@openssh.com" |
91 | | #else |
92 | | #define ZLIB "none" |
93 | | #define ZLIB_DEFAULT "none" |
94 | | #endif /* WITH_ZLIB */ |
95 | | |
96 | | #ifdef HAVE_CURVE25519 |
97 | | #define CURVE25519 "curve25519-sha256,curve25519-sha256@libssh.org," |
98 | | #else |
99 | | #define CURVE25519 "" |
100 | | #endif /* HAVE_CURVE25519 */ |
101 | | |
102 | | #ifdef HAVE_SNTRUP761 |
103 | | #define SNTRUP761X25519 "sntrup761x25519-sha512,sntrup761x25519-sha512@openssh.com," |
104 | | #else |
105 | | #define SNTRUP761X25519 "" |
106 | | #endif /* HAVE_SNTRUP761 */ |
107 | | |
108 | | #ifdef HAVE_MLKEM1024 |
109 | | #define HYBRID_MLKEM "mlkem768x25519-sha256," \ |
110 | | "mlkem768nistp256-sha256," \ |
111 | | "mlkem1024nistp384-sha384," |
112 | | #else |
113 | | #define HYBRID_MLKEM "mlkem768x25519-sha256," \ |
114 | | "mlkem768nistp256-sha256," |
115 | | #endif /* HAVE_MLKEM1024 */ |
116 | | |
117 | | #ifdef HAVE_ECC |
118 | | #define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521," |
119 | | #define EC_HOSTKEYS "ecdsa-sha2-nistp521," \ |
120 | | "ecdsa-sha2-nistp384," \ |
121 | | "ecdsa-sha2-nistp256," |
122 | | #define EC_SK_HOSTKEYS "sk-ecdsa-sha2-nistp256@openssh.com," |
123 | | #define EC_FIPS_PUBLIC_KEY_ALGOS "ecdsa-sha2-nistp521-cert-v01@openssh.com," \ |
124 | | "ecdsa-sha2-nistp384-cert-v01@openssh.com," \ |
125 | | "ecdsa-sha2-nistp256-cert-v01@openssh.com," |
126 | | #define EC_PUBLIC_KEY_ALGORITHMS EC_FIPS_PUBLIC_KEY_ALGOS \ |
127 | | "sk-ecdsa-sha2-nistp256-cert-v01@openssh.com," |
128 | | #else |
129 | | #define ECDH "" |
130 | | #define EC_HOSTKEYS "" |
131 | | #define EC_SK_HOSTKEYS "" |
132 | | #define EC_FIPS_PUBLIC_KEY_ALGOS "" |
133 | | #define EC_PUBLIC_KEY_ALGORITHMS "" |
134 | | #endif /* HAVE_ECC */ |
135 | | |
136 | | #ifdef WITH_INSECURE_NONE |
137 | | #define NONE ",none" |
138 | | #else |
139 | | #define NONE |
140 | | #endif /* WITH_INSECURE_NONE */ |
141 | | |
142 | 4.74k | #define HOSTKEYS "ssh-ed25519," \ |
143 | 4.74k | EC_HOSTKEYS \ |
144 | 4.74k | "sk-ssh-ed25519@openssh.com," \ |
145 | 4.74k | EC_SK_HOSTKEYS \ |
146 | 4.74k | "rsa-sha2-512," \ |
147 | 4.74k | "rsa-sha2-256," \ |
148 | 4.74k | "ssh-rsa" |
149 | | #define DEFAULT_HOSTKEYS "ssh-ed25519," \ |
150 | | EC_HOSTKEYS \ |
151 | | "sk-ssh-ed25519@openssh.com," \ |
152 | | EC_SK_HOSTKEYS \ |
153 | | "rsa-sha2-512," \ |
154 | | "rsa-sha2-256" |
155 | | |
156 | | #define PUBLIC_KEY_ALGORITHMS "ssh-ed25519-cert-v01@openssh.com," \ |
157 | | "sk-ssh-ed25519-cert-v01@openssh.com," \ |
158 | | EC_PUBLIC_KEY_ALGORITHMS \ |
159 | | "rsa-sha2-512-cert-v01@openssh.com," \ |
160 | | "rsa-sha2-256-cert-v01@openssh.com," \ |
161 | | "ssh-rsa-cert-v01@openssh.com," \ |
162 | | HOSTKEYS |
163 | | #define DEFAULT_PUBLIC_KEY_ALGORITHMS "ssh-ed25519-cert-v01@openssh.com," \ |
164 | | EC_PUBLIC_KEY_ALGORITHMS \ |
165 | | "rsa-sha2-512-cert-v01@openssh.com," \ |
166 | | "rsa-sha2-256-cert-v01@openssh.com," \ |
167 | | DEFAULT_HOSTKEYS |
168 | | |
169 | | #ifdef WITH_GEX |
170 | | #define GEX_SHA256 "diffie-hellman-group-exchange-sha256," |
171 | | #define GEX_SHA1 "diffie-hellman-group-exchange-sha1," |
172 | | #else |
173 | | #define GEX_SHA256 |
174 | | #define GEX_SHA1 |
175 | | #endif /* WITH_GEX */ |
176 | | |
177 | | #define CHACHA20 "chacha20-poly1305@openssh.com," |
178 | | |
179 | | #define DEFAULT_KEY_EXCHANGE \ |
180 | | HYBRID_MLKEM \ |
181 | | SNTRUP761X25519 \ |
182 | | CURVE25519 \ |
183 | | ECDH \ |
184 | | "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512," \ |
185 | | GEX_SHA256 \ |
186 | | "diffie-hellman-group14-sha256" \ |
187 | | |
188 | | #define KEY_EXCHANGE_SUPPORTED \ |
189 | | GEX_SHA1 \ |
190 | | DEFAULT_KEY_EXCHANGE \ |
191 | | ",diffie-hellman-group14-sha1,diffie-hellman-group1-sha1" |
192 | | |
193 | | /* RFC 8308 */ |
194 | 22.7k | #define KEX_EXTENSION_CLIENT "ext-info-c" |
195 | | /* Strict kex mitigation against CVE-2023-48795 */ |
196 | 19.0k | #define KEX_STRICT_CLIENT "kex-strict-c-v00@openssh.com" |
197 | 33.4k | #define KEX_STRICT_SERVER "kex-strict-s-v00@openssh.com" |
198 | | |
199 | | /* Allowed algorithms in FIPS mode */ |
200 | | #define FIPS_ALLOWED_CIPHERS "aes256-gcm@openssh.com,"\ |
201 | | "aes256-ctr,"\ |
202 | | "aes256-cbc,"\ |
203 | | "aes128-gcm@openssh.com,"\ |
204 | | "aes128-ctr,"\ |
205 | | "aes128-cbc" |
206 | | |
207 | | #define FIPS_ALLOWED_HOSTKEYS EC_HOSTKEYS \ |
208 | | "rsa-sha2-512," \ |
209 | | "rsa-sha2-256" |
210 | | |
211 | | #define FIPS_ALLOWED_PUBLIC_KEY_ALGORITHMS EC_FIPS_PUBLIC_KEY_ALGOS \ |
212 | | "rsa-sha2-512-cert-v01@openssh.com," \ |
213 | | "rsa-sha2-256-cert-v01@openssh.com," \ |
214 | | FIPS_ALLOWED_HOSTKEYS |
215 | | |
216 | | #ifdef HAVE_MLKEM1024 |
217 | | #define FIPS_MLKEM_KEX "mlkem768nistp256-sha256," \ |
218 | | "mlkem1024nistp384-sha384," |
219 | | #else |
220 | | #define FIPS_MLKEM_KEX "mlkem768nistp256-sha256," |
221 | | #endif |
222 | | |
223 | | #define FIPS_ALLOWED_KEX FIPS_MLKEM_KEX \ |
224 | | "ecdh-sha2-nistp256,"\ |
225 | | "ecdh-sha2-nistp384,"\ |
226 | | "ecdh-sha2-nistp521,"\ |
227 | | "diffie-hellman-group-exchange-sha256,"\ |
228 | | "diffie-hellman-group14-sha256,"\ |
229 | | "diffie-hellman-group16-sha512,"\ |
230 | | "diffie-hellman-group18-sha512" |
231 | | |
232 | | #define FIPS_ALLOWED_MACS "hmac-sha2-256-etm@openssh.com,"\ |
233 | | "hmac-sha1-etm@openssh.com,"\ |
234 | | "hmac-sha2-512-etm@openssh.com,"\ |
235 | | "hmac-sha2-256,"\ |
236 | | "hmac-sha1,"\ |
237 | | "hmac-sha2-512" |
238 | | |
239 | | /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ |
240 | | static const char *fips_methods[] = { |
241 | | FIPS_ALLOWED_KEX, |
242 | | FIPS_ALLOWED_PUBLIC_KEY_ALGORITHMS, |
243 | | FIPS_ALLOWED_CIPHERS, |
244 | | FIPS_ALLOWED_CIPHERS, |
245 | | FIPS_ALLOWED_MACS, |
246 | | FIPS_ALLOWED_MACS, |
247 | | ZLIB_DEFAULT, |
248 | | ZLIB_DEFAULT, |
249 | | "", |
250 | | "", |
251 | | NULL |
252 | | }; |
253 | | |
254 | | /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ |
255 | | static const char *default_methods[] = { |
256 | | DEFAULT_KEY_EXCHANGE, |
257 | | DEFAULT_PUBLIC_KEY_ALGORITHMS, |
258 | | CHACHA20 AES, |
259 | | CHACHA20 AES, |
260 | | "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512", |
261 | | "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha2-256,hmac-sha2-512", |
262 | | ZLIB_DEFAULT, |
263 | | ZLIB_DEFAULT, |
264 | | "", |
265 | | "", |
266 | | NULL |
267 | | }; |
268 | | |
269 | | /* NOTE: This is a fixed API and the index is defined by ssh_kex_types_e */ |
270 | | static const char *supported_methods[] = { |
271 | | KEY_EXCHANGE_SUPPORTED, |
272 | | PUBLIC_KEY_ALGORITHMS, |
273 | | CHACHA20 AES AES_CBC BLOWFISH DES_SUPPORTED NONE, |
274 | | CHACHA20 AES AES_CBC BLOWFISH DES_SUPPORTED NONE, |
275 | | "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1" NONE, |
276 | | "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1" NONE, |
277 | | ZLIB, |
278 | | ZLIB, |
279 | | "", |
280 | | "", |
281 | | NULL |
282 | | }; |
283 | | |
284 | | /* descriptions of the key exchange packet */ |
285 | | static const char *ssh_kex_descriptions[] = { |
286 | | "kex algos", |
287 | | "server host key algo", |
288 | | "encryption client->server", |
289 | | "encryption server->client", |
290 | | "mac algo client->server", |
291 | | "mac algo server->client", |
292 | | "compression algo client->server", |
293 | | "compression algo server->client", |
294 | | "languages client->server", |
295 | | "languages server->client", |
296 | | NULL |
297 | | }; |
298 | | |
299 | | const char *ssh_kex_get_default_methods(enum ssh_kex_types_e type) |
300 | 67.0k | { |
301 | 67.0k | if (type >= SSH_KEX_METHODS) { |
302 | 0 | return NULL; |
303 | 0 | } |
304 | | |
305 | 67.0k | return default_methods[type]; |
306 | 67.0k | } |
307 | | const char *ssh_kex_get_supported_method(enum ssh_kex_types_e type) |
308 | 0 | { |
309 | 0 | if (type >= SSH_KEX_METHODS) { |
310 | 0 | return NULL; |
311 | 0 | } |
312 | | |
313 | 0 | return supported_methods[type]; |
314 | 0 | } |
315 | | |
316 | | const char *ssh_kex_get_description(enum ssh_kex_types_e type) |
317 | 603 | { |
318 | 603 | if (type >= SSH_KEX_METHODS) { |
319 | 0 | return NULL; |
320 | 0 | } |
321 | | |
322 | 603 | return ssh_kex_descriptions[type]; |
323 | 603 | } |
324 | | |
325 | | const char *ssh_kex_get_fips_methods(enum ssh_kex_types_e type) |
326 | 0 | { |
327 | 0 | if (type >= SSH_KEX_METHODS) { |
328 | 0 | return NULL; |
329 | 0 | } |
330 | | |
331 | 0 | return fips_methods[type]; |
332 | 0 | } |
333 | | |
334 | | /** |
335 | | * @brief Get a list of supported algorithms of a given type. This respects the |
336 | | * FIPS mode status. |
337 | | * |
338 | | * @param[in] type The type of the algorithm to query (SSH_KEX, SSH_MAC_C_S, |
339 | | * ...). |
340 | | * |
341 | | * @return The list of supported methods as comma-separated string, or NULL for |
342 | | * unknown type. |
343 | | */ |
344 | | const char *ssh_get_supported_methods(enum ssh_kex_types_e type) |
345 | 0 | { |
346 | 0 | if (ssh_fips_mode()) { |
347 | 0 | return ssh_kex_get_fips_methods(type); |
348 | 0 | } else { |
349 | 0 | return ssh_kex_get_supported_method(type); |
350 | 0 | } |
351 | 0 | } |
352 | | |
353 | | /** |
354 | | * @internal |
355 | | * @brief returns whether the first client key exchange algorithm or |
356 | | * hostkey type matches its server counterpart |
357 | | * @returns whether the first client key exchange algorithm or hostkey type |
358 | | * matches its server counterpart |
359 | | */ |
360 | | static int cmp_first_kex_algo(const char *client_str, |
361 | 2.12k | const char *server_str) { |
362 | 2.12k | size_t client_kex_len; |
363 | 2.12k | size_t server_kex_len; |
364 | | |
365 | 2.12k | const char *colon = NULL; |
366 | | |
367 | 2.12k | int is_wrong = 1; |
368 | | |
369 | 2.12k | if (client_str == NULL || server_str == NULL) { |
370 | 0 | return is_wrong; |
371 | 0 | } |
372 | | |
373 | 2.12k | colon = strchr(client_str, ','); |
374 | 2.12k | if (colon == NULL) { |
375 | 189 | client_kex_len = strlen(client_str); |
376 | 1.94k | } else { |
377 | 1.94k | client_kex_len = colon - client_str; |
378 | 1.94k | } |
379 | | |
380 | 2.12k | colon = strchr(server_str, ','); |
381 | 2.12k | if (colon == NULL) { |
382 | 282 | server_kex_len = strlen(server_str); |
383 | 1.84k | } else { |
384 | 1.84k | server_kex_len = colon - server_str; |
385 | 1.84k | } |
386 | | |
387 | 2.12k | if (client_kex_len != server_kex_len) { |
388 | 1.88k | return is_wrong; |
389 | 1.88k | } |
390 | | |
391 | 245 | is_wrong = (strncmp(client_str, server_str, client_kex_len) != 0); |
392 | | |
393 | 245 | return is_wrong; |
394 | 2.12k | } |
395 | | |
396 | | SSH_PACKET_CALLBACK(ssh_packet_kexinit) |
397 | 13.4k | { |
398 | 13.4k | int i, ok; |
399 | 13.4k | struct ssh_crypto_struct *crypto = session->next_crypto; |
400 | 13.4k | int server_kex = session->server; |
401 | 13.4k | ssh_string str = NULL; |
402 | 13.4k | char *strings[SSH_KEX_METHODS] = {0}; |
403 | 13.4k | int rc = SSH_ERROR; |
404 | 13.4k | size_t len; |
405 | | |
406 | 13.4k | uint8_t first_kex_packet_follows = 0; |
407 | 13.4k | uint32_t kexinit_reserved = 0; |
408 | | |
409 | 13.4k | (void)type; |
410 | 13.4k | (void)user; |
411 | | |
412 | 13.4k | SSH_LOG(SSH_LOG_TRACE, "KEXINIT received"); |
413 | | |
414 | 13.4k | if (session->session_state == SSH_SESSION_STATE_AUTHENTICATED) { |
415 | 238 | if (session->dh_handshake_state == DH_STATE_FINISHED) { |
416 | 238 | SSH_LOG(SSH_LOG_DEBUG, "Peer initiated key re-exchange"); |
417 | | /* Reset the sent flag if the re-kex was initiated by the peer */ |
418 | 238 | session->flags &= ~SSH_SESSION_FLAG_KEXINIT_SENT; |
419 | 238 | } else if (session->flags & SSH_SESSION_FLAG_KEXINIT_SENT && |
420 | 0 | session->dh_handshake_state == DH_STATE_INIT_SENT) { |
421 | | /* This happens only when we are sending our-guessed first kex |
422 | | * packet right after our KEXINIT packet. */ |
423 | 0 | SSH_LOG(SSH_LOG_DEBUG, "Received peer kexinit answer."); |
424 | 0 | } else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) { |
425 | 0 | ssh_set_error(session, SSH_FATAL, |
426 | 0 | "SSH_KEXINIT received in wrong state"); |
427 | 0 | goto error; |
428 | 0 | } |
429 | 13.2k | } else if (session->session_state != SSH_SESSION_STATE_INITIAL_KEX) { |
430 | 0 | ssh_set_error(session, SSH_FATAL, |
431 | 0 | "SSH_KEXINIT received in wrong state"); |
432 | 0 | goto error; |
433 | 0 | } |
434 | | |
435 | 13.4k | if (server_kex) { |
436 | 9.67k | #ifdef WITH_SERVER |
437 | 9.67k | len = ssh_buffer_get_data(packet, crypto->client_kex.cookie, 16); |
438 | 9.67k | if (len != 16) { |
439 | 6 | ssh_set_error(session, SSH_FATAL, |
440 | 6 | "ssh_packet_kexinit: no cookie in packet"); |
441 | 6 | goto error; |
442 | 6 | } |
443 | | |
444 | 9.67k | ok = ssh_hashbufin_add_cookie(session, crypto->client_kex.cookie); |
445 | 9.67k | if (ok < 0) { |
446 | 0 | ssh_set_error(session, SSH_FATAL, |
447 | 0 | "ssh_packet_kexinit: adding cookie failed"); |
448 | 0 | goto error; |
449 | 0 | } |
450 | | |
451 | 9.67k | ok = server_set_kex(session); |
452 | 9.67k | if (ok == SSH_ERROR) { |
453 | 0 | goto error; |
454 | 0 | } |
455 | 9.67k | #endif /* WITH_SERVER */ |
456 | 9.67k | } else { |
457 | 3.82k | len = ssh_buffer_get_data(packet, crypto->server_kex.cookie, 16); |
458 | 3.82k | if (len != 16) { |
459 | 6 | ssh_set_error(session, SSH_FATAL, |
460 | 6 | "ssh_packet_kexinit: no cookie in packet"); |
461 | 6 | goto error; |
462 | 6 | } |
463 | | |
464 | 3.81k | ok = ssh_hashbufin_add_cookie(session, crypto->server_kex.cookie); |
465 | 3.81k | if (ok < 0) { |
466 | 0 | ssh_set_error(session, SSH_FATAL, |
467 | 0 | "ssh_packet_kexinit: adding cookie failed"); |
468 | 0 | goto error; |
469 | 0 | } |
470 | | |
471 | 3.81k | ok = ssh_set_client_kex(session); |
472 | 3.81k | if (ok == SSH_ERROR) { |
473 | 0 | goto error; |
474 | 0 | } |
475 | 3.81k | } |
476 | | |
477 | 147k | for (i = 0; i < SSH_KEX_METHODS; i++) { |
478 | 133k | str = ssh_buffer_get_ssh_string(packet); |
479 | 133k | if (str == NULL) { |
480 | 169 | goto error; |
481 | 169 | } |
482 | | |
483 | 133k | rc = ssh_buffer_add_ssh_string(session->in_hashbuf, str); |
484 | 133k | if (rc < 0) { |
485 | 0 | ssh_set_error(session, SSH_FATAL, |
486 | 0 | "Error adding string in hash buffer"); |
487 | 0 | goto error; |
488 | 0 | } |
489 | | |
490 | 133k | strings[i] = ssh_string_to_char(str); |
491 | 133k | if (strings[i] == NULL) { |
492 | 0 | ssh_set_error_oom(session); |
493 | 0 | goto error; |
494 | 0 | } |
495 | 133k | SSH_STRING_FREE(str); |
496 | 133k | str = NULL; |
497 | 133k | } |
498 | | |
499 | | /* copy the peer kex info into an array of strings */ |
500 | 13.3k | if (server_kex) { |
501 | 9.58k | #ifdef WITH_SERVER |
502 | 105k | for (i = 0; i < SSH_KEX_METHODS; i++) { |
503 | 95.8k | crypto->client_kex.methods[i] = strings[i]; |
504 | 95.8k | } |
505 | 9.58k | #endif /* WITH_SERVER */ |
506 | 9.58k | } else { /* client */ |
507 | 40.9k | for (i = 0; i < SSH_KEX_METHODS; i++) { |
508 | 37.2k | crypto->server_kex.methods[i] = strings[i]; |
509 | 37.2k | } |
510 | 3.72k | } |
511 | | |
512 | | /* |
513 | | * Handle the two final fields for the KEXINIT message (RFC 4253 7.1): |
514 | | * |
515 | | * boolean first_kex_packet_follows |
516 | | * uint32 0 (reserved for future extension) |
517 | | * |
518 | | * Notably if clients set 'first_kex_packet_follows', it is expected |
519 | | * that its value is included when computing the session ID (see |
520 | | * 'make_sessionid'). |
521 | | */ |
522 | | |
523 | 13.3k | rc = ssh_buffer_get_u8(packet, &first_kex_packet_follows); |
524 | 13.3k | if (rc != 1) { |
525 | 7 | goto error; |
526 | 7 | } |
527 | | |
528 | 13.3k | rc = ssh_buffer_add_u8(session->in_hashbuf, first_kex_packet_follows); |
529 | 13.3k | if (rc < 0) { |
530 | 0 | goto error; |
531 | 0 | } |
532 | | |
533 | 13.3k | rc = ssh_buffer_add_u32(session->in_hashbuf, kexinit_reserved); |
534 | 13.3k | if (rc < 0) { |
535 | 0 | goto error; |
536 | 0 | } |
537 | | |
538 | | /* |
539 | | * Remember whether 'first_kex_packet_follows' was set and the client |
540 | | * guess was wrong: in this case the next SSH_MSG_KEXDH_INIT message |
541 | | * must be ignored on the server side. |
542 | | * Client needs to start the Key exchange over with the correct method |
543 | | */ |
544 | 13.3k | if (first_kex_packet_follows || session->send_first_kex_follows) { |
545 | 2.11k | char **client_methods = crypto->client_kex.methods; |
546 | 2.11k | char **server_methods = crypto->server_kex.methods; |
547 | 2.11k | session->first_kex_follows_guess_wrong = |
548 | 2.11k | cmp_first_kex_algo(client_methods[SSH_KEX], |
549 | 2.11k | server_methods[SSH_KEX]) || |
550 | 14 | cmp_first_kex_algo(client_methods[SSH_HOSTKEYS], |
551 | 14 | server_methods[SSH_HOSTKEYS]); |
552 | 2.11k | SSH_LOG(SSH_LOG_DEBUG, "The initial guess was %s.", |
553 | 2.11k | session->first_kex_follows_guess_wrong ? "wrong" : "right"); |
554 | 2.11k | } |
555 | | |
556 | | /* |
557 | | * handle the "strict KEX" feature. If supported by peer, then set up the |
558 | | * flag and verify packet sequence numbers. |
559 | | */ |
560 | 13.3k | if (server_kex) { |
561 | 9.58k | ok = match_group(crypto->client_kex.methods[SSH_KEX], |
562 | 9.58k | KEX_STRICT_CLIENT); |
563 | 9.58k | if (ok) { |
564 | 677 | SSH_LOG(SSH_LOG_DEBUG, "Client supports strict kex, enabling."); |
565 | 677 | session->flags |= SSH_SESSION_FLAG_KEX_STRICT; |
566 | 677 | } |
567 | 9.58k | } else { |
568 | | /* client kex */ |
569 | 3.72k | ok = match_group(crypto->server_kex.methods[SSH_KEX], |
570 | 3.72k | KEX_STRICT_SERVER); |
571 | 3.72k | if (ok) { |
572 | 381 | SSH_LOG(SSH_LOG_DEBUG, "Server supports strict kex, enabling."); |
573 | 381 | session->flags |= SSH_SESSION_FLAG_KEX_STRICT; |
574 | 381 | } |
575 | 3.72k | } |
576 | 13.3k | #ifdef WITH_SERVER |
577 | 13.3k | if (server_kex) { |
578 | | /* |
579 | | * If client sent a ext-info-c message in the kex list, it supports |
580 | | * RFC 8308 extension negotiation. |
581 | | */ |
582 | 9.58k | ok = match_group(crypto->client_kex.methods[SSH_KEX], |
583 | 9.58k | KEX_EXTENSION_CLIENT); |
584 | 9.58k | if (ok) { |
585 | 740 | const char *hostkeys = NULL, *wanted_hostkeys = NULL; |
586 | | |
587 | | /* The client supports extension negotiation */ |
588 | 740 | session->extensions |= SSH_EXT_NEGOTIATION; |
589 | | /* |
590 | | * RFC 8332 Section 3.1: Use for Server Authentication |
591 | | * Check what algorithms were provided in the SSH_HOSTKEYS list |
592 | | * by the client and enable the respective extensions to provide |
593 | | * correct signature in the next packet if RSA is negotiated |
594 | | */ |
595 | 740 | hostkeys = crypto->client_kex.methods[SSH_HOSTKEYS]; |
596 | 740 | wanted_hostkeys = session->opts.wanted_methods[SSH_HOSTKEYS]; |
597 | 740 | ok = match_group(hostkeys, "rsa-sha2-512"); |
598 | 740 | if (ok) { |
599 | | /* Check if rsa-sha2-512 is allowed by config */ |
600 | 608 | if (wanted_hostkeys != NULL) { |
601 | 608 | char *is_allowed = ssh_find_matching(wanted_hostkeys, |
602 | 608 | "rsa-sha2-512"); |
603 | 608 | if (is_allowed != NULL) { |
604 | 339 | session->extensions |= SSH_EXT_SIG_RSA_SHA512; |
605 | 339 | } |
606 | 608 | SAFE_FREE(is_allowed); |
607 | 608 | } |
608 | 608 | } |
609 | 740 | ok = match_group(hostkeys, "rsa-sha2-256"); |
610 | 740 | if (ok) { |
611 | | /* Check if rsa-sha2-256 is allowed by config */ |
612 | 463 | if (wanted_hostkeys != NULL) { |
613 | 463 | char *is_allowed = ssh_find_matching(wanted_hostkeys, |
614 | 463 | "rsa-sha2-256"); |
615 | 463 | if (is_allowed != NULL) { |
616 | 194 | session->extensions |= SSH_EXT_SIG_RSA_SHA256; |
617 | 194 | } |
618 | 463 | SAFE_FREE(is_allowed); |
619 | 463 | } |
620 | 463 | } |
621 | | |
622 | | /* |
623 | | * Ensure that the client preference is honored for the case |
624 | | * both signature types are enabled. |
625 | | */ |
626 | 740 | if ((session->extensions & SSH_EXT_SIG_RSA_SHA256) && |
627 | 196 | (session->extensions & SSH_EXT_SIG_RSA_SHA512)) { |
628 | 106 | char *rsa_sig_ext = NULL; |
629 | 106 | session->extensions &= ~(SSH_EXT_SIG_RSA_SHA256 | SSH_EXT_SIG_RSA_SHA512); |
630 | 106 | rsa_sig_ext = ssh_find_matching("rsa-sha2-512,rsa-sha2-256", |
631 | 106 | hostkeys); |
632 | 106 | if (rsa_sig_ext == NULL) { |
633 | 4 | goto error; /* should never happen */ |
634 | 102 | } else if (strcmp(rsa_sig_ext, "rsa-sha2-512") == 0) { |
635 | 8 | session->extensions |= SSH_EXT_SIG_RSA_SHA512; |
636 | 94 | } else if (strcmp(rsa_sig_ext, "rsa-sha2-256") == 0) { |
637 | 94 | session->extensions |= SSH_EXT_SIG_RSA_SHA256; |
638 | 94 | } else { |
639 | 0 | SAFE_FREE(rsa_sig_ext); |
640 | 0 | goto error; /* should never happen */ |
641 | 0 | } |
642 | 102 | SAFE_FREE(rsa_sig_ext); |
643 | 102 | } |
644 | | |
645 | 736 | SSH_LOG(SSH_LOG_DEBUG, "The client supports extension " |
646 | 736 | "negotiation. Enabled signature algorithms: %s%s", |
647 | 736 | session->extensions & SSH_EXT_SIG_RSA_SHA256 ? "SHA256" : "", |
648 | 736 | session->extensions & SSH_EXT_SIG_RSA_SHA512 ? " SHA512" : ""); |
649 | 736 | } |
650 | 9.58k | } |
651 | 13.3k | #endif /* WITH_SERVER */ |
652 | | |
653 | | /* Note, that his overwrites authenticated state in case of rekeying */ |
654 | 13.3k | session->session_state = SSH_SESSION_STATE_KEXINIT_RECEIVED; |
655 | | /* if we already sent our initial key exchange packet, do not reset the |
656 | | * DH state. We will know if we were right with our guess only in |
657 | | * dh_handshake_state() */ |
658 | 13.3k | if (session->send_first_kex_follows == false) { |
659 | 13.3k | session->dh_handshake_state = DH_STATE_INIT; |
660 | 13.3k | } |
661 | 13.3k | session->ssh_connection_callback(session); |
662 | 13.3k | return SSH_PACKET_USED; |
663 | | |
664 | 192 | error: |
665 | 192 | SSH_STRING_FREE(str); |
666 | 2.11k | for (i = 0; i < SSH_KEX_METHODS; i++) { |
667 | 1.92k | if (server_kex) { |
668 | 930 | #ifdef WITH_SERVER |
669 | 930 | session->next_crypto->client_kex.methods[i] = NULL; |
670 | 930 | #endif /* WITH_SERVER */ |
671 | 990 | } else { /* client */ |
672 | 990 | session->next_crypto->server_kex.methods[i] = NULL; |
673 | 990 | } |
674 | 1.92k | SAFE_FREE(strings[i]); |
675 | 1.92k | } |
676 | | |
677 | 192 | session->session_state = SSH_SESSION_STATE_ERROR; |
678 | | |
679 | 192 | return SSH_PACKET_USED; |
680 | 13.3k | } |
681 | | |
682 | 28.2k | void ssh_list_kex(struct ssh_kex_struct *kex) { |
683 | 28.2k | int i = 0; |
684 | | |
685 | | #ifdef DEBUG_CRYPTO |
686 | | ssh_log_hexdump("session cookie", kex->cookie, 16); |
687 | | #endif |
688 | | |
689 | 310k | for(i = 0; i < SSH_KEX_METHODS; i++) { |
690 | 282k | if (kex->methods[i] == NULL) { |
691 | 0 | continue; |
692 | 0 | } |
693 | 282k | SSH_LOG(SSH_LOG_FUNCTIONS, "%s: %s", |
694 | 282k | ssh_kex_descriptions[i], kex->methods[i]); |
695 | 282k | } |
696 | 28.2k | } |
697 | | |
698 | | /** |
699 | | * @internal |
700 | | * |
701 | | * @brief selects the hostkey mechanisms to be chosen for the key exchange, |
702 | | * as some hostkey mechanisms may be present in known_hosts files. |
703 | | * |
704 | | * @returns a cstring containing a comma-separated list of hostkey methods. |
705 | | * NULL if no method matches |
706 | | */ |
707 | | char *ssh_client_select_hostkeys(ssh_session session) |
708 | 4.74k | { |
709 | 4.74k | const char *wanted = NULL; |
710 | 4.74k | char *wanted_without_certs = NULL; |
711 | 4.74k | char *known_hosts_algorithms = NULL; |
712 | 4.74k | char *known_hosts_ordered = NULL; |
713 | 4.74k | char *new_hostkeys = NULL; |
714 | 4.74k | char *fips_hostkeys = NULL; |
715 | | |
716 | 4.74k | wanted = session->opts.wanted_methods[SSH_HOSTKEYS]; |
717 | 4.74k | if (wanted == NULL) { |
718 | 4.74k | if (ssh_fips_mode()) { |
719 | 0 | wanted = ssh_kex_get_fips_methods(SSH_HOSTKEYS); |
720 | 4.74k | } else { |
721 | 4.74k | wanted = ssh_kex_get_default_methods(SSH_HOSTKEYS); |
722 | 4.74k | } |
723 | 4.74k | } |
724 | | |
725 | | /* This removes the certificate types, unsupported for now */ |
726 | 4.74k | wanted_without_certs = ssh_find_all_matching(HOSTKEYS, wanted); |
727 | 4.74k | if (wanted_without_certs == NULL) { |
728 | 0 | SSH_LOG(SSH_LOG_TRACE, |
729 | 0 | "List of allowed host key algorithms is empty or contains only " |
730 | 0 | "unsupported algorithms"); |
731 | 0 | return NULL; |
732 | 0 | } |
733 | | |
734 | 4.74k | SSH_LOG(SSH_LOG_DEBUG, |
735 | 4.74k | "Order of wanted host keys: \"%s\"", |
736 | 4.74k | wanted_without_certs); |
737 | | |
738 | 4.74k | known_hosts_algorithms = ssh_known_hosts_get_algorithms_names(session); |
739 | 4.74k | if (known_hosts_algorithms == NULL) { |
740 | 4.74k | SSH_LOG(SSH_LOG_DEBUG, |
741 | 4.74k | "No key found in known_hosts; " |
742 | 4.74k | "changing host key method to \"%s\"", |
743 | 4.74k | wanted_without_certs); |
744 | | |
745 | 4.74k | return wanted_without_certs; |
746 | 4.74k | } |
747 | | |
748 | 0 | SSH_LOG(SSH_LOG_DEBUG, |
749 | 0 | "Algorithms found in known_hosts files: \"%s\"", |
750 | 0 | known_hosts_algorithms); |
751 | | |
752 | | /* Filter and order the keys from known_hosts according to wanted list */ |
753 | 0 | known_hosts_ordered = ssh_find_all_matching(known_hosts_algorithms, |
754 | 0 | wanted_without_certs); |
755 | 0 | SAFE_FREE(known_hosts_algorithms); |
756 | 0 | if (known_hosts_ordered == NULL) { |
757 | 0 | SSH_LOG(SSH_LOG_DEBUG, |
758 | 0 | "No key found in known_hosts is allowed; " |
759 | 0 | "changing host key method to \"%s\"", |
760 | 0 | wanted_without_certs); |
761 | |
|
762 | 0 | return wanted_without_certs; |
763 | 0 | } |
764 | | |
765 | | /* Append the other supported keys after the preferred ones |
766 | | * This function tolerates NULL pointers in parameters */ |
767 | 0 | new_hostkeys = ssh_append_without_duplicates(known_hosts_ordered, |
768 | 0 | wanted_without_certs); |
769 | 0 | SAFE_FREE(known_hosts_ordered); |
770 | 0 | SAFE_FREE(wanted_without_certs); |
771 | 0 | if (new_hostkeys == NULL) { |
772 | 0 | ssh_set_error_oom(session); |
773 | 0 | return NULL; |
774 | 0 | } |
775 | | |
776 | 0 | if (ssh_fips_mode()) { |
777 | | /* Filter out algorithms not allowed in FIPS mode */ |
778 | 0 | fips_hostkeys = ssh_keep_fips_algos(SSH_HOSTKEYS, new_hostkeys); |
779 | 0 | SAFE_FREE(new_hostkeys); |
780 | 0 | if (fips_hostkeys == NULL) { |
781 | 0 | SSH_LOG(SSH_LOG_TRACE, |
782 | 0 | "None of the wanted host keys or keys in known_hosts files " |
783 | 0 | "is allowed in FIPS mode."); |
784 | 0 | return NULL; |
785 | 0 | } |
786 | 0 | new_hostkeys = fips_hostkeys; |
787 | 0 | } |
788 | | |
789 | 0 | SSH_LOG(SSH_LOG_DEBUG, |
790 | 0 | "Changing host key method to \"%s\"", |
791 | 0 | new_hostkeys); |
792 | |
|
793 | 0 | return new_hostkeys; |
794 | 0 | } |
795 | | |
796 | | /** |
797 | | * @brief sets the key exchange parameters to be sent to the server, |
798 | | * in function of the options and available methods. |
799 | | */ |
800 | | int ssh_set_client_kex(ssh_session session) |
801 | 8.55k | { |
802 | 8.55k | struct ssh_kex_struct *client = &session->next_crypto->client_kex; |
803 | 8.55k | const char *wanted = NULL; |
804 | 8.55k | int ok; |
805 | 8.55k | int i; |
806 | 8.55k | bool gssapi_null_alg = false; |
807 | 8.55k | char *hostkeys = NULL; |
808 | | |
809 | | /* Skip if already set, for example for the rekey or when we do the guessing |
810 | | * it could have been already used to make some protocol decisions. */ |
811 | 8.55k | if (client->methods[0] != NULL) { |
812 | 3.81k | return SSH_OK; |
813 | 3.81k | } |
814 | | |
815 | 4.74k | ok = ssh_get_random(client->cookie, 16, 0); |
816 | 4.74k | if (!ok) { |
817 | 0 | ssh_set_error(session, SSH_FATAL, "PRNG error"); |
818 | 0 | return SSH_ERROR; |
819 | 0 | } |
820 | | #ifdef WITH_GSSAPI |
821 | | if (session->opts.gssapi_key_exchange) { |
822 | | char *gssapi_algs = NULL; |
823 | | |
824 | | ok = ssh_gssapi_init(session); |
825 | | if (ok != SSH_OK) { |
826 | | ssh_set_error_oom(session); |
827 | | return SSH_ERROR; |
828 | | } |
829 | | |
830 | | ok = ssh_gssapi_import_name(session->gssapi, session->opts.host); |
831 | | if (ok != SSH_OK) { |
832 | | return SSH_ERROR; |
833 | | } |
834 | | |
835 | | gssapi_algs = ssh_gssapi_kex_mechs(session); |
836 | | if (gssapi_algs == NULL) { |
837 | | return SSH_ERROR; |
838 | | } |
839 | | |
840 | | /* Prefix the default algorithms with gsskex algs */ |
841 | | if (ssh_fips_mode()) { |
842 | | session->opts.wanted_methods[SSH_KEX] = |
843 | | ssh_prefix_without_duplicates(fips_methods[SSH_KEX], |
844 | | gssapi_algs); |
845 | | } else { |
846 | | session->opts.wanted_methods[SSH_KEX] = |
847 | | ssh_prefix_without_duplicates(default_methods[SSH_KEX], |
848 | | gssapi_algs); |
849 | | } |
850 | | |
851 | | gssapi_null_alg = true; |
852 | | |
853 | | SAFE_FREE(gssapi_algs); |
854 | | } |
855 | | #endif |
856 | | |
857 | | /* Set the list of allowed algorithms in order of preference, if it hadn't |
858 | | * been set yet. */ |
859 | 52.1k | for (i = 0; i < SSH_KEX_METHODS; i++) { |
860 | 47.4k | if (i == SSH_HOSTKEYS) { |
861 | | /* Set the hostkeys in the following order: |
862 | | * - First: keys present in known_hosts files ordered by preference |
863 | | * - Next: other wanted algorithms ordered by preference */ |
864 | 4.74k | client->methods[i] = ssh_client_select_hostkeys(session); |
865 | 4.74k | if (client->methods[i] == NULL) { |
866 | 0 | ssh_set_error_oom(session); |
867 | 0 | return SSH_ERROR; |
868 | 0 | } |
869 | 4.74k | if (gssapi_null_alg) { |
870 | 0 | hostkeys = |
871 | 0 | ssh_append_without_duplicates(client->methods[i], "null"); |
872 | 0 | if (hostkeys == NULL) { |
873 | 0 | ssh_set_error_oom(session); |
874 | 0 | return SSH_ERROR; |
875 | 0 | } |
876 | 0 | SAFE_FREE(client->methods[i]); |
877 | 0 | client->methods[i] = hostkeys; |
878 | 0 | } |
879 | 4.74k | continue; |
880 | 4.74k | } |
881 | | |
882 | 42.6k | wanted = session->opts.wanted_methods[i]; |
883 | 42.6k | if (wanted == NULL) { |
884 | 23.7k | if (ssh_fips_mode()) { |
885 | 0 | wanted = fips_methods[i]; |
886 | 23.7k | } else { |
887 | 23.7k | wanted = default_methods[i]; |
888 | 23.7k | } |
889 | 23.7k | } |
890 | 42.6k | client->methods[i] = strdup(wanted); |
891 | 42.6k | if (client->methods[i] == NULL) { |
892 | 0 | ssh_set_error_oom(session); |
893 | 0 | return SSH_ERROR; |
894 | 0 | } |
895 | 42.6k | } |
896 | | |
897 | | /* For rekeying, skip the extension negotiation */ |
898 | 4.74k | if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { |
899 | 0 | return SSH_OK; |
900 | 0 | } |
901 | | |
902 | 4.74k | ok = ssh_kex_append_extensions(session, client); |
903 | 4.74k | if (ok != SSH_OK){ |
904 | 0 | return ok; |
905 | 0 | } |
906 | | |
907 | 4.74k | return SSH_OK; |
908 | 4.74k | } |
909 | | |
910 | | int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex) |
911 | 14.8k | { |
912 | 14.8k | char *kex = NULL; |
913 | 14.8k | char *kex_tmp = NULL; |
914 | 14.8k | size_t kex_len, len; |
915 | | |
916 | | /* Here we append ext-info-c and kex-strict-c-v00@openssh.com for client |
917 | | * and kex-strict-s-v00@openssh.com for server to the list of kex algorithms |
918 | | */ |
919 | 14.8k | kex = pkex->methods[SSH_KEX]; |
920 | 14.8k | len = strlen(kex); |
921 | 14.8k | if (session->server) { |
922 | | /* Comma, nul byte */ |
923 | 10.0k | kex_len = len + 1 + strlen(KEX_STRICT_SERVER) + 1; |
924 | 10.0k | } else { |
925 | | /* Comma, comma, nul byte */ |
926 | 4.74k | kex_len = len + 1 + strlen(KEX_EXTENSION_CLIENT) + 1 + |
927 | 4.74k | strlen(KEX_STRICT_CLIENT) + 1; |
928 | 4.74k | } |
929 | 14.8k | if (kex_len >= MAX_PACKET_LEN) { |
930 | | /* Overflow */ |
931 | 0 | return SSH_ERROR; |
932 | 0 | } |
933 | 14.8k | kex_tmp = realloc(kex, kex_len); |
934 | 14.8k | if (kex_tmp == NULL) { |
935 | 0 | ssh_set_error_oom(session); |
936 | 0 | return SSH_ERROR; |
937 | 0 | } |
938 | 14.8k | if (session->server){ |
939 | 10.0k | snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_STRICT_SERVER); |
940 | 10.0k | } else { |
941 | 4.74k | snprintf(kex_tmp + len, |
942 | 4.74k | kex_len - len, |
943 | 4.74k | ",%s,%s", |
944 | 4.74k | KEX_EXTENSION_CLIENT, |
945 | 4.74k | KEX_STRICT_CLIENT); |
946 | 4.74k | } |
947 | 14.8k | pkex->methods[SSH_KEX] = kex_tmp; |
948 | 14.8k | return SSH_OK; |
949 | 14.8k | } |
950 | | |
951 | | static const char *ssh_find_aead_hmac(const char *cipher) |
952 | 24.5k | { |
953 | 24.5k | if (cipher == NULL) { |
954 | 0 | return NULL; |
955 | 24.5k | } else if (strcmp(cipher, "chacha20-poly1305@openssh.com") == 0) { |
956 | 0 | return "aead-poly1305"; |
957 | 24.5k | } else if (strcmp(cipher, "aes256-gcm@openssh.com") == 0) { |
958 | 0 | return "aead-gcm"; |
959 | 24.5k | } else if (strcmp(cipher, "aes128-gcm@openssh.com") == 0) { |
960 | 0 | return "aead-gcm"; |
961 | 0 | } |
962 | 24.5k | return NULL; |
963 | 24.5k | } |
964 | | |
965 | | static enum ssh_key_exchange_e |
966 | | kex_select_kex_type(const char *kex) |
967 | 12.2k | { |
968 | 12.2k | if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) { |
969 | 0 | return SSH_KEX_DH_GROUP1_SHA1; |
970 | 12.2k | } else if (strncmp(kex, "gss-group14-sha256-", 19) == 0) { |
971 | 0 | return SSH_GSS_KEX_DH_GROUP14_SHA256; |
972 | 12.2k | } else if (strncmp(kex, "gss-group16-sha512-", 19) == 0) { |
973 | 0 | return SSH_GSS_KEX_DH_GROUP16_SHA512; |
974 | 12.2k | } else if (strncmp(kex, "gss-nistp256-sha256-", 20) == 0) { |
975 | 0 | return SSH_GSS_KEX_ECDH_NISTP256_SHA256; |
976 | 12.2k | } else if (strncmp(kex, "gss-curve25519-sha256-", 22) == 0) { |
977 | 0 | return SSH_GSS_KEX_CURVE25519_SHA256; |
978 | 12.2k | } else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) { |
979 | 0 | return SSH_KEX_DH_GROUP14_SHA1; |
980 | 12.2k | } else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) { |
981 | 770 | return SSH_KEX_DH_GROUP14_SHA256; |
982 | 11.4k | } else if (strcmp(kex, "diffie-hellman-group16-sha512") == 0) { |
983 | 104 | return SSH_KEX_DH_GROUP16_SHA512; |
984 | 11.3k | } else if (strcmp(kex, "diffie-hellman-group18-sha512") == 0) { |
985 | 268 | return SSH_KEX_DH_GROUP18_SHA512; |
986 | 268 | #ifdef WITH_GEX |
987 | 11.1k | } else if (strcmp(kex, "diffie-hellman-group-exchange-sha1") == 0) { |
988 | 0 | return SSH_KEX_DH_GEX_SHA1; |
989 | 11.1k | } else if (strcmp(kex, "diffie-hellman-group-exchange-sha256") == 0) { |
990 | 377 | return SSH_KEX_DH_GEX_SHA256; |
991 | 377 | #endif /* WITH_GEX */ |
992 | 10.7k | } else if (strcmp(kex, "ecdh-sha2-nistp256") == 0) { |
993 | 119 | return SSH_KEX_ECDH_SHA2_NISTP256; |
994 | 10.6k | } else if (strcmp(kex, "ecdh-sha2-nistp384") == 0) { |
995 | 14 | return SSH_KEX_ECDH_SHA2_NISTP384; |
996 | 10.6k | } else if (strcmp(kex, "ecdh-sha2-nistp521") == 0) { |
997 | 15 | return SSH_KEX_ECDH_SHA2_NISTP521; |
998 | 10.6k | } else if (strcmp(kex, "curve25519-sha256@libssh.org") == 0) { |
999 | 439 | return SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG; |
1000 | 10.1k | } else if (strcmp(kex, "curve25519-sha256") == 0) { |
1001 | 8.87k | return SSH_KEX_CURVE25519_SHA256; |
1002 | 8.87k | } else if (strcmp(kex, "sntrup761x25519-sha512@openssh.com") == 0) { |
1003 | 106 | return SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM; |
1004 | 1.17k | } else if (strcmp(kex, "sntrup761x25519-sha512") == 0) { |
1005 | 256 | return SSH_KEX_SNTRUP761X25519_SHA512; |
1006 | 923 | } else if (strcmp(kex, "mlkem768x25519-sha256") == 0) { |
1007 | 616 | return SSH_KEX_MLKEM768X25519_SHA256; |
1008 | 616 | } else if (strcmp(kex, "mlkem768nistp256-sha256") == 0) { |
1009 | 307 | return SSH_KEX_MLKEM768NISTP256_SHA256; |
1010 | | #ifdef HAVE_MLKEM1024 |
1011 | | } else if (strcmp(kex, "mlkem1024nistp384-sha384") == 0) { |
1012 | | return SSH_KEX_MLKEM1024NISTP384_SHA384; |
1013 | | #endif |
1014 | 307 | } |
1015 | | /* should not happen. We should be getting only valid names at this stage */ |
1016 | 0 | return 0; |
1017 | 12.2k | } |
1018 | | |
1019 | | |
1020 | | /** @internal |
1021 | | * @brief Reverts guessed callbacks set during the dh_handshake() |
1022 | | * @param session session handle |
1023 | | * @returns void |
1024 | | */ |
1025 | | static void revert_kex_callbacks(ssh_session session) |
1026 | 1.20k | { |
1027 | 1.20k | switch (session->next_crypto->kex_type) { |
1028 | 0 | case SSH_KEX_DH_GROUP1_SHA1: |
1029 | 0 | case SSH_KEX_DH_GROUP14_SHA1: |
1030 | 0 | case SSH_KEX_DH_GROUP14_SHA256: |
1031 | 0 | case SSH_KEX_DH_GROUP16_SHA512: |
1032 | 0 | case SSH_KEX_DH_GROUP18_SHA512: |
1033 | 0 | ssh_client_dh_remove_callbacks(session); |
1034 | 0 | break; |
1035 | 0 | case SSH_GSS_KEX_DH_GROUP14_SHA256: |
1036 | 0 | case SSH_GSS_KEX_DH_GROUP16_SHA512: |
1037 | 0 | case SSH_GSS_KEX_ECDH_NISTP256_SHA256: |
1038 | 0 | case SSH_GSS_KEX_CURVE25519_SHA256: |
1039 | | #ifdef WITH_GSSAPI |
1040 | | ssh_client_gss_kex_remove_callbacks(session); |
1041 | | #endif /* WITH_GSSAPI */ |
1042 | 0 | break; |
1043 | 0 | #ifdef WITH_GEX |
1044 | 0 | case SSH_KEX_DH_GEX_SHA1: |
1045 | 0 | case SSH_KEX_DH_GEX_SHA256: |
1046 | 0 | ssh_client_dhgex_remove_callbacks(session); |
1047 | 0 | break; |
1048 | 0 | #endif /* WITH_GEX */ |
1049 | 0 | #ifdef HAVE_ECDH |
1050 | 0 | case SSH_KEX_ECDH_SHA2_NISTP256: |
1051 | 0 | case SSH_KEX_ECDH_SHA2_NISTP384: |
1052 | 0 | case SSH_KEX_ECDH_SHA2_NISTP521: |
1053 | 0 | ssh_client_ecdh_remove_callbacks(session); |
1054 | 0 | break; |
1055 | 0 | #endif |
1056 | 0 | #ifdef HAVE_CURVE25519 |
1057 | 0 | case SSH_KEX_CURVE25519_SHA256: |
1058 | 0 | case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: |
1059 | 0 | ssh_client_curve25519_remove_callbacks(session); |
1060 | 0 | break; |
1061 | 0 | #endif |
1062 | 0 | #ifdef HAVE_SNTRUP761 |
1063 | 0 | case SSH_KEX_SNTRUP761X25519_SHA512: |
1064 | 0 | case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM: |
1065 | 0 | ssh_client_sntrup761x25519_remove_callbacks(session); |
1066 | 0 | break; |
1067 | 0 | #endif |
1068 | 0 | case SSH_KEX_MLKEM768X25519_SHA256: |
1069 | 0 | case SSH_KEX_MLKEM768NISTP256_SHA256: |
1070 | | #ifdef HAVE_MLKEM1024 |
1071 | | case SSH_KEX_MLKEM1024NISTP384_SHA384: |
1072 | | #endif |
1073 | 0 | ssh_client_hybrid_mlkem_remove_callbacks(session); |
1074 | 0 | break; |
1075 | 1.20k | } |
1076 | 1.20k | } |
1077 | | |
1078 | | /** @brief Select the different methods on basis of client's and |
1079 | | * server's kex messages, and watches out if a match is possible. |
1080 | | */ |
1081 | | int ssh_kex_select_methods (ssh_session session) |
1082 | 13.3k | { |
1083 | 13.3k | struct ssh_crypto_struct *crypto = session->next_crypto; |
1084 | 13.3k | struct ssh_kex_struct *server = &crypto->server_kex; |
1085 | 13.3k | struct ssh_kex_struct *client = &crypto->client_kex; |
1086 | 13.3k | char *ext_start = NULL; |
1087 | 13.3k | const char *aead_hmac = NULL; |
1088 | 13.3k | enum ssh_key_exchange_e kex_type; |
1089 | 13.3k | int i; |
1090 | | |
1091 | | /* Here we should drop the extensions from the list so we avoid matching. |
1092 | | * it. We added it to the end, so we can just truncate the string here */ |
1093 | 13.3k | if (session->client) { |
1094 | 3.72k | ext_start = strstr(client->methods[SSH_KEX], "," KEX_EXTENSION_CLIENT); |
1095 | 3.72k | if (ext_start != NULL) { |
1096 | 3.72k | ext_start[0] = '\0'; |
1097 | 3.72k | } |
1098 | 3.72k | } |
1099 | 13.3k | if (session->server) { |
1100 | 9.58k | ext_start = strstr(server->methods[SSH_KEX], "," KEX_STRICT_SERVER); |
1101 | 9.58k | if (ext_start != NULL) { |
1102 | 9.35k | ext_start[0] = '\0'; |
1103 | 9.35k | } |
1104 | 9.58k | } |
1105 | | |
1106 | 136k | for (i = 0; i < SSH_KEX_METHODS; i++) { |
1107 | 123k | crypto->kex_methods[i] = ssh_find_matching(server->methods[i], |
1108 | 123k | client->methods[i]); |
1109 | | |
1110 | 123k | if (i == SSH_MAC_C_S || i == SSH_MAC_S_C) { |
1111 | 24.5k | aead_hmac = ssh_find_aead_hmac(crypto->kex_methods[i - 2]); |
1112 | 24.5k | if (aead_hmac) { |
1113 | 0 | free(crypto->kex_methods[i]); |
1114 | 0 | crypto->kex_methods[i] = strdup(aead_hmac); |
1115 | 0 | } |
1116 | 24.5k | } |
1117 | 123k | if (crypto->kex_methods[i] == NULL && i < SSH_LANG_C_S) { |
1118 | 1.03k | ssh_set_error(session, SSH_FATAL, |
1119 | 1.03k | "kex error : no match for method %s: server [%s], " |
1120 | 1.03k | "client [%s]", ssh_kex_descriptions[i], |
1121 | 1.03k | server->methods[i], client->methods[i]); |
1122 | 1.03k | return SSH_ERROR; |
1123 | 122k | } else if ((i >= SSH_LANG_C_S) && (crypto->kex_methods[i] == NULL)) { |
1124 | | /* we can safely do that for languages */ |
1125 | 4.30k | crypto->kex_methods[i] = strdup(""); |
1126 | 4.30k | } |
1127 | 123k | } |
1128 | | |
1129 | | /* We can not set this value directly as the old value is needed to revert |
1130 | | * callbacks if we are client */ |
1131 | 12.2k | kex_type = kex_select_kex_type(crypto->kex_methods[SSH_KEX]); |
1132 | 12.2k | if (session->client && session->first_kex_follows_guess_wrong) { |
1133 | 1.20k | SSH_LOG(SSH_LOG_DEBUG, "Our guess was wrong. Restarting the KEX"); |
1134 | | /* We need to remove the wrong callbacks and start kex again */ |
1135 | 1.20k | revert_kex_callbacks(session); |
1136 | 1.20k | session->dh_handshake_state = DH_STATE_INIT; |
1137 | 1.20k | session->first_kex_follows_guess_wrong = false; |
1138 | 1.20k | } |
1139 | 12.2k | crypto->kex_type = kex_type; |
1140 | | |
1141 | 12.2k | SSH_LOG(SSH_LOG_DEBUG, "Negotiated %s,%s,%s,%s,%s,%s,%s,%s,%s,%s", |
1142 | 12.2k | session->next_crypto->kex_methods[SSH_KEX], |
1143 | 12.2k | session->next_crypto->kex_methods[SSH_HOSTKEYS], |
1144 | 12.2k | session->next_crypto->kex_methods[SSH_CRYPT_C_S], |
1145 | 12.2k | session->next_crypto->kex_methods[SSH_CRYPT_S_C], |
1146 | 12.2k | session->next_crypto->kex_methods[SSH_MAC_C_S], |
1147 | 12.2k | session->next_crypto->kex_methods[SSH_MAC_S_C], |
1148 | 12.2k | session->next_crypto->kex_methods[SSH_COMP_C_S], |
1149 | 12.2k | session->next_crypto->kex_methods[SSH_COMP_S_C], |
1150 | 12.2k | session->next_crypto->kex_methods[SSH_LANG_C_S], |
1151 | 12.2k | session->next_crypto->kex_methods[SSH_LANG_S_C] |
1152 | 12.2k | ); |
1153 | 12.2k | return SSH_OK; |
1154 | 13.3k | } |
1155 | | |
1156 | | |
1157 | | /* this function only sends the predefined set of kex methods */ |
1158 | | int ssh_send_kex(ssh_session session) |
1159 | 14.9k | { |
1160 | 14.9k | struct ssh_kex_struct *kex = (session->server ? |
1161 | 10.2k | &session->next_crypto->server_kex : |
1162 | 14.9k | &session->next_crypto->client_kex); |
1163 | 14.9k | ssh_string str = NULL; |
1164 | 14.9k | int i; |
1165 | 14.9k | int rc; |
1166 | 14.9k | int first_kex_packet_follows = 0; |
1167 | | |
1168 | | /* Only client can initiate the handshake methods we implement. If we |
1169 | | * already received the peer mechanisms, there is no point in guessing */ |
1170 | 14.9k | if (session->client && |
1171 | 4.74k | session->session_state != SSH_SESSION_STATE_KEXINIT_RECEIVED && |
1172 | 4.74k | session->send_first_kex_follows) { |
1173 | 0 | first_kex_packet_follows = 1; |
1174 | 0 | } |
1175 | | |
1176 | 14.9k | SSH_LOG(SSH_LOG_TRACE, |
1177 | 14.9k | "Sending KEXINIT packet, first_kex_packet_follows = %d", |
1178 | 14.9k | first_kex_packet_follows); |
1179 | | |
1180 | 14.9k | rc = ssh_buffer_pack(session->out_buffer, |
1181 | 14.9k | "bP", |
1182 | 14.9k | SSH2_MSG_KEXINIT, |
1183 | 14.9k | (size_t)16, |
1184 | 14.9k | kex->cookie); /* cookie */ |
1185 | 14.9k | if (rc != SSH_OK) |
1186 | 0 | goto error; |
1187 | 14.9k | if (ssh_hashbufout_add_cookie(session) < 0) { |
1188 | 0 | goto error; |
1189 | 0 | } |
1190 | | |
1191 | 14.9k | ssh_list_kex(kex); |
1192 | | |
1193 | 164k | for (i = 0; i < SSH_KEX_METHODS; i++) { |
1194 | 149k | str = ssh_string_from_char(kex->methods[i]); |
1195 | 149k | if (str == NULL) { |
1196 | 0 | goto error; |
1197 | 0 | } |
1198 | | |
1199 | 149k | rc = ssh_buffer_add_ssh_string(session->out_hashbuf, str); |
1200 | 149k | if (rc < 0) { |
1201 | 0 | goto error; |
1202 | 0 | } |
1203 | 149k | rc = ssh_buffer_add_ssh_string(session->out_buffer, str); |
1204 | 149k | if (rc < 0) { |
1205 | 0 | goto error; |
1206 | 0 | } |
1207 | 149k | SSH_STRING_FREE(str); |
1208 | 149k | str = NULL; |
1209 | 149k | } |
1210 | | |
1211 | 14.9k | rc = ssh_buffer_pack(session->out_buffer, |
1212 | 14.9k | "bd", |
1213 | 14.9k | first_kex_packet_follows, |
1214 | 14.9k | 0); |
1215 | 14.9k | if (rc != SSH_OK) { |
1216 | 0 | goto error; |
1217 | 0 | } |
1218 | | |
1219 | | /* Prepare also the first_kex_packet_follows and reserved to 0 */ |
1220 | 14.9k | rc = ssh_buffer_add_u8(session->out_hashbuf, first_kex_packet_follows); |
1221 | 14.9k | if (rc < 0) { |
1222 | 0 | goto error; |
1223 | 0 | } |
1224 | 14.9k | rc = ssh_buffer_add_u32(session->out_hashbuf, 0); |
1225 | 14.9k | if (rc < 0) { |
1226 | 0 | goto error; |
1227 | 0 | } |
1228 | | |
1229 | 14.9k | rc = ssh_packet_send(session); |
1230 | 14.9k | if (rc == SSH_ERROR) { |
1231 | 0 | return -1; |
1232 | 0 | } |
1233 | | |
1234 | 14.9k | session->flags |= SSH_SESSION_FLAG_KEXINIT_SENT; |
1235 | 14.9k | SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_KEXINIT sent"); |
1236 | | |
1237 | | /* If we indicated that we are sending the guessed key exchange packet, |
1238 | | * do it now. The packet is simple, but we need to do some preparations */ |
1239 | 14.9k | if (first_kex_packet_follows == 1) { |
1240 | 0 | char *list = kex->methods[SSH_KEX]; |
1241 | 0 | const char *colon = strchr(list, ','); |
1242 | 0 | size_t kex_name_len = colon ? (size_t)(colon - list) : strlen(list); |
1243 | 0 | char *kex_name = calloc(kex_name_len + 1, 1); |
1244 | 0 | if (kex_name == NULL) { |
1245 | 0 | ssh_set_error_oom(session); |
1246 | 0 | goto error; |
1247 | 0 | } |
1248 | 0 | snprintf(kex_name, kex_name_len + 1, "%.*s", (int)kex_name_len, list); |
1249 | 0 | SSH_LOG(SSH_LOG_TRACE, "Sending the first kex packet for %s", kex_name); |
1250 | |
|
1251 | 0 | session->next_crypto->kex_type = kex_select_kex_type(kex_name); |
1252 | 0 | free(kex_name); |
1253 | | |
1254 | | /* run the first step of the DH handshake */ |
1255 | 0 | session->dh_handshake_state = DH_STATE_INIT; |
1256 | 0 | if (dh_handshake(session) == SSH_ERROR) { |
1257 | 0 | goto error; |
1258 | 0 | } |
1259 | 0 | } |
1260 | 14.9k | return 0; |
1261 | | |
1262 | 0 | error: |
1263 | 0 | ssh_buffer_reinit(session->out_buffer); |
1264 | 0 | ssh_buffer_reinit(session->out_hashbuf); |
1265 | 0 | SSH_STRING_FREE(str); |
1266 | |
|
1267 | 0 | return -1; |
1268 | 14.9k | } |
1269 | | |
1270 | | /* |
1271 | | * Key re-exchange (rekey) is triggered by this function. |
1272 | | * It can not be called again after the rekey is initialized! |
1273 | | */ |
1274 | | int ssh_send_rekex(ssh_session session) |
1275 | 0 | { |
1276 | 0 | int rc; |
1277 | |
|
1278 | 0 | if (session->dh_handshake_state != DH_STATE_FINISHED) { |
1279 | | /* Rekey/Key exchange is already in progress */ |
1280 | 0 | SSH_LOG(SSH_LOG_PACKET, "Attempting rekey in bad state"); |
1281 | 0 | return SSH_ERROR; |
1282 | 0 | } |
1283 | | |
1284 | 0 | if (session->current_crypto == NULL) { |
1285 | | /* No current crypto used -- can not exchange it */ |
1286 | 0 | SSH_LOG(SSH_LOG_PACKET, "No crypto to rekey"); |
1287 | 0 | return SSH_ERROR; |
1288 | 0 | } |
1289 | | |
1290 | 0 | if (session->client) { |
1291 | 0 | rc = ssh_set_client_kex(session); |
1292 | 0 | if (rc != SSH_OK) { |
1293 | 0 | SSH_LOG(SSH_LOG_PACKET, "Failed to set client kex"); |
1294 | 0 | return rc; |
1295 | 0 | } |
1296 | 0 | } else { |
1297 | 0 | #ifdef WITH_SERVER |
1298 | 0 | rc = server_set_kex(session); |
1299 | 0 | if (rc == SSH_ERROR) { |
1300 | 0 | SSH_LOG(SSH_LOG_PACKET, "Failed to set server kex"); |
1301 | 0 | return rc; |
1302 | 0 | } |
1303 | | #else |
1304 | | SSH_LOG(SSH_LOG_PACKET, "Invalid session state."); |
1305 | | return SSH_ERROR; |
1306 | | #endif /* WITH_SERVER */ |
1307 | 0 | } |
1308 | | |
1309 | 0 | session->dh_handshake_state = DH_STATE_INIT; |
1310 | 0 | rc = ssh_send_kex(session); |
1311 | 0 | if (rc < 0) { |
1312 | 0 | SSH_LOG(SSH_LOG_PACKET, "Failed to send kex"); |
1313 | 0 | return rc; |
1314 | 0 | } |
1315 | | |
1316 | | /* Reset the handshake state */ |
1317 | 0 | session->dh_handshake_state = DH_STATE_INIT_SENT; |
1318 | 0 | return SSH_OK; |
1319 | 0 | } |
1320 | | |
1321 | | /* returns a copy of the provided list if everything is supported, |
1322 | | * otherwise a new list of the supported algorithms */ |
1323 | | char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list) |
1324 | 71.1k | { |
1325 | 71.1k | if (algo > SSH_LANG_S_C) { |
1326 | 0 | return NULL; |
1327 | 0 | } |
1328 | | |
1329 | 71.1k | return ssh_find_all_matching(supported_methods[algo], list); |
1330 | 71.1k | } |
1331 | | |
1332 | | /** |
1333 | | * @internal |
1334 | | * |
1335 | | * @brief Return a newly allocated string containing only the FIPS allowed |
1336 | | * algorithms from the list. |
1337 | | * |
1338 | | * @param[in] algo The type of the methods to filter |
1339 | | * @param[in] list The list to be filtered |
1340 | | * |
1341 | | * @return A newly allocated list containing only the FIPS allowed algorithms from |
1342 | | * the list; NULL in case of error. |
1343 | | */ |
1344 | | char *ssh_keep_fips_algos(enum ssh_kex_types_e algo, const char *list) |
1345 | 0 | { |
1346 | 0 | if (algo > SSH_LANG_S_C) { |
1347 | 0 | return NULL; |
1348 | 0 | } |
1349 | | |
1350 | 0 | return ssh_find_all_matching(fips_methods[algo], list); |
1351 | 0 | } |
1352 | | |
1353 | | /** |
1354 | | * @internal |
1355 | | * |
1356 | | * @brief Return a newly allocated string containing the default |
1357 | | * algorithms plus the algorithms specified in list. If the system |
1358 | | * runs in fips mode, this will add only fips approved algorithms. |
1359 | | * Empty list will cause error. |
1360 | | * |
1361 | | * @param[in] algo The type of the methods to filter |
1362 | | * @param[in] list The list to be appended |
1363 | | * |
1364 | | * @return A newly allocated list containing the default algorithms and the |
1365 | | * algorithms in list at the end; NULL in case of error. |
1366 | | */ |
1367 | | char *ssh_add_to_default_algos(enum ssh_kex_types_e algo, const char *list) |
1368 | 912 | { |
1369 | 912 | char *tmp = NULL, *ret = NULL; |
1370 | | |
1371 | 912 | if (algo > SSH_LANG_S_C || list == NULL || list[0] == '\0') { |
1372 | 15 | return NULL; |
1373 | 15 | } |
1374 | | |
1375 | 897 | if (ssh_fips_mode()) { |
1376 | 0 | tmp = ssh_append_without_duplicates(fips_methods[algo], list); |
1377 | 0 | ret = ssh_find_all_matching(fips_methods[algo], tmp); |
1378 | 897 | } else { |
1379 | 897 | tmp = ssh_append_without_duplicates(default_methods[algo], list); |
1380 | 897 | ret = ssh_find_all_matching(supported_methods[algo], tmp); |
1381 | 897 | } |
1382 | | |
1383 | 897 | free(tmp); |
1384 | 897 | return ret; |
1385 | 912 | } |
1386 | | |
1387 | | /** |
1388 | | * @internal |
1389 | | * |
1390 | | * @brief Return a newly allocated string containing the default |
1391 | | * algorithms excluding the algorithms specified in list. If the system |
1392 | | * runs in fips mode, this will remove from the fips_methods list. |
1393 | | * |
1394 | | * @param[in] algo The type of the methods to filter |
1395 | | * @param[in] list The list to be exclude |
1396 | | * |
1397 | | * @return A newly allocated list containing the default algorithms without the |
1398 | | * algorithms in list; NULL in case of error. |
1399 | | */ |
1400 | | char *ssh_remove_from_default_algos(enum ssh_kex_types_e algo, const char *list) |
1401 | 597 | { |
1402 | 597 | if (algo > SSH_LANG_S_C) { |
1403 | 0 | return NULL; |
1404 | 0 | } |
1405 | | |
1406 | 597 | if (list == NULL || list[0] == '\0') { |
1407 | 27 | if (ssh_fips_mode()) { |
1408 | 0 | return strdup(fips_methods[algo]); |
1409 | 27 | } else { |
1410 | 27 | return strdup(default_methods[algo]); |
1411 | 27 | } |
1412 | 27 | } |
1413 | | |
1414 | 570 | if (ssh_fips_mode()) { |
1415 | 0 | return ssh_remove_all_matching(fips_methods[algo], list); |
1416 | 570 | } else { |
1417 | 570 | return ssh_remove_all_matching(default_methods[algo], list); |
1418 | 570 | } |
1419 | 570 | } |
1420 | | |
1421 | | /** |
1422 | | * @internal |
1423 | | * |
1424 | | * @brief Return a newly allocated string containing the default |
1425 | | * algorithms with prioritized algorithms specified in list. If the |
1426 | | * algorithms are present in the default list they get prioritized, if not |
1427 | | * they are added to the front of the default list. If the system |
1428 | | * runs in fips mode, this will work with the fips_methods list. |
1429 | | * Empty list will cause error. |
1430 | | * |
1431 | | * @param[in] algo The type of the methods to filter |
1432 | | * @param[in] list The list to be pushed to priority |
1433 | | * |
1434 | | * @return A newly allocated list containing the default algorithms prioritized |
1435 | | * with the algorithms in list at the beginning of the list; NULL in case |
1436 | | * of error. |
1437 | | */ |
1438 | | char *ssh_prefix_default_algos(enum ssh_kex_types_e algo, const char *list) |
1439 | 376 | { |
1440 | 376 | char *ret = NULL, *tmp = NULL; |
1441 | | |
1442 | 376 | if (algo > SSH_LANG_S_C || list == NULL || list[0] == '\0') { |
1443 | 15 | return NULL; |
1444 | 15 | } |
1445 | | |
1446 | 361 | if (ssh_fips_mode()) { |
1447 | 0 | tmp = ssh_prefix_without_duplicates(fips_methods[algo], list); |
1448 | 0 | ret = ssh_find_all_matching(fips_methods[algo], tmp); |
1449 | 361 | } else { |
1450 | 361 | tmp = ssh_prefix_without_duplicates(default_methods[algo], list); |
1451 | 361 | ret = ssh_find_all_matching(supported_methods[algo], tmp); |
1452 | 361 | } |
1453 | | |
1454 | 361 | free(tmp); |
1455 | 361 | return ret; |
1456 | 376 | } |
1457 | | |
1458 | | int ssh_make_sessionid(ssh_session session) |
1459 | 10.3k | { |
1460 | 10.3k | ssh_string num = NULL; |
1461 | 10.3k | ssh_buffer server_hash = NULL; |
1462 | 10.3k | ssh_buffer client_hash = NULL; |
1463 | 10.3k | ssh_buffer buf = NULL; |
1464 | 10.3k | ssh_string server_pubkey_blob = NULL; |
1465 | 10.3k | #if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L |
1466 | 10.3k | const_bignum client_pubkey, server_pubkey; |
1467 | | #else |
1468 | | bignum client_pubkey = NULL, server_pubkey = NULL; |
1469 | | #endif /* OPENSSL_VERSION_NUMBER */ |
1470 | 10.3k | #ifdef WITH_GEX |
1471 | 10.3k | #if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L |
1472 | 10.3k | const_bignum modulus, generator; |
1473 | | #else |
1474 | | bignum modulus = NULL, generator = NULL; |
1475 | | #endif /* OPENSSL_VERSION_NUMBER */ |
1476 | 10.3k | #endif /* WITH_GEX */ |
1477 | 10.3k | int rc = SSH_ERROR; |
1478 | | |
1479 | 10.3k | buf = ssh_buffer_new(); |
1480 | 10.3k | if (buf == NULL) { |
1481 | 0 | ssh_set_error_oom(session); |
1482 | 0 | return rc; |
1483 | 0 | } |
1484 | | |
1485 | 10.3k | rc = ssh_buffer_pack(buf, |
1486 | 10.3k | "ss", |
1487 | 10.3k | session->clientbanner, |
1488 | 10.3k | session->serverbanner); |
1489 | 10.3k | if (rc == SSH_ERROR) { |
1490 | 0 | ssh_set_error(session, |
1491 | 0 | SSH_FATAL, |
1492 | 0 | "Failed to pack client and server banner"); |
1493 | 0 | goto error; |
1494 | 0 | } |
1495 | | |
1496 | 10.3k | if (session->client) { |
1497 | 1.78k | server_hash = session->in_hashbuf; |
1498 | 1.78k | client_hash = session->out_hashbuf; |
1499 | 8.52k | } else { |
1500 | 8.52k | server_hash = session->out_hashbuf; |
1501 | 8.52k | client_hash = session->in_hashbuf; |
1502 | 8.52k | } |
1503 | | |
1504 | 10.3k | rc = ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob); |
1505 | 10.3k | if (rc != SSH_OK) { |
1506 | 0 | ssh_set_error(session, |
1507 | 0 | SSH_FATAL, |
1508 | 0 | "Failed to get next server pubkey blob"); |
1509 | 0 | goto error; |
1510 | 0 | } |
1511 | | |
1512 | 10.3k | if (server_pubkey_blob == NULL) { |
1513 | 0 | if ((session->server && ssh_kex_is_gss(session->next_crypto)) || |
1514 | 0 | session->opts.gssapi_key_exchange) { |
1515 | 0 | server_pubkey_blob = ssh_string_new(0); |
1516 | 0 | if (server_pubkey_blob == NULL) { |
1517 | 0 | ssh_set_error_oom(session); |
1518 | 0 | rc = SSH_ERROR; |
1519 | 0 | goto error; |
1520 | 0 | } |
1521 | 0 | } |
1522 | 0 | } |
1523 | | |
1524 | 10.3k | rc = ssh_buffer_pack(buf, |
1525 | 10.3k | "dPdPS", |
1526 | 10.3k | ssh_buffer_get_len(client_hash), |
1527 | 10.3k | (size_t)ssh_buffer_get_len(client_hash), |
1528 | 10.3k | ssh_buffer_get(client_hash), |
1529 | 10.3k | ssh_buffer_get_len(server_hash), |
1530 | 10.3k | (size_t)ssh_buffer_get_len(server_hash), |
1531 | 10.3k | ssh_buffer_get(server_hash), |
1532 | 10.3k | server_pubkey_blob); |
1533 | 10.3k | SSH_STRING_FREE(server_pubkey_blob); |
1534 | 10.3k | if (rc != SSH_OK){ |
1535 | 0 | ssh_set_error(session, |
1536 | 0 | SSH_FATAL, |
1537 | 0 | "Failed to pack hashes and pubkey blob"); |
1538 | 0 | goto error; |
1539 | 0 | } |
1540 | | |
1541 | 10.3k | switch(session->next_crypto->kex_type) { |
1542 | 0 | case SSH_KEX_DH_GROUP1_SHA1: |
1543 | 0 | case SSH_KEX_DH_GROUP14_SHA1: |
1544 | 733 | case SSH_KEX_DH_GROUP14_SHA256: |
1545 | 733 | case SSH_GSS_KEX_DH_GROUP14_SHA256: |
1546 | 805 | case SSH_KEX_DH_GROUP16_SHA512: |
1547 | 805 | case SSH_GSS_KEX_DH_GROUP16_SHA512: |
1548 | 1.04k | case SSH_KEX_DH_GROUP18_SHA512: |
1549 | 1.04k | rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx, |
1550 | 1.04k | DH_CLIENT_KEYPAIR, NULL, &client_pubkey); |
1551 | 1.04k | if (rc != SSH_OK) { |
1552 | 0 | goto error; |
1553 | 0 | } |
1554 | 1.04k | rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx, |
1555 | 1.04k | DH_SERVER_KEYPAIR, NULL, &server_pubkey); |
1556 | 1.04k | if (rc != SSH_OK) { |
1557 | 0 | goto error; |
1558 | 0 | } |
1559 | 1.04k | rc = ssh_buffer_pack(buf, |
1560 | 1.04k | "BB", |
1561 | 1.04k | client_pubkey, |
1562 | 1.04k | server_pubkey); |
1563 | 1.04k | if (rc != SSH_OK) { |
1564 | 0 | ssh_set_error(session, SSH_FATAL, "Failed to pack DH pubkeys"); |
1565 | 0 | goto error; |
1566 | 0 | } |
1567 | | #if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L |
1568 | | bignum_safe_free(client_pubkey); |
1569 | | bignum_safe_free(server_pubkey); |
1570 | | #endif /* OPENSSL_VERSION_NUMBER */ |
1571 | 1.04k | break; |
1572 | 1.04k | #ifdef WITH_GEX |
1573 | 1.04k | case SSH_KEX_DH_GEX_SHA1: |
1574 | 18 | case SSH_KEX_DH_GEX_SHA256: |
1575 | 18 | rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx, |
1576 | 18 | DH_CLIENT_KEYPAIR, NULL, &client_pubkey); |
1577 | 18 | if (rc != SSH_OK) { |
1578 | 0 | goto error; |
1579 | 0 | } |
1580 | 18 | rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx, |
1581 | 18 | DH_SERVER_KEYPAIR, NULL, &server_pubkey); |
1582 | 18 | if (rc != SSH_OK) { |
1583 | 0 | goto error; |
1584 | 0 | } |
1585 | 18 | rc = ssh_dh_get_parameters(session->next_crypto->dh_ctx, |
1586 | 18 | &modulus, &generator); |
1587 | 18 | if (rc != SSH_OK) { |
1588 | 0 | goto error; |
1589 | 0 | } |
1590 | 18 | rc = ssh_buffer_pack(buf, |
1591 | 18 | "dddBBBB", |
1592 | 18 | session->next_crypto->dh_pmin, |
1593 | 18 | session->next_crypto->dh_pn, |
1594 | 18 | session->next_crypto->dh_pmax, |
1595 | 18 | modulus, |
1596 | 18 | generator, |
1597 | 18 | client_pubkey, |
1598 | 18 | server_pubkey); |
1599 | 18 | if (rc != SSH_OK) { |
1600 | 0 | ssh_set_error(session, SSH_FATAL, "Failed to pack DH GEX params"); |
1601 | 0 | goto error; |
1602 | 0 | } |
1603 | | #if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L |
1604 | | bignum_safe_free(modulus); |
1605 | | bignum_safe_free(generator); |
1606 | | #endif /* OPENSSL_VERSION_NUMBER */ |
1607 | 18 | break; |
1608 | 18 | #endif /* WITH_GEX */ |
1609 | 18 | #ifdef HAVE_ECDH |
1610 | 66 | case SSH_KEX_ECDH_SHA2_NISTP256: |
1611 | 67 | case SSH_KEX_ECDH_SHA2_NISTP384: |
1612 | 67 | case SSH_KEX_ECDH_SHA2_NISTP521: |
1613 | 67 | case SSH_GSS_KEX_ECDH_NISTP256_SHA256: |
1614 | 67 | if (session->next_crypto->ecdh_client_pubkey == NULL || |
1615 | 67 | session->next_crypto->ecdh_server_pubkey == NULL) { |
1616 | 0 | SSH_LOG(SSH_LOG_TRACE, "ECDH parameter missing"); |
1617 | 0 | goto error; |
1618 | 0 | } |
1619 | 67 | rc = ssh_buffer_pack(buf, |
1620 | 67 | "SS", |
1621 | 67 | session->next_crypto->ecdh_client_pubkey, |
1622 | 67 | session->next_crypto->ecdh_server_pubkey); |
1623 | 67 | if (rc != SSH_OK) { |
1624 | 0 | ssh_set_error(session, SSH_FATAL, "Failed to pack ECDH pubkeys"); |
1625 | 0 | goto error; |
1626 | 0 | } |
1627 | 67 | break; |
1628 | 67 | #endif /* HAVE_ECDH */ |
1629 | 67 | #ifdef HAVE_CURVE25519 |
1630 | 8.18k | case SSH_KEX_CURVE25519_SHA256: |
1631 | 8.56k | case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: |
1632 | 8.56k | case SSH_GSS_KEX_CURVE25519_SHA256: |
1633 | 8.56k | rc = ssh_buffer_pack(buf, |
1634 | 8.56k | "dPdP", |
1635 | 8.56k | CURVE25519_PUBKEY_SIZE, |
1636 | 8.56k | (size_t)CURVE25519_PUBKEY_SIZE, |
1637 | 8.56k | session->next_crypto->curve25519_client_pubkey, |
1638 | 8.56k | CURVE25519_PUBKEY_SIZE, |
1639 | 8.56k | (size_t)CURVE25519_PUBKEY_SIZE, |
1640 | 8.56k | session->next_crypto->curve25519_server_pubkey); |
1641 | | |
1642 | 8.56k | if (rc != SSH_OK) { |
1643 | 0 | ssh_set_error(session, |
1644 | 0 | SSH_FATAL, |
1645 | 0 | "Failed to pack Curve25519 pubkeys"); |
1646 | 0 | goto error; |
1647 | 0 | } |
1648 | 8.56k | break; |
1649 | 8.56k | #endif /* HAVE_CURVE25519 */ |
1650 | 8.56k | #ifdef HAVE_SNTRUP761 |
1651 | 8.56k | case SSH_KEX_SNTRUP761X25519_SHA512: |
1652 | 51 | case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM: |
1653 | 51 | rc = ssh_buffer_pack(buf, |
1654 | 51 | "dPPdPP", |
1655 | 51 | SNTRUP761_PUBLICKEY_SIZE + CURVE25519_PUBKEY_SIZE, |
1656 | 51 | (size_t)SNTRUP761_PUBLICKEY_SIZE, |
1657 | 51 | session->next_crypto->sntrup761_client_pubkey, |
1658 | 51 | (size_t)CURVE25519_PUBKEY_SIZE, |
1659 | 51 | session->next_crypto->curve25519_client_pubkey, |
1660 | 51 | SNTRUP761_CIPHERTEXT_SIZE + CURVE25519_PUBKEY_SIZE, |
1661 | 51 | (size_t)SNTRUP761_CIPHERTEXT_SIZE, |
1662 | 51 | session->next_crypto->sntrup761_ciphertext, |
1663 | 51 | (size_t)CURVE25519_PUBKEY_SIZE, |
1664 | 51 | session->next_crypto->curve25519_server_pubkey); |
1665 | | |
1666 | 51 | if (rc != SSH_OK) { |
1667 | 0 | ssh_set_error(session, |
1668 | 0 | SSH_FATAL, |
1669 | 0 | "Failed to pack SNTRU Prime params"); |
1670 | 0 | goto error; |
1671 | 0 | } |
1672 | 51 | break; |
1673 | 51 | #endif /* HAVE_SNTRUP761 */ |
1674 | 561 | case SSH_KEX_MLKEM768X25519_SHA256: |
1675 | 569 | case SSH_KEX_MLKEM768NISTP256_SHA256: |
1676 | | #ifdef HAVE_MLKEM1024 |
1677 | | case SSH_KEX_MLKEM1024NISTP384_SHA384: |
1678 | | #endif |
1679 | 569 | rc = ssh_buffer_pack(buf, |
1680 | 569 | "SS", |
1681 | 569 | session->next_crypto->hybrid_client_init, |
1682 | 569 | session->next_crypto->hybrid_server_reply); |
1683 | 569 | if (rc != SSH_OK) { |
1684 | 0 | ssh_set_error(session, |
1685 | 0 | SSH_FATAL, |
1686 | 0 | "Failed to pack ML-KEM individual components"); |
1687 | 0 | goto error; |
1688 | 0 | } |
1689 | 569 | break; |
1690 | 569 | default: |
1691 | | /* Handle unsupported kex types - this should not happen in normal operation */ |
1692 | 0 | rc = SSH_ERROR; |
1693 | 0 | ssh_set_error(session, SSH_FATAL, "Unsupported KEX algorithm"); |
1694 | 0 | goto error; |
1695 | 10.3k | } |
1696 | 10.3k | switch (session->next_crypto->kex_type) { |
1697 | 30 | case SSH_KEX_SNTRUP761X25519_SHA512: |
1698 | 51 | case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM: |
1699 | 612 | case SSH_KEX_MLKEM768X25519_SHA256: |
1700 | 620 | case SSH_KEX_MLKEM768NISTP256_SHA256: |
1701 | | #ifdef HAVE_MLKEM1024 |
1702 | | case SSH_KEX_MLKEM1024NISTP384_SHA384: |
1703 | | #endif |
1704 | 620 | rc = ssh_buffer_pack(buf, "S", session->next_crypto->hybrid_shared_secret); |
1705 | 620 | break; |
1706 | 9.69k | default: |
1707 | 9.69k | rc = ssh_buffer_pack(buf, "B", session->next_crypto->shared_secret); |
1708 | 9.69k | break; |
1709 | 10.3k | } |
1710 | 10.3k | if (rc != SSH_OK) { |
1711 | 0 | ssh_set_error(session, SSH_FATAL, "Failed to pack shared secret"); |
1712 | 0 | goto error; |
1713 | 0 | } |
1714 | | |
1715 | | #ifdef DEBUG_CRYPTO |
1716 | | ssh_log_hexdump("hash buffer", ssh_buffer_get(buf), ssh_buffer_get_len(buf)); |
1717 | | #endif |
1718 | | |
1719 | | /* Set rc for the following switch statement in case we goto error. */ |
1720 | 10.3k | rc = SSH_ERROR; |
1721 | 10.3k | switch (session->next_crypto->kex_type) { |
1722 | 0 | case SSH_KEX_DH_GROUP1_SHA1: |
1723 | 0 | case SSH_KEX_DH_GROUP14_SHA1: |
1724 | 0 | #ifdef WITH_GEX |
1725 | 0 | case SSH_KEX_DH_GEX_SHA1: |
1726 | 0 | #endif /* WITH_GEX */ |
1727 | 0 | session->next_crypto->digest_len = SHA_DIGEST_LENGTH; |
1728 | 0 | session->next_crypto->digest_type = SSH_KDF_SHA1; |
1729 | 0 | session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); |
1730 | 0 | if (session->next_crypto->secret_hash == NULL) { |
1731 | 0 | ssh_set_error_oom(session); |
1732 | 0 | goto error; |
1733 | 0 | } |
1734 | 0 | sha1(ssh_buffer_get(buf), ssh_buffer_get_len(buf), |
1735 | 0 | session->next_crypto->secret_hash); |
1736 | 0 | break; |
1737 | 733 | case SSH_KEX_DH_GROUP14_SHA256: |
1738 | 733 | case SSH_GSS_KEX_DH_GROUP14_SHA256: |
1739 | 799 | case SSH_KEX_ECDH_SHA2_NISTP256: |
1740 | 8.98k | case SSH_KEX_CURVE25519_SHA256: |
1741 | 9.36k | case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: |
1742 | 9.92k | case SSH_KEX_MLKEM768X25519_SHA256: |
1743 | 9.93k | case SSH_KEX_MLKEM768NISTP256_SHA256: |
1744 | 9.93k | case SSH_GSS_KEX_ECDH_NISTP256_SHA256: |
1745 | 9.93k | case SSH_GSS_KEX_CURVE25519_SHA256: |
1746 | 9.93k | #ifdef WITH_GEX |
1747 | 9.95k | case SSH_KEX_DH_GEX_SHA256: |
1748 | 9.95k | #endif /* WITH_GEX */ |
1749 | 9.95k | session->next_crypto->digest_len = SHA256_DIGEST_LENGTH; |
1750 | 9.95k | session->next_crypto->digest_type = SSH_KDF_SHA256; |
1751 | 9.95k | session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); |
1752 | 9.95k | if (session->next_crypto->secret_hash == NULL) { |
1753 | 0 | ssh_set_error_oom(session); |
1754 | 0 | goto error; |
1755 | 0 | } |
1756 | 9.95k | sha256(ssh_buffer_get(buf), ssh_buffer_get_len(buf), |
1757 | 9.95k | session->next_crypto->secret_hash); |
1758 | 9.95k | break; |
1759 | 1 | case SSH_KEX_ECDH_SHA2_NISTP384: |
1760 | | #ifdef HAVE_MLKEM1024 |
1761 | | case SSH_KEX_MLKEM1024NISTP384_SHA384: |
1762 | | #endif |
1763 | 1 | session->next_crypto->digest_len = SHA384_DIGEST_LENGTH; |
1764 | 1 | session->next_crypto->digest_type = SSH_KDF_SHA384; |
1765 | 1 | session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); |
1766 | 1 | if (session->next_crypto->secret_hash == NULL) { |
1767 | 0 | ssh_set_error_oom(session); |
1768 | 0 | goto error; |
1769 | 0 | } |
1770 | 1 | sha384(ssh_buffer_get(buf), ssh_buffer_get_len(buf), |
1771 | 1 | session->next_crypto->secret_hash); |
1772 | 1 | break; |
1773 | 72 | case SSH_KEX_DH_GROUP16_SHA512: |
1774 | 72 | case SSH_GSS_KEX_DH_GROUP16_SHA512: |
1775 | 309 | case SSH_KEX_DH_GROUP18_SHA512: |
1776 | 309 | case SSH_KEX_ECDH_SHA2_NISTP521: |
1777 | 339 | case SSH_KEX_SNTRUP761X25519_SHA512: |
1778 | 360 | case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM: |
1779 | 360 | session->next_crypto->digest_len = SHA512_DIGEST_LENGTH; |
1780 | 360 | session->next_crypto->digest_type = SSH_KDF_SHA512; |
1781 | 360 | session->next_crypto->secret_hash = malloc(session->next_crypto->digest_len); |
1782 | 360 | if (session->next_crypto->secret_hash == NULL) { |
1783 | 0 | ssh_set_error_oom(session); |
1784 | 0 | goto error; |
1785 | 0 | } |
1786 | 360 | sha512(ssh_buffer_get(buf), |
1787 | 360 | ssh_buffer_get_len(buf), |
1788 | 360 | session->next_crypto->secret_hash); |
1789 | 360 | break; |
1790 | 0 | default: |
1791 | | /* Handle unsupported kex types - this should not happen in normal operation */ |
1792 | 0 | ssh_set_error(session, SSH_FATAL, "Unsupported KEX algorithm for hash computation"); |
1793 | 0 | rc = SSH_ERROR; |
1794 | 0 | goto error; |
1795 | 10.3k | } |
1796 | | |
1797 | | /* During the first kex, secret hash and session ID are equal. However, after |
1798 | | * a key re-exchange, a new secret hash is calculated. This hash will not replace |
1799 | | * but complement existing session id. |
1800 | | */ |
1801 | 10.3k | if (!session->next_crypto->session_id) { |
1802 | 10.1k | session->next_crypto->session_id = malloc(session->next_crypto->digest_len); |
1803 | 10.1k | if (session->next_crypto->session_id == NULL) { |
1804 | 0 | ssh_set_error_oom(session); |
1805 | 0 | rc = SSH_ERROR; |
1806 | 0 | goto error; |
1807 | 0 | } |
1808 | 10.1k | memcpy(session->next_crypto->session_id, session->next_crypto->secret_hash, |
1809 | 10.1k | session->next_crypto->digest_len); |
1810 | | /* Initial length is the same as secret hash */ |
1811 | 10.1k | session->next_crypto->session_id_len = session->next_crypto->digest_len; |
1812 | 10.1k | } |
1813 | | #ifdef DEBUG_CRYPTO |
1814 | | SSH_LOG(SSH_LOG_DEBUG, "Session hash: \n"); |
1815 | | ssh_log_hexdump("secret hash", session->next_crypto->secret_hash, session->next_crypto->digest_len); |
1816 | | ssh_log_hexdump("session id", session->next_crypto->session_id, session->next_crypto->session_id_len); |
1817 | | #endif /* DEBUG_CRYPTO */ |
1818 | | |
1819 | 10.3k | rc = SSH_OK; |
1820 | 10.3k | error: |
1821 | 10.3k | SSH_BUFFER_FREE(buf); |
1822 | 10.3k | SSH_BUFFER_FREE(client_hash); |
1823 | 10.3k | SSH_BUFFER_FREE(server_hash); |
1824 | | |
1825 | 10.3k | session->in_hashbuf = NULL; |
1826 | 10.3k | session->out_hashbuf = NULL; |
1827 | | |
1828 | 10.3k | SSH_STRING_FREE(num); |
1829 | | #if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER >= 0x30000000L |
1830 | | bignum_safe_free(client_pubkey); |
1831 | | bignum_safe_free(server_pubkey); |
1832 | | #endif /* OPENSSL_VERSION_NUMBER */ |
1833 | | |
1834 | 10.3k | return rc; |
1835 | 10.3k | } |
1836 | | |
1837 | | int ssh_hashbufout_add_cookie(ssh_session session) |
1838 | 14.9k | { |
1839 | 14.9k | int rc; |
1840 | | |
1841 | 14.9k | session->out_hashbuf = ssh_buffer_new(); |
1842 | 14.9k | if (session->out_hashbuf == NULL) { |
1843 | 0 | return -1; |
1844 | 0 | } |
1845 | | |
1846 | 14.9k | rc = ssh_buffer_allocate_size(session->out_hashbuf, |
1847 | 14.9k | sizeof(uint8_t) + 16); |
1848 | 14.9k | if (rc < 0) { |
1849 | 0 | ssh_buffer_reinit(session->out_hashbuf); |
1850 | 0 | return -1; |
1851 | 0 | } |
1852 | | |
1853 | 14.9k | if (ssh_buffer_add_u8(session->out_hashbuf, 20) < 0) { |
1854 | 0 | ssh_buffer_reinit(session->out_hashbuf); |
1855 | 0 | return -1; |
1856 | 0 | } |
1857 | | |
1858 | 14.9k | if (session->server) { |
1859 | 10.2k | if (ssh_buffer_add_data(session->out_hashbuf, |
1860 | 10.2k | session->next_crypto->server_kex.cookie, 16) < 0) { |
1861 | 0 | ssh_buffer_reinit(session->out_hashbuf); |
1862 | 0 | return -1; |
1863 | 0 | } |
1864 | 10.2k | } else { |
1865 | 4.74k | if (ssh_buffer_add_data(session->out_hashbuf, |
1866 | 4.74k | session->next_crypto->client_kex.cookie, 16) < 0) { |
1867 | 0 | ssh_buffer_reinit(session->out_hashbuf); |
1868 | 0 | return -1; |
1869 | 0 | } |
1870 | 4.74k | } |
1871 | | |
1872 | 14.9k | return 0; |
1873 | 14.9k | } |
1874 | | |
1875 | | int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie) |
1876 | 13.4k | { |
1877 | 13.4k | int rc; |
1878 | | |
1879 | 13.4k | session->in_hashbuf = ssh_buffer_new(); |
1880 | 13.4k | if (session->in_hashbuf == NULL) { |
1881 | 0 | return -1; |
1882 | 0 | } |
1883 | | |
1884 | 13.4k | rc = ssh_buffer_allocate_size(session->in_hashbuf, |
1885 | 13.4k | sizeof(uint8_t) + 20 + 16); |
1886 | 13.4k | if (rc < 0) { |
1887 | 0 | ssh_buffer_reinit(session->in_hashbuf); |
1888 | 0 | return -1; |
1889 | 0 | } |
1890 | | |
1891 | 13.4k | if (ssh_buffer_add_u8(session->in_hashbuf, 20) < 0) { |
1892 | 0 | ssh_buffer_reinit(session->in_hashbuf); |
1893 | 0 | return -1; |
1894 | 0 | } |
1895 | 13.4k | if (ssh_buffer_add_data(session->in_hashbuf,cookie, 16) < 0) { |
1896 | 0 | ssh_buffer_reinit(session->in_hashbuf); |
1897 | 0 | return -1; |
1898 | 0 | } |
1899 | | |
1900 | 13.4k | return 0; |
1901 | 13.4k | } |
1902 | | |
1903 | | int ssh_generate_session_keys(ssh_session session) |
1904 | 10.3k | { |
1905 | 10.3k | ssh_string k_string = NULL; |
1906 | 10.3k | struct ssh_crypto_struct *crypto = session->next_crypto; |
1907 | 10.3k | unsigned char *key = NULL; |
1908 | 10.3k | unsigned char *IV_cli_to_srv = NULL; |
1909 | 10.3k | unsigned char *IV_srv_to_cli = NULL; |
1910 | 10.3k | unsigned char *enckey_cli_to_srv = NULL; |
1911 | 10.3k | unsigned char *enckey_srv_to_cli = NULL; |
1912 | 10.3k | unsigned char *intkey_cli_to_srv = NULL; |
1913 | 10.3k | unsigned char *intkey_srv_to_cli = NULL; |
1914 | 10.3k | size_t key_len = 0; |
1915 | 10.3k | size_t IV_len = 0; |
1916 | 10.3k | size_t enckey_cli_to_srv_len = 0; |
1917 | 10.3k | size_t enckey_srv_to_cli_len = 0; |
1918 | 10.3k | size_t intkey_cli_to_srv_len = 0; |
1919 | 10.3k | size_t intkey_srv_to_cli_len = 0; |
1920 | 10.3k | int rc = -1; |
1921 | | |
1922 | 10.3k | switch (session->next_crypto->kex_type) { |
1923 | 30 | case SSH_KEX_SNTRUP761X25519_SHA512: |
1924 | 51 | case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM: |
1925 | 612 | case SSH_KEX_MLKEM768X25519_SHA256: |
1926 | 620 | case SSH_KEX_MLKEM768NISTP256_SHA256: |
1927 | | #ifdef HAVE_MLKEM1024 |
1928 | | case SSH_KEX_MLKEM1024NISTP384_SHA384: |
1929 | | #endif |
1930 | 620 | k_string = ssh_string_copy(crypto->hybrid_shared_secret); |
1931 | 620 | break; |
1932 | 9.69k | default: |
1933 | 9.69k | k_string = ssh_make_bignum_string(crypto->shared_secret); |
1934 | 9.69k | break; |
1935 | 10.3k | } |
1936 | 10.3k | if (k_string == NULL) { |
1937 | 0 | ssh_set_error_oom(session); |
1938 | 0 | goto error; |
1939 | 0 | } |
1940 | | /* See RFC4251 Section 5 for the definition of mpint which is the |
1941 | | * encoding we need to use for key in the SSH KDF */ |
1942 | 10.3k | key = (unsigned char *)k_string; |
1943 | 10.3k | key_len = ssh_string_len(k_string) + 4; |
1944 | | |
1945 | 10.3k | IV_len = crypto->digest_len; |
1946 | 10.3k | if (session->client) { |
1947 | 1.78k | enckey_cli_to_srv_len = crypto->out_cipher->keysize / 8; |
1948 | 1.78k | enckey_srv_to_cli_len = crypto->in_cipher->keysize / 8; |
1949 | 1.78k | intkey_cli_to_srv_len = hmac_digest_len(crypto->out_hmac); |
1950 | 1.78k | intkey_srv_to_cli_len = hmac_digest_len(crypto->in_hmac); |
1951 | 8.52k | } else { |
1952 | 8.52k | enckey_cli_to_srv_len = crypto->in_cipher->keysize / 8; |
1953 | 8.52k | enckey_srv_to_cli_len = crypto->out_cipher->keysize / 8; |
1954 | 8.52k | intkey_cli_to_srv_len = hmac_digest_len(crypto->in_hmac); |
1955 | 8.52k | intkey_srv_to_cli_len = hmac_digest_len(crypto->out_hmac); |
1956 | 8.52k | } |
1957 | | |
1958 | 10.3k | IV_cli_to_srv = malloc(IV_len); |
1959 | 10.3k | IV_srv_to_cli = malloc(IV_len); |
1960 | 10.3k | enckey_cli_to_srv = malloc(enckey_cli_to_srv_len); |
1961 | 10.3k | enckey_srv_to_cli = malloc(enckey_srv_to_cli_len); |
1962 | 10.3k | intkey_cli_to_srv = malloc(intkey_cli_to_srv_len); |
1963 | 10.3k | intkey_srv_to_cli = malloc(intkey_srv_to_cli_len); |
1964 | 10.3k | if (IV_cli_to_srv == NULL || IV_srv_to_cli == NULL || |
1965 | 10.3k | enckey_cli_to_srv == NULL || enckey_srv_to_cli == NULL || |
1966 | 10.3k | intkey_cli_to_srv == NULL || intkey_srv_to_cli == NULL) { |
1967 | 0 | ssh_set_error_oom(session); |
1968 | 0 | goto error; |
1969 | 0 | } |
1970 | | |
1971 | | /* IV */ |
1972 | 10.3k | rc = ssh_kdf(crypto, key, key_len, 'A', IV_cli_to_srv, IV_len); |
1973 | 10.3k | if (rc < 0) { |
1974 | 0 | goto error; |
1975 | 0 | } |
1976 | 10.3k | rc = ssh_kdf(crypto, key, key_len, 'B', IV_srv_to_cli, IV_len); |
1977 | 10.3k | if (rc < 0) { |
1978 | 0 | goto error; |
1979 | 0 | } |
1980 | | /* Encryption Key */ |
1981 | 10.3k | rc = ssh_kdf(crypto, key, key_len, 'C', enckey_cli_to_srv, |
1982 | 10.3k | enckey_cli_to_srv_len); |
1983 | 10.3k | if (rc < 0) { |
1984 | 0 | goto error; |
1985 | 0 | } |
1986 | 10.3k | rc = ssh_kdf(crypto, key, key_len, 'D', enckey_srv_to_cli, |
1987 | 10.3k | enckey_srv_to_cli_len); |
1988 | 10.3k | if (rc < 0) { |
1989 | 0 | goto error; |
1990 | 0 | } |
1991 | | /* Integrity Key */ |
1992 | 10.3k | rc = ssh_kdf(crypto, key, key_len, 'E', intkey_cli_to_srv, |
1993 | 10.3k | intkey_cli_to_srv_len); |
1994 | 10.3k | if (rc < 0) { |
1995 | 0 | goto error; |
1996 | 0 | } |
1997 | 10.3k | rc = ssh_kdf(crypto, key, key_len, 'F', intkey_srv_to_cli, |
1998 | 10.3k | intkey_srv_to_cli_len); |
1999 | 10.3k | if (rc < 0) { |
2000 | 0 | goto error; |
2001 | 0 | } |
2002 | | |
2003 | 10.3k | if (session->client) { |
2004 | 1.78k | crypto->encryptIV = IV_cli_to_srv; |
2005 | 1.78k | crypto->decryptIV = IV_srv_to_cli; |
2006 | 1.78k | crypto->encryptkey = enckey_cli_to_srv; |
2007 | 1.78k | crypto->decryptkey = enckey_srv_to_cli; |
2008 | 1.78k | crypto->encryptMAC = intkey_cli_to_srv; |
2009 | 1.78k | crypto->decryptMAC = intkey_srv_to_cli; |
2010 | 8.52k | } else { |
2011 | 8.52k | crypto->encryptIV = IV_srv_to_cli; |
2012 | 8.52k | crypto->decryptIV = IV_cli_to_srv; |
2013 | 8.52k | crypto->encryptkey = enckey_srv_to_cli; |
2014 | 8.52k | crypto->decryptkey = enckey_cli_to_srv; |
2015 | 8.52k | crypto->encryptMAC = intkey_srv_to_cli; |
2016 | 8.52k | crypto->decryptMAC = intkey_cli_to_srv; |
2017 | 8.52k | } |
2018 | | |
2019 | | #ifdef DEBUG_CRYPTO |
2020 | | ssh_log_hexdump("Client to Server IV", IV_cli_to_srv, IV_len); |
2021 | | ssh_log_hexdump("Server to Client IV", IV_srv_to_cli, IV_len); |
2022 | | ssh_log_hexdump("Client to Server Encryption Key", enckey_cli_to_srv, |
2023 | | enckey_cli_to_srv_len); |
2024 | | ssh_log_hexdump("Server to Client Encryption Key", enckey_srv_to_cli, |
2025 | | enckey_srv_to_cli_len); |
2026 | | ssh_log_hexdump("Client to Server Integrity Key", intkey_cli_to_srv, |
2027 | | intkey_cli_to_srv_len); |
2028 | | ssh_log_hexdump("Server to Client Integrity Key", intkey_srv_to_cli, |
2029 | | intkey_srv_to_cli_len); |
2030 | | #endif /* DEBUG_CRYPTO */ |
2031 | | |
2032 | 10.3k | rc = 0; |
2033 | 10.3k | error: |
2034 | 10.3k | ssh_string_burn(k_string); |
2035 | 10.3k | SSH_STRING_FREE(k_string); |
2036 | 10.3k | if (rc != 0) { |
2037 | 0 | free(IV_cli_to_srv); |
2038 | 0 | free(IV_srv_to_cli); |
2039 | 0 | free(enckey_cli_to_srv); |
2040 | 0 | free(enckey_srv_to_cli); |
2041 | 0 | free(intkey_cli_to_srv); |
2042 | 0 | free(intkey_srv_to_cli); |
2043 | 0 | } |
2044 | | |
2045 | 10.3k | return rc; |
2046 | 10.3k | } |
2047 | | |
2048 | | /** @internal |
2049 | | * @brief Check if a given crypto context has a GSSAPI KEX set |
2050 | | * |
2051 | | * @param[in] crypto The SSH crypto context |
2052 | | * @return true if the KEX of the context is a GSSAPI KEX, false otherwise |
2053 | | */ |
2054 | | bool ssh_kex_is_gss(struct ssh_crypto_struct *crypto) |
2055 | 37 | { |
2056 | 37 | switch (crypto->kex_type) { |
2057 | 0 | case SSH_GSS_KEX_DH_GROUP14_SHA256: |
2058 | 0 | case SSH_GSS_KEX_DH_GROUP16_SHA512: |
2059 | 0 | case SSH_GSS_KEX_ECDH_NISTP256_SHA256: |
2060 | 0 | case SSH_GSS_KEX_CURVE25519_SHA256: |
2061 | 0 | return true; |
2062 | 37 | default: |
2063 | | return false; |
2064 | 37 | } |
2065 | 37 | } |