/src/libssh/src/session.c
Line | Count | Source |
1 | | /* |
2 | | * session.c - non-networking functions |
3 | | * |
4 | | * This file is part of the SSH Library |
5 | | * |
6 | | * Copyright (c) 2005-2013 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 | | |
29 | | #ifdef _WIN32 |
30 | | #include <winsock2.h> |
31 | | #endif |
32 | | |
33 | | #include "libssh/priv.h" |
34 | | #include "libssh/libssh.h" |
35 | | #include "libssh/crypto.h" |
36 | | #include "libssh/server.h" |
37 | | #include "libssh/socket.h" |
38 | | #include "libssh/ssh2.h" |
39 | | #include "libssh/agent.h" |
40 | | #include "libssh/packet.h" |
41 | | #include "libssh/kex.h" |
42 | | #include "libssh/session.h" |
43 | | #include "libssh/misc.h" |
44 | | #include "libssh/buffer.h" |
45 | | #include "libssh/poll.h" |
46 | | #include "libssh/pki.h" |
47 | | #include "libssh/gssapi.h" |
48 | | |
49 | 8.03k | #define FIRST_CHANNEL 42 // why not ? it helps to find bugs. |
50 | | |
51 | | /** |
52 | | * @defgroup libssh_session The SSH session functions |
53 | | * @ingroup libssh |
54 | | * |
55 | | * Functions that manage a session. |
56 | | * |
57 | | * @{ |
58 | | */ |
59 | | |
60 | | /** |
61 | | * @brief Create a new ssh session. |
62 | | * |
63 | | * @returns A new ssh_session pointer, NULL on error. |
64 | | */ |
65 | | ssh_session ssh_new(void) |
66 | 8.03k | { |
67 | 8.03k | ssh_session session = NULL; |
68 | 8.03k | char *id = NULL; |
69 | 8.03k | int rc; |
70 | | |
71 | 8.03k | session = calloc(1, sizeof (struct ssh_session_struct)); |
72 | 8.03k | if (session == NULL) { |
73 | 0 | return NULL; |
74 | 0 | } |
75 | | |
76 | 8.03k | session->next_crypto = crypto_new(); |
77 | 8.03k | if (session->next_crypto == NULL) { |
78 | 0 | goto err; |
79 | 0 | } |
80 | | |
81 | 8.03k | session->socket = ssh_socket_new(session); |
82 | 8.03k | if (session->socket == NULL) { |
83 | 0 | goto err; |
84 | 0 | } |
85 | | |
86 | 8.03k | session->out_buffer = ssh_buffer_new(); |
87 | 8.03k | if (session->out_buffer == NULL) { |
88 | 0 | goto err; |
89 | 0 | } |
90 | | |
91 | 8.03k | session->in_buffer = ssh_buffer_new(); |
92 | 8.03k | if (session->in_buffer == NULL) { |
93 | 0 | goto err; |
94 | 0 | } |
95 | | |
96 | 8.03k | session->out_queue = ssh_list_new(); |
97 | 8.03k | if (session->out_queue == NULL) { |
98 | 0 | goto err; |
99 | 0 | } |
100 | | |
101 | 8.03k | session->alive = 0; |
102 | 8.03k | session->auth.supported_methods = 0; |
103 | 8.03k | ssh_set_blocking(session, 1); |
104 | 8.03k | session->maxchannel = FIRST_CHANNEL; |
105 | 8.03k | session->proxy_root = true; |
106 | | |
107 | 8.03k | session->agent = ssh_agent_new(session); |
108 | 8.03k | if (session->agent == NULL) { |
109 | 0 | goto err; |
110 | 0 | } |
111 | | |
112 | | /* Initialise a default PKI context */ |
113 | 8.03k | session->pki_context = ssh_pki_ctx_new(); |
114 | 8.03k | if (session->pki_context == NULL) { |
115 | 0 | goto err; |
116 | 0 | } |
117 | | |
118 | | /* OPTIONS */ |
119 | 8.03k | session->opts.StrictHostKeyChecking = SSH_STRICT_HOSTKEY_ASK; |
120 | 8.03k | session->opts.port = 22; |
121 | 8.03k | session->opts.fd = -1; |
122 | 8.03k | session->opts.compressionlevel = 7; |
123 | 8.03k | session->opts.nodelay = 0; |
124 | 8.03k | session->opts.identities_only = false; |
125 | 8.03k | session->opts.server_alive_interval = 0; |
126 | 8.03k | session->opts.server_alive_count_max = 3; |
127 | 8.03k | session->opts.batch_mode = false; |
128 | 8.03k | session->opts.gateway_ports = false; |
129 | 8.03k | session->opts.control_master = SSH_CONTROL_MASTER_NO; |
130 | | |
131 | 8.03k | session->opts.flags = SSH_OPT_FLAG_PASSWORD_AUTH | |
132 | 8.03k | SSH_OPT_FLAG_PUBKEY_AUTH | |
133 | 8.03k | SSH_OPT_FLAG_KBDINT_AUTH | |
134 | 8.03k | SSH_OPT_FLAG_GSSAPI_AUTH; |
135 | 8.03k | session->opts.pubkey_auth = SSH_PUBKEY_AUTH_ALL; |
136 | | |
137 | 8.03k | session->opts.exp_flags = 0; |
138 | | |
139 | | #ifdef WITH_GSSAPI |
140 | | session->opts.gssapi_key_exchange_algs = |
141 | | strdup(GSSAPI_KEY_EXCHANGE_SUPPORTED); |
142 | | if (session->opts.gssapi_key_exchange_algs == NULL) { |
143 | | goto err; |
144 | | } |
145 | | #endif /* WITH_GSSAPI */ |
146 | | |
147 | | /* Lazy allocation for identity_non_exp before populating the defaults */ |
148 | 8.03k | if (session->opts.identity_non_exp == NULL) { |
149 | 8.03k | session->opts.identity_non_exp = ssh_list_new(); |
150 | 8.03k | if (session->opts.identity_non_exp == NULL) { |
151 | 0 | goto err; |
152 | 0 | } |
153 | 8.03k | } |
154 | | |
155 | 8.03k | id = strdup("%d/.ssh/id_ed25519"); |
156 | 8.03k | if (id == NULL) { |
157 | 0 | goto err; |
158 | 0 | } |
159 | | |
160 | 8.03k | rc = ssh_list_append(session->opts.identity_non_exp, id); |
161 | 8.03k | if (rc == SSH_ERROR) { |
162 | 0 | goto err; |
163 | 0 | } |
164 | | |
165 | 8.03k | #ifdef HAVE_ECC |
166 | 8.03k | id = strdup("%d/.ssh/id_ecdsa"); |
167 | 8.03k | if (id == NULL) { |
168 | 0 | goto err; |
169 | 0 | } |
170 | 8.03k | rc = ssh_list_append(session->opts.identity_non_exp, id); |
171 | 8.03k | if (rc == SSH_ERROR) { |
172 | 0 | goto err; |
173 | 0 | } |
174 | 8.03k | #endif |
175 | | |
176 | 8.03k | id = strdup("%d/.ssh/id_rsa"); |
177 | 8.03k | if (id == NULL) { |
178 | 0 | goto err; |
179 | 0 | } |
180 | 8.03k | rc = ssh_list_append(session->opts.identity_non_exp, id); |
181 | 8.03k | if (rc == SSH_ERROR) { |
182 | 0 | goto err; |
183 | 0 | } |
184 | | |
185 | | #ifdef WITH_FIDO2 |
186 | | /* Add security key identities */ |
187 | | id = strdup("%d/.ssh/id_ed25519_sk"); |
188 | | if (id == NULL) { |
189 | | goto err; |
190 | | } |
191 | | rc = ssh_list_append(session->opts.identity_non_exp, id); |
192 | | if (rc == SSH_ERROR) { |
193 | | goto err; |
194 | | } |
195 | | |
196 | | #ifdef HAVE_ECC |
197 | | id = strdup("%d/.ssh/id_ecdsa_sk"); |
198 | | if (id == NULL) { |
199 | | goto err; |
200 | | } |
201 | | rc = ssh_list_append(session->opts.identity_non_exp, id); |
202 | | if (rc == SSH_ERROR) { |
203 | | goto err; |
204 | | } |
205 | | #endif /* HAVE_ECC */ |
206 | | #endif /* WITH_FIDO2 */ |
207 | | |
208 | | /* Explicitly initialize states */ |
209 | 8.03k | session->session_state = SSH_SESSION_STATE_NONE; |
210 | 8.03k | session->pending_call_state = SSH_PENDING_CALL_NONE; |
211 | 8.03k | session->packet_state = PACKET_STATE_INIT; |
212 | 8.03k | session->dh_handshake_state = DH_STATE_INIT; |
213 | 8.03k | session->global_req_state = SSH_CHANNEL_REQ_STATE_NONE; |
214 | | |
215 | 8.03k | session->auth.state = SSH_AUTH_STATE_NONE; |
216 | 8.03k | session->auth.service_state = SSH_AUTH_SERVICE_NONE; |
217 | | |
218 | 8.03k | return session; |
219 | | |
220 | 0 | err: |
221 | 0 | free(id); |
222 | 0 | ssh_free(session); |
223 | 0 | return NULL; |
224 | 8.03k | } |
225 | | |
226 | | /** |
227 | | * @brief Deallocate a SSH session handle. |
228 | | * |
229 | | * @param[in] session The SSH session to free. |
230 | | * |
231 | | * @see ssh_disconnect() |
232 | | * @see ssh_new() |
233 | | */ |
234 | | void ssh_free(ssh_session session) |
235 | 8.03k | { |
236 | 8.03k | int i; |
237 | 8.03k | struct ssh_iterator *it = NULL; |
238 | 8.03k | struct ssh_buffer_struct *b = NULL; |
239 | | |
240 | 8.03k | if (session == NULL) { |
241 | 0 | return; |
242 | 0 | } |
243 | | |
244 | | /* |
245 | | * Delete all channels |
246 | | * |
247 | | * This needs the first thing we clean up cause if there is still an open |
248 | | * channel we call ssh_channel_close() first. So we need a working socket |
249 | | * and poll context for it. |
250 | | */ |
251 | 8.03k | for (it = ssh_list_get_iterator(session->channels); |
252 | 8.03k | it != NULL; |
253 | 8.03k | it = ssh_list_get_iterator(session->channels)) { |
254 | 0 | ssh_channel_do_free(ssh_iterator_value(ssh_channel,it)); |
255 | 0 | ssh_list_remove(session->channels, it); |
256 | 0 | } |
257 | 8.03k | ssh_list_free(session->channels); |
258 | 8.03k | session->channels = NULL; |
259 | | |
260 | 8.03k | #ifdef WITH_PCAP |
261 | 8.03k | if (session->pcap_ctx) { |
262 | 0 | ssh_pcap_context_free(session->pcap_ctx); |
263 | 0 | session->pcap_ctx = NULL; |
264 | 0 | } |
265 | 8.03k | #endif |
266 | | |
267 | 8.03k | ssh_socket_free(session->socket); |
268 | 8.03k | session->socket = NULL; |
269 | | |
270 | 8.03k | if (session->default_poll_ctx) { |
271 | 7.60k | ssh_poll_ctx_free(session->default_poll_ctx); |
272 | 7.60k | } |
273 | | |
274 | 8.03k | SSH_BUFFER_FREE(session->in_buffer); |
275 | 8.03k | SSH_BUFFER_FREE(session->out_buffer); |
276 | 8.03k | session->in_buffer = session->out_buffer = NULL; |
277 | | |
278 | 8.03k | if (session->in_hashbuf != NULL) { |
279 | 887 | SSH_BUFFER_FREE(session->in_hashbuf); |
280 | 887 | } |
281 | 8.03k | if (session->out_hashbuf != NULL) { |
282 | 1.46k | SSH_BUFFER_FREE(session->out_hashbuf); |
283 | 1.46k | } |
284 | | |
285 | 8.03k | crypto_free(session->current_crypto); |
286 | 8.03k | crypto_free(session->next_crypto); |
287 | | |
288 | 8.03k | ssh_agent_free(session->agent); |
289 | | |
290 | 8.03k | SSH_PKI_CTX_FREE(session->pki_context); |
291 | | |
292 | 8.03k | ssh_key_free(session->srv.rsa_key); |
293 | 8.03k | session->srv.rsa_key = NULL; |
294 | 8.03k | ssh_key_free(session->srv.ecdsa_key); |
295 | 8.03k | session->srv.ecdsa_key = NULL; |
296 | 8.03k | ssh_key_free(session->srv.ed25519_key); |
297 | 8.03k | session->srv.ed25519_key = NULL; |
298 | | |
299 | 8.03k | if (session->ssh_message_list) { |
300 | 0 | ssh_message msg; |
301 | |
|
302 | 0 | for (msg = ssh_list_pop_head(ssh_message, session->ssh_message_list); |
303 | 0 | msg != NULL; |
304 | 0 | msg = ssh_list_pop_head(ssh_message, session->ssh_message_list)) { |
305 | 0 | ssh_message_free(msg); |
306 | 0 | } |
307 | 0 | ssh_list_free(session->ssh_message_list); |
308 | 0 | } |
309 | | |
310 | 8.03k | if (session->kbdint != NULL) { |
311 | 0 | ssh_kbdint_free(session->kbdint); |
312 | 0 | } |
313 | | |
314 | 8.03k | if (session->packet_callbacks) { |
315 | 0 | ssh_list_free(session->packet_callbacks); |
316 | 0 | } |
317 | | |
318 | | #ifdef WITH_GSSAPI |
319 | | ssh_gssapi_free(session); |
320 | | SAFE_FREE(session->opts.gssapi_key_exchange_algs); |
321 | | #endif |
322 | | |
323 | | /* options */ |
324 | 8.03k | if (session->opts.identity) { |
325 | 0 | char *id = NULL; |
326 | |
|
327 | 0 | for (id = ssh_list_pop_head(char *, session->opts.identity); |
328 | 0 | id != NULL; |
329 | 0 | id = ssh_list_pop_head(char *, session->opts.identity)) { |
330 | 0 | SAFE_FREE(id); |
331 | 0 | } |
332 | 0 | ssh_list_free(session->opts.identity); |
333 | 0 | } |
334 | | |
335 | 8.03k | if (session->opts.identity_non_exp) { |
336 | 8.03k | char *id = NULL; |
337 | | |
338 | 8.03k | for (id = ssh_list_pop_head(char *, session->opts.identity_non_exp); |
339 | 32.1k | id != NULL; |
340 | 24.0k | id = ssh_list_pop_head(char *, session->opts.identity_non_exp)) { |
341 | 24.0k | SAFE_FREE(id); |
342 | 24.0k | } |
343 | 8.03k | ssh_list_free(session->opts.identity_non_exp); |
344 | 8.03k | } |
345 | | |
346 | 8.03k | if (session->opts.certificate) { |
347 | 0 | char *cert = NULL; |
348 | |
|
349 | 0 | for (cert = ssh_list_pop_head(char *, session->opts.certificate); |
350 | 0 | cert != NULL; |
351 | 0 | cert = ssh_list_pop_head(char *, session->opts.certificate)) { |
352 | 0 | SAFE_FREE(cert); |
353 | 0 | } |
354 | 0 | ssh_list_free(session->opts.certificate); |
355 | 0 | } |
356 | | |
357 | 8.03k | if (session->opts.certificate_non_exp) { |
358 | 0 | char *cert = NULL; |
359 | |
|
360 | 0 | for (cert = ssh_list_pop_head(char *, session->opts.certificate_non_exp); |
361 | 0 | cert != NULL; |
362 | 0 | cert = ssh_list_pop_head(char *, session->opts.certificate_non_exp)) { |
363 | 0 | SAFE_FREE(cert); |
364 | 0 | } |
365 | 0 | ssh_list_free(session->opts.certificate_non_exp); |
366 | 0 | } |
367 | | |
368 | 8.03k | ssh_proxyjumps_free(session->opts.proxy_jumps); |
369 | 8.03k | SSH_LIST_FREE(session->opts.proxy_jumps); |
370 | 8.03k | SSH_LIST_FREE(session->opts.proxy_jumps_user_cb); |
371 | 8.03k | SAFE_FREE(session->opts.proxy_jumps_str); |
372 | | |
373 | 8.03k | if (session->opts.send_env) { |
374 | 0 | char *pattern = NULL; |
375 | |
|
376 | 0 | for (pattern = ssh_list_pop_head(char *, session->opts.send_env); |
377 | 0 | pattern != NULL; |
378 | 0 | pattern = ssh_list_pop_head(char *, session->opts.send_env)) { |
379 | 0 | SAFE_FREE(pattern); |
380 | 0 | } |
381 | 0 | ssh_list_free(session->opts.send_env); |
382 | 0 | } |
383 | | |
384 | 8.18k | while ((b = ssh_list_pop_head(struct ssh_buffer_struct *, |
385 | 8.18k | session->out_queue)) != NULL) { |
386 | 155 | SSH_BUFFER_FREE(b); |
387 | 155 | } |
388 | 8.03k | ssh_list_free(session->out_queue); |
389 | | |
390 | 8.03k | ssh_agent_state_free(session->agent_state); |
391 | 8.03k | session->agent_state = NULL; |
392 | | |
393 | 8.03k | SAFE_FREE(session->auth.auto_state); |
394 | 8.03k | SAFE_FREE(session->serverbanner); |
395 | 8.03k | SAFE_FREE(session->clientbanner); |
396 | 8.03k | SAFE_FREE(session->banner); |
397 | 8.03k | SAFE_FREE(session->disconnect_message); |
398 | 8.03k | SAFE_FREE(session->peer_discon_msg); |
399 | | |
400 | 8.03k | SAFE_FREE(session->opts.agent_socket); |
401 | 8.03k | SAFE_FREE(session->opts.bindaddr); |
402 | 8.03k | SAFE_FREE(session->opts.username); |
403 | 8.03k | SAFE_FREE(session->opts.host); |
404 | 8.03k | SAFE_FREE(session->opts.originalhost); |
405 | 8.03k | SAFE_FREE(session->opts.config_hostname); |
406 | 8.03k | SAFE_FREE(session->opts.tag); |
407 | 8.03k | SAFE_FREE(session->opts.homedir); |
408 | 8.03k | SAFE_FREE(session->opts.sshdir); |
409 | 8.03k | SAFE_FREE(session->opts.knownhosts); |
410 | 8.03k | SAFE_FREE(session->opts.global_knownhosts); |
411 | 8.03k | SAFE_FREE(session->opts.ProxyCommand); |
412 | 8.03k | SAFE_FREE(session->opts.gss_server_identity); |
413 | 8.03k | SAFE_FREE(session->opts.gss_client_identity); |
414 | 8.03k | SAFE_FREE(session->opts.pubkey_accepted_types); |
415 | 8.03k | SAFE_FREE(session->opts.control_path); |
416 | 8.03k | SAFE_FREE(session->opts.preferred_authentications); |
417 | 8.03k | SAFE_FREE(session->opts.forward_agent_sock_path); |
418 | | |
419 | 8.03k | if (session->opts.local_forward) { |
420 | 0 | char *entry = NULL; |
421 | |
|
422 | 0 | for (entry = ssh_list_pop_head(char *, session->opts.local_forward); |
423 | 0 | entry != NULL; |
424 | 0 | entry = ssh_list_pop_head(char *, session->opts.local_forward)) { |
425 | 0 | SAFE_FREE(entry); |
426 | 0 | } |
427 | 0 | ssh_list_free(session->opts.local_forward); |
428 | 0 | } |
429 | | |
430 | 8.03k | if (session->opts.remote_forward) { |
431 | 0 | char *entry = NULL; |
432 | |
|
433 | 0 | for (entry = ssh_list_pop_head(char *, session->opts.remote_forward); |
434 | 0 | entry != NULL; |
435 | 0 | entry = ssh_list_pop_head(char *, session->opts.remote_forward)) { |
436 | 0 | SAFE_FREE(entry); |
437 | 0 | } |
438 | 0 | ssh_list_free(session->opts.remote_forward); |
439 | 0 | } |
440 | | |
441 | 88.3k | for (i = 0; i < SSH_KEX_METHODS; i++) { |
442 | 80.3k | if (session->opts.wanted_methods[i]) { |
443 | 38.0k | SAFE_FREE(session->opts.wanted_methods[i]); |
444 | 38.0k | } |
445 | 80.3k | } |
446 | | |
447 | 8.03k | SAFE_FREE(session->server_opts.custombanner); |
448 | 8.03k | SAFE_FREE(session->server_opts.moduli_file); |
449 | | |
450 | 8.03k | _ssh_remove_legacy_log_cb(); |
451 | | |
452 | | /* burn connection, it could contain sensitive data */ |
453 | 8.03k | ssh_burn(session, sizeof(struct ssh_session_struct)); |
454 | 8.03k | SAFE_FREE(session); |
455 | 8.03k | } |
456 | | |
457 | | /** |
458 | | * @brief get the client banner |
459 | | * |
460 | | * @param[in] session The SSH session |
461 | | * |
462 | | * @return Returns the client banner string or NULL. |
463 | | */ |
464 | 0 | const char* ssh_get_clientbanner(ssh_session session) { |
465 | 0 | if (session == NULL) { |
466 | 0 | return NULL; |
467 | 0 | } |
468 | | |
469 | 0 | return session->clientbanner; |
470 | 0 | } |
471 | | |
472 | | /** |
473 | | * @brief get the server banner |
474 | | * |
475 | | * @param[in] session The SSH session |
476 | | * |
477 | | * @return Returns the server banner string or NULL. |
478 | | */ |
479 | 0 | const char* ssh_get_serverbanner(ssh_session session) { |
480 | 0 | if (!session) { |
481 | 0 | return NULL; |
482 | 0 | } |
483 | 0 | return session->serverbanner; |
484 | 0 | } |
485 | | |
486 | | /** |
487 | | * @brief get the name of the current key exchange algorithm. |
488 | | * |
489 | | * @param[in] session The SSH session |
490 | | * |
491 | | * @return Returns the key exchange algorithm string or NULL. |
492 | | */ |
493 | 0 | const char* ssh_get_kex_algo(ssh_session session) { |
494 | 0 | if ((session == NULL) || |
495 | 0 | (session->current_crypto == NULL)) { |
496 | 0 | return NULL; |
497 | 0 | } |
498 | | |
499 | 0 | switch (session->current_crypto->kex_type) { |
500 | 0 | case SSH_KEX_DH_GROUP1_SHA1: |
501 | 0 | return "diffie-hellman-group1-sha1"; |
502 | 0 | case SSH_KEX_DH_GROUP14_SHA1: |
503 | 0 | return "diffie-hellman-group14-sha1"; |
504 | 0 | case SSH_KEX_DH_GROUP14_SHA256: |
505 | 0 | return "diffie-hellman-group14-sha256"; |
506 | 0 | case SSH_GSS_KEX_DH_GROUP14_SHA256: |
507 | 0 | return "gss-group14-sha256-"; |
508 | 0 | case SSH_KEX_DH_GROUP16_SHA512: |
509 | 0 | return "diffie-hellman-group16-sha512"; |
510 | 0 | case SSH_GSS_KEX_DH_GROUP16_SHA512: |
511 | 0 | return "gss-group16-sha512-"; |
512 | 0 | case SSH_KEX_DH_GROUP18_SHA512: |
513 | 0 | return "diffie-hellman-group18-sha512"; |
514 | 0 | case SSH_KEX_ECDH_SHA2_NISTP256: |
515 | 0 | return "ecdh-sha2-nistp256"; |
516 | 0 | case SSH_GSS_KEX_ECDH_NISTP256_SHA256: |
517 | 0 | return "gss-nistp256-sha256-"; |
518 | 0 | case SSH_KEX_ECDH_SHA2_NISTP384: |
519 | 0 | return "ecdh-sha2-nistp384"; |
520 | 0 | case SSH_KEX_ECDH_SHA2_NISTP521: |
521 | 0 | return "ecdh-sha2-nistp521"; |
522 | 0 | case SSH_KEX_CURVE25519_SHA256: |
523 | 0 | return "curve25519-sha256"; |
524 | 0 | case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG: |
525 | 0 | return "curve25519-sha256@libssh.org"; |
526 | 0 | case SSH_GSS_KEX_CURVE25519_SHA256: |
527 | 0 | return "gss-curve25519-sha256-"; |
528 | 0 | case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM: |
529 | 0 | return "sntrup761x25519-sha512@openssh.com"; |
530 | 0 | case SSH_KEX_SNTRUP761X25519_SHA512: |
531 | 0 | return "sntrup761x25519-sha512"; |
532 | 0 | case SSH_KEX_MLKEM768X25519_SHA256: |
533 | 0 | return "mlkem768x25519-sha256"; |
534 | 0 | case SSH_KEX_MLKEM768NISTP256_SHA256: |
535 | 0 | return "mlkem768nistp256-sha256"; |
536 | | #ifdef HAVE_MLKEM1024 |
537 | | case SSH_KEX_MLKEM1024NISTP384_SHA384: |
538 | | return "mlkem1024nistp384-sha384"; |
539 | | #endif /* HAVE_MLKEM1024 */ |
540 | 0 | #ifdef WITH_GEX |
541 | 0 | case SSH_KEX_DH_GEX_SHA1: |
542 | 0 | return "diffie-hellman-group-exchange-sha1"; |
543 | 0 | case SSH_KEX_DH_GEX_SHA256: |
544 | 0 | return "diffie-hellman-group-exchange-sha256"; |
545 | 0 | #endif /* WITH_GEX */ |
546 | 0 | } |
547 | 0 | return NULL; |
548 | 0 | } |
549 | | |
550 | | /** |
551 | | * @brief Check if current session keys were exchanged using |
552 | | * a GSSAPI key exchange method. |
553 | | * |
554 | | * @param[in] session The SSH session. |
555 | | * |
556 | | * @return True if GSSAPI key exchange took place, false otherwise |
557 | | * (other or no key exchange took place). |
558 | | */ |
559 | | bool ssh_session_kex_is_gss(ssh_session session) |
560 | 0 | { |
561 | 0 | if (session == NULL || session->current_crypto == NULL) { |
562 | 0 | return false; |
563 | 0 | } |
564 | 0 | return ssh_kex_is_gss(session->current_crypto); |
565 | 0 | } |
566 | | /** |
567 | | * @brief get the name of the input cipher for the given session. |
568 | | * |
569 | | * @param[in] session The SSH session. |
570 | | * |
571 | | * @return Returns cipher name or NULL. |
572 | | */ |
573 | 0 | const char* ssh_get_cipher_in(ssh_session session) { |
574 | 0 | if ((session != NULL) && |
575 | 0 | (session->current_crypto != NULL) && |
576 | 0 | (session->current_crypto->in_cipher != NULL)) { |
577 | 0 | return session->current_crypto->in_cipher->name; |
578 | 0 | } |
579 | 0 | return NULL; |
580 | 0 | } |
581 | | |
582 | | /** |
583 | | * @brief get the name of the output cipher for the given session. |
584 | | * |
585 | | * @param[in] session The SSH session. |
586 | | * |
587 | | * @return Returns cipher name or NULL. |
588 | | */ |
589 | 0 | const char* ssh_get_cipher_out(ssh_session session) { |
590 | 0 | if ((session != NULL) && |
591 | 0 | (session->current_crypto != NULL) && |
592 | 0 | (session->current_crypto->out_cipher != NULL)) { |
593 | 0 | return session->current_crypto->out_cipher->name; |
594 | 0 | } |
595 | 0 | return NULL; |
596 | 0 | } |
597 | | |
598 | | /** |
599 | | * @brief get the name of the input HMAC algorithm for the given session. |
600 | | * |
601 | | * @param[in] session The SSH session. |
602 | | * |
603 | | * @return Returns HMAC algorithm name or NULL if unknown. |
604 | | */ |
605 | 0 | const char* ssh_get_hmac_in(ssh_session session) { |
606 | 0 | if ((session != NULL) && |
607 | 0 | (session->current_crypto != NULL)) { |
608 | 0 | return ssh_hmac_type_to_string(session->current_crypto->in_hmac, session->current_crypto->in_hmac_etm); |
609 | 0 | } |
610 | 0 | return NULL; |
611 | 0 | } |
612 | | |
613 | | /** |
614 | | * @brief get the name of the output HMAC algorithm for the given session. |
615 | | * |
616 | | * @param[in] session The SSH session. |
617 | | * |
618 | | * @return Returns HMAC algorithm name or NULL if unknown. |
619 | | */ |
620 | 0 | const char* ssh_get_hmac_out(ssh_session session) { |
621 | 0 | if ((session != NULL) && |
622 | 0 | (session->current_crypto != NULL)) { |
623 | 0 | return ssh_hmac_type_to_string(session->current_crypto->out_hmac, session->current_crypto->out_hmac_etm); |
624 | 0 | } |
625 | 0 | return NULL; |
626 | 0 | } |
627 | | |
628 | | /** |
629 | | * @internal |
630 | | * @brief Close the connection socket if it is a socket created by us. |
631 | | * Does not close the sockets provided by the user through options API. |
632 | | */ |
633 | | void |
634 | | ssh_session_socket_close(ssh_session session) |
635 | 2.08k | { |
636 | 2.08k | if (session->opts.fd == SSH_INVALID_SOCKET) { |
637 | 2.08k | ssh_socket_close(session->socket); |
638 | 2.08k | } |
639 | 2.08k | session->alive = 0; |
640 | 2.08k | session->session_state = SSH_SESSION_STATE_ERROR; |
641 | 2.08k | } |
642 | | |
643 | | /** |
644 | | * @brief Disconnect impolitely from a remote host by closing the socket. |
645 | | * |
646 | | * Suitable if you forked and want to destroy this session. |
647 | | * |
648 | | * @param[in] session The SSH session to disconnect. |
649 | | */ |
650 | | void |
651 | | ssh_silent_disconnect(ssh_session session) |
652 | 0 | { |
653 | 0 | if (session == NULL) { |
654 | 0 | return; |
655 | 0 | } |
656 | | |
657 | 0 | ssh_session_socket_close(session); |
658 | 0 | ssh_disconnect(session); |
659 | 0 | } |
660 | | |
661 | | /** |
662 | | * @brief Set the session in blocking/nonblocking mode. |
663 | | * |
664 | | * @param[in] session The ssh session to change. |
665 | | * |
666 | | * @param[in] blocking Zero for nonblocking mode. |
667 | | */ |
668 | | void ssh_set_blocking(ssh_session session, int blocking) |
669 | 8.03k | { |
670 | 8.03k | if (session == NULL) { |
671 | 0 | return; |
672 | 0 | } |
673 | 8.03k | session->flags &= ~SSH_SESSION_FLAG_BLOCKING; |
674 | 8.03k | session->flags |= blocking ? SSH_SESSION_FLAG_BLOCKING : 0; |
675 | 8.03k | } |
676 | | |
677 | | /** |
678 | | * @brief Return the blocking mode of libssh |
679 | | * @param[in] session The SSH session |
680 | | * @returns 0 if the session is nonblocking, |
681 | | * @returns 1 if the functions may block. |
682 | | */ |
683 | | int ssh_is_blocking(ssh_session session) |
684 | 7.60k | { |
685 | 7.60k | return (session->flags & SSH_SESSION_FLAG_BLOCKING) ? 1 : 0; |
686 | 7.60k | } |
687 | | |
688 | | /* Waits until the output socket is empty */ |
689 | 0 | static int ssh_flush_termination(void *c){ |
690 | 0 | ssh_session session = c; |
691 | 0 | if (ssh_socket_buffered_write_bytes(session->socket) == 0 || |
692 | 0 | session->session_state == SSH_SESSION_STATE_ERROR) |
693 | 0 | return 1; |
694 | 0 | else |
695 | 0 | return 0; |
696 | 0 | } |
697 | | |
698 | | /** |
699 | | * @brief Blocking flush of the outgoing buffer |
700 | | * @param[in] session The SSH session |
701 | | * @param[in] timeout Set an upper limit on the time for which this function |
702 | | * will block, in milliseconds. Specifying -1 |
703 | | * means an infinite timeout. This parameter is passed to |
704 | | * the poll() function. |
705 | | * @returns `SSH_OK` on success, `SSH_AGAIN` if timeout occurred, |
706 | | * `SSH_ERROR` otherwise. |
707 | | */ |
708 | 0 | int ssh_blocking_flush(ssh_session session, int timeout){ |
709 | 0 | int rc; |
710 | 0 | if (session == NULL) { |
711 | 0 | return SSH_ERROR; |
712 | 0 | } |
713 | | |
714 | 0 | rc = ssh_handle_packets_termination(session, timeout, |
715 | 0 | ssh_flush_termination, session); |
716 | 0 | if (rc == SSH_ERROR) { |
717 | 0 | return rc; |
718 | 0 | } |
719 | 0 | if (!ssh_flush_termination(session)) { |
720 | 0 | rc = SSH_AGAIN; |
721 | 0 | } |
722 | |
|
723 | 0 | return rc; |
724 | 0 | } |
725 | | |
726 | | /** |
727 | | * @brief Check if we are connected. |
728 | | * |
729 | | * @param[in] session The session to check if it is connected. |
730 | | * |
731 | | * @return 1 if we are connected, 0 if not. |
732 | | */ |
733 | 0 | int ssh_is_connected(ssh_session session) { |
734 | 0 | if (session == NULL) { |
735 | 0 | return 0; |
736 | 0 | } |
737 | | |
738 | 0 | return session->alive; |
739 | 0 | } |
740 | | |
741 | | /** |
742 | | * @brief Get the fd of a connection. |
743 | | * |
744 | | * In case you'd need the file descriptor of the connection to the server/client. |
745 | | * |
746 | | * @param[in] session The ssh session to use. |
747 | | * |
748 | | * @return The file descriptor of the connection, or -1 if it is |
749 | | * not connected |
750 | | */ |
751 | 0 | socket_t ssh_get_fd(ssh_session session) { |
752 | 0 | if (session == NULL) { |
753 | 0 | return -1; |
754 | 0 | } |
755 | | |
756 | 0 | return ssh_socket_get_fd(session->socket); |
757 | 0 | } |
758 | | |
759 | | /** |
760 | | * @brief Tell the session it has data to read on the file descriptor without |
761 | | * blocking. |
762 | | * |
763 | | * @param[in] session The ssh session to use. |
764 | | */ |
765 | 0 | void ssh_set_fd_toread(ssh_session session) { |
766 | 0 | if (session == NULL) { |
767 | 0 | return; |
768 | 0 | } |
769 | | |
770 | 0 | ssh_socket_set_read_wontblock(session->socket); |
771 | 0 | } |
772 | | |
773 | | /** |
774 | | * @brief Tell the session it may write to the file descriptor without blocking. |
775 | | * |
776 | | * @param[in] session The ssh session to use. |
777 | | */ |
778 | 0 | void ssh_set_fd_towrite(ssh_session session) { |
779 | 0 | if (session == NULL) { |
780 | 0 | return; |
781 | 0 | } |
782 | | |
783 | 0 | ssh_socket_set_write_wontblock(session->socket); |
784 | 0 | } |
785 | | |
786 | | /** |
787 | | * @brief Tell the session it has an exception to catch on the file descriptor. |
788 | | * |
789 | | * @param[in] session The ssh session to use. |
790 | | */ |
791 | 0 | void ssh_set_fd_except(ssh_session session) { |
792 | 0 | if (session == NULL) { |
793 | 0 | return; |
794 | 0 | } |
795 | | |
796 | 0 | ssh_socket_set_except(session->socket); |
797 | 0 | } |
798 | | |
799 | | /** |
800 | | * @internal |
801 | | * |
802 | | * @brief Poll the current session for an event and call the appropriate |
803 | | * callbacks. This function will not loop until the @p timeout is expired. |
804 | | * |
805 | | * This will block until one event happens. |
806 | | * |
807 | | * @param[in] session The session handle to use. |
808 | | * |
809 | | * @param[in] timeout Set an upper limit on the time for which this function |
810 | | * will block, in milliseconds. Specifying |
811 | | * `SSH_TIMEOUT_INFINITE` |
812 | | * (-1) means an infinite timeout. |
813 | | * Specifying `SSH_TIMEOUT_USER` means to use the timeout |
814 | | * specified in options. 0 means poll will return |
815 | | * immediately. |
816 | | * This parameter is passed to the poll() function. |
817 | | * |
818 | | * @return `SSH_OK` on success, `SSH_ERROR` otherwise. |
819 | | */ |
820 | | int ssh_handle_packets(ssh_session session, int timeout) |
821 | 10.2k | { |
822 | 10.2k | ssh_poll_handle spoll = NULL; |
823 | 10.2k | ssh_poll_ctx ctx = NULL; |
824 | 10.2k | int tm = timeout; |
825 | 10.2k | int rc; |
826 | | |
827 | 10.2k | if (session == NULL || session->socket == NULL) { |
828 | 0 | return SSH_ERROR; |
829 | 0 | } |
830 | | |
831 | 10.2k | spoll = ssh_socket_get_poll_handle(session->socket); |
832 | 10.2k | if (spoll == NULL) { |
833 | 0 | ssh_set_error_oom(session); |
834 | 0 | return SSH_ERROR; |
835 | 0 | } |
836 | 10.2k | ssh_poll_add_events(spoll, POLLIN); |
837 | 10.2k | ctx = ssh_poll_get_ctx(spoll); |
838 | | |
839 | 10.2k | if (ctx == NULL) { |
840 | 7.60k | ctx = ssh_poll_get_default_ctx(session); |
841 | 7.60k | if (ctx == NULL) { |
842 | 0 | ssh_set_error_oom(session); |
843 | 0 | return SSH_ERROR; |
844 | 0 | } |
845 | 7.60k | rc = ssh_poll_ctx_add(ctx, spoll); |
846 | 7.60k | if (rc != SSH_OK) { |
847 | 0 | return SSH_ERROR; |
848 | 0 | } |
849 | 7.60k | } |
850 | | |
851 | 10.2k | if (timeout == SSH_TIMEOUT_USER) { |
852 | 0 | if (ssh_is_blocking(session)) { |
853 | 0 | tm = ssh_make_milliseconds(session->opts.timeout, |
854 | 0 | session->opts.timeout_usec); |
855 | 0 | } else { |
856 | 0 | tm = 0; |
857 | 0 | } |
858 | 0 | } |
859 | 10.2k | rc = ssh_poll_ctx_dopoll(ctx, tm); |
860 | 10.2k | if (rc == SSH_ERROR) { |
861 | 841 | session->session_state = SSH_SESSION_STATE_ERROR; |
862 | 841 | } |
863 | | |
864 | 10.2k | return rc; |
865 | 10.2k | } |
866 | | |
867 | | /** |
868 | | * @internal |
869 | | * |
870 | | * @brief Poll the current session for an event and call the appropriate |
871 | | * callbacks. |
872 | | * |
873 | | * This will block until termination function returns true, or @p timeout |
874 | | * expired. |
875 | | * |
876 | | * @param[in] session The session handle to use. |
877 | | * |
878 | | * @param[in] timeout Set an upper limit on the time for which this function |
879 | | * will block, in milliseconds. Specifying |
880 | | * `SSH_TIMEOUT_INFINITE` (-1) means an infinite timeout. |
881 | | * Specifying SSH_TIMEOUT_USER means using the timeout |
882 | | * specified in options. 0 means poll will return |
883 | | * immediately. |
884 | | * SSH_TIMEOUT_DEFAULT uses the session timeout if set or |
885 | | * uses blocking parameters of the session. |
886 | | * This parameter is passed to the poll() function. |
887 | | * |
888 | | * @param[in] fct Termination function to be used to determine if it is |
889 | | * possible to stop polling. |
890 | | * @param[in] user User parameter to be passed to fct termination function. |
891 | | * @returns `SSH_OK` on success, `SSH_AGAIN` if timeout occurred, |
892 | | * `SSH_ERROR` otherwise. |
893 | | */ |
894 | | int ssh_handle_packets_termination(ssh_session session, |
895 | | int timeout, |
896 | | ssh_termination_function fct, |
897 | | void *user) |
898 | 7.60k | { |
899 | 7.60k | struct ssh_timestamp ts; |
900 | 7.60k | int timeout_ms = SSH_TIMEOUT_INFINITE; |
901 | 7.60k | int tm; |
902 | 7.60k | int ret = SSH_OK; |
903 | | |
904 | | /* If a timeout has been provided, use it */ |
905 | 7.60k | if (timeout >= 0) { |
906 | 0 | timeout_ms = timeout; |
907 | 7.60k | } else { |
908 | 7.60k | if (ssh_is_blocking(session)) { |
909 | 7.60k | if (timeout == SSH_TIMEOUT_USER || timeout == SSH_TIMEOUT_DEFAULT) { |
910 | 7.60k | if (session->opts.timeout > 0 || |
911 | 7.60k | session->opts.timeout_usec > 0) { |
912 | 0 | timeout_ms = |
913 | 0 | ssh_make_milliseconds(session->opts.timeout, |
914 | 0 | session->opts.timeout_usec); |
915 | 0 | } |
916 | 7.60k | } |
917 | 7.60k | } else { |
918 | 0 | timeout_ms = SSH_TIMEOUT_NONBLOCKING; |
919 | 0 | } |
920 | 7.60k | } |
921 | | |
922 | | /* avoid unnecessary syscall for the SSH_TIMEOUT_NONBLOCKING case */ |
923 | 7.60k | if (timeout_ms != SSH_TIMEOUT_NONBLOCKING) { |
924 | 7.60k | ssh_timestamp_init(&ts); |
925 | 7.60k | } |
926 | | |
927 | 7.60k | tm = timeout_ms; |
928 | 16.9k | while(!fct(user)) { |
929 | 10.2k | ret = ssh_handle_packets(session, tm); |
930 | 10.2k | if (ret == SSH_ERROR) { |
931 | 841 | break; |
932 | 841 | } |
933 | 9.37k | if (ssh_timeout_elapsed(&ts, timeout_ms)) { |
934 | 0 | ret = fct(user) ? SSH_OK : SSH_AGAIN; |
935 | 0 | break; |
936 | 0 | } |
937 | | |
938 | 9.37k | tm = ssh_timeout_update(&ts, timeout_ms); |
939 | 9.37k | } |
940 | | |
941 | 7.60k | return ret; |
942 | 7.60k | } |
943 | | |
944 | | /** |
945 | | * @brief Get session status |
946 | | * |
947 | | * @param session The ssh session to use. |
948 | | * |
949 | | * @returns A bitmask including `SSH_CLOSED`, `SSH_READ_PENDING`, |
950 | | * `SSH_WRITE_PENDING` or `SSH_CLOSED_ERROR` which |
951 | | * respectively means the session is closed, has data to |
952 | | * read on the connection socket and session was closed |
953 | | * due to an error. |
954 | | */ |
955 | 0 | int ssh_get_status(ssh_session session) { |
956 | 0 | int socketstate; |
957 | 0 | int r = 0; |
958 | |
|
959 | 0 | if (session == NULL) { |
960 | 0 | return 0; |
961 | 0 | } |
962 | | |
963 | 0 | socketstate = ssh_socket_get_status(session->socket); |
964 | |
|
965 | 0 | if (session->session_state == SSH_SESSION_STATE_DISCONNECTED) { |
966 | 0 | r |= SSH_CLOSED; |
967 | 0 | } |
968 | 0 | if (socketstate & SSH_READ_PENDING) { |
969 | 0 | r |= SSH_READ_PENDING; |
970 | 0 | } |
971 | 0 | if (socketstate & SSH_WRITE_PENDING) { |
972 | 0 | r |= SSH_WRITE_PENDING; |
973 | 0 | } |
974 | 0 | if ((session->session_state == SSH_SESSION_STATE_DISCONNECTED && |
975 | 0 | (socketstate & SSH_CLOSED_ERROR)) || |
976 | 0 | session->session_state == SSH_SESSION_STATE_ERROR) { |
977 | 0 | r |= SSH_CLOSED_ERROR; |
978 | 0 | } |
979 | |
|
980 | 0 | return r; |
981 | 0 | } |
982 | | |
983 | | /** |
984 | | * @brief Get poll flags for an external mainloop |
985 | | * |
986 | | * @param session The ssh session to use. |
987 | | * |
988 | | * @returns A bitmask including `SSH_READ_PENDING` or `SSH_WRITE_PENDING`. |
989 | | * For `SSH_READ_PENDING`, your invocation of poll() should include |
990 | | * POLLIN. For `SSH_WRITE_PENDING`, your invocation of poll() should |
991 | | * include POLLOUT. |
992 | | */ |
993 | | int ssh_get_poll_flags(ssh_session session) |
994 | 0 | { |
995 | 0 | if (session == NULL) { |
996 | 0 | return 0; |
997 | 0 | } |
998 | | |
999 | 0 | return ssh_socket_get_poll_flags (session->socket); |
1000 | 0 | } |
1001 | | |
1002 | | /** |
1003 | | * @brief Get the disconnect message from the server. |
1004 | | * |
1005 | | * @param[in] session The ssh session to use. |
1006 | | * |
1007 | | * @return The message sent by the server along with the |
1008 | | * disconnect, or NULL in which case the reason of the |
1009 | | * disconnect may be found with ssh_get_error. |
1010 | | * |
1011 | | * @see ssh_get_error() |
1012 | | */ |
1013 | 0 | const char *ssh_get_disconnect_message(ssh_session session) { |
1014 | 0 | if (session == NULL) { |
1015 | 0 | return NULL; |
1016 | 0 | } |
1017 | | |
1018 | 0 | if (session->session_state != SSH_SESSION_STATE_DISCONNECTED) { |
1019 | 0 | ssh_set_error(session, SSH_REQUEST_DENIED, |
1020 | 0 | "Connection not closed yet"); |
1021 | 0 | } else if(!session->peer_discon_msg) { |
1022 | 0 | ssh_set_error(session, SSH_FATAL, |
1023 | 0 | "Connection correctly closed but no disconnect message"); |
1024 | 0 | } else { |
1025 | 0 | return session->peer_discon_msg; |
1026 | 0 | } |
1027 | | |
1028 | 0 | return NULL; |
1029 | 0 | } |
1030 | | |
1031 | | /** |
1032 | | * @brief Get the protocol version of the session. |
1033 | | * |
1034 | | * @param session The ssh session to use. |
1035 | | * |
1036 | | * @return The SSH version as integer, < 0 on error. |
1037 | | */ |
1038 | 0 | int ssh_get_version(ssh_session session) { |
1039 | 0 | if (session == NULL) { |
1040 | 0 | return -1; |
1041 | 0 | } |
1042 | | |
1043 | 0 | return 2; |
1044 | 0 | } |
1045 | | |
1046 | | /** |
1047 | | * @internal |
1048 | | * @brief Callback to be called when the socket received an exception code. |
1049 | | * @param code The exception code from the socket layer. |
1050 | | * @param errno_code The errno value associated with the exception. |
1051 | | * @param user Pointer to the SSH session. |
1052 | | */ |
1053 | 5.58k | void ssh_socket_exception_callback(int code, int errno_code, void *user){ |
1054 | 5.58k | ssh_session session = (ssh_session)user; |
1055 | | |
1056 | 5.58k | SSH_LOG(SSH_LOG_RARE, |
1057 | 5.58k | "Socket exception callback: %d (%d)", |
1058 | 5.58k | code, |
1059 | 5.58k | errno_code); |
1060 | 5.58k | session->session_state = SSH_SESSION_STATE_ERROR; |
1061 | 5.58k | if (errno_code == 0 && code == SSH_SOCKET_EXCEPTION_EOF) { |
1062 | 5.18k | ssh_set_error(session, SSH_FATAL, "Socket error: disconnected"); |
1063 | | #ifdef _WIN32 |
1064 | | } else if (errno_code == WSAENETDOWN) { |
1065 | | ssh_set_error(session, SSH_FATAL, "Socket error: network down"); |
1066 | | } else if (errno_code == WSAENETUNREACH) { |
1067 | | ssh_set_error(session, SSH_FATAL, "Socket error: network unreachable"); |
1068 | | } else if (errno_code == WSAENETRESET) { |
1069 | | ssh_set_error(session, SSH_FATAL, "Socket error: network reset"); |
1070 | | } else if (errno_code == WSAECONNABORTED) { |
1071 | | ssh_set_error(session, SSH_FATAL, "Socket error: connection aborted"); |
1072 | | } else if (errno_code == WSAECONNRESET) { |
1073 | | ssh_set_error(session, |
1074 | | SSH_FATAL, |
1075 | | "Socket error: connection reset by peer"); |
1076 | | } else if (errno_code == WSAETIMEDOUT) { |
1077 | | ssh_set_error(session, SSH_FATAL, "Socket error: connection timed out"); |
1078 | | } else if (errno_code == WSAECONNREFUSED) { |
1079 | | ssh_set_error(session, SSH_FATAL, "Socket error: connection refused"); |
1080 | | } else if (errno_code == WSAEHOSTUNREACH) { |
1081 | | ssh_set_error(session, SSH_FATAL, "Socket error: host unreachable"); |
1082 | | #endif |
1083 | 5.18k | } else { |
1084 | 400 | char err_msg[SSH_ERRNO_MSG_MAX] = {0}; |
1085 | 400 | ssh_set_error(session, |
1086 | 400 | SSH_FATAL, |
1087 | 400 | "Socket error: %s", |
1088 | 400 | ssh_strerror(errno_code, err_msg, SSH_ERRNO_MSG_MAX)); |
1089 | 400 | } |
1090 | | |
1091 | 5.58k | session->ssh_connection_callback(session); |
1092 | 5.58k | } |
1093 | | |
1094 | | /** |
1095 | | * @brief Send a message that should be ignored |
1096 | | * |
1097 | | * @param[in] session The SSH session |
1098 | | * @param[in] data Data to be sent |
1099 | | * |
1100 | | * @return `SSH_OK` on success, `SSH_ERROR` otherwise. |
1101 | | */ |
1102 | 0 | int ssh_send_ignore (ssh_session session, const char *data) { |
1103 | 0 | const int type = SSH2_MSG_IGNORE; |
1104 | 0 | int rc; |
1105 | |
|
1106 | 0 | if (ssh_socket_is_open(session->socket)) { |
1107 | 0 | rc = ssh_buffer_pack(session->out_buffer, |
1108 | 0 | "bs", |
1109 | 0 | type, |
1110 | 0 | data); |
1111 | 0 | if (rc != SSH_OK){ |
1112 | 0 | ssh_set_error_oom(session); |
1113 | 0 | goto error; |
1114 | 0 | } |
1115 | 0 | ssh_packet_send(session); |
1116 | 0 | ssh_handle_packets(session, 0); |
1117 | 0 | } |
1118 | | |
1119 | 0 | return SSH_OK; |
1120 | | |
1121 | 0 | error: |
1122 | 0 | ssh_buffer_reinit(session->out_buffer); |
1123 | 0 | return SSH_ERROR; |
1124 | 0 | } |
1125 | | |
1126 | | /** |
1127 | | * @brief Send a debug message |
1128 | | * |
1129 | | * @param[in] session The SSH session |
1130 | | * @param[in] message Data to be sent |
1131 | | * @param[in] always_display Message SHOULD be displayed by the server. It |
1132 | | * SHOULD NOT be displayed unless debugging |
1133 | | * information has been explicitly requested. |
1134 | | * |
1135 | | * @return `SSH_OK` on success, `SSH_ERROR` otherwise. |
1136 | | */ |
1137 | 0 | int ssh_send_debug (ssh_session session, const char *message, int always_display) { |
1138 | 0 | int rc; |
1139 | |
|
1140 | 0 | if (ssh_socket_is_open(session->socket)) { |
1141 | 0 | rc = ssh_buffer_pack(session->out_buffer, |
1142 | 0 | "bbsd", |
1143 | 0 | SSH2_MSG_DEBUG, |
1144 | 0 | always_display != 0 ? 1 : 0, |
1145 | 0 | message, |
1146 | 0 | 0); /* empty language tag */ |
1147 | 0 | if (rc != SSH_OK) { |
1148 | 0 | ssh_set_error_oom(session); |
1149 | 0 | goto error; |
1150 | 0 | } |
1151 | 0 | ssh_packet_send(session); |
1152 | 0 | ssh_handle_packets(session, 0); |
1153 | 0 | } |
1154 | | |
1155 | 0 | return SSH_OK; |
1156 | | |
1157 | 0 | error: |
1158 | 0 | ssh_buffer_reinit(session->out_buffer); |
1159 | 0 | return SSH_ERROR; |
1160 | 0 | } |
1161 | | |
1162 | | /** |
1163 | | * @brief Set the session data counters. |
1164 | | * |
1165 | | * This function sets the counter structures to be used to calculate data |
1166 | | * which comes in and goes out through the session at different levels. |
1167 | | * |
1168 | | * @code |
1169 | | * struct ssh_counter_struct scounter = { |
1170 | | * .in_bytes = 0, |
1171 | | * .out_bytes = 0, |
1172 | | * .in_packets = 0, |
1173 | | * .out_packets = 0 |
1174 | | * }; |
1175 | | * |
1176 | | * struct ssh_counter_struct rcounter = { |
1177 | | * .in_bytes = 0, |
1178 | | * .out_bytes = 0, |
1179 | | * .in_packets = 0, |
1180 | | * .out_packets = 0 |
1181 | | * }; |
1182 | | * |
1183 | | * ssh_set_counters(session, &scounter, &rcounter); |
1184 | | * @endcode |
1185 | | * |
1186 | | * @param[in] session The SSH session. |
1187 | | * |
1188 | | * @param[in] scounter Counter for byte data handled by the session sockets. |
1189 | | * |
1190 | | * @param[in] rcounter Counter for byte and packet data handled by the session, |
1191 | | * prior compression and SSH overhead. |
1192 | | */ |
1193 | | void ssh_set_counters(ssh_session session, ssh_counter scounter, |
1194 | 0 | ssh_counter rcounter) { |
1195 | 0 | if (session != NULL) { |
1196 | 0 | session->socket_counter = scounter; |
1197 | 0 | session->raw_counter = rcounter; |
1198 | 0 | } |
1199 | 0 | } |
1200 | | |
1201 | | /** |
1202 | | * @deprecated Use ssh_get_publickey_hash() |
1203 | | */ |
1204 | | int ssh_get_pubkey_hash(ssh_session session, unsigned char **hash) |
1205 | 0 | { |
1206 | 0 | ssh_key pubkey = NULL; |
1207 | 0 | ssh_string pubkey_blob = NULL; |
1208 | 0 | MD5CTX ctx = NULL; |
1209 | 0 | unsigned char *h = NULL; |
1210 | 0 | int rc; |
1211 | |
|
1212 | 0 | if (session == NULL || hash == NULL) { |
1213 | 0 | return SSH_ERROR; |
1214 | 0 | } |
1215 | | |
1216 | | /* In FIPS mode, we cannot use MD5 */ |
1217 | 0 | if (ssh_fips_mode()) { |
1218 | 0 | ssh_set_error(session, |
1219 | 0 | SSH_FATAL, |
1220 | 0 | "In FIPS mode MD5 is not allowed." |
1221 | 0 | "Try ssh_get_publickey_hash() with" |
1222 | 0 | "SSH_PUBLICKEY_HASH_SHA256"); |
1223 | 0 | return SSH_ERROR; |
1224 | 0 | } |
1225 | | |
1226 | 0 | *hash = NULL; |
1227 | 0 | if (session->current_crypto == NULL || |
1228 | 0 | session->current_crypto->server_pubkey == NULL) { |
1229 | 0 | ssh_set_error(session, SSH_FATAL, "No current cryptographic context"); |
1230 | 0 | return SSH_ERROR; |
1231 | 0 | } |
1232 | | |
1233 | 0 | rc = ssh_get_server_publickey(session, &pubkey); |
1234 | 0 | if (rc != SSH_OK) { |
1235 | 0 | return SSH_ERROR; |
1236 | 0 | } |
1237 | | |
1238 | 0 | rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob); |
1239 | 0 | ssh_key_free(pubkey); |
1240 | 0 | if (rc != SSH_OK) { |
1241 | 0 | return SSH_ERROR; |
1242 | 0 | } |
1243 | | |
1244 | 0 | h = calloc(MD5_DIGEST_LEN, sizeof(unsigned char)); |
1245 | 0 | if (h == NULL) { |
1246 | 0 | SSH_STRING_FREE(pubkey_blob); |
1247 | 0 | return SSH_ERROR; |
1248 | 0 | } |
1249 | | |
1250 | 0 | ctx = md5_init(); |
1251 | 0 | if (ctx == NULL) { |
1252 | 0 | SSH_STRING_FREE(pubkey_blob); |
1253 | 0 | SAFE_FREE(h); |
1254 | 0 | return SSH_ERROR; |
1255 | 0 | } |
1256 | | |
1257 | 0 | rc = md5_update(ctx, |
1258 | 0 | ssh_string_data(pubkey_blob), |
1259 | 0 | ssh_string_len(pubkey_blob)); |
1260 | 0 | if (rc != SSH_OK) { |
1261 | 0 | SSH_STRING_FREE(pubkey_blob); |
1262 | 0 | md5_ctx_free(ctx); |
1263 | 0 | SAFE_FREE(h); |
1264 | 0 | return rc; |
1265 | 0 | } |
1266 | 0 | SSH_STRING_FREE(pubkey_blob); |
1267 | 0 | rc = md5_final(h, ctx); |
1268 | 0 | if (rc != SSH_OK) { |
1269 | 0 | SAFE_FREE(h); |
1270 | 0 | return rc; |
1271 | 0 | } |
1272 | | |
1273 | 0 | *hash = h; |
1274 | |
|
1275 | 0 | return MD5_DIGEST_LEN; |
1276 | 0 | } |
1277 | | |
1278 | | /** |
1279 | | * @brief Deallocate the hash obtained by ssh_get_pubkey_hash. |
1280 | | * |
1281 | | * This is required under Microsoft platform as this library might use a |
1282 | | * different C library than your software, hence a different heap. |
1283 | | * |
1284 | | * @param[in] hash The buffer to deallocate. |
1285 | | * |
1286 | | * @see ssh_get_pubkey_hash() |
1287 | | */ |
1288 | | void ssh_clean_pubkey_hash(unsigned char **hash) |
1289 | 0 | { |
1290 | 0 | SAFE_FREE(*hash); |
1291 | 0 | } |
1292 | | |
1293 | | /** |
1294 | | * @brief Get the server public key from a session. |
1295 | | * |
1296 | | * @param[in] session The session to get the key from. |
1297 | | * |
1298 | | * @param[out] key A pointer to store the allocated key. You need to free |
1299 | | * the key using ssh_key_free(). |
1300 | | * |
1301 | | * @return `SSH_OK` on success, `SSH_ERROR` on error. |
1302 | | * |
1303 | | * @see ssh_key_free() |
1304 | | */ |
1305 | | int ssh_get_server_publickey(ssh_session session, ssh_key *key) |
1306 | 0 | { |
1307 | 0 | ssh_key pubkey = NULL; |
1308 | |
|
1309 | 0 | if (session == NULL || |
1310 | 0 | session->current_crypto == NULL || |
1311 | 0 | session->current_crypto->server_pubkey == NULL) { |
1312 | 0 | return SSH_ERROR; |
1313 | 0 | } |
1314 | | |
1315 | 0 | pubkey = ssh_key_dup(session->current_crypto->server_pubkey); |
1316 | 0 | if (pubkey == NULL) { |
1317 | 0 | return SSH_ERROR; |
1318 | 0 | } |
1319 | | |
1320 | 0 | *key = pubkey; |
1321 | 0 | return SSH_OK; |
1322 | 0 | } |
1323 | | |
1324 | | /** |
1325 | | * @deprecated Use ssh_get_server_publickey() |
1326 | | */ |
1327 | | int ssh_get_publickey(ssh_session session, ssh_key *key) |
1328 | 0 | { |
1329 | 0 | return ssh_get_server_publickey(session, key); |
1330 | 0 | } |
1331 | | |
1332 | | /** |
1333 | | * @brief Allocates a buffer with the hash of the public key. |
1334 | | * |
1335 | | * This function allows you to get a @p hash of the public @p key. You can then |
1336 | | * print this hash in a human-readable form to the user so that he is able to |
1337 | | * verify it. Use ssh_get_hexa() or ssh_print_hash() to display it. |
1338 | | * |
1339 | | * @param[in] key The public key to create the hash for. |
1340 | | * |
1341 | | * @param[in] type The type of the hash you want. |
1342 | | * |
1343 | | * @param[out] hash A pointer to store the allocated buffer. It can be |
1344 | | * freed using ssh_clean_pubkey_hash(). |
1345 | | * |
1346 | | * @param[in] hlen The length of the hash. |
1347 | | * |
1348 | | * @return SSH_OK on success, SSH_ERROR if an error occurred. |
1349 | | * |
1350 | | * @warning It is very important that you verify at some moment that the hash |
1351 | | * matches a known server. If you don't do it, cryptography won't help |
1352 | | * you at making things secure. |
1353 | | * OpenSSH uses SHA256 to print public key digests. |
1354 | | * |
1355 | | * @see ssh_session_update_known_hosts() |
1356 | | * @see ssh_get_hexa() |
1357 | | * @see ssh_print_hash() |
1358 | | * @see ssh_clean_pubkey_hash() |
1359 | | */ |
1360 | | int ssh_get_publickey_hash(const ssh_key key, |
1361 | | enum ssh_publickey_hash_type type, |
1362 | | unsigned char **hash, |
1363 | | size_t *hlen) |
1364 | 0 | { |
1365 | 0 | ssh_string blob = NULL; |
1366 | 0 | unsigned char *h = NULL; |
1367 | 0 | int (*digest)(const unsigned char *, size_t, unsigned char *) = NULL; |
1368 | 0 | int rc, ret = SSH_ERROR; |
1369 | |
|
1370 | 0 | rc = ssh_pki_export_pubkey_blob(key, &blob); |
1371 | 0 | if (rc < 0) { |
1372 | 0 | goto out; |
1373 | 0 | } |
1374 | | |
1375 | 0 | switch (type) { |
1376 | 0 | case SSH_PUBLICKEY_HASH_SHA1: |
1377 | 0 | digest = sha1; |
1378 | 0 | *hlen = SHA_DIGEST_LEN; |
1379 | 0 | break; |
1380 | 0 | case SSH_PUBLICKEY_HASH_SHA256: |
1381 | 0 | digest = sha256; |
1382 | 0 | *hlen = SHA256_DIGEST_LEN; |
1383 | 0 | break; |
1384 | 0 | case SSH_PUBLICKEY_HASH_MD5: |
1385 | 0 | #if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER < 0x30000000L |
1386 | | /* In FIPS mode without OpenSSL providers, we cannot use MD5 */ |
1387 | 0 | if (ssh_fips_mode()) { |
1388 | 0 | SSH_LOG(SSH_LOG_TRACE, |
1389 | 0 | "In FIPS mode MD5 is not allowed." |
1390 | 0 | "Try using SSH_PUBLICKEY_HASH_SHA256"); |
1391 | 0 | goto out; |
1392 | 0 | } |
1393 | 0 | #endif |
1394 | 0 | digest = md5; |
1395 | 0 | *hlen = MD5_DIGEST_LEN; |
1396 | 0 | break; |
1397 | 0 | default: |
1398 | 0 | goto out; |
1399 | 0 | } |
1400 | | |
1401 | 0 | h = calloc(1, *hlen); |
1402 | 0 | if (h == NULL) { |
1403 | 0 | goto out; |
1404 | 0 | } |
1405 | | |
1406 | 0 | rc = digest(ssh_string_data(blob), ssh_string_len(blob), h); |
1407 | 0 | if (rc != SSH_OK) { |
1408 | 0 | free(h); |
1409 | 0 | goto out; |
1410 | 0 | } |
1411 | | |
1412 | 0 | *hash = h; |
1413 | 0 | ret = SSH_OK; |
1414 | |
|
1415 | 0 | out: |
1416 | | SSH_STRING_FREE(blob); |
1417 | 0 | return ret; |
1418 | 0 | } |
1419 | | |
1420 | | /** @} */ |