Coverage Report

Created: 2026-01-15 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libssh/src/client.c
Line
Count
Source
1
/*
2
 * client.c - SSH client functions
3
 *
4
 * This file is part of the SSH Library
5
 *
6
 * Copyright (c) 2003-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 <stdio.h>
27
28
#ifndef _WIN32
29
#include <netinet/in.h>
30
#include <arpa/inet.h>
31
#endif
32
33
#include "libssh/buffer.h"
34
#include "libssh/kex-gss.h"
35
#include "libssh/dh.h"
36
#include "libssh/options.h"
37
#include "libssh/packet.h"
38
#include "libssh/priv.h"
39
#include "libssh/session.h"
40
#include "libssh/socket.h"
41
#include "libssh/ssh2.h"
42
#ifdef WITH_GEX
43
#include "libssh/dh-gex.h"
44
#endif /* WITH_GEX */
45
#include "libssh/ecdh.h"
46
#include "libssh/threads.h"
47
#include "libssh/misc.h"
48
#include "libssh/pki.h"
49
#include "libssh/kex.h"
50
#ifdef HAVE_MLKEM
51
#include "libssh/hybrid_mlkem.h"
52
#endif
53
54
#ifndef _WIN32
55
#ifdef HAVE_PTHREAD
56
extern int proxy_disconnect;
57
#endif /* HAVE_PTHREAD */
58
#endif /* _WIN32 */
59
60
0
#define set_status(session, status) do {\
61
0
        if (session->common.callbacks && session->common.callbacks->connect_status_function) \
62
0
            session->common.callbacks->connect_status_function(session->common.callbacks->userdata, status); \
63
0
    } while (0)
64
65
/**
66
 * @internal
67
 * @brief Callback to be called when the socket is connected or had a
68
 * connection error. Changes the state of the session and updates the error
69
 * message.
70
 * @param code one of SSH_SOCKET_CONNECTED_OK or SSH_SOCKET_CONNECTED_ERROR
71
 * @param user is a pointer to session
72
 */
73
static void socket_callback_connected(int code, int errno_code, void *user)
74
0
{
75
0
  ssh_session session=(ssh_session)user;
76
77
0
  if (session->session_state != SSH_SESSION_STATE_CONNECTING &&
78
0
      session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED)
79
0
  {
80
0
    ssh_set_error(session,SSH_FATAL, "Wrong state in socket_callback_connected : %d",
81
0
        session->session_state);
82
83
0
    return;
84
0
  }
85
86
0
  SSH_LOG(SSH_LOG_TRACE,"Socket connection callback: %d (%d)",code, errno_code);
87
0
  if(code == SSH_SOCKET_CONNECTED_OK)
88
0
    session->session_state=SSH_SESSION_STATE_SOCKET_CONNECTED;
89
0
  else {
90
0
        char err_msg[SSH_ERRNO_MSG_MAX] = {0};
91
0
    session->session_state=SSH_SESSION_STATE_ERROR;
92
0
    ssh_set_error(session,SSH_FATAL,"%s",
93
0
                      ssh_strerror(errno_code, err_msg, SSH_ERRNO_MSG_MAX));
94
0
  }
95
0
  session->ssh_connection_callback(session);
96
0
}
97
98
/**
99
 * @internal
100
 *
101
 * @brief Gets the banner from socket and saves it in session.
102
 * Updates the session state
103
 *
104
 * @param  data pointer to the beginning of header
105
 * @param  len size of the banner
106
 * @param  user is a pointer to session
107
 * @returns Number of bytes processed, or zero if the banner is not complete.
108
 */
