Compare commits

...

43 Commits

Author SHA1 Message Date
Andreas Schneider
78d5d64b38 Update ChangeLog. 2014-02-10 10:17:43 +01:00
Andreas Schneider
f73a44c223 cpack: Ignore obj directory. 2014-02-10 10:17:43 +01:00
Andreas Schneider
1cccfdf8a0 packet: Improve readablity of packet decrypt.
After discussion with Aris and it was not obvious enough to understand
the issue we decided to refactor it.

Reviewd-by: Aris Adamantiadis <aris@0xbadc0de.be>
2014-02-06 20:32:05 +01:00
Alan Dunn
abe4ed0e75 packet_crypt: Make packet_{en,de}crypt fail consistently on len == 0
Right now the behavior of packet_{en,de}crypt on len == 0 depends on
the behavior of malloc.  Instead, make these consistently fail based
on what I assume the desired behavior is due to the first error
message in each.

Signed-off-by: Alan Dunn <amdunn@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-06 19:40:29 +01:00
Alan Dunn
e7f831f0a3 packet: Do not decrypt zero length rest of buffer
If we receive a packet of length exactly blocksize, then
packet_decrypt gets called on a buffer of size 0.  The check at the
beginning of packet_decrypt indicates that the function should be
called on buffers of at least one blocksize, though the check allows
through zero length.  As is packet_decrypt can return -1 when len is 0
because malloc can return NULL in this case: according to the ISO C
standard, malloc is free to return NULL or a pointer that can be freed
when size == 0, and uclibc by default will return NULL here (in
"non-glibc-compatible" mode).  The net result is that when using
uclibc connections with libssh can anomalously fail.

Alternatively, packet_decrypt (and probably packet_encrypt for
consistency) could be made to always succeed on len == 0 without
depending on the behavior of malloc.

Thanks to Josh Berlin for bringing conneciton failures with uclibc to
my attention.

Signed-off-by: Alan Dunn <amdunn@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-06 19:40:09 +01:00
Raphael Kubo da Costa
4ea4e12df2 build: Use Threads_FOUND to decide whether to build ssh_threads.
Follow-up to 4e04ec8, which caused a regression on OS X.

Checking the value of CMAKE_THREAD_LIBS_INIT to decide whether any threading
library is present on a system turns out to be wrong -- in OS X, for
example, usage of pthreads does not depend on any additional linker or
compiler flags, so CMAKE_THREAD_LIBS_INIT is empty and our check in
src/CMakeLists.txt failed (it used to work before 4e04ec8 because
CMAKE_HAVE_THREADS_LIBRARY is set).

Instead, just look for Threads_FOUND, which FindThreads sets just like any
other Find module when it has found what it was looking for.

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-06 11:13:23 +01:00
Jon Simons
fb49e194df session: skip timestamp init for non-blocking case
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-06 11:13:22 +01:00
Jon Simons
13f4e31ad1 session: add getters for session cipher names
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-06 11:13:20 +01:00
Aris Adamantiadis
da5fa4ef66 Revert f2c2687ca6
Fix bug #142
The mode does need to be an octal numeric string. Mode 0600 now gets sent on the wire as 0384, triggering a "scp: protocol error: bad mode" response, and an "scp status code 1d not valid" message from libssh.
2014-02-05 22:30:10 +01:00
Aris Adamantiadis
dca415a38e knownhosts: resolve leaks found by coverity 2014-02-05 08:08:31 +01:00
Aris Adamantiadis
56f86cd4a1 knownhosts: detect variations of ecdsa 2014-02-05 08:08:31 +01:00
Aris Adamantiadis
f265afacfb tests: lines lost during last cherry-pick merge 2014-02-04 16:20:20 +01:00
Aris Adamantiadis
99f8b2b803 Kex: fix coverity warning + edge case 2014-02-04 16:04:45 +01:00
Audrius Butkevicius
22edaf43ee server: use custom server banners
Value of session->serverbanner never gets used

