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