Coverage Report

Created: 2026-07-05 06:56

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