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