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