Coverage Report

Created: 2026-10-22 06:47

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