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