Coverage Report

Created: 2025-12-21 06:55

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