Signed-off-by: Audrius Butkevicius <audrius.butkevicius@gmail.com>
2014-02-04 16:04:26 +01:00
Aris Adamantiadis
497bd31364 server: allow custom server banners (bug #83) 2014-02-04 16:04:26 +01:00
Aris Adamantiadis
8ed0c0b3c8 Knownhosts: implement hostkey with knownhosts heuristic 2014-02-04 16:01:37 +01:00
Aris Adamantiadis
ce39d2fa73 knownhosts: add test case for bug #138 2014-02-04 16:01:37 +01:00
Aris Adamantiadis
90d3768f0f known_hosts: add ssh_knownhosts_algorithms()
Goal of that function is to test the preferred key exchange methods
based on what's available in the known_hosts file

Conflicts:
	tests/client/torture_knownhosts.c
2014-02-04 16:01:02 +01:00
Aris Adamantiadis
6f66032209 build: remove OSX deprecated warnings for openssl 2014-02-04 15:55:37 +01:00
Raphael Kubo da Costa
c571cd8402 threads: Be less strict when deciding whether to build libssh_threads.
As mentioned in the previous commit, there are cases where
CMAKE_HAVE_THREADS_LIBRARY is not set and pthreads _is_ being used: one can
pass -DTHREADS_HAVE_PTHREAD_ARG=1 to CMake directly so that it just passes
-pthread to the compiler/linker and does not set CMAKE_HAVE_THREADS_LIBRARY.

Since we are only interested in knowing whether any threading library has
been found, we should use CMAKE_THREAD_LIBS_INIT instead (Threads_FOUND
would also work).

Note that, at the moment, there is only a pthreads backend available in
threads/, so if it is not found configuration will fail because CMake will
try to create a library from an empty set of source files.

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-03 14:39:15 +01:00
Raphael Kubo da Costa
b049e12652 ConfigureChecks: Stop checking for CMAKE_HAVE_THREADS_LIBRARY.
libssh is primarily interested in whether pthreads is present and can be
used. Checking for CMAKE_HAVE_THREADS_LIBRARY is not the same thing, as
there are cases where pthread exists but CMAKE_HAVE_THREADS_LIBRARY is not
set (for example, FreeBSD passes -DTHREADS_HAVE_PTHREAD_ARG=1 to CMake by
default as a way to skip the checks for -lpthread, -lpthreads and others and
tell the build system that -pthread is the one expected to be used).

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-03 14:39:14 +01:00
Jon Simons
785682ac28 socket: fix read of non-connected socket
Ensure to check whether the socket at hand is indeed still connected
throughout POLLIN processing in ssh_socket_pollcallback.

Before this change, the POLLIN block in ssh_socket_pollcallback is
predicated against the condition (s->state == SSH_SOCKET_CONNECTED).
Once entered, data from the socket is consumed through the data
callback in this loop:

  do {
    r = s->callbacks->data(buffer_get_rest(s->in_buffer),
                           buffer_get_rest_len(s->in_buffer),
                           s->callbacks->userdata);
    buffer_pass_bytes(s->in_buffer,r);
  } while (r > 0);

However, it is possible for the socket data callback to change the
state of the socket (closing it, for example).  Fix the loop to only
continue so long as the socket remains connected: this also entails
setting the ssh_socket state to SSH_SOCKET_CLOSED upon close.

The bug can be observed before the change by sending a bogus banner
to the server: 'echo -e "A\r\nB\r\n" | nc localhost 22'.  Each of
'A' and 'B' will be processed by 'callback_receive_banner', even
though the client socket is closed after rejection of 'A'.

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-02 22:21:07 +01:00
Jon Simons
f29f10876a doc: correct ssh_channel_read_timeout units
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-02 22:21:07 +01:00
Audrius Butkevicius
45d28c7682 doc: Document expected return value of channel data callback
Signed-off-by: Audrius Butkevicius <audrius.butkevicius@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-02 22:21:07 +01:00
Audrius Butkevicius
2786565e77 src: Fix argument order in ssh_channel_pty_window_change_callback
So that it would match ssh_channel_pty_request_callback as well as the documentation

Signed-off-by: Audrius Butkevicius <audrius.butkevicius@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-02 22:21:07 +01:00
Joseph Southwell
96ad690c80 src: Define MAX_BUF_SIZE globally and use it.
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-02 22:21:07 +01:00
Joseph Southwell
0d82186503 client: Fix EOF session error reporting.
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-02 22:21:07 +01:00
Oleksandr Shneyder
5157d96958 Make function ssh_channel_accept() nonblocking if timeout is 0.
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-02-02 22:21:07 +01:00
Andreas Schneider
6a0787a366 pki_crypto: Fix memory leak with EC_KEY_set_public_key().
BUG: https://red.libssh.org/issues/146
2014-01-28 12:01:35 +01:00
Andreas Schneider
709e921942 doc: Document the unit for ssh_select() timeout.
BUG: https://red.libssh.org/issues/143
2014-01-23 11:29:58 +01:00
Rod Vagg
43a69b0a65 dh: Fix NULL check for p_group14.
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-23 11:22:22 +01:00
Jon Simons
18506f697c pki_crypto: fix DSA signature extraction
Fix the DSA portion of 'pki_signature_to_blob': before this change, it
is possible to sometimes observe DSA signature validation failure when
testing with OpenSSH clients.  The problem ended up being the following
snippet which did not account for the case when 'ssh_string_len(x)' may
be less than 20:

  r = make_bignum_string(sig->dsa_sig->r);
  ...
  memcpy(buffer,
         ((char *) ssh_string_data(r)) + ssh_string_len(r) - 20,
         20);

Above consider the case that ssh_string_len(r) is 19; in that case the
memcpy unintentionally starts in the wrong place.  The same situation
can happen for value 's' in this code.

To fix, adjust the offsets used for the input and output pointers, taking
into account that the lengths of 'r' and 's' can be less than 20.  With
the fix I am no longer able to reproduce the original failure mode.

BUG: https://red.libssh.org/issues/144

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-23 11:17:45 +01:00
Alan Dunn
15bede0c0e doc: Fix description of error parameter for ssh_get_error*
ssh_get_error can actually work on anything with an ssh_common_struct
as its first member.  It is already used in examples in the
distribution with ssh_sessions and ssh_binds.

Signed-off-by: Alan Dunn <amdunn@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-22 09:46:00 +01:00
Jon Simons
92dde09a37 pki_crypto: pad RSA signature blobs
Pad RSA signature blobs to the expected RSA signature length
when processing via 'pki_signature_to_blob'.

Some clients, notably PuTTY, may send unpadded RSA signatures
during the public key exchange: before this change, one can
sometimes observe failure in signature validation when using
PuTTY's 'plink' client, along these lines:

   ssh_packet_process: ssh_packet_process: Dispatching handler for packet type 50
   ssh_packet_userauth_request: ssh_packet_userauth_request: Auth request for service ssh-connection, method publickey for user 'foo'
   ssh_pki_signature_verify_blob: ssh_pki_signature_verify_blob: Going to verify a ssh-rsa type signature
   pki_signature_verify: pki_signature_verify: RSA error: error:04091077:rsa routines:INT_RSA_VERIFY:wrong signature length
   ssh_packet_userauth_request: ssh_packet_userauth_request: Received an invalid  signature from peer

For cross-reference this issue once also existed between
PuTTY and OpenSSH:

  http://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/rsa-verify-failed.html

  http://www.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/ssh-rsa.c?rev=1.19;content-type=text%2Fx-cvsweb-markup

With the fix I am unable to reproduce the above failure mode when
testing with 'plink'.

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-21 16:12:00 +01:00
Alan Dunn
809d76cbf2 Test change to ssh_bind_accept_fd
Signed-off-by: Alan Dunn <amdunn@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-21 16:08:12 +01:00
Alan Dunn
f78a74c160 Import keys during ssh_bind_accept_fd
Signed-off-by: Alan Dunn <amdunn@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-21 16:08:12 +01:00
Alan Dunn
b3b3045a81 Separate out key import functionality from ssh_bind_listen
Signed-off-by: Alan Dunn <amdunn@gmail.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-21 16:08:11 +01:00
Andreas Schneider
72fd3a73df doc: Fix channel documentation. 2014-01-17 11:09:07 +01:00
Jon Simons
cf19770ede bind: fix possible double-frees in ssh_bind_free
Make sure to explicitly set key pointers to NULL following the use
of 'ssh_key_free' throughout bind.c.

Before this change, a double free can happen via 'ssh_bind_free'
as in this example callpath:

  // create an ssh_bind
  ssh_bind b = ssh_bind_new();

  // provide a path to a wrong key-type
  ssh_bind_options_set(b, SSH_BIND_OPTIONS_DSAKEY, path_to_rsa_key);

  // initialize set key-type
  ssh_bind_listen(b);

    -> error path "The DSA host key has the wrong type: %d",

       ssh_key_free(sshbind->dsa)

         -> ssh_key_clean(key) // OK

         -> SAFE_FREE(key)     // OK, but, sshbind->dsa is *not* set to NULL

  // ssh_bind_listen failed, so clean up ssh_bind
  ssh_bind_free(b);

    -> ssh_key_free(sshbind->dsa)  // double-free here

To fix, set pointers to NULL that have been free'd with 'ssh_key_free'.

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-16 09:27:55 +01:00
Andreas Schneider
7f42f5a3c9 cmake: Increase version numbers for 0.6.1. 2014-01-16 09:16:11 +01:00
Andreas Schneider
6223e05b23 doc: Use ssh_channel_accept_forward() in documentation. 2014-01-16 09:14:52 +01:00
Oleksandr Shneyder
634671db11 channel: Add ssh_channel_accept_forward().
This works same way as ssh_forward_accept() but can return a destination
port of the channel (useful if SSH connection forwarding several TCP/IP
ports).

Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2014-01-16 09:13:57 +01:00
Aris Adamantiadis
1f689261ec threads: support libgcrypt 1.6 hack
Not 100% satisfied of this patch, but the way libgcrypt handles
threading in 1.6 is not compatible with custom handlers. The
new code basicaly uses pthreads in every case. This will probably
not work on windows.
2014-01-08 22:06:38 +01:00
34 changed files with 927 additions and 165 deletions

View File

@@ -8,7 +8,7 @@ set(APPLICATION_NAME ${PROJECT_NAME})
set(APPLICATION_VERSION_MAJOR "0") set(APPLICATION_VERSION_MAJOR "0")
set(APPLICATION_VERSION_MINOR "6") set(APPLICATION_VERSION_MINOR "6")
set(APPLICATION_VERSION_PATCH "0") set(APPLICATION_VERSION_PATCH "1")
set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}") set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINOR}.${APPLICATION_VERSION_PATCH}")
@@ -19,7 +19,7 @@ set(APPLICATION_VERSION "${APPLICATION_VERSION_MAJOR}.${APPLICATION_VERSION_MINO
# Increment AGE. Set REVISION to 0 # Increment AGE. Set REVISION to 0
# If the source code was changed, but there were no interface changes: # If the source code was changed, but there were no interface changes:
# Increment REVISION. # Increment REVISION.
set(LIBRARY_VERSION "4.3.0") set(LIBRARY_VERSION "4.4.0")
set(LIBRARY_SOVERSION "4") set(LIBRARY_SOVERSION "4")
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked

View File

@@ -19,7 +19,7 @@ set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSIO
### source generator ### source generator
set(CPACK_SOURCE_GENERATOR "TGZ") set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build/;tags;cscope.*") set(CPACK_SOURCE_IGNORE_FILES "~$;[.]swp$;/[.]svn/;/[.]git/;.gitignore;/build/;/obj/;tags;cscope.*")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
if (WIN32) if (WIN32)

View File

@@ -1,6 +1,18 @@
ChangeLog ChangeLog
========== ==========
version 0.6.1 (released 2014-02-08)
* Added support for libgcrypt 1.6.
* Added ssh_channel_accept_forward().
* Added known_hosts heuristic during connection (#138).
* Added getters for session cipher names.
* Fixed decrypt of zero length buffer.
* Fixed padding in RSA signature blobs.
* Fixed DSA signature extraction.
* Fixed some memory leaks.
* Fixed read of non-connected socket.
* Fixed thread dectection.
version 0.6.0 (released 2014-01-08) version 0.6.0 (released 2014-01-08)
* Added new publicy key API. * Added new publicy key API.
* Added new userauth API. * Added new userauth API.

View File

@@ -169,11 +169,9 @@ if (GCRYPT_FOUND)
endif (GCRYPT_VERSION VERSION_GREATER "1.4.6") endif (GCRYPT_VERSION VERSION_GREATER "1.4.6")
endif (GCRYPT_FOUND) endif (GCRYPT_FOUND)
if (CMAKE_HAVE_THREADS_LIBRARY) if (CMAKE_USE_PTHREADS_INIT)
if (CMAKE_USE_PTHREADS_INIT) set(HAVE_PTHREAD 1)
set(HAVE_PTHREAD 1) endif (CMAKE_USE_PTHREADS_INIT)
endif (CMAKE_USE_PTHREADS_INIT)
endif (CMAKE_HAVE_THREADS_LIBRARY)
# OPTIONS # OPTIONS
check_c_source_compiles(" check_c_source_compiles("

View File

@@ -75,3 +75,10 @@ if (MSVC)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1")
endif (MSVC) endif (MSVC)
# This removes this annoying warning
# "warning: 'BN_CTX_free' is deprecated: first deprecated in OS X 10.7 [-Wdeprecated-declarations]"
if (OSX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-deprecated-declarations")
endif (OSX)

View File

@@ -145,7 +145,7 @@ or whatever use you have for it.
@subsection libssh_reverse Doing reverse port forwarding with libssh @subsection libssh_reverse Doing reverse port forwarding with libssh
To do reverse port forwarding, call ssh_forward_listen(), To do reverse port forwarding, call ssh_forward_listen(),
then ssh_forward_accept(). then ssh_channel_accept_forward().
When you call ssh_forward_listen(), you can let the remote server When you call ssh_forward_listen(), you can let the remote server
chose the non-priviledged port it should listen to. Otherwise, you can chose chose the non-priviledged port it should listen to. Otherwise, you can chose
@@ -164,6 +164,7 @@ int web_server(ssh_session session)
ssh_channel channel; ssh_channel channel;
char buffer[256]; char buffer[256];
int nbytes, nwritten; int nbytes, nwritten;
int port;
char *helloworld = "" char *helloworld = ""
"HTTP/1.1 200 OK\n" "HTTP/1.1 200 OK\n"
"Content-Type: text/html\n" "Content-Type: text/html\n"
@@ -186,7 +187,7 @@ int web_server(ssh_session session)
return rc; return rc;
} }
channel = ssh_forward_accept(session, 60000); channel = ssh_channel_accept_forward(session, 60000, &port);
if (channel == NULL) if (channel == NULL)
{ {
fprintf(stderr, "Error waiting for incoming connection: %s\n", fprintf(stderr, "Error waiting for incoming connection: %s\n",

View File

@@ -61,5 +61,6 @@ implement the following methods :
- mutex_destroy - mutex_destroy
- thread_id - thread_id
libgcrypt 1.6 and bigger backend does not support custom callback. Using anything else than pthreads (ssh_threads_get_pthread()) here will fail.
Good luck ! Good luck !
*/ */

View File

@@ -495,6 +495,8 @@ LIBSSH_API int ssh_set_callbacks(ssh_session session, ssh_callbacks cb);
* @param len the length of the data * @param len the length of the data
* @param is_stderr is 0 for stdout or 1 for stderr * @param is_stderr is 0 for stdout or 1 for stderr
* @param userdata Userdata to be passed to the callback function. * @param userdata Userdata to be passed to the callback function.
* @returns number of bytes processed by the callee. The remaining bytes will
* be sent in the next callback message, when more data is available.
*/ */
typedef int (*ssh_channel_data_callback) (ssh_session session, typedef int (*ssh_channel_data_callback) (ssh_session session,
ssh_channel channel, ssh_channel channel,
@@ -801,6 +803,8 @@ struct ssh_threads_callbacks_struct {
* *
* @see ssh_threads_callbacks_struct * @see ssh_threads_callbacks_struct
* @see SSH_THREADS_PTHREAD * @see SSH_THREADS_PTHREAD
* @bug libgcrypt 1.6 and bigger backend does not support custom callback.
* Using anything else than pthreads here will fail.
*/ */
LIBSSH_API int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct LIBSSH_API int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct
*cb); *cb);

View File

@@ -0,0 +1,27 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 20014 by Aris Adamantiadis <aris@badcode.be>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef KNOWNHOSTS_H_
#define KNOWNHOSTS_H_
char **ssh_knownhosts_algorithms(ssh_session session);
#endif /* KNOWNHOSTS_H_ */

View File

@@ -78,7 +78,7 @@
/* libssh version */ /* libssh version */
#define LIBSSH_VERSION_MAJOR 0 #define LIBSSH_VERSION_MAJOR 0
#define LIBSSH_VERSION_MINOR 6 #define LIBSSH_VERSION_MINOR 6
#define LIBSSH_VERSION_MICRO 0 #define LIBSSH_VERSION_MICRO 1
#define LIBSSH_VERSION_INT SSH_VERSION_INT(LIBSSH_VERSION_MAJOR, \ #define LIBSSH_VERSION_INT SSH_VERSION_INT(LIBSSH_VERSION_MAJOR, \
LIBSSH_VERSION_MINOR, \ LIBSSH_VERSION_MINOR, \
@@ -377,7 +377,7 @@ LIBSSH_API int ssh_channel_open_x11(ssh_channel channel, const char *orig_addr,
LIBSSH_API int ssh_channel_poll(ssh_channel channel, int is_stderr); LIBSSH_API int ssh_channel_poll(ssh_channel channel, int is_stderr);
LIBSSH_API int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr); LIBSSH_API int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr);
LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr); LIBSSH_API int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr);
LIBSSH_API int ssh_channel_read_timeout(ssh_channel channel, void *dest, uint32_t count, int is_stderr, int timeout); LIBSSH_API int ssh_channel_read_timeout(ssh_channel channel, void *dest, uint32_t count, int is_stderr, int timeout_ms);
LIBSSH_API int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, LIBSSH_API int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count,
int is_stderr); int is_stderr);
LIBSSH_API int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value); LIBSSH_API int ssh_channel_request_env(ssh_channel channel, const char *name, const char *value);
@@ -406,6 +406,7 @@ LIBSSH_API void ssh_disconnect(ssh_session session);
LIBSSH_API char *ssh_dirname (const char *path); LIBSSH_API char *ssh_dirname (const char *path);
LIBSSH_API int ssh_finalize(void); LIBSSH_API int ssh_finalize(void);
LIBSSH_API ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms); LIBSSH_API ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms);
LIBSSH_API ssh_channel ssh_channel_accept_forward(ssh_session session, int timeout_ms, int *destination_port);
LIBSSH_API int ssh_forward_cancel(ssh_session session, const char *address, int port); LIBSSH_API int ssh_forward_cancel(ssh_session session, const char *address, int port);
LIBSSH_API int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port); LIBSSH_API int ssh_forward_listen(ssh_session session, const char *address, int port, int *bound_port);
LIBSSH_API void ssh_free(ssh_session session); LIBSSH_API void ssh_free(ssh_session session);
@@ -627,6 +628,8 @@ LIBSSH_API int ssh_event_remove_session(ssh_event event, ssh_session session);
LIBSSH_API void ssh_event_free(ssh_event event); LIBSSH_API void ssh_event_free(ssh_event event);
LIBSSH_API const char* ssh_get_clientbanner(ssh_session session); LIBSSH_API const char* ssh_get_clientbanner(ssh_session session);
LIBSSH_API const char* ssh_get_serverbanner(ssh_session session); LIBSSH_API const char* ssh_get_serverbanner(ssh_session session);
LIBSSH_API const char* ssh_get_cipher_in(ssh_session session);
LIBSSH_API const char* ssh_get_cipher_out(ssh_session session);
#ifndef LIBSSH_LEGACY_0_4 #ifndef LIBSSH_LEGACY_0_4
#include "libssh/legacy.h" #include "libssh/legacy.h"

