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