109
static size_t callback_receive_banner(const void *data, size_t len, void *user)
110
0
{
111
0
    char *buffer = (char *)data;
112
0
    ssh_session session = (ssh_session) user;
113
0
    char *str = NULL;
114
0
    uint32_t i;
115
0
    int ret=0;
116
117
0
    if (session->session_state != SSH_SESSION_STATE_SOCKET_CONNECTED) {
118
0
        ssh_set_error(session,SSH_FATAL,
119
0
                      "Wrong state in callback_receive_banner : %d",
120
0
                      session->session_state);
121
122
0
        return 0;
123
0
    }
124
0
    for (i = 0; i < len; ++i) {
125
0
#ifdef WITH_PCAP
126
0
        if (session->pcap_ctx && buffer[i] == '\n') {
127
0
            ssh_pcap_context_write(session->pcap_ctx,
128
0
                                   SSH_PCAP_DIR_IN,
129
0
                                   buffer,i+1,
130
0
                                   i+1);
131
0
        }
132
0
#endif
133
0
        if (buffer[i] == '\r') {
134
0
            buffer[i] = '\0';
135
0
        }
136
0
        if (buffer[i] == '\n') {
137
0
            int cmp;
138
139
0
            buffer[i] = '\0';
140
141
            /* The server MAY send other lines of data... */
142
0
            cmp = strncmp(buffer, "SSH-", 4);
143
0
            if (cmp == 0) {
144
0
                str = strdup(buffer);
145
0
                if (str == NULL) {
146
0
                    return SSH_ERROR;
147
0
                }
148
                /* number of bytes read */
149
0
                ret = i + 1;
150
0
                session->serverbanner = str;
151
0
                session->session_state = SSH_SESSION_STATE_BANNER_RECEIVED;
152
0
                SSH_LOG(SSH_LOG_PACKET, "Received banner: %s", str);
153
0
                session->ssh_connection_callback(session);
154
155
0
                return ret;
156
0
            } else {
157
0
                SSH_LOG(SSH_LOG_DEBUG,
158
0
                        "ssh_protocol_version_exchange: %s",
159
0
                        buffer);
160
0
                ret = i + 1;
161
0
                break;
162
0
            }
163
0
        }
164
        /* According to RFC 4253 the max banner length is 255 */
165
0
        if (i > 255) {
166
            /* Too big banner */
167
0
            session->session_state=SSH_SESSION_STATE_ERROR;
168
0
            ssh_set_error(session,
169
0
                          SSH_FATAL,
170
0
                          "Receiving banner: too large banner");
171
172
0
            return 0;
173
0
        }
174
0
    }
175
176
0
    return ret;
177
0
}
178
179
/** @internal
180
 * @brief Sends a SSH banner to the server.
181
 *
182
 * @param session      The SSH session to use.
183
 *
184
 * @param server       Send client or server banner.
185
 *
186
 * @return 0 on success, < 0 on error.
187
 */
188
int ssh_send_banner(ssh_session session, int server)
189
6.42k
{
190
6.42k
    const char *banner = CLIENT_BANNER_SSH2;
191
6.42k
    const char *terminator = "\r\n";
192
    /* The maximum banner length is 255 for SSH2 */
193
6.42k
    char buffer[256] = {0};
194
6.42k
    size_t len;
195
6.42k
    int rc = SSH_ERROR;
196
197
6.42k
    if (server == 1) {
198
6.42k
        if (session->server_opts.custombanner == NULL) {
199
6.42k
            session->serverbanner = strdup(banner);
200
6.42k
            if (session->serverbanner == NULL) {
201
0
                goto end;
202
0
            }
203
6.42k
        } else {
204
0
            len = strlen(session->server_opts.custombanner);
205
0
            session->serverbanner = malloc(len + 8 + 1);
206
0
            if(session->serverbanner == NULL) {
207
0
                goto end;
208
0
            }
209
0
            snprintf(session->serverbanner,
210
0
                     len + 8 + 1,
211
0
                     "SSH-2.0-%s",
212
0
                     session->server_opts.custombanner);
213
0
        }
214
215
6.42k
        snprintf(buffer,
216
6.42k
                 sizeof(buffer),
217
6.42k
                 "%s%s",
218
6.42k
                 session->serverbanner,
219
6.42k
                 terminator);
220
6.42k
    } else {
221
0
        session->clientbanner = strdup(banner);
222
0
        if (session->clientbanner == NULL) {
223
0
            goto end;
224
0
        }
225
226
0
        snprintf(buffer,
227
0
                 sizeof(buffer),
228
0
                 "%s%s",
229
0
                 session->clientbanner,
230
0
                 terminator);
231
0
    }
232
233
6.42k
    rc = ssh_socket_write(session->socket, buffer, (uint32_t)strlen(buffer));
234
6.42k
    if (rc == SSH_ERROR) {
235
0
        goto end;
236
0
    }
237
6.42k
#ifdef WITH_PCAP
238
6.42k
    if (session->pcap_ctx != NULL) {
239
0
        ssh_pcap_context_write(session->pcap_ctx,
240
0
                               SSH_PCAP_DIR_OUT,
241
0
                               buffer,
242
0
                               (uint32_t)strlen(buffer),
243
0
                               (uint32_t)strlen(buffer));
244
0
    }
245
6.42k
#endif
246
247
6.42k
    rc = SSH_OK;
248
6.42k
end:
249
6.42k
    return rc;
250
6.42k
}
251
252
/** @internal
253
 * @brief launches the DH handshake state machine
254
 * @param session session handle
255
 * @returns SSH_OK or SSH_ERROR
256
 * @warning this function returning is no proof that DH handshake is
257
 * completed
258
 */