View File

@@ -120,11 +120,24 @@ int gettimeofday(struct timeval *__p, void *__t);
#include "libssh/callbacks.h" #include "libssh/callbacks.h"
/* some constants */ /* some constants */
#ifndef MAX_PACKAT_LEN
#define MAX_PACKET_LEN 262144 #define MAX_PACKET_LEN 262144
#endif
#ifndef ERROR_BUFFERLEN
#define ERROR_BUFFERLEN 1024 #define ERROR_BUFFERLEN 1024
#endif
#ifndef CLIENTBANNER1
#define CLIENTBANNER1 "SSH-1.5-libssh-" SSH_STRINGIFY(LIBSSH_VERSION) #define CLIENTBANNER1 "SSH-1.5-libssh-" SSH_STRINGIFY(LIBSSH_VERSION)
#endif
#ifndef CLIENTBANNER2
#define CLIENTBANNER2 "SSH-2.0-libssh-" SSH_STRINGIFY(LIBSSH_VERSION) #define CLIENTBANNER2 "SSH-2.0-libssh-" SSH_STRINGIFY(LIBSSH_VERSION)
#endif
#ifndef KBDINT_MAX_PROMPT
#define KBDINT_MAX_PROMPT 256 /* more than openssh's :) */ #define KBDINT_MAX_PROMPT 256 /* more than openssh's :) */
#endif
#ifndef MAX_BUF_SIZE
#define MAX_BUF_SIZE 4096
#endif
#ifndef __FUNCTION__ #ifndef __FUNCTION__
#if defined(__SUNPRO_C) #if defined(__SUNPRO_C)
@@ -155,16 +168,6 @@ int gettimeofday(struct timeval *__p, void *__t);
#include <sys/time.h> #include <sys/time.h>
#endif #endif
/*
* get rid of deprecacy warnings on OSX when using OpenSSL
*/
#if defined(__APPLE__)
#ifdef MAC_OS_X_VERSION_MIN_REQUIRED
#undef MAC_OS_X_VERSION_MIN_REQUIRED
#endif
#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6
#endif
/* forward declarations */ /* forward declarations */
struct ssh_common_struct; struct ssh_common_struct;
struct ssh_kex_struct; struct ssh_kex_struct;

View File

@@ -175,6 +175,7 @@ struct ssh_session_struct {
char *knownhosts; char *knownhosts;
char *wanted_methods[10]; char *wanted_methods[10];
char *ProxyCommand; char *ProxyCommand;
char *custombanner;
unsigned long timeout; /* seconds */ unsigned long timeout; /* seconds */
unsigned long timeout_usec; unsigned long timeout_usec;
unsigned int port; unsigned int port;

View File

@@ -288,6 +288,6 @@ if (WITH_STATIC_LIB)
) )
endif (WITH_STATIC_LIB) endif (WITH_STATIC_LIB)
if (CMAKE_HAVE_THREADS_LIBRARY) if (Threads_FOUND)
add_subdirectory(threads) add_subdirectory(threads)
endif (CMAKE_HAVE_THREADS_LIBRARY) endif (Threads_FOUND)

View File

@@ -144,26 +144,19 @@ ssh_bind ssh_bind_new(void) {
return ptr; return ptr;
} }
int ssh_bind_listen(ssh_bind sshbind) { static int ssh_bind_import_keys(ssh_bind sshbind) {
const char *host;
socket_t fd;
int rc; int rc;
if (ssh_init() < 0) {
ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed");
return -1;
}
if (sshbind->ecdsakey == NULL && if (sshbind->ecdsakey == NULL &&
sshbind->dsakey == NULL && sshbind->dsakey == NULL &&
sshbind->rsakey == NULL) { sshbind->rsakey == NULL) {
ssh_set_error(sshbind, SSH_FATAL, ssh_set_error(sshbind, SSH_FATAL,
"DSA or RSA host key file must be set before listen()"); "ECDSA, DSA, or RSA host key file must be set");
return SSH_ERROR; return SSH_ERROR;
} }
#ifdef HAVE_ECC #ifdef HAVE_ECC
if (sshbind->ecdsakey) { if (sshbind->ecdsa == NULL && sshbind->ecdsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->ecdsakey, rc = ssh_pki_import_privkey_file(sshbind->ecdsakey,
NULL, NULL,
NULL, NULL,
@@ -179,12 +172,13 @@ int ssh_bind_listen(ssh_bind sshbind) {
ssh_set_error(sshbind, SSH_FATAL, ssh_set_error(sshbind, SSH_FATAL,
"The ECDSA host key has the wrong type"); "The ECDSA host key has the wrong type");
ssh_key_free(sshbind->ecdsa); ssh_key_free(sshbind->ecdsa);
sshbind->ecdsa = NULL;
return SSH_ERROR; return SSH_ERROR;
} }
} }
#endif #endif
if (sshbind->dsakey) { if (sshbind->dsa == NULL && sshbind->dsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->dsakey, rc = ssh_pki_import_privkey_file(sshbind->dsakey,
NULL, NULL,
NULL, NULL,
@@ -201,11 +195,12 @@ int ssh_bind_listen(ssh_bind sshbind) {
"The DSA host key has the wrong type: %d", "The DSA host key has the wrong type: %d",
ssh_key_type(sshbind->dsa)); ssh_key_type(sshbind->dsa));
ssh_key_free(sshbind->dsa); ssh_key_free(sshbind->dsa);
sshbind->dsa = NULL;
return SSH_ERROR; return SSH_ERROR;
} }
} }
if (sshbind->rsakey) { if (sshbind->rsa == NULL && sshbind->rsakey != NULL) {
rc = ssh_pki_import_privkey_file(sshbind->rsakey, rc = ssh_pki_import_privkey_file(sshbind->rsakey,
NULL, NULL,
NULL, NULL,
@@ -222,10 +217,29 @@ int ssh_bind_listen(ssh_bind sshbind) {
ssh_set_error(sshbind, SSH_FATAL, ssh_set_error(sshbind, SSH_FATAL,
"The RSA host key has the wrong type"); "The RSA host key has the wrong type");
ssh_key_free(sshbind->rsa); ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL;
return SSH_ERROR; return SSH_ERROR;
} }
} }
return SSH_OK;
}
int ssh_bind_listen(ssh_bind sshbind) {
const char *host;
socket_t fd;
int rc;
if (ssh_init() < 0) {
ssh_set_error(sshbind, SSH_FATAL, "ssh_init() failed");
return -1;
}
rc = ssh_bind_import_keys(sshbind);
if (rc != SSH_OK) {
return SSH_ERROR;
}
if (sshbind->bindfd == SSH_INVALID_SOCKET) { if (sshbind->bindfd == SSH_INVALID_SOCKET) {
host = sshbind->bindaddr; host = sshbind->bindaddr;
if (host == NULL) { if (host == NULL) {
@@ -235,7 +249,9 @@ int ssh_bind_listen(ssh_bind sshbind) {
fd = bind_socket(sshbind, host, sshbind->bindport); fd = bind_socket(sshbind, host, sshbind->bindport);
if (fd == SSH_INVALID_SOCKET) { if (fd == SSH_INVALID_SOCKET) {
ssh_key_free(sshbind->dsa); ssh_key_free(sshbind->dsa);
sshbind->dsa = NULL;
ssh_key_free(sshbind->rsa); ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL;
return -1; return -1;
} }
sshbind->bindfd = fd; sshbind->bindfd = fd;
@@ -246,7 +262,9 @@ int ssh_bind_listen(ssh_bind sshbind) {
fd, strerror(errno)); fd, strerror(errno));
close(fd); close(fd);
ssh_key_free(sshbind->dsa); ssh_key_free(sshbind->dsa);
sshbind->dsa = NULL;
ssh_key_free(sshbind->rsa); ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL;
return -1; return -1;
} }
} else { } else {
@@ -348,8 +366,11 @@ void ssh_bind_free(ssh_bind sshbind){
SAFE_FREE(sshbind->ecdsakey); SAFE_FREE(sshbind->ecdsakey);
ssh_key_free(sshbind->dsa); ssh_key_free(sshbind->dsa);
sshbind->dsa = NULL;
ssh_key_free(sshbind->rsa); ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL;
ssh_key_free(sshbind->ecdsa); ssh_key_free(sshbind->ecdsa);
sshbind->ecdsa = NULL;
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
if (sshbind->wanted_methods[i]) { if (sshbind->wanted_methods[i]) {
@@ -361,7 +382,7 @@ void ssh_bind_free(ssh_bind sshbind){
} }
int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
int i; int i, rc;
if (session == NULL){ if (session == NULL){
ssh_set_error(sshbind, SSH_FATAL,"session is null"); ssh_set_error(sshbind, SSH_FATAL,"session is null");
@@ -392,7 +413,8 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
} }
session->common.log_verbosity = sshbind->common.log_verbosity; session->common.log_verbosity = sshbind->common.log_verbosity;
if(sshbind->banner != NULL)
session->opts.custombanner = strdup(sshbind->banner);
ssh_socket_free(session->socket); ssh_socket_free(session->socket);
session->socket = ssh_socket_new(session); session->socket = ssh_socket_new(session);
if (session->socket == NULL) { if (session->socket == NULL) {
@@ -403,6 +425,16 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd){
ssh_socket_set_fd(session->socket, fd); ssh_socket_set_fd(session->socket, fd);
ssh_socket_get_poll_handle_out(session->socket); ssh_socket_get_poll_handle_out(session->socket);
/* We must try to import any keys that could be imported in case
* we are not using ssh_bind_listen (which is the other place
* where keys can be imported) on this ssh_bind and are instead
* only using ssh_bind_accept_fd to manage sockets ourselves.
*/
rc = ssh_bind_import_keys(sshbind);
if (rc != SSH_OK) {
return SSH_ERROR;
}
#ifdef HAVE_ECC #ifdef HAVE_ECC
if (sshbind->ecdsa) { if (sshbind->ecdsa) {
session->srv.ecdsa_key = ssh_key_dup(sshbind->ecdsa); session->srv.ecdsa_key = ssh_key_dup(sshbind->ecdsa);

View File

@@ -916,10 +916,10 @@ int channel_default_bufferize(ssh_channel channel, void *data, int len,
* SSH_AGAIN if in nonblocking mode and call has * SSH_AGAIN if in nonblocking mode and call has
* to be done again. * to be done again.
* *
* @see channel_open_forward() * @see ssh_channel_open_forward()
* @see channel_request_env() * @see ssh_channel_request_env()
* @see channel_request_shell() * @see ssh_channel_request_shell()
* @see channel_request_exec() * @see ssh_channel_request_exec()
*/ */
int ssh_channel_open_session(ssh_channel channel) { int ssh_channel_open_session(ssh_channel channel) {
if(channel == NULL) { if(channel == NULL) {
@@ -952,7 +952,7 @@ int ssh_channel_open_session(ssh_channel channel) {
* SSH_AGAIN if in nonblocking mode and call has * SSH_AGAIN if in nonblocking mode and call has
* to be done again. * to be done again.
* *
* @see channel_open_forward() * @see ssh_channel_open_forward()
*/ */
int ssh_channel_open_auth_agent(ssh_channel channel){ int ssh_channel_open_auth_agent(ssh_channel channel){
if(channel == NULL) { if(channel == NULL) {
@@ -1120,8 +1120,24 @@ void ssh_channel_do_free(ssh_channel channel){
* *
* @return SSH_OK on success, SSH_ERROR if an error occurred. * @return SSH_OK on success, SSH_ERROR if an error occurred.
* *
* @see channel_close() * Example:
* @see channel_free() @code
rc = ssh_channel_send_eof(channel);
if (rc == SSH_ERROR) {
return -1;
}
while(!ssh_channel_is_eof(channel)) {
rc = ssh_channel_read(channel, buf, sizeof(buf), 0);
if (rc == SSH_ERROR) {
return -1;
}
}
ssh_channel_close(channel);
@endcode
*
* @see ssh_channel_close()
* @see ssh_channel_free()
* @see ssh_channel_is_eof()
*/ */
int ssh_channel_send_eof(ssh_channel channel){ int ssh_channel_send_eof(ssh_channel channel){
ssh_session session; ssh_session session;
@@ -1170,8 +1186,8 @@ error:
* *
* @return SSH_OK on success, SSH_ERROR if an error occurred. * @return SSH_OK on success, SSH_ERROR if an error occurred.
* *
* @see channel_free() * @see ssh_channel_free()
* @see channel_eof() * @see ssh_channel_is_eof()
*/ */
int ssh_channel_close(ssh_channel channel){ int ssh_channel_close(ssh_channel channel){
ssh_session session; ssh_session session;
@@ -1397,7 +1413,7 @@ uint32_t ssh_channel_window_size(ssh_channel channel) {
* *
* @return The number of bytes written, SSH_ERROR on error. * @return The number of bytes written, SSH_ERROR on error.
* *
* @see channel_read() * @see ssh_channel_read()
*/ */
int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) { int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) {
return channel_write_common(channel, data, len, 0); return channel_write_common(channel, data, len, 0);
@@ -1410,7 +1426,7 @@ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len) {
* *
* @return 0 if channel is closed, nonzero otherwise. * @return 0 if channel is closed, nonzero otherwise.
* *
* @see channel_is_closed() * @see ssh_channel_is_closed()
*/ */
int ssh_channel_is_open(ssh_channel channel) { int ssh_channel_is_open(ssh_channel channel) {
if(channel == NULL) { if(channel == NULL) {
@@ -1426,7 +1442,7 @@ int ssh_channel_is_open(ssh_channel channel) {
* *
* @return 0 if channel is opened, nonzero otherwise. * @return 0 if channel is opened, nonzero otherwise.
* *
* @see channel_is_open() * @see ssh_channel_is_open()
*/ */
int ssh_channel_is_closed(ssh_channel channel) { int ssh_channel_is_closed(ssh_channel channel) {
if(channel == NULL) { if(channel == NULL) {
@@ -1725,7 +1741,7 @@ error:
* SSH_AGAIN if in nonblocking mode and call has * SSH_AGAIN if in nonblocking mode and call has
* to be done again. * to be done again.
* *
* @see channel_request_pty_size() * @see ssh_channel_request_pty_size()
*/ */
int ssh_channel_request_pty(ssh_channel channel) { int ssh_channel_request_pty(ssh_channel channel) {
return ssh_channel_request_pty_size(channel, "xterm", 80, 24); return ssh_channel_request_pty_size(channel, "xterm", 80, 24);
@@ -1964,7 +1980,7 @@ error:
} }
static ssh_channel ssh_channel_accept(ssh_session session, int channeltype, static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
int timeout_ms) { int timeout_ms, int *destination_port) {
#ifndef _WIN32 #ifndef _WIN32
static const struct timespec ts = { static const struct timespec ts = {
.tv_sec = 0, .tv_sec = 0,
@@ -1981,7 +1997,11 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
* 50 ms. So we need to decrement by 100 ms. * 50 ms. So we need to decrement by 100 ms.
*/ */
for (t = timeout_ms; t >= 0; t -= 100) { for (t = timeout_ms; t >= 0; t -= 100) {
ssh_handle_packets(session, 50); if (timeout_ms == 0) {
ssh_handle_packets(session, 0);
} else {
ssh_handle_packets(session, 50);
}
if (session->ssh_message_list) { if (session->ssh_message_list) {
iterator = ssh_list_get_iterator(session->ssh_message_list); iterator = ssh_list_get_iterator(session->ssh_message_list);
@@ -1991,6 +2011,10 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
ssh_message_subtype(msg) == channeltype) { ssh_message_subtype(msg) == channeltype) {
ssh_list_remove(session->ssh_message_list, iterator); ssh_list_remove(session->ssh_message_list, iterator);
channel = ssh_message_channel_request_open_reply_accept(msg); channel = ssh_message_channel_request_open_reply_accept(msg);
if(destination_port) {
*destination_port=msg->channel_request_open.destination_port;
}
ssh_message_free(msg); ssh_message_free(msg);
return channel; return channel;
} }
@@ -2021,7 +2045,7 @@ static ssh_channel ssh_channel_accept(ssh_session session, int channeltype,
* the server. * the server.
*/ */
ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) { ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms) {
return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms); return ssh_channel_accept(channel->session, SSH_CHANNEL_X11, timeout_ms, NULL);
} }
/** /**
@@ -2275,7 +2299,23 @@ error:
* the server * the server
*/ */
ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) { ssh_channel ssh_forward_accept(ssh_session session, int timeout_ms) {
return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms); return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, NULL);
}
/**
* @brief Accept an incoming TCP/IP forwarding channel and get information
* about incomming connection
* @param[in] session The ssh session to use.
*
* @param[in] timeout_ms A timeout in milliseconds.
*
* @param[in] destination_port A pointer to destination port or NULL.
*
* @return Newly created channel, or NULL if no incoming channel request from
* the server
*/
ssh_channel ssh_channel_accept_forward(ssh_session session, int timeout_ms, int* destination_port) {
return ssh_channel_accept(session, SSH_CHANNEL_FORWARDED_TCPIP, timeout_ms, destination_port);
} }
/** /**
@@ -2411,20 +2451,22 @@ error:
* SSH_ERROR if an error occurred, * SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has * SSH_AGAIN if in nonblocking mode and call has
* to be done again. * to be done again.
* @code
* rc = channel_request_exec(channel, "ps aux");
* if (rc > 0) {
* return -1;
* }
* *
* while ((rc = channel_read(channel, buffer, sizeof(buffer), 0)) > 0) { * Example:
* if (fwrite(buffer, 1, rc, stdout) != (unsigned int) rc) { @code
* return -1; rc = channel_request_exec(channel, "ps aux");
* } if (rc > 0) {
* } return -1;
* @endcode }
while ((rc = channel_read(channel, buffer, sizeof(buffer), 0)) > 0) {
if (fwrite(buffer, 1, rc, stdout) != (unsigned int) rc) {
return -1;
}
}
@endcode
* *
* @see channel_request_shell() * @see ssh_channel_request_shell()
*/ */
int ssh_channel_request_exec(ssh_channel channel, const char *cmd) { int ssh_channel_request_exec(ssh_channel channel, const char *cmd) {
ssh_buffer buffer = NULL; ssh_buffer buffer = NULL;
@@ -2678,16 +2720,16 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std
/** /**
* @brief Reads data from a channel. * @brief Reads data from a channel.
* *
* @param[in] channel The channel to read from. * @param[in] channel The channel to read from.
* *
* @param[in] dest The destination buffer which will get the data. * @param[in] dest The destination buffer which will get the data.
* *
* @param[in] count The count of bytes to be read. * @param[in] count The count of bytes to be read.
* *
* @param[in] is_stderr A boolean value to mark reading from the stderr flow. * @param[in] is_stderr A boolean value to mark reading from the stderr flow.
* *
* @param[in] timeout A timeout in seconds. A value of -1 means infinite * @param[in] timeout_ms A timeout in milliseconds. A value of -1 means
* timeout. * infinite timeout.
* *
* @return The number of bytes read, 0 on end of file or SSH_ERROR * @return The number of bytes read, 0 on end of file or SSH_ERROR
* on error. In nonblocking mode it Can return 0 if no data * on error. In nonblocking mode it Can return 0 if no data
@@ -2803,7 +2845,7 @@ int ssh_channel_read_timeout(ssh_channel channel,
* *
* @warning Don't forget to check for EOF as it would return 0 here. * @warning Don't forget to check for EOF as it would return 0 here.
* *
* @see channel_is_eof() * @see ssh_channel_is_eof()
*/ */
int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count, int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count,
int is_stderr) { int is_stderr) {
@@ -2855,7 +2897,7 @@ int ssh_channel_read_nonblocking(ssh_channel channel, void *dest, uint32_t count
* *
* @warning When the channel is in EOF state, the function returns SSH_EOF. * @warning When the channel is in EOF state, the function returns SSH_EOF.
* *
* @see channel_is_eof() * @see ssh_channel_is_eof()
*/ */
int ssh_channel_poll(ssh_channel channel, int is_stderr){ int ssh_channel_poll(ssh_channel channel, int is_stderr){
ssh_buffer stdbuf; ssh_buffer stdbuf;
@@ -2907,7 +2949,7 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr){
* *
* @warning When the channel is in EOF state, the function returns SSH_EOF. * @warning When the channel is in EOF state, the function returns SSH_EOF.
* *
* @see channel_is_eof() * @see ssh_channel_is_eof()
*/ */
int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){ int ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr){
ssh_session session; ssh_session session;
@@ -3212,7 +3254,7 @@ int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
* *
* @return The number of bytes written, SSH_ERROR on error. * @return The number of bytes written, SSH_ERROR on error.
* *
* @see channel_read() * @see ssh_channel_read()
*/ */
int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) { int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len) {
return channel_write_common(channel, data, len, 1); return channel_write_common(channel, data, len, 1);

View File

@@ -152,19 +152,27 @@ int ssh_send_banner(ssh_session session, int server) {
banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2; banner = session->version == 1 ? CLIENTBANNER1 : CLIENTBANNER2;
if (server) { if (server) {
session->serverbanner = strdup(banner); if(session->opts.custombanner == NULL){
session->serverbanner = strdup(banner);
} else {
session->serverbanner = malloc(strlen(session->opts.custombanner) + 9);
if(!session->serverbanner)
goto end;
strcpy(session->serverbanner, "SSH-2.0-");
strcat(session->serverbanner, session->opts.custombanner);
}
if (session->serverbanner == NULL) { if (session->serverbanner == NULL) {
goto end; goto end;
} }
snprintf(buffer, 128, "%s\n", session->serverbanner);
} else { } else {
session->clientbanner = strdup(banner); session->clientbanner = strdup(banner);
if (session->clientbanner == NULL) { if (session->clientbanner == NULL) {
goto end; goto end;
} }
snprintf(buffer, 128, "%s\n", session->clientbanner);
} }
snprintf(buffer, 128, "%s\n", banner);
if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERROR) { if (ssh_socket_write(session->socket, buffer, strlen(buffer)) == SSH_ERROR) {
goto end; goto end;
} }
@@ -536,7 +544,8 @@ pending:
} }
SSH_LOG(SSH_LOG_PACKET,"ssh_connect: Actual timeout : %d", timeout); SSH_LOG(SSH_LOG_PACKET,"ssh_connect: Actual timeout : %d", timeout);
ret = ssh_handle_packets_termination(session, timeout, ssh_connect_termination, session); ret = ssh_handle_packets_termination(session, timeout, ssh_connect_termination, session);
if (ret == SSH_ERROR || !ssh_connect_termination(session)) { if (session->session_state != SSH_SESSION_STATE_ERROR &&
(ret == SSH_ERROR || !ssh_connect_termination(session))) {
ssh_set_error(session, SSH_FATAL, ssh_set_error(session, SSH_FATAL,
"Timeout connecting to %s", session->opts.host); "Timeout connecting to %s", session->opts.host);
session->session_state = SSH_SESSION_STATE_ERROR; session->session_state = SSH_SESSION_STATE_ERROR;

View File

@@ -421,7 +421,7 @@ static int ssh_select_cb (socket_t fd, int revents, void *userdata){
* @param[in] readfds A fd_set of file descriptors to be select'ed for * @param[in] readfds A fd_set of file descriptors to be select'ed for
* reading. * reading.
* *
* @param[in] timeout A timeout for the select. * @param[in] timeout The timeout in milliseconds.
* *
* @return SSH_OK on success, * @return SSH_OK on success,
* SSH_ERROR on error, * SSH_ERROR on error,

View File

@@ -170,7 +170,7 @@ int ssh_crypto_init(void) {
return -1; return -1;
} }
bignum_bin2bn(p_group14_value, P_GROUP14_LEN, &p_group14); bignum_bin2bn(p_group14_value, P_GROUP14_LEN, &p_group14);
if (p_group1 == NULL) { if (p_group14 == NULL) {
bignum_free(g); bignum_free(g);
bignum_free(p_group1); bignum_free(p_group1);
g = NULL; g = NULL;

View File

@@ -104,7 +104,7 @@ void _ssh_set_error_invalid(void *error, const char *function)
/** /**
* @brief Retrieve the error text message from the last error. * @brief Retrieve the error text message from the last error.
* *
* @param error The SSH session pointer. * @param error An ssh_session or ssh_bind.
* *
* @return A static string describing the error. * @return A static string describing the error.
*/ */
@@ -117,7 +117,7 @@ const char *ssh_get_error(void *error) {
/** /**
* @brief Retrieve the error code from the last error. * @brief Retrieve the error code from the last error.
* *
* @param error The SSH session pointer. * @param error An ssh_session or ssh_bind.
* *
* \return SSH_NO_ERROR No error occurred\n * \return SSH_NO_ERROR No error occurred\n
* SSH_REQUEST_DENIED The last request was denied but situation is * SSH_REQUEST_DENIED The last request was denied but situation is

View File

@@ -35,6 +35,7 @@
#include "libssh/ssh2.h" #include "libssh/ssh2.h"
#include "libssh/string.h" #include "libssh/string.h"
#include "libssh/curve25519.h" #include "libssh/curve25519.h"
#include "libssh/knownhosts.h"
#ifdef HAVE_LIBGCRYPT #ifdef HAVE_LIBGCRYPT
# define BLOWFISH "blowfish-cbc," # define BLOWFISH "blowfish-cbc,"
@@ -373,6 +374,53 @@ void ssh_list_kex(struct ssh_kex_struct *kex) {
} }
} }
/**
* @internal
* @brief selects the hostkey mechanisms to be chosen for the key exchange,
* as some hostkey mechanisms may be present in known_hosts file and preferred
* @returns a cstring containing a comma-separated list of hostkey methods.
* NULL if no method matches
*/
static char *ssh_client_select_hostkeys(ssh_session session){
char methods_buffer[128]={0};
static const char *preferred_hostkeys[]={"ecdsa-sha2-nistp521","ecdsa-sha2-nistp384",
"ecdsa-sha2-nistp256", "ssh-rsa", "ssh-dss", "ssh-rsa1", NULL};
char **methods;
int i,j;
int needcoma=0;
methods = ssh_knownhosts_algorithms(session);
if (methods == NULL || methods[0] == NULL){
SAFE_FREE(methods);
return NULL;
}
for (i=0;preferred_hostkeys[i] != NULL; ++i){
for (j=0; methods[j] != NULL; ++j){
if(strcmp(preferred_hostkeys[i], methods[j]) == 0){
if (verify_existing_algo(SSH_HOSTKEYS, methods[j])){
if(needcoma)
strncat(methods_buffer,",",sizeof(methods_buffer)-strlen(methods_buffer)-1);
strncat(methods_buffer, methods[j], sizeof(methods_buffer)-strlen(methods_buffer)-1);
needcoma = 1;
}
}
}
}
for(i=0;methods[i]!= NULL; ++i){
SAFE_FREE(methods[i]);
}
SAFE_FREE(methods);
if(strlen(methods_buffer) > 0){
SSH_LOG(SSH_LOG_DEBUG, "Changing host key method to \"%s\"", methods_buffer);
return strdup(methods_buffer);
} else {
SSH_LOG(SSH_LOG_DEBUG, "No supported kex method for existing key in known_hosts file");
return NULL;
}
}
/** /**
* @brief sets the key exchange parameters to be sent to the server, * @brief sets the key exchange parameters to be sent to the server,
* in function of the options and available methods. * in function of the options and available methods.
@@ -385,6 +433,13 @@ int set_client_kex(ssh_session session){
ssh_get_random(client->cookie, 16, 0); ssh_get_random(client->cookie, 16, 0);
memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **)); memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **));
/* first check if we have specific host key methods */
if(session->opts.wanted_methods[SSH_HOSTKEYS] == NULL){
/* Only if no override */
session->opts.wanted_methods[SSH_HOSTKEYS] =
ssh_client_select_hostkeys(session);
}
for (i = 0; i < KEX_METHODS_SIZE; i++) { for (i = 0; i < KEX_METHODS_SIZE; i++) {
wanted = session->opts.wanted_methods[i]; wanted = session->opts.wanted_methods[i];
if (wanted == NULL) if (wanted == NULL)

View File

@@ -34,7 +34,7 @@
#include "libssh/misc.h" #include "libssh/misc.h"
#include "libssh/pki.h" #include "libssh/pki.h"
#include "libssh/options.h" #include "libssh/options.h"
#include "libssh/knownhosts.h"
/*todo: remove this include */ /*todo: remove this include */
#include "libssh/string.h" #include "libssh/string.h"
@@ -647,6 +647,102 @@ int ssh_write_knownhost(ssh_session session) {
return 0; return 0;
} }
#define KNOWNHOSTS_MAXTYPES 10
/**
* @internal
* @brief Check which kind of host keys should be preferred for connection
* by reading the known_hosts file.
*
* @param[in] session The SSH session to use.
*
* @returns array of supported key types
* NULL on error
*/
char **ssh_knownhosts_algorithms(ssh_session session) {
FILE *file = NULL;
char **tokens;
char *host;
char *hostport;
const char *type;
int match;
char **array;
int i=0, j;
if (session->opts.knownhosts == NULL) {
if (ssh_options_apply(session) < 0) {
ssh_set_error(session, SSH_REQUEST_DENIED,
"Can't find a known_hosts file");
return NULL;
}
}
if (session->opts.host == NULL) {
return NULL;
}
host = ssh_lowercase(session->opts.host);
hostport = ssh_hostport(host, session->opts.port);
array = malloc(sizeof(char *) * KNOWNHOSTS_MAXTYPES);
if (host == NULL || hostport == NULL || array == NULL) {
ssh_set_error_oom(session);
SAFE_FREE(host);
SAFE_FREE(hostport);
SAFE_FREE(array);
return NULL;
}
do {
tokens = ssh_get_knownhost_line(&file,
session->opts.knownhosts, &type);
/* End of file, return the current state */
if (tokens == NULL) {
break;
}
match = match_hashed_host(host, tokens[0]);
if (match == 0){
match = match_hostname(hostport, tokens[0], strlen(tokens[0]));
}
if (match == 0) {
match = match_hostname(host, tokens[0], strlen(tokens[0]));
}
if (match == 0) {
match = match_hashed_host(hostport, tokens[0]);
}
if (match) {
/* We got a match. Now check the key type */
SSH_LOG(SSH_LOG_DEBUG, "server %s:%d has %s in known_hosts",
host, session->opts.port, type);
/* don't copy more than once */
for(j=0;j<i && match;++j){
if(strcmp(array[j], type)==0)
match=0;
}
if (match){
array[i] = strdup(type);
i++;
if(i>= KNOWNHOSTS_MAXTYPES-1){
tokens_free(tokens);
break;
}
}
}
tokens_free(tokens);
} while (1);
array[i]=NULL;
SAFE_FREE(host);
SAFE_FREE(hostport);
if (file != NULL) {
fclose(file);
}
/* Return the current state at end of file */
return array;
}
/** @} */ /** @} */
/* vim: set ts=4 sw=4 et cindent: */ /* vim: set ts=4 sw=4 et cindent: */

View File

@@ -208,8 +208,8 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
ssh_callbacks_exists(channel->callbacks, channel_pty_window_change_function)) { ssh_callbacks_exists(channel->callbacks, channel_pty_window_change_function)) {
rc = channel->callbacks->channel_pty_window_change_function(session, rc = channel->callbacks->channel_pty_window_change_function(session,
channel, channel,
msg->channel_request.height, msg->channel_request.width, msg->channel_request.width, msg->channel_request.height,
msg->channel_request.pxheight, msg->channel_request.pxwidth, msg->channel_request.pxwidth, msg->channel_request.pxheight,
channel->callbacks->userdata); channel->callbacks->userdata);
} else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_EXEC && } else if (msg->channel_request.type == SSH_CHANNEL_REQUEST_EXEC &&
ssh_callbacks_exists(channel->callbacks, channel_exec_request_function)) { ssh_callbacks_exists(channel->callbacks, channel_exec_request_function)) {

View File

@@ -697,7 +697,6 @@ char *ssh_path_expand_tilde(const char *d) {
} }
char *ssh_path_expand_escape(ssh_session session, const char *s) { char *ssh_path_expand_escape(ssh_session session, const char *s) {
#define MAX_BUF_SIZE 4096
char host[NI_MAXHOST]; char host[NI_MAXHOST];
char buf[MAX_BUF_SIZE]; char buf[MAX_BUF_SIZE];
char *r, *x = NULL; char *r, *x = NULL;

View File

@@ -251,12 +251,18 @@ int ssh_packet_socket_callback(const void *data, size_t receivedlen, void *user)
* Decrypt the rest of the packet (blocksize bytes already * Decrypt the rest of the packet (blocksize bytes already
* have been decrypted) * have been decrypted)
*/ */
rc = packet_decrypt(session, uint32_t buffer_len = buffer_get_rest_len(session->in_buffer);
((uint8_t*)buffer_get_rest(session->in_buffer) + blocksize),
buffer_get_rest_len(session->in_buffer) - blocksize); /* The following check avoids decrypting zero bytes */
if (rc < 0) { if (buffer_len > blocksize) {
ssh_set_error(session, SSH_FATAL, "Decrypt error"); uint8_t *payload = ((uint8_t*)buffer_get_rest(session->in_buffer) + blocksize);
goto error; uint32_t plen = buffer_len - blocksize;
rc = packet_decrypt(session, payload, plen);
if (rc < 0) {
ssh_set_error(session, SSH_FATAL, "Decrypt error");
goto error;
}
} }
/* copy the last part from the incoming buffer */ /* copy the last part from the incoming buffer */

View File

@@ -106,7 +106,7 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user
size_t processed=0; size_t processed=0;
uint32_t padding; uint32_t padding;
uint32_t crc; uint32_t crc;
uint32_t len; uint32_t len, buffer_len;
ssh_session session=(ssh_session)user; ssh_session session=(ssh_session)user;
switch (session->packet_state){ switch (session->packet_state){
@@ -168,11 +168,16 @@ int ssh_packet_socket_callback1(const void *data, size_t receivedlen, void *user
* We decrypt everything, missing the lenght part (which was * We decrypt everything, missing the lenght part (which was
* previously read, unencrypted, and is not part of the buffer * previously read, unencrypted, and is not part of the buffer
*/ */
if (packet_decrypt(session, buffer_len = ssh_buffer_get_len(session->in_buffer);
ssh_buffer_get_begin(session->in_buffer), if (buffer_len > 0) {
ssh_buffer_get_len(session->in_buffer)) < 0) { int rc;
ssh_set_error(session, SSH_FATAL, "Packet decrypt error"); rc = packet_decrypt(session,
goto error; ssh_buffer_get_begin(session->in_buffer),
buffer_len);
if (rc < 0) {
ssh_set_error(session, SSH_FATAL, "Packet decrypt error");
goto error;
}
} }
} }
#ifdef DEBUG_CRYPTO #ifdef DEBUG_CRYPTO
@@ -300,6 +305,8 @@ int packet_send1(ssh_session session) {
ssh_buffer_get_len(session->out_buffer)); ssh_buffer_get_len(session->out_buffer));
#endif #endif
/* session->out_buffer should have more than sizeof(uint32_t) bytes
in it as required for packet_encrypt */
packet_encrypt(session, (unsigned char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t), packet_encrypt(session, (unsigned char *)ssh_buffer_get_begin(session->out_buffer) + sizeof(uint32_t),
ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t)); ssh_buffer_get_len(session->out_buffer) - sizeof(uint32_t));

View File

@@ -22,6 +22,7 @@
*/ */
#include "config.h" #include "config.h"
#include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -59,6 +60,9 @@ uint32_t packet_decrypt_len(ssh_session session, char *crypted){
int packet_decrypt(ssh_session session, void *data,uint32_t len) { int packet_decrypt(ssh_session session, void *data,uint32_t len) {
struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher; struct ssh_cipher_struct *crypto = session->current_crypto->in_cipher;
char *out = NULL; char *out = NULL;
assert(len);
if(len % session->current_crypto->in_cipher->blocksize != 0){ if(len % session->current_crypto->in_cipher->blocksize != 0){
ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len); ssh_set_error(session, SSH_FATAL, "Cryptographic functions must be set on at least one blocksize (received %d)",len);
return SSH_ERROR; return SSH_ERROR;
@@ -89,6 +93,8 @@ unsigned char *packet_encrypt(ssh_session session, void *data, uint32_t len) {
unsigned int finallen; unsigned int finallen;
uint32_t seq; uint32_t seq;
assert(len);
if (!session->current_crypto) { if (!session->current_crypto) {
return NULL; /* nothing to do here */ return NULL; /* nothing to do here */
} }

View File

@@ -200,9 +200,11 @@ int pki_pubkey_build_ecdsa(ssh_key key, int nid, ssh_string e)
return -1; return -1;
} }
/* EC_KEY_set_public_key duplicates p */
ok = EC_KEY_set_public_key(key->ecdsa, p); ok = EC_KEY_set_public_key(key->ecdsa, p);
EC_POINT_free(p);
if (!ok) { if (!ok) {
EC_POINT_free(p); return -1;
} }
return 0; return 0;
@@ -1094,41 +1096,65 @@ static ssh_string _RSA_do_sign(const unsigned char *digest,
return sig_blob; return sig_blob;
} }
static ssh_string pki_dsa_signature_to_blob(const ssh_signature sig)
{
char buffer[40] = { 0 };
ssh_string sig_blob = NULL;
ssh_string r;
int r_len, r_offset_in, r_offset_out;
ssh_string s;
int s_len, s_offset_in, s_offset_out;
r = make_bignum_string(sig->dsa_sig->r);
if (r == NULL) {
return NULL;
}
s = make_bignum_string(sig->dsa_sig->s);
if (s == NULL) {
ssh_string_free(r);
return NULL;
}
r_len = ssh_string_len(r);
r_offset_in = (r_len > 20) ? (r_len - 20) : 0;
r_offset_out = (r_len < 20) ? (20 - r_len) : 0;
s_len = ssh_string_len(s);
s_offset_in = (s_len > 20) ? (s_len - 20) : 0;
s_offset_out = (s_len < 20) ? (20 - s_len) : 0;
memcpy(buffer + r_offset_out,
((char *)ssh_string_data(r)) + r_offset_in,
r_len - r_offset_in);
memcpy(buffer + 20 + s_offset_out,
((char *)ssh_string_data(s)) + s_offset_in,
s_len - s_offset_in);
ssh_string_free(r);
ssh_string_free(s);
sig_blob = ssh_string_new(40);
if (sig_blob == NULL) {
return NULL;
}
ssh_string_fill(sig_blob, buffer, 40);
return sig_blob;
}
ssh_string pki_signature_to_blob(const ssh_signature sig) ssh_string pki_signature_to_blob(const ssh_signature sig)
{ {
char buffer[40] = {0};
ssh_string sig_blob = NULL;
ssh_string r; ssh_string r;
ssh_string s; ssh_string s;
ssh_string sig_blob = NULL;
switch(sig->type) { switch(sig->type) {
case SSH_KEYTYPE_DSS: case SSH_KEYTYPE_DSS:
r = make_bignum_string(sig->dsa_sig->r); sig_blob = pki_dsa_signature_to_blob(sig);
if (r == NULL) {
return NULL;
}
s = make_bignum_string(sig->dsa_sig->s);
if (s == NULL) {
ssh_string_free(r);
return NULL;
}
memcpy(buffer,
((char *)ssh_string_data(r)) + ssh_string_len(r) - 20,
20);
memcpy(buffer + 20,
((char *)ssh_string_data(s)) + ssh_string_len(s) - 20,
20);
ssh_string_free(r);
ssh_string_free(s);
sig_blob = ssh_string_new(40);
if (sig_blob == NULL) {
return NULL;
}
ssh_string_fill(sig_blob, buffer, 40);
break; break;
case SSH_KEYTYPE_RSA: case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1: case SSH_KEYTYPE_RSA1:
@@ -1188,6 +1214,61 @@ ssh_string pki_signature_to_blob(const ssh_signature sig)
return sig_blob; return sig_blob;
} }
static ssh_signature pki_signature_from_rsa_blob(const ssh_key pubkey,
const ssh_string sig_blob,
ssh_signature sig)
{
uint32_t pad_len = 0;
char *blob_orig;
char *blob_padded_data;
ssh_string sig_blob_padded;
size_t len = ssh_string_len(sig_blob);
size_t rsalen= RSA_size(pubkey->rsa);
if (len > rsalen) {
ssh_pki_log("Signature is too big: %lu > %lu",
(unsigned long)len, (unsigned long)rsalen);
goto errout;
}
#ifdef DEBUG_CRYPTO
ssh_pki_log("RSA signature len: %lu", (unsigned long)len);
ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len);
#endif
if (len == rsalen) {
sig->rsa_sig = ssh_string_copy(sig_blob);
} else {
/* pad the blob to the expected rsalen size */
ssh_pki_log("RSA signature len %lu < %lu",
(unsigned long)len, (unsigned long)rsalen);
pad_len = rsalen - len;
sig_blob_padded = ssh_string_new(rsalen);
if (sig_blob_padded == NULL) {
goto errout;
}
blob_padded_data = (char *) ssh_string_data(sig_blob_padded);
blob_orig = (char *) ssh_string_data(sig_blob);
/* front-pad the buffer with zeroes */
BURN_BUFFER(blob_padded_data, pad_len);
/* fill the rest with the actual signature blob */
memcpy(blob_padded_data + pad_len, blob_orig, len);
sig->rsa_sig = sig_blob_padded;
}
return sig;
errout:
ssh_signature_free(sig);
return NULL;
}
ssh_signature pki_signature_from_blob(const ssh_key pubkey, ssh_signature pki_signature_from_blob(const ssh_key pubkey,
const ssh_string sig_blob, const ssh_string sig_blob,
enum ssh_keytypes_e type) enum ssh_keytypes_e type)
@@ -1196,7 +1277,6 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
ssh_string r; ssh_string r;
ssh_string s; ssh_string s;
size_t len; size_t len;
size_t rsalen;
sig = ssh_signature_new(); sig = ssh_signature_new();
if (sig == NULL) { if (sig == NULL) {
@@ -1260,29 +1340,7 @@ ssh_signature pki_signature_from_blob(const ssh_key pubkey,
break; break;
case SSH_KEYTYPE_RSA: case SSH_KEYTYPE_RSA:
case SSH_KEYTYPE_RSA1: case SSH_KEYTYPE_RSA1:
rsalen = RSA_size(pubkey->rsa); sig = pki_signature_from_rsa_blob(pubkey, sig_blob, sig);
if (len > rsalen) {
ssh_pki_log("Signature is to big size: %lu",
(unsigned long)len);
ssh_signature_free(sig);
return NULL;
}
if (len < rsalen) {
ssh_pki_log("RSA signature len %lu < %lu",
(unsigned long)len, (unsigned long)rsalen);
}
#ifdef DEBUG_CRYPTO
ssh_pki_log("RSA signature len: %lu", (unsigned long)len);
ssh_print_hexa("RSA signature", ssh_string_data(sig_blob), len);
#endif
sig->rsa_sig = ssh_string_copy(sig_blob);
if (sig->rsa_sig == NULL) {
ssh_signature_free(sig);
return NULL;
}
break; break;
case SSH_KEYTYPE_ECDSA: case SSH_KEYTYPE_ECDSA:
#ifdef HAVE_OPENSSL_ECC #ifdef HAVE_OPENSSL_ECC

View File

@@ -545,7 +545,7 @@ int ssh_scp_read_string(ssh_scp scp, char *buffer, size_t len){
* @see ssh_scp_request_get_warning() * @see ssh_scp_request_get_warning()
*/ */
int ssh_scp_pull_request(ssh_scp scp){ int ssh_scp_pull_request(ssh_scp scp){
char buffer[4096] = {0}; char buffer[MAX_BUF_SIZE] = {0};
char *mode=NULL; char *mode=NULL;
char *p,*tmp; char *p,*tmp;
uint64_t size; uint64_t size;
@@ -642,7 +642,7 @@ int ssh_scp_pull_request(ssh_scp scp){
* the message failed, or sending it in a bad state. * the message failed, or sending it in a bad state.
*/ */
int ssh_scp_deny_request(ssh_scp scp, const char *reason){ int ssh_scp_deny_request(ssh_scp scp, const char *reason){
char buffer[4096]; char buffer[MAX_BUF_SIZE];
int err; int err;
if(scp==NULL) if(scp==NULL)
return SSH_ERROR; return SSH_ERROR;
@@ -814,7 +814,7 @@ int ssh_scp_integer_mode(const char *mode){
*/ */
char *ssh_scp_string_mode(int mode){ char *ssh_scp_string_mode(int mode){
char buffer[16]; char buffer[16];
snprintf(buffer,sizeof(buffer),"%.4d",mode); snprintf(buffer,sizeof(buffer),"%.4o",mode);
return strdup(buffer); return strdup(buffer);
} }

View File

@@ -309,6 +309,38 @@ const char* ssh_get_serverbanner(ssh_session session) {
return session->serverbanner; return session->serverbanner;
} }
/**
* @brief get the name of the input for the given session.
*
* @param[in] session The SSH session.
*
* @return Returns cipher name or NULL.
*/
const char* ssh_get_cipher_in(ssh_session session) {
if ((session != NULL) &&
(session->current_crypto != NULL) &&
(session->current_crypto->in_cipher != NULL)) {
return session->current_crypto->in_cipher->name;
}
return NULL;
}
/**
* @brief get the name of the output cipher for the given session.
*
* @param[in] session The SSH session.
*
* @return Returns cipher name or NULL.
*/
const char* ssh_get_cipher_out(ssh_session session) {
if ((session != NULL) &&
(session->current_crypto != NULL) &&
(session->current_crypto->out_cipher != NULL)) {
return session->current_crypto->out_cipher->name;
}
return NULL;
}
/** /**
* @brief Disconnect impolitely from a remote host by closing the socket. * @brief Disconnect impolitely from a remote host by closing the socket.
* *
@@ -567,7 +599,11 @@ int ssh_handle_packets_termination(ssh_session session,
} }
} }
ssh_timestamp_init(&ts); /* avoid unnecessary syscall for the SSH_TIMEOUT_NONBLOCKING case */
if (timeout != SSH_TIMEOUT_NONBLOCKING) {
ssh_timestamp_init(&ts);
}
tm = timeout; tm = timeout;
while(!fct(user)) { while(!fct(user)) {
ret = ssh_handle_packets(session, tm); ret = ssh_handle_packets(session, tm);
@@ -695,8 +731,13 @@ void ssh_socket_exception_callback(int code, int errno_code, void *user){
ssh_session session=(ssh_session)user; ssh_session session=(ssh_session)user;
SSH_LOG(SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code); SSH_LOG(SSH_LOG_RARE,"Socket exception callback: %d (%d)",code, errno_code);
session->session_state=SSH_SESSION_STATE_ERROR; session->session_state = SSH_SESSION_STATE_ERROR;
ssh_set_error(session,SSH_FATAL,"Socket error: %s",strerror(errno_code)); if (errno_code == 0 && code == SSH_SOCKET_EXCEPTION_EOF) {
ssh_set_error(session, SSH_FATAL, "Socket error: disconnected");
} else {
ssh_set_error(session, SSH_FATAL, "Socket error: %s", strerror(errno_code));
}
session->ssh_connection_callback(session); session->ssh_connection_callback(session);
} }

View File

@@ -308,7 +308,7 @@ int sftp_packet_write(sftp_session sftp, uint8_t type, ssh_buffer payload){
} }
sftp_packet sftp_packet_read(sftp_session sftp) { sftp_packet sftp_packet_read(sftp_session sftp) {
unsigned char buffer[4096]; unsigned char buffer[MAX_BUF_SIZE];
sftp_packet packet = NULL; sftp_packet packet = NULL;
uint32_t size; uint32_t size;
int r; int r;

View File

@@ -218,7 +218,7 @@ void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks){
*/ */
int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s){ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int revents, void *v_s){
ssh_socket s=(ssh_socket )v_s; ssh_socket s=(ssh_socket )v_s;
char buffer[4096]; char buffer[MAX_BUF_SIZE];
int r; int r;
int err=0; int err=0;
socklen_t errlen=sizeof(err); socklen_t errlen=sizeof(err);
@@ -291,7 +291,7 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p, socket_t fd, int r
buffer_get_rest_len(s->in_buffer), buffer_get_rest_len(s->in_buffer),
s->callbacks->userdata); s->callbacks->userdata);
buffer_pass_bytes(s->in_buffer,r); buffer_pass_bytes(s->in_buffer,r);
} while (r > 0); } while ((r > 0) && (s->state == SSH_SOCKET_CONNECTED));
/* p may have been freed, so don't use it /* p may have been freed, so don't use it
* anymore in this function */ * anymore in this function */
p = NULL; p = NULL;
@@ -440,6 +440,8 @@ void ssh_socket_close(ssh_socket s){
ssh_poll_free(s->poll_out); ssh_poll_free(s->poll_out);
s->poll_out=NULL; s->poll_out=NULL;
} }
s->state = SSH_SOCKET_CLOSED;
} }
/** /**

View File

@@ -59,8 +59,28 @@ struct ssh_threads_callbacks_struct *ssh_threads_get_noop(void) {
static struct ssh_threads_callbacks_struct *user_callbacks =&ssh_threads_noop; static struct ssh_threads_callbacks_struct *user_callbacks =&ssh_threads_noop;
#ifdef HAVE_LIBGCRYPT #ifdef HAVE_LIBGCRYPT
#if (GCRYPT_VERSION_NUMBER >= 0x010600)
/* libgcrypt >= 1.6 does not support custom callbacks */
GCRY_THREAD_OPTION_PTHREAD_IMPL;
/* Libgcrypt specific way of handling thread callbacks */ static int libgcrypt_thread_init(void){
if(user_callbacks == NULL)
return SSH_ERROR;
if(user_callbacks == &ssh_threads_noop)
return SSH_OK;
if (strcmp(user_callbacks->type, "threads_pthread") == 0){
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
return SSH_OK;
} else {
/* not supported */
SSH_LOG(SSH_LOG_WARN, "Custom thread handlers not supported with libgcrypt >=1.6, using pthreads");
gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread);
return SSH_OK;
}
}
#else
/* Libgcrypt < 1.6 specific way of handling thread callbacks */
static struct gcry_thread_cbs gcrypt_threads_callbacks; static struct gcry_thread_cbs gcrypt_threads_callbacks;
@@ -79,7 +99,8 @@ static int libgcrypt_thread_init(void){
gcry_control(GCRYCTL_SET_THREAD_CBS, &gcrypt_threads_callbacks); gcry_control(GCRYCTL_SET_THREAD_CBS, &gcrypt_threads_callbacks);
return SSH_OK; return SSH_OK;
} }
#else #endif /* GCRYPT_VERSION_NUMBER */
#else /* HAVE_LIBGCRYPT */
/* Libcrypto specific stuff */ /* Libcrypto specific stuff */

View File

@@ -23,8 +23,26 @@
#include "torture.h" #include "torture.h"
#include "session.c" #include "session.c"
#include "known_hosts.c"
#define KNOWNHOSTFILES "libssh_torture_knownhosts" #define KNOWNHOSTFILES "libssh_torture_knownhosts"
#define BADRSA "AAAAB3NzaC1yc2EAAAADAQABAAABAQChm5" \
"a6Av65O8cKtx5YXOnui3wJnYE6A6J/I4kZSAibbn14Jcl+34VJQwv96f25AxNmo" \
"NwoiZV93IzdypQmiuieh6s6wB9WhYjU9K/6CkIpNhpCxswA90b3ePjS7LnR9B9J" \
"slPSbG1H0KC1c5lb7G3utXteXtM+4YvCvpN5VdC4CpghT+p0cwN2Na8Md5vRItz" \
"YgIytryNn7LLiwYfoSxvWigFrTTZsrVtCOYyNgklmffpGdzuC43wdANvTewfI9G" \
"o71r8EXmEc228CrYPmb8Scv3mpXFK/BosohSGkPlEHu9lf3YjnknBicDaVtJOYp" \
"wnXJPjZo2EhG79HxDRpjJHH"
#define BADDSA "AAAAB3NzaC1kc3MAAACBAITDKqGQ5aC5wHySG6ZdL1+BVBY2nLP5vzw3i3pvZfP" \
"yNUS0UCwrt5pajsMvDRGXXebTJhWVonDnv8tpSgiuIBXMZrma8CU1KCFGRzwb/n8" \
"cc5tJmIphlOUTrObjBmsRz7u1eZmoaddXC9ask6BNnt0DmhzYi2esL3mbardy8IN" \
"zAAAAFQDlPFCm410pgQQPb3X5FWjyVEIl+QAAAIAp0vqfir8K8p+zP4dzFG7ppnt" \
"DjaXf3ge6URF7f5xPDo6CClGo2JQ2REF8NxM7K9cLgR9Ifx2ahO48UMgrXEl/BOp" \
"IQHpeBqUz26a49O5J0WEW16YSUHxWwMxWVe/SRmyKdTUZJ6fcepH88JNqm3XudNn" \
"s78grM+yx9mcXnK2AsAAAAIBxpF8ZQIlGrSgwCmCfwjP156bC3Ya6LYf9ZpLJ0dX" \
"EcxqLVllrNEvd2EGD9p16BYO2yaalYon8im59PtOcul2ay5XQ6rVDQ2T0pgNUpsI" \
"h0dSi8VJXI1wes5HTyLsv9VBmU1uCXUUvufoQKfF/OcSH0ufcCpnd62g1/adZcy2" \
"WJg=="
static void setup(void **state) { static void setup(void **state) {
int verbosity=torture_libssh_verbosity(); int verbosity=torture_libssh_verbosity();
@@ -93,10 +111,184 @@ static void torture_knownhosts_port(void **state) {
assert_true(rc == SSH_SERVER_KNOWN_OK); assert_true(rc == SSH_SERVER_KNOWN_OK);
} }
static void torture_knownhosts_fail(void **state) {
ssh_session session = *state;
FILE *file;
int rc;
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES);
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa");
assert_true(rc == SSH_OK);
file = fopen(KNOWNHOSTFILES, "w");
assert_true(file != NULL);
fprintf(file, "localhost ssh-rsa %s\n", BADRSA);
fclose(file);
rc = ssh_connect(session);
assert_true(rc==SSH_OK);
rc = ssh_is_server_known(session);
assert_true(rc == SSH_SERVER_KNOWN_CHANGED);
}
static void torture_knownhosts_other(void **state) {
ssh_session session = *state;
FILE *file;
int rc;
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES);
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-dss");
assert_true(rc == SSH_OK);
file = fopen(KNOWNHOSTFILES, "w");
assert_true(file != NULL);
fprintf(file, "localhost ssh-rsa %s\n", BADRSA);
fclose(file);
rc = ssh_connect(session);
assert_true(rc==SSH_OK);
rc = ssh_is_server_known(session);
assert_true(rc == SSH_SERVER_FOUND_OTHER);
}
static void torture_knownhosts_other_auto(void **state) {
ssh_session session = *state;
int rc;
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES);
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-dss");
assert_true(rc == SSH_OK);
rc = ssh_connect(session);
assert_true(rc==SSH_OK);
rc = ssh_is_server_known(session);
assert_true(rc == SSH_SERVER_NOT_KNOWN);
rc = ssh_write_knownhost(session);
assert_true(rc == SSH_OK);
ssh_disconnect(session);
ssh_free(session);
/* connect again and check host key */
*state = session = ssh_new();
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES);
assert_true(rc == SSH_OK);
rc = ssh_connect(session);
assert_true(rc==SSH_OK);
/* ssh-rsa is the default but libssh should try ssh-dss instead */
rc = ssh_is_server_known(session);
assert_true(rc == SSH_SERVER_KNOWN_OK);
}
static void torture_knownhosts_conflict(void **state) {
ssh_session session = *state;
FILE *file;
int rc;
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES);
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa");
assert_true(rc == SSH_OK);
file = fopen(KNOWNHOSTFILES, "w");
assert_true(file != NULL);
fprintf(file, "localhost ssh-rsa %s\n", BADRSA);
fprintf(file, "localhost ssh-dss %s\n", BADDSA);
fclose(file);
rc = ssh_connect(session);
assert_true(rc==SSH_OK);
rc = ssh_is_server_known(session);
assert_true(rc == SSH_SERVER_KNOWN_CHANGED);
rc = ssh_write_knownhost(session);
assert_true(rc==SSH_OK);
ssh_disconnect(session);
ssh_free(session);
/* connect again and check host key */
*state = session = ssh_new();
ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES);
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "ssh-rsa");
assert_true(rc == SSH_OK);
rc = ssh_connect(session);
assert_true(rc == SSH_OK);
rc = ssh_is_server_known(session);
assert_true(rc == SSH_SERVER_KNOWN_OK);
}
static void torture_knownhosts_precheck(void **state) {
ssh_session session = *state;
FILE *file;
int rc;
char **kex;
rc = ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
assert_true(rc == SSH_OK);
rc = ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, KNOWNHOSTFILES);
assert_true(rc == SSH_OK);
file = fopen(KNOWNHOSTFILES, "w");
assert_true(file != NULL);
fprintf(file, "localhost ssh-rsa %s\n", BADRSA);
fprintf(file, "localhost ssh-dss %s\n", BADDSA);
fclose(file);
kex = ssh_knownhosts_algorithms(session);
assert_true(kex != NULL);
assert_string_equal(kex[0],"ssh-rsa");
assert_string_equal(kex[1],"ssh-dss");
assert_true(kex[2]==NULL);
free(kex[1]);
free(kex[0]);
free(kex);
}
int torture_run_tests(void) { int torture_run_tests(void) {
int rc; int rc;
const UnitTest tests[] = { const UnitTest tests[] = {
unit_test_setup_teardown(torture_knownhosts_port, setup, teardown), unit_test_setup_teardown(torture_knownhosts_port, setup, teardown),
unit_test_setup_teardown(torture_knownhosts_fail, setup, teardown),
unit_test_setup_teardown(torture_knownhosts_other, setup, teardown),
unit_test_setup_teardown(torture_knownhosts_other_auto, setup, teardown),
unit_test_setup_teardown(torture_knownhosts_conflict, setup, teardown),
unit_test_setup_teardown(torture_knownhosts_precheck, setup, teardown)
}; };
ssh_init(); ssh_init();

View File

@@ -0,0 +1,139 @@
/* Test the ability to use ssh_bind_accept_fd.
*
* Expected behavior: Prints "SUCCESS!"
*
* Faulty behavior observed before change: Connection timeout
*/
#include <arpa/inet.h>
#include <err.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
struct options {
const char *server_keyfile;
} options;
const char HOST[] = "127.0.0.1";
const int PORT = 3333;
int get_connection() {
int rc, server_socket, client_conn = -1;
struct sockaddr_in server_socket_addr;
struct sockaddr_storage client_conn_addr;
socklen_t client_conn_addr_size = sizeof(client_conn_addr);
server_socket = socket(PF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
goto out;
}
server_socket_addr.sin_family = AF_INET;
server_socket_addr.sin_port = htons(PORT);
if (inet_pton(AF_INET, HOST, &server_socket_addr.sin_addr) != 1) {
goto out;
}
rc = bind(server_socket, (struct sockaddr *)&server_socket_addr,
sizeof(server_socket_addr));
if (rc < 0) {
goto out;
}
if (listen(server_socket, 0) < 0) {
goto out;
}
client_conn = accept(server_socket,
(struct sockaddr *)&client_conn_addr,
&client_conn_addr_size);
out:
return client_conn;
}
void ssh_server() {
ssh_bind bind;
ssh_session session;
int client_conn = get_connection();
if (client_conn < 0) {
err(1, "get_connection");
}
bind = ssh_bind_new();
if (!bind) {
errx(1, "ssh_bind_new");
}
if (ssh_bind_options_set(bind, SSH_BIND_OPTIONS_DSAKEY,
options.server_keyfile) != SSH_OK) {
errx(1, "ssh_bind_options_set(SSH_BIND_OPTIONS_DSAKEY");
}
session = ssh_new();
if (!session) {
errx(1, "ssh_new");
}
if (ssh_bind_accept_fd(bind, session, client_conn) != SSH_OK) {
errx(1, "ssh_bind_accept: %s", ssh_get_error(bind));
}
if (ssh_handle_key_exchange(session) != SSH_OK) {
errx(1, "ssh_handle_key_exchange: %s", ssh_get_error(session));
}
printf("SUCCESS!\n");
}
void ssh_client() {
ssh_session session;
session = ssh_new();
if (!session) {
errx(1, "ssh_new");
}
if (ssh_options_set(session, SSH_OPTIONS_HOST, HOST) < 0) {
errx(1, "ssh_options_set(SSH_OPTIONS_HOST)");
}
if (ssh_options_set(session, SSH_OPTIONS_PORT, &PORT) < 0) {
errx(1, "ssh_options_set(SSH_OPTIONS_PORT)");
}
if (ssh_connect(session) != SSH_OK) {
errx(1, "ssh_connect: %s", ssh_get_error(session));
}
}
int main(int argc, const char *argv[]) {
if (argc != 2) {
printf("Usage: %s <private key file>\n", argv[0]);
exit(1);
}
options.server_keyfile = argv[1];
pid_t pid = fork();
if (pid < 0) {
errx(1, "fork");
}
if (pid == 0) {
/* Allow the server to get set up */
sleep(3);
ssh_client();
} else {
ssh_server();
}
return 0;
}