2
* client.c - SSH client functions
4
* This file is part of the SSH Library
6
* Copyright (c) 2003-2008 by Aris Adamantiadis
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.
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.
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,
29
#include <arpa/inet.h>
32
#include "libssh/priv.h"
33
#include "libssh/ssh2.h"
34
#include "libssh/buffer.h"
35
#include "libssh/packet.h"
36
#include "libssh/socket.h"
37
#include "libssh/session.h"
38
#include "libssh/dh.h"
40
#define set_status(session, status) do {\
41
if (session->callbacks && session->callbacks->connect_status_function) \
42
session->callbacks->connect_status_function(session->callbacks->userdata, status); \
48
* @brief Get a banner from a socket.
50
* The caller has to free memroy.
52
* @param session The session to get the banner from.
54
* @return A newly allocated string with the banner or NULL on error.
56
char *ssh_get_banner(ssh_session session) {
57
char buffer[128] = {0};
63
for (i = 0; i < 127; i++) {
64
if (ssh_socket_read(session->socket, &buffer[i], 1) != SSH_OK) {
65
ssh_set_error(session, SSH_FATAL, "Remote host closed connection");
70
if(session->pcap_ctx && buffer[i] == '\n'){
71
ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_IN,buffer,i+1,i+1);
74
if (buffer[i] == '\r') {
77
if (buffer[i] == '\n') {
89
ssh_set_error(session, SSH_FATAL, "Too large banner");
98
* @brief Analyze the SSH banner to find out if we have a SSHv1 or SSHv2
101
* @param session The session to analyze the banner from.
102
* @param ssh1 The variable which is set if it is a SSHv1 server.
103
* @param ssh2 The variable which is set if it is a SSHv2 server.
105
* @return 0 on success, < 0 on error.
107
* @see ssh_get_banner()
109
static int ssh_analyze_banner(ssh_session session, int *ssh1, int *ssh2) {
110
const char *banner = session->serverbanner;
113
if (banner == NULL) {
114
ssh_set_error(session, SSH_FATAL, "Invalid banner");
119
* Typical banners e.g. are:
121
* SSH-1.5-openSSH_5.4
122
* SSH-1.99-openSSH_3.0
125
* 012345678901234567890
127
if (strlen(banner) < 6 ||
128
strncmp(banner, "SSH-", 4) != 0) {
129
ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner);
133
ssh_log(session, SSH_LOG_RARE, "Analyzing banner: %s", banner);
138
if (strlen(banner) > 6) {
139
if (banner[6] == '9') {
151
ssh_set_error(session, SSH_FATAL, "Protocol mismatch: %s", banner);
155
openssh = strstr(banner, "OpenSSH");
156
if (openssh != NULL) {
160
* The banner is typical:
162
* 012345678901234567890
164
if (strlen(openssh) > 9) {
165
major = strtol(openssh + 8, (char **) NULL, 10);
166
minor = strtol(openssh + 10, (char **) NULL, 10);
167
session->openssh = SSH_VERSION_INT(major, minor, 0);
168
ssh_log(session, SSH_LOG_RARE,
169
"We are talking to an OpenSSH client version: %d.%d (%x)",
170
major, minor, session->openssh);
178
* @brief Sends a SSH banner to the server.
180
* @param session The SSH session to use.
182
* @param server Send client or server banner.
184
* @return 0 on success, < 0 on error.
186
int ssh_send_banner(ssh_session session, int server) {
187
const char *banner = NULL;
188
char buffer[128] = {0};
192
banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2;
194
if (session->xbanner) {
195
banner = session->xbanner;
199
session->serverbanner = strdup(banner);
200
if (session->serverbanner == NULL) {
205
session->clientbanner = strdup(banner);
206
if (session->clientbanner == NULL) {
212
snprintf(buffer, 128, "%s\r\n", banner);
214
if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERROR) {
219
if (ssh_socket_blocking_flush(session->socket) != SSH_OK) {
224
if(session->pcap_ctx)
225
ssh_pcap_context_write(session->pcap_ctx,SSH_PCAP_DIR_OUT,buffer,strlen(buffer),strlen(buffer));
231
#define DH_STATE_INIT 0
232
#define DH_STATE_INIT_TO_SEND 1
233
#define DH_STATE_INIT_SENT 2
234
#define DH_STATE_NEWKEYS_TO_SEND 3
235
#define DH_STATE_NEWKEYS_SENT 4
236
#define DH_STATE_FINISHED 5
237
static int dh_handshake(ssh_session session) {
240
ssh_string pubkey = NULL;
241
ssh_string signature = NULL;
246
switch (session->dh_handshake_state) {
248
if (buffer_add_u8(session->out_buffer, SSH2_MSG_KEXDH_INIT) < 0) {
252
if (dh_generate_x(session) < 0) {
255
if (dh_generate_e(session) < 0) {
259
e = dh_get_e(session);
264
if (buffer_add_ssh_string(session->out_buffer, e) < 0) {
271
rc = packet_send(session);
272
if (rc == SSH_ERROR) {
276
session->dh_handshake_state = DH_STATE_INIT_TO_SEND;
277
case DH_STATE_INIT_TO_SEND:
278
rc = packet_flush(session, 0);
282
session->dh_handshake_state = DH_STATE_INIT_SENT;
283
case DH_STATE_INIT_SENT:
284
rc = packet_wait(session, SSH2_MSG_KEXDH_REPLY, 1);
289
pubkey = buffer_get_ssh_string(session->in_buffer);
291
ssh_set_error(session,SSH_FATAL, "No public key in packet");
295
dh_import_pubkey(session, pubkey);
297
f = buffer_get_ssh_string(session->in_buffer);
299
ssh_set_error(session,SSH_FATAL, "No F number in packet");
303
if (dh_import_f(session, f) < 0) {
304
ssh_set_error(session, SSH_FATAL, "Cannot import f number");
311
signature = buffer_get_ssh_string(session->in_buffer);
312
if (signature == NULL) {
313
ssh_set_error(session, SSH_FATAL, "No signature in packet");
317
session->dh_server_signature = signature;
318
signature=NULL; /* ownership changed */
319
if (dh_build_k(session) < 0) {
320
ssh_set_error(session, SSH_FATAL, "Cannot build k number");
325
/* Send the MSG_NEWKEYS */
326
if (buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) {
331
rc = packet_send(session);
332
if (rc == SSH_ERROR) {
336
session->dh_handshake_state = DH_STATE_NEWKEYS_TO_SEND;
337
case DH_STATE_NEWKEYS_TO_SEND:
338
rc = packet_flush(session, 0);
342
ssh_log(session, SSH_LOG_RARE, "SSH_MSG_NEWKEYS sent\n");
344
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
345
case DH_STATE_NEWKEYS_SENT:
346
rc = packet_wait(session, SSH2_MSG_NEWKEYS, 1);
350
ssh_log(session, SSH_LOG_RARE, "Got SSH_MSG_NEWKEYS\n");
352
rc = make_sessionid(session);
358
* Set the cryptographic functions for the next crypto
359
* (it is needed for generate_session_keys for key lenghts)
361
if (crypt_set_algorithms(session)) {
366
if (generate_session_keys(session) < 0) {
371
/* Verify the host's signature. FIXME do it sooner */
372
signature = session->dh_server_signature;
373
session->dh_server_signature = NULL;
374
if (signature_verify(session, signature)) {
379
/* forget it for now ... */
380
string_burn(signature);
381
string_free(signature);
384
* Once we got SSH2_MSG_NEWKEYS we can switch next_crypto and
387
if (session->current_crypto) {
388
crypto_free(session->current_crypto);
389
session->current_crypto=NULL;
392
/* FIXME later, include a function to change keys */
393
session->current_crypto = session->next_crypto;
395
session->next_crypto = crypto_new();
396
if (session->next_crypto == NULL) {
401
session->dh_handshake_state = DH_STATE_FINISHED;
406
ssh_set_error(session, SSH_FATAL, "Invalid state in dh_handshake(): %d",
407
session->dh_handshake_state);
423
if(signature != NULL){
424
string_burn(signature);
425
string_free(signature);
435
* @brief Request a service from the SSH server.
437
* Service requests are for example: ssh-userauth, ssh-connection, etc.
439
* @param session The session to use to ask for a service request.
440
* @param service The service request.
442
* @return 0 on success, < 0 on error.
444
int ssh_service_request(ssh_session session, const char *service) {
445
ssh_string service_s = NULL;
449
if (buffer_add_u8(session->out_buffer, SSH2_MSG_SERVICE_REQUEST) < 0) {
454
service_s = string_from_char(service);
455
if (service_s == NULL) {
460
if (buffer_add_ssh_string(session->out_buffer,service_s) < 0) {
461
string_free(service_s);
465
string_free(service_s);
467
if (packet_send(session) != SSH_OK) {
468
ssh_set_error(session, SSH_FATAL,
469
"Sending SSH2_MSG_SERVICE_REQUEST failed.");
474
ssh_log(session, SSH_LOG_PACKET,
475
"Sent SSH_MSG_SERVICE_REQUEST (service %s)", service);
477
if (packet_wait(session,SSH2_MSG_SERVICE_ACCEPT,1) != SSH_OK) {
478
ssh_set_error(session, SSH_FATAL, "Did not receive SERVICE_ACCEPT");
483
ssh_log(session, SSH_LOG_PACKET,
484
"Received SSH_MSG_SERVICE_ACCEPT (service %s)", service);
490
/** \addtogroup ssh_session
494
/** \brief connect to the ssh server
495
* \param session ssh session
496
* \return SSH_OK on success, SSH_ERROR on error
498
* \see ssh_disconnect()
500
int ssh_connect(ssh_session session) {
503
socket_t fd = SSH_INVALID_SOCKET;
506
if (session == NULL) {
507
ssh_set_error(session, SSH_FATAL, "Invalid session pointer");
516
if (ssh_init() < 0) {
520
if (session->fd == SSH_INVALID_SOCKET && session->host == NULL &&
521
session->ProxyCommand == NULL) {
522
ssh_set_error(session, SSH_FATAL, "Hostname required");
527
ret = ssh_options_apply(session);
529
ssh_set_error(session, SSH_FATAL, "Couldn't apply options");
534
if (session->fd != SSH_INVALID_SOCKET) {
537
} else if (session->ProxyCommand != NULL) {
538
fd=ssh_socket_connect_proxycommand(session, session->ProxyCommand);
541
fd = ssh_connect_host(session, session->host, session->bindaddr,
542
session->port, session->timeout, session->timeout_usec);
544
if (fd == SSH_INVALID_SOCKET) {
548
set_status(session, 0.2);
550
ssh_socket_set_fd(session->socket, fd);
553
session->serverbanner = ssh_get_banner(session);
554
if (session->serverbanner == NULL) {
555
ssh_socket_close(session->socket);
560
set_status(session, 0.4);
562
ssh_log(session, SSH_LOG_RARE,
563
"SSH server banner: %s", session->serverbanner);
565
/* Here we analyse the different protocols the server allows. */
566
if (ssh_analyze_banner(session, &ssh1, &ssh2) < 0) {
567
ssh_socket_close(session->socket);
573
/* Here we decide which version of the protocol to use. */
574
if (ssh2 && session->ssh2) {
575
session->version = 2;
576
} else if(ssh1 && session->ssh1) {
577
session->version = 1;
579
ssh_set_error(session, SSH_FATAL,
580
"No version of SSH protocol usable (banner: %s)",
581
session->serverbanner);
582
ssh_socket_close(session->socket);
588
if (ssh_send_banner(session, 0) < 0) {
589
ssh_set_error(session, SSH_FATAL, "Sending the banner failed");
590
ssh_socket_close(session->socket);
595
set_status(session, 0.5);
597
switch (session->version) {
599
if (ssh_get_kex(session,0) < 0) {
600
ssh_socket_close(session->socket);
605
set_status(session,0.6);
607
ssh_list_kex(session, &session->server_kex);
608
if (set_kex(session) < 0) {
609
ssh_socket_close(session->socket);
614
if (ssh_send_kex(session, 0) < 0) {
615
ssh_socket_close(session->socket);
620
set_status(session,0.8);
622
if (dh_handshake(session) < 0) {
623
ssh_socket_close(session->socket);
628
set_status(session,1.0);
630
session->connected = 1;
633
if (ssh_get_kex1(session) < 0) {
634
ssh_socket_close(session->socket);
639
set_status(session,0.6);
641
session->connected = 1;
650
* @brief Get the issue banner from the server.
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.
655
* @param session The SSH session to use.
657
* @return A newly allocated string with the banner, NULL on error.
659
char *ssh_get_issue_banner(ssh_session session) {
660
if (session == NULL || session->banner == NULL) {
664
return string_to_char(session->banner);
668
* @brief Get the version of the OpenSSH server, if it is not an OpenSSH server
669
* then 0 will be returned.
671
* You can use the SSH_VERSION_INT macro to compare version numbers.
673
* @param session The SSH session to use.
675
* @return The version number if available, 0 otherwise.
677
int ssh_get_openssh_version(ssh_session session) {
678
if (session == NULL) {
682
return session->openssh;
686
* @brief Disconnect from a session (client or server).
687
* The session can then be reused to open a new session.
689
* @param session The SSH session to disconnect.
691
void ssh_disconnect(ssh_session session) {
692
ssh_string str = NULL;
694
if (session == NULL) {
700
if (ssh_socket_is_open(session->socket)) {
701
if (buffer_add_u8(session->out_buffer, SSH2_MSG_DISCONNECT) < 0) {
704
if (buffer_add_u32(session->out_buffer,
705
htonl(SSH2_DISCONNECT_BY_APPLICATION)) < 0) {
709
str = string_from_char("Bye Bye");
714
if (buffer_add_ssh_string(session->out_buffer,str) < 0) {
720
packet_send(session);
721
ssh_socket_close(session->socket);
729
const char *ssh_copyright(void) {
730
return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2008 Aris Adamantiadis "
731
"(aris@0xbadc0de.be) Distributed under the LGPL, please refer to COPYING"
732
"file for information about your rights";
735
/* vim: set ts=2 sw=2 et cindent: */