259
int dh_handshake(ssh_session session)
260
0
{
261
0
    int rc = SSH_AGAIN;
262
263
0
    SSH_LOG(SSH_LOG_TRACE,
264
0
            "dh_handshake_state = %d, kex_type = %d",
265
0
            session->dh_handshake_state,
266
0
            session->next_crypto->kex_type);
267
268
0
    switch (session->dh_handshake_state) {
269
0
    case DH_STATE_INIT:
270
0
        switch (session->next_crypto->kex_type) {
271
#ifdef WITH_GSSAPI
272
        case SSH_GSS_KEX_DH_GROUP14_SHA256:
273
        case SSH_GSS_KEX_DH_GROUP16_SHA512:
274
        case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
275
        case SSH_GSS_KEX_CURVE25519_SHA256:
276
            rc = ssh_client_gss_kex_init(session);
277
            break;
278
#endif
279
0
        case SSH_KEX_DH_GROUP1_SHA1:
280
0
        case SSH_KEX_DH_GROUP14_SHA1:
281
0
        case SSH_KEX_DH_GROUP14_SHA256:
282
0
        case SSH_KEX_DH_GROUP16_SHA512:
283
0
        case SSH_KEX_DH_GROUP18_SHA512:
284
0
            rc = ssh_client_dh_init(session);
285
0
            break;
286
0
#ifdef WITH_GEX
287
0
        case SSH_KEX_DH_GEX_SHA1:
288
0
        case SSH_KEX_DH_GEX_SHA256:
289
0
            rc = ssh_client_dhgex_init(session);
290
0
            break;
291
0
#endif /* WITH_GEX */
292
0
#ifdef HAVE_ECDH
293
0
        case SSH_KEX_ECDH_SHA2_NISTP256:
294
0
        case SSH_KEX_ECDH_SHA2_NISTP384:
295
0
        case SSH_KEX_ECDH_SHA2_NISTP521:
296
0
            rc = ssh_client_ecdh_init(session);
297
0
            break;
298
0
#endif
299
0
#ifdef HAVE_CURVE25519
300
0
        case SSH_KEX_CURVE25519_SHA256:
301
0
        case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
302
0
            rc = ssh_client_curve25519_init(session);
303
0
            break;
304
0
#endif
305
0
#ifdef HAVE_SNTRUP761
306
0
        case SSH_KEX_SNTRUP761X25519_SHA512:
307
0
        case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
308
0
            rc = ssh_client_sntrup761x25519_init(session);
309
0
            break;
310
0
#endif
311
#ifdef HAVE_MLKEM
312
        case SSH_KEX_MLKEM768X25519_SHA256:
313
        case SSH_KEX_MLKEM768NISTP256_SHA256:
314
        case SSH_KEX_MLKEM1024NISTP384_SHA384:
315
            rc = ssh_client_hybrid_mlkem_init(session);
316
            break;
317
#endif
318
0
        default:
319
0
            rc = SSH_ERROR;
320
0
        }
321
322
0
        break;
323
0
    case DH_STATE_INIT_SENT:
324
      /* wait until ssh_packet_dh_reply is called */
325
0
      break;
326
0
    case DH_STATE_NEWKEYS_SENT:
327
      /* wait until ssh_packet_newkeys is called */
328
0
      break;
329
0
    case DH_STATE_FINISHED:
330
0
        return SSH_OK;
331
0
    default:
332
0
        ssh_set_error(session,
333
0
                      SSH_FATAL,
334
0
                      "Invalid state in dh_handshake(): %d",
335
0
                      session->dh_handshake_state);
336
337
0
        return SSH_ERROR;
338
0
    }
339
340
0
    return rc;
341
0
}
342
343
static int ssh_service_request_termination(void *s)
344
0
{
345
0
    ssh_session session = (ssh_session)s;
346
347
0
    if (session->session_state == SSH_SESSION_STATE_ERROR ||
348
0
        session->auth.service_state != SSH_AUTH_SERVICE_SENT)
349
0
        return 1;
350
0
    else
351
0
        return 0;
352
0
}
353
354
/**
355
 * @addtogroup libssh_session
356
 *
357
 * @{
358
 */
359
360
/**
361
 * @internal
362
 * @brief Request a service from the SSH server.
363
 *
364
 * Service requests are for example: ssh-userauth, ssh-connection, etc.
365
 *
366
 * @param  session      The session to use to ask for a service request.
367
 * @param  service      The service request.
368
 *
369
 * @return SSH_OK on success
370
 * @return SSH_ERROR on error
371
 * @return SSH_AGAIN No response received yet
372
 * @bug actually only works with ssh-userauth
373
 */
