Coverage Report

Created: 2023-03-26 07:12

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