374
int ssh_service_request(ssh_session session, const char *service)
375
0
{
376
0
  int rc = SSH_ERROR;
377
378
0
  if(session->auth.service_state != SSH_AUTH_SERVICE_NONE)
379
0
    goto pending;
380
381
0
  rc = ssh_buffer_pack(session->out_buffer,
382
0
                       "bs",
383
0
                       SSH2_MSG_SERVICE_REQUEST,
384
0
                       service);
385
0
  if (rc != SSH_OK){
386
0
      ssh_set_error_oom(session);
387
0
      return SSH_ERROR;
388
0
  }
389
0
  session->auth.service_state = SSH_AUTH_SERVICE_SENT;
390
0
  if (ssh_packet_send(session) == SSH_ERROR) {
391
0
    ssh_set_error(session, SSH_FATAL,
392
0
        "Sending SSH2_MSG_SERVICE_REQUEST failed.");
393
0
      return SSH_ERROR;
394
0
  }
395
396
0
  SSH_LOG(SSH_LOG_PACKET,
397
0
      "Sent SSH_MSG_SERVICE_REQUEST (service %s)", service);
398
0
pending:
399
0
  rc=ssh_handle_packets_termination(session,SSH_TIMEOUT_USER,
400
0
      ssh_service_request_termination, session);
401
0
  if (rc == SSH_ERROR) {
402
0
      return SSH_ERROR;
403
0
  }
404
0
  switch(session->auth.service_state) {
405
0
  case SSH_AUTH_SERVICE_DENIED:
406
0
    ssh_set_error(session,SSH_FATAL,"ssh_auth_service request denied");
407
0
    break;
408
0
  case SSH_AUTH_SERVICE_ACCEPTED:
409
0
    rc=SSH_OK;
410
0
    break;
411
0
  case SSH_AUTH_SERVICE_SENT:
412
0
    rc=SSH_AGAIN;
413
0
    break;
414
0
  case SSH_AUTH_SERVICE_NONE:
415
0
    rc=SSH_ERROR;
416
0
    break;
417
0
  }
418
419
0
  return rc;
420
0
}
421
422
/**
423
 * @internal
424
 *
425
 * @brief A function to be called each time a step has been done in the
426
 * connection.
427
 */
428
static void ssh_client_connection_callback(ssh_session session)
429
0
{
430
0
    int rc;
431
432
0
    SSH_LOG(SSH_LOG_DEBUG, "session_state=%d", session->session_state);
433
434
0
    switch (session->session_state) {
435
0
    case SSH_SESSION_STATE_NONE:
436
0
    case SSH_SESSION_STATE_CONNECTING:
437
0
        break;
438
0
    case SSH_SESSION_STATE_SOCKET_CONNECTED:
439
0
        ssh_set_fd_towrite(session);
440
0
        ssh_send_banner(session, 0);
441
442
0
        break;
443
0
    case SSH_SESSION_STATE_BANNER_RECEIVED:
444
0
        if (session->serverbanner == NULL) {
445
0
            goto error;
446
0
        }
447
0
        set_status(session, 0.4f);
448
0
        SSH_LOG(SSH_LOG_DEBUG, "SSH server banner: %s", session->serverbanner);
449
450
        /* Here we analyze the different protocols the server allows. */
451
0
        rc = ssh_analyze_banner(session, 0);
452
0
        if (rc < 0) {
453
0
            ssh_set_error(session, SSH_FATAL,
454
0
                          "No version of SSH protocol usable (banner: %s)",
455
0
                          session->serverbanner);
456
0
            goto error;
457
0
        }
458
459
0
        ssh_packet_register_socket_callback(session, session->socket);
460
461
0
        ssh_packet_set_default_callbacks(session);
462
0
        session->session_state = SSH_SESSION_STATE_INITIAL_KEX;
463
0
        rc = ssh_set_client_kex(session);
464
0
        if (rc != SSH_OK) {
465
0
            goto error;
466
0
        }
467
0
        rc = ssh_send_kex(session);
468
0
        if (rc < 0) {
469
0
            goto error;
470
0
        }
471
0
        set_status(session, 0.5f);
472
473
0
        break;
474
0
    case SSH_SESSION_STATE_INITIAL_KEX:
475
        /* TODO: This state should disappear in favor of get_key handle */
476
0
        break;
477
0
    case SSH_SESSION_STATE_KEXINIT_RECEIVED:
478
0
        set_status(session, 0.6f);
479
0
        ssh_list_kex(&session->next_crypto->server_kex);
480
0
        if ((session->flags & SSH_SESSION_FLAG_KEXINIT_SENT) == 0) {
481
            /* in rekeying state if next_crypto client_kex might be empty */
482
0
            rc = ssh_set_client_kex(session);
483
0
            if (rc != SSH_OK) {
484
0
                goto error;
485
0
            }
486
0
            rc = ssh_send_kex(session);
487
0
            if (rc < 0) {
488
0
                goto error;
489
0
            }
490
0
        }
491
0
        if (ssh_kex_select_methods(session) == SSH_ERROR)
492
0
            goto error;
493
0
        set_status(session, 0.8f);
494
0
        session->session_state = SSH_SESSION_STATE_DH;
495
496
        /* If the init packet was already sent in previous step, this will be no
497
         * operation */
498
0
        if (dh_handshake(session) == SSH_ERROR) {
499
0
            goto error;
500
0
        }
501
0
        FALL_THROUGH;
502
0
    case SSH_SESSION_STATE_DH:
503
0
        if (session->dh_handshake_state == DH_STATE_FINISHED) {
504
0
            set_status(session, 1.0f);
505
0
            session->connected = 1;
506
0
            if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) {
507
0
                session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
508
0
            } else {
509
0
                session->session_state = SSH_SESSION_STATE_AUTHENTICATING;
510
0
            }
511
0
        }
512
0
        break;
513
0
    case SSH_SESSION_STATE_AUTHENTICATING:
514
0
        break;
515
0
    case SSH_SESSION_STATE_ERROR:
516
0
        goto error;
517
0
    default:
518
0
        ssh_set_error(session, SSH_FATAL, "Invalid state %d",
519
0
                      session->session_state);
520
0
    }
521
522
0
    return;
523
0
error:
524
0
    ssh_session_socket_close(session);
525
0
    SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(session));
526
0
}
527
528
/** @internal
529
 * @brief describe under which conditions the ssh_connect function may stop
530
 */
531
static int ssh_connect_termination(void *user)
532
0
{
533
0
    ssh_session session = (ssh_session)user;
534
535
0
    switch (session->session_state) {
536
0
    case SSH_SESSION_STATE_ERROR:
537
0
    case SSH_SESSION_STATE_AUTHENTICATING:
538
0
    case SSH_SESSION_STATE_DISCONNECTED:
539
0
        return 1;
540
0
    default:
541
0
        return 0;
542
0
    }
543
0
}
544
545
/**
546
 * @brief Connect to the ssh server.
547
 *
548
 * @param[in]  session  The ssh session to connect.
549
 *
550
 * @returns             SSH_OK on success, SSH_ERROR on error.
551
 * @returns             SSH_AGAIN, if the session is in nonblocking mode,
552
 *                      and call must be done again.
553
 *
554
 * @see ssh_new()
555
 * @see ssh_disconnect()
556
 */
557
int ssh_connect(ssh_session session)
558
0
{
559
0
    int ret;
560
561
0
    if (!is_ssh_initialized()) {
562
0
        ssh_set_error(session, SSH_FATAL,
563
0
                      "Library not initialized.");
564
565
0
        return SSH_ERROR;
566
0
    }
567
568
0
    if (session == NULL) {
569
0
        return SSH_ERROR;
570
0
    }
571
572
0
    switch(session->pending_call_state) {
573
0
    case SSH_PENDING_CALL_NONE:
574
0
        break;
575
0
    case SSH_PENDING_CALL_CONNECT:
576
0
        goto pending;
577
0
    default:
578
0
        ssh_set_error(session, SSH_FATAL,
579
0
                      "Bad call during pending SSH call in ssh_connect");
580
581
0
        return SSH_ERROR;
582
0
    }
583
0
    session->alive = 0;
584
0
    session->client = 1;
585
586
0
    if (session->opts.fd == SSH_INVALID_SOCKET &&
587
0
        session->opts.host == NULL &&
588
0
        session->opts.ProxyCommand == NULL)
589
0
    {
590
0
        ssh_set_error(session, SSH_FATAL, "Hostname required");
591
0
        return SSH_ERROR;
592
0
    }
593
594
    /* If the system configuration files were not yet processed, do it now */
595
0
    if (!session->opts.config_processed) {
596
0
        ret = ssh_options_parse_config(session, NULL);
597
0
        if (ret != 0) {
598
0
            ssh_set_error(session, SSH_FATAL,
599
0
                          "Failed to process system configuration files");
600
0
            return SSH_ERROR;
601
0
        }
602
0
    }
603
604
0
    ret = ssh_options_apply(session);
605
0
    if (ret < 0) {
606
0
        ssh_set_error(session, SSH_FATAL, "Couldn't apply options");
607
0
        return SSH_ERROR;
608
0
    }
609
610
0
    SSH_LOG(SSH_LOG_DEBUG,
611
0
            "libssh %s, using threading %s",
612
0
            ssh_copyright(),
613
0
            ssh_threads_get_type());
614
615
0
    session->ssh_connection_callback = ssh_client_connection_callback;
616
0
    session->session_state = SSH_SESSION_STATE_CONNECTING;
617
0
    ssh_socket_set_callbacks(session->socket, &session->socket_callbacks);
618
0
    session->socket_callbacks.connected = socket_callback_connected;
619
0
    session->socket_callbacks.data = callback_receive_banner;
620
0
    session->socket_callbacks.exception = ssh_socket_exception_callback;
621
0
    session->socket_callbacks.userdata = session;
622
623
0
    if (session->opts.fd != SSH_INVALID_SOCKET) {
624
0
        session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
625
0
        ret = ssh_socket_set_fd(session->socket, session->opts.fd);
626
0
#ifndef _WIN32
627
0
#ifdef HAVE_PTHREAD
628
0
    } else if (ssh_libssh_proxy_jumps() &&
629
0
               ssh_list_count(session->opts.proxy_jumps) != 0) {
630
0
        ret = ssh_socket_connect_proxyjump(session->socket);
631
0
#endif /* HAVE_PTHREAD */
632
0
#endif /* _WIN32 */
633
0
    } else if (session->opts.ProxyCommand != NULL) {
634
#ifdef WITH_EXEC
635
        ret = ssh_socket_connect_proxycommand(session->socket,
636
                session->opts.ProxyCommand);
637
#else
638
0
        ssh_set_error(session,
639
0
                      SSH_FATAL,
640
0
                      "The libssh is built without support for proxy commands.");
641
0
        ret = SSH_ERROR;
642
0
#endif /* WITH_EXEC */
643
0
    } else {
644
0
        ret = ssh_socket_connect(session->socket,
645
0
                                 session->opts.host,
646
0
                                 session->opts.port > 0 ? session->opts.port : 22,
647
0
                                 session->opts.bindaddr);
648
0
    }
649
0
    if (ret == SSH_ERROR) {
650
0
        return SSH_ERROR;
651
0
    }
652
653
0
    set_status(session, 0.2f);
654
655
0
    session->alive = 1;
656
0
    SSH_LOG(SSH_LOG_DEBUG,
657
0
            "Socket connecting, now waiting for the callbacks to work");
658
659
0
pending:
660
0
    session->pending_call_state = SSH_PENDING_CALL_CONNECT;
661
0
    if(ssh_is_blocking(session)) {
662
0
        int timeout = (session->opts.timeout * 1000) +
663
0
            (session->opts.timeout_usec / 1000);
664
0
        if (timeout == 0) {
665
0
            timeout = 10 * 1000;
666
0
        }
667
0
        SSH_LOG(SSH_LOG_PACKET, "Actual timeout : %d", timeout);
668
0
        ret = ssh_handle_packets_termination(session, timeout,
669
0
                                             ssh_connect_termination, session);
670
0
        if (session->session_state != SSH_SESSION_STATE_ERROR &&
671
0
            (ret == SSH_ERROR || !ssh_connect_termination(session)))
672
0
        {
673
0
            ssh_set_error(session, SSH_FATAL,
674
0
                          "Timeout connecting to %s", session->opts.host);
675
0
            session->session_state = SSH_SESSION_STATE_ERROR;
676
0
        }
677
0
    } else {
678
0
        ret = ssh_handle_packets_termination(session,
679
0
                                             SSH_TIMEOUT_NONBLOCKING,
680
0
                                             ssh_connect_termination,
681
0
                                             session);
682
0
        if (ret == SSH_ERROR) {
683
0
            session->session_state = SSH_SESSION_STATE_ERROR;
684
0
        }
685
0
    }
686
687
0
    SSH_LOG(SSH_LOG_PACKET, "current state : %d", session->session_state);
688
0
    if (!ssh_is_blocking(session) && !ssh_connect_termination(session)) {
689
0
        return SSH_AGAIN;
690
0
    }
691
692
0
    session->pending_call_state = SSH_PENDING_CALL_NONE;
693
0
    if (session->session_state == SSH_SESSION_STATE_ERROR ||
694
0
        session->session_state == SSH_SESSION_STATE_DISCONNECTED)
695
0
    {
696
0
        return SSH_ERROR;
697
0
    }
698
699
0
    return SSH_OK;
700
0
}
701
702
/**
703
 * @brief Get the issue banner from the server.
704
 *
705
 * This is the banner showing a disclaimer to users who log in,
706
 * typically their right or the fact that they will be monitored.
707
 *
708
 * @param[in]  session  The SSH session to use.
709
 *
710
 * @return A newly allocated string with the banner, NULL on error.
711
 */
712
char *ssh_get_issue_banner(ssh_session session)
713
0
{
714
0
    if (session == NULL || session->banner == NULL) {
715
0
        return NULL;
716
0
    }
717
718
0
    return ssh_string_to_char(session->banner);
719
0
}
720
721
/**
722
 * @brief Get the version of the OpenSSH server, if it is not an OpenSSH server
723
 * then 0 will be returned.
724
 *
725
 * You can use the SSH_VERSION_INT macro to compare version numbers.
726
 *
727
 * @param[in]  session  The SSH session to use.
728
 *
729
 * @return The version number if available, 0 otherwise.
730
 *
731
 * @code
732
 * int openssh = ssh_get_openssh_version();
733
 *
734
 * if (openssh == SSH_INT_VERSION(6, 1, 0)) {
735
 *     printf("Version match!\m");
736
 * }
737
 * @endcode
738
 */
739
int ssh_get_openssh_version(ssh_session session)
740
0
{
741
0
    if (session == NULL) {
742
0
        return 0;
743
0
    }
744
745
0
    return session->openssh;
746
0
}
747
748
/**
749
 * @brief Most SSH connections will only ever request a single session, but an
750
 * attacker may abuse a running ssh client to surreptitiously open
751
 * additional sessions under their control. OpenSSH provides a global
752
 * request "no-more-sessions@openssh.com" to mitigate this attack.
753
 *
754
 * @param[in]  session  The SSH session to use.
755
 *
756
 * @returns             SSH_OK on success, SSH_ERROR on error.
757
 * @returns             SSH_AGAIN, if the session is in nonblocking mode,
758
 *                      and call must be done again.
759
 */
760
int ssh_request_no_more_sessions(ssh_session session)
761
0
{
762
0
    if (session == NULL) {
763
0
        return SSH_ERROR;
764
0
    }
765
766
0
    return ssh_global_request(session, "no-more-sessions@openssh.com", NULL, 1);
767
0
}
768
769
/**
770
 * @brief Add disconnect message when ssh_session is disconnected
771
 * To add a disconnect message to give peer a better hint.
772
 * @param  session      The SSH session to use.
773
 * @param  message      The message to send after the session is disconnected.
774
 *                      If no message is passed then a default message i.e
775
 *                      "Bye Bye" will be sent.
776
 */
777
int
778
ssh_session_set_disconnect_message(ssh_session session, const char *message)
779
1
{
780
1
    if (session == NULL) {
781
0
        return SSH_ERROR;
782
0
    }
783
784
1
    if (message == NULL || strlen(message) == 0) {
785
0
        SAFE_FREE(session->disconnect_message);  //To free any message set earlier.
786
0
        session->disconnect_message = strdup("Bye Bye") ;
787
0
        if (session->disconnect_message == NULL) {
788
0
            ssh_set_error_oom(session);
789
0
            return SSH_ERROR;
790
0
        }
791
0
        return SSH_OK;
792
0
    }
793
1
    SAFE_FREE(session->disconnect_message);  //To free any message set earlier.
794
1
    session->disconnect_message = strdup(message);
795
1
    if (session->disconnect_message == NULL) {
796
0
        ssh_set_error_oom(session);
797
0
        return SSH_ERROR;
798
0
    }
799
1
    return SSH_OK;
800
1
}
801
802
/**
803
 * @brief Disconnect from a session (client or server).
804
 *
805
 * The session can then be reused to open a new session.
806
 *
807
 * @note Note that this function won't close the socket if it was set with
808
 * ssh_options_set and SSH_OPTIONS_FD. You're responsible for closing the
809
 * socket. This is new behavior in libssh 0.10.
810
 *
811
 * @param[in]  session  The SSH session to use.
812
 */
813
void
814
ssh_disconnect(ssh_session session)
815
6.87k
{
816
6.87k
    struct ssh_iterator *it = NULL;
817
6.87k
    int rc;
818
819
6.87k
    if (session == NULL) {
820
0
        return;
821
0
    }
822
823
6.87k
#ifndef _WIN32
824
6.87k
#ifdef HAVE_PTHREAD
825
    /* Only send the disconnect to all other threads when the root session calls
826
     * ssh_disconnect() */
827
6.87k
    if (session->proxy_root) {
828
6.87k
        proxy_disconnect = 1;
829
6.87k
    }
830
6.87k
#endif /* HAVE_PTHREAD */
831
6.87k
#endif /* _WIN32 */
832
833
6.87k
    if (session->disconnect_message == NULL) {
834
6.87k
        session->disconnect_message = strdup("Bye Bye") ;
835
6.87k
        if (session->disconnect_message == NULL) {
836
0
            ssh_set_error_oom(session);
837
0
            goto error;
838
0
        }
839
6.87k
    }
840
841
6.87k
    if (session->socket != NULL && ssh_socket_is_open(session->socket)) {
842
1.60k
        rc = ssh_buffer_pack(session->out_buffer,
843
1.60k
                             "bdss",
844
1.60k
                             SSH2_MSG_DISCONNECT,
845
1.60k
                             SSH2_DISCONNECT_BY_APPLICATION,
846
1.60k
                             session->disconnect_message,
847
1.60k
                             ""); /* language tag */
848
1.60k
        if (rc != SSH_OK) {
849
0
            ssh_set_error_oom(session);
850
0
            goto error;
851
0
        }
852
853
1.60k
        ssh_packet_send(session);
854
1.60k
        ssh_session_socket_close(session);
855
1.60k
    }
856
857
6.87k
error:
858
6.87k
    session->recv_seq = 0;
859
6.87k
    session->send_seq = 0;
860
6.87k
    session->alive = 0;
861
6.87k
    if (session->socket != NULL){
862
6.87k
        ssh_socket_reset(session->socket);
863
6.87k
    }
864
6.87k
    session->opts.fd = SSH_INVALID_SOCKET;
865
6.87k
    session->session_state = SSH_SESSION_STATE_DISCONNECTED;
866
6.87k
    session->pending_call_state = SSH_PENDING_CALL_NONE;
867
6.87k
    session->packet_state = PACKET_STATE_INIT;
868
869
8.14k
    while ((it = ssh_list_get_iterator(session->channels)) != NULL) {
870
1.27k
        ssh_channel_do_free(ssh_iterator_value(ssh_channel, it));
871
1.27k
        ssh_list_remove(session->channels, it);
872
1.27k
    }
873
6.87k
    if (session->current_crypto) {
874
5.06k
      crypto_free(session->current_crypto);
875
5.06k
      session->current_crypto = NULL;
876
5.06k
    }
877
6.87k
    if (session->next_crypto) {
878
6.87k
        crypto_free(session->next_crypto);
879
6.87k
        session->next_crypto = crypto_new();
880
6.87k
        if (session->next_crypto == NULL) {
881
0
            ssh_set_error_oom(session);
882
0
        }
883
6.87k
    }
884
6.87k
    if (session->in_buffer) {
885
6.87k
        ssh_buffer_reinit(session->in_buffer);
886
6.87k
    }
887
6.87k
    if (session->out_buffer) {
888
6.87k
        ssh_buffer_reinit(session->out_buffer);
889
6.87k
    }
890
6.87k
    if (session->in_hashbuf) {
891
794
        ssh_buffer_reinit(session->in_hashbuf);
892
794
    }
893
6.87k
    if (session->out_hashbuf) {
894
1.24k
        ssh_buffer_reinit(session->out_hashbuf);
895
1.24k
    }
896
6.87k
    session->auth.supported_methods = 0;
897
6.87k
    SAFE_FREE(session->serverbanner);
898
6.87k
    SAFE_FREE(session->clientbanner);
899
6.87k
    SAFE_FREE(session->disconnect_message);
900
901
6.87k
    if (session->ssh_message_list) {
902
0
        ssh_message msg = NULL;
903
904
0
        while ((msg = ssh_list_pop_head(ssh_message,
905
0
                                        session->ssh_message_list)) != NULL) {
906
0
              ssh_message_free(msg);
907
0
        }
908
0
        ssh_list_free(session->ssh_message_list);
909
0
        session->ssh_message_list = NULL;
910
0
    }
911
912
6.87k
    if (session->packet_callbacks) {
913
6.38k
        ssh_list_free(session->packet_callbacks);
914
6.38k
        session->packet_callbacks = NULL;
915
6.38k
    }
916
6.87k
}
917
918
/**
919
 * @brief Copyright information
920
 *
921
 * Returns copyright information
922
 *
923
 * @returns SSH_STRING copyright
924
 */
925
const char *ssh_copyright(void)
926
0
{
927
0
    return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2026 "
928
0
           "Aris Adamantiadis, Andreas Schneider "
929
0
           "and libssh contributors. "
930
0
           "Distributed under the LGPL, please refer to COPYING "
931
0
           "file for information about your rights";
932
0
}
933
/** @} */