Compare commits

...

46 Commits

Author SHA1 Message Date
Jakub Jelen
ca9c055d7c libssh-0.11.4
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-06 20:13:56 +01:00
Andreas Schneider
c53b0ef3ec CVE-2025-14821 cmake: Fix global config location on Windows
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit 6a7f19ec34)
2026-02-06 20:13:56 +01:00
Andreas Schneider
f49b4442a9 cmake: Reformat DefineOptions.cmake with cmake-format
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 12ccea8dd8)
2026-02-06 09:56:22 +01:00
Jakub Jelen
a5e4b12090 CVE-2026-0964 scp: Reject invalid paths received through scp
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit daa80818f8)
2026-02-06 09:55:37 +01:00
Jakub Jelen
bf390a0426 CVE-2026-0965 config: Do not attempt to read non-regular and too large configuration files
Changes also the reading of known_hosts to use the new helper function

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit a5eb30dbfd)
2026-02-06 09:55:09 +01:00
Jakub Jelen
3e1d276a5a CVE-2026-0966 doc: Update guided tour to use SHA256 fingerprints
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 1b2a4f760b)
2026-02-06 09:50:08 +01:00
Jakub Jelen
b156391833 CVE-2026-0966 tests: Test coverage for ssh_get_hexa
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 9be83584a5)
2026-02-06 09:50:05 +01:00
Jakub Jelen
6ba5ff1b7b CVE-2026-0966 misc: Avoid heap buffer underflow in ssh_get_hexa
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 417a095e67)
2026-02-06 09:49:54 +01:00
Jakub Jelen
6d74aa6138 CVE-2026-0967 match: Avoid recursive matching (ReDoS)
The specially crafted patterns (from configuration files) could cause
exhaustive search or timeouts.

Previous attempts to fix this by limiting recursion to depth 16 avoided
stack overflow, but not timeouts. This is due to the backtracking,
which caused the exponential time complexity O(N^16) of existing algorithm.

This is code comes from the same function from OpenSSH, where this code
originates from, which is not having this issue (due to not limiting the number
of recursion), but will also easily exhaust stack due to unbound recursion:

05bcd0cadf

This is an attempt to simplify the algorithm by preventing the backtracking
to previous wildcard, which should keep the same behavior for existing inputs
while reducing the complexity to linear O(N*M).

This fixes the long-term issue we had with fuzzing as well as recently reported
security issue by Kang Yang.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit a411de5ce8)
2026-02-06 09:48:36 +01:00
Jakub Jelen
212121971f CVE-2026-0968 tests: Reproducer for invalid longname data
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 90a5d8f473)
2026-02-06 09:48:28 +01:00
Jakub Jelen
796d85f786 CVE-2026-0968: sftp: Sanitize input handling in sftp_parse_longname()
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 20856f44c1)
2026-02-06 09:48:03 +01:00
Jakub Jelen
4c0c4ea32e sftp: Initialize pointers
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 28d6d10ddc)
2026-02-06 09:48:01 +01:00
Jakub Jelen
02c6f5f7ec Reproducer for out of bounds read of SFTP extensions
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit b90b7f2451)
2026-02-06 09:46:57 +01:00
Jakub Jelen
f80670a7ab sftp: Fix out-of-bound read from sftp extensions
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 855a0853ad)
2026-02-06 09:46:55 +01:00
Jakub Jelen
829fae6404 sftp: Reformat sftp_extensions_* API
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit f0fdfd4f49)
2026-02-06 09:46:54 +01:00
Eshan Kelkar
74670207bf connector: Fix sftp aio read/write with ProxyJump
Addresses issue #319

The commit description explains:
1. Fix for sftp aio + read
2. Fix for sftp aio + write

1. Fix for sftp aio + read
-------------------------
The reproducer provided in the issue description had a model
as follows (with one jump host):
fd_1---(socket_pair)---fd_2---(connector)----channel(fd_3)-----server

Via debugging, it was noticed that the channel connected directly to
the server stored a lot of unbuffered data (received from the server)
that wasn't being written to fd_2 via the connector API.

(Here on, channel refers to the channel(fd_3) in the diagram connected
directly to the server)

Consider the situation, where after a bit of progress in the transfer,
the server has sent all the requested data (requested via outstanding
requests) and all of that data is stored in channel->stdout_buffer. Say
this data is 10,000 bytes.

At this point, all the client (fd_1) is doing is waiting for all
outstanding requests. (and processing thei responses)

- POLLOUT event callback gets generated indicating that fd_2 is
  available for writing.

- ssh_connector_fd_out_cb() gets called to handle the POLLOUT.

- Assuming connector->in_available was true, 4096 (CHUNKSIZE) bytes
  get read from the channel. (really channel->stdout_buffer) leaving
  10,000 - 4096 = 5904 bytes unread in the channel.

- The read bytes are sent via fd_2 (so that fd_1 can recv them)

- After this, the callback sets connector->in_available to 0 and
  connector->out_wontblock to 0.

- Since out_wontblock has been set to 0 ssh_connector_reset_pollevents()
  (called after the callback returns) will consider POLLOUT events on the
  connector output.

- (Based on assumption before) Since the client (fd_1) is eagerly
  awaiting responses and processing them, the received data gets
  processed quickly and fd_2 is available for sending/writing.

- POLLOUT event gets generated for fd_2 indicating that its available
  for writing/sending to fd_1

- ssh_connector_fd_out_cb() gets called to handle the POLLOUT

- Since connector->in_available is 0 (and
  ssh_connector_channel_data_cb() has not been trigerred in between
  as we have assumed before that all the data has already been received on the
  channel and is stored in the channel->stdout_buffer), ssh_connector_fd_out_cb()
  does nothing besides setting connector->out_wontblock to 1.

- Since out_wontblock has been set to 1 ssh_connector_reset_pollevents()
  (called after the callback returns) will IGNORE POLLOUT events on the
  connector output.

- So, at this point, the channel->buffer contains 5706 bytes and the
  fd_2 is available for writing/sending (out_wontblock is 1), but
  nothing happens and the transfer gets stalled/hanged.

In my opinion, this hanging occurs because connector->in_available was
incorrectly set to 0 despite the channel buffer having 5706 bytes in it.

This commit changes that code to consider the data available to read
on the channel (includes buffered data as well as polled data on
channel's internal fd) and taking that into consideration to set
in_available appropriately. (Instead of unconditionally setting it to 0 as the
current code does) so that the next time POLLOUT gets received on fd_2
the ssh_connector_fd_out_cb() does read from the channel and write to
fd_2 (as the connector->in_available flag would be set).

2. Fix for sftp aio + write
-------------------------------------
On writing tests for sftp aio + proxyjump, it was encountered
that file uploads were also hanging. Though I was not able to
pin point the exact cause for this hanging, the nature of hanging
was observed to be as follows:

- sftp aio write + proxyjump blocks/hangs occasionally (not always)

- It hangs at different points in the test

- hang point 1: Sometimes it hangs after sending the first write request
  (i.e. the second write request call hangs and never returns, at this point
  we are not even waiting for response, just sending data). A lot of pending
  data to write to socket/fd was noticed at this hang point.

- hang point 2: Sometimes it hangs while waiting for the second write request
  response.

- It hangs at ssh_handle_packets_termination (i.e. this is the
  call that never returns), in context to hang point 1, this occurs due to
  trying to flush the channel during sftp_packet_write, and in context to
  hang point 2, this occurs due to trying to read an sftp response packet.

- Not sure why, but more the verbose logging/printing I do, the lesser
  occasionally test hangs (e.g. 1 test in 6-7 test runs), maybe this could
  be a hint for a race condition / thread interaction related bug, but am
  not sure.

Fix: On modifying the connector code to mark out_wontblock
to 0 in case of output channel only when the channel's
remote window is 0, the hanging no longer occured.

Though, as mentioned before, I don't know the exact problem
(i.e. case causing hanging) the fix addresses, but the fix
is logical (if remote window is +ve data can still be written
to channel and hence out_wontblock should not be reset to 0, it should
be set to 1) and fixes the issue hence is added to this commit.

Signed-off-by: Eshan Kelkar <eshankelkar@galorithm.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit dc39902006)
2026-02-06 09:46:45 +01:00
Jakub Jelen
e34e6c8c94 sftp: Remove needless newline from log messages
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 7342e73d10)
2026-02-06 09:45:26 +01:00
Jakub Jelen
7ecca1fd26 auth: Log the username used for authentication
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 7e235f8748)
2026-02-06 09:44:56 +01:00
Eshan Kelkar
dbb4c4cc11 bugfix: test presence of before_connection before dereferencing
A proxyjump callback structure consists of three callbacks
as of this writing: before_connection, authenticate and
verify_knownhost. One or more of these callbacks can be
set as NULL by the user to indicate that libssh should use
the defaults.

The code checked the presence of the callback stucture but
not whether before_connection was available or not (non NULL)
before dereferencing it.

This could lead to undefined behaviour if the user specifies
say authenticate and verify_knownhost for a jump host but not
before_connection.

This commit fixes the code to add a check for before_connection
being non NULL before trying access it.

Signed-off-by: Eshan Kelkar <eshankelkar@galorithm.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 26b9ba5f8c)
2026-02-06 09:44:46 +01:00
Jakub Jelen
ce4c81c614 Reproducer for memory leak from parsing knonw hosts
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <norbertpocs0@gmail.com>
(cherry picked from commit 1b3c061aae)
2026-02-06 09:44:40 +01:00
Jakub Jelen
441dfa5327 knownhosts: Avoid memory leaks on invalid entries
When `known_hosts` file contained matching valid entry followed by
invalid entry, the first record was already allocated in
`ssh_known_hosts_read_entries()`, but not freed on error.

This could cause possible memory leaks in client, but we do not
consider them as security relevant as the leaks do not add up and
successful exploitaition is hard or impossible.

Originally reported by Kang Yang.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <norbertpocs0@gmail.com>
(cherry picked from commit 1525ea3dda)
2026-02-06 09:44:30 +01:00
Jakub Jelen
c4681304cc cmake: Propagate WITH_FINAL to abimap conditionally
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit b2abcf8534)
2026-02-06 09:44:21 +01:00
Jakub Jelen
1887c9dbcd Require abimap 0.4.0
The version 0.4.0 fixed the issues of multi-digit version numbers
which we hit with releaseing libssh ABI version 4_10 with last
release.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 809f9b7729)
2026-02-06 09:44:16 +01:00
Jakub Jelen
9e2ec491de tests: Workaround softhsm-2.7.0 bug in hashed ECDSA
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit d297621c33)
2026-02-06 09:44:08 +01:00
Jakub Jelen
ee1f7293cf ci: Use pkcs11-provider on c9s
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit ad8d0c1e03)
2026-02-06 09:42:59 +01:00
Jakub Jelen
8e85a1067c pki: Improve documentation about pubkey import functions
Resolves: #253 and #254

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 1f1309c915)
2026-02-06 09:41:46 +01:00
Jakub Jelen
cc1a811ae5 connect: Avoid calling close with negative argument
The `first` is intialized to -1 and if we reach this without setting this, we
needlessly call close(-1). It should be no-op, but better be safe.

Thanks coverity!

CID 1644001

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit c9abf5ebbb)
2026-02-06 09:40:48 +01:00
Jakub Jelen
463665db9d connect: Close possibly leaking socket
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit c36bd2304a)
2026-02-06 09:40:19 +01:00
Jakub Jelen
20d78046aa socket: Properly close the proxyjump FD when proxy connection fails
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit deffea5ad2)
2026-02-06 09:40:01 +01:00
Samir Benmendil
39a62cef44 tests: suppress leaks from NSS modules
Signed-off-by: Samir Benmendil <me@rmz.io>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit ce45ba8c61)
2026-01-05 16:38:12 +01:00
Jakub Jelen
7969b6de3c Suppress remaining OpenSSL 3.5 memory leaks
Reported as

https://github.com/openssl/openssl/issues/29077

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit b042477f83)
2026-01-05 16:37:46 +01:00
Jakub Jelen
b207e39d28 tests: Adjust valgrind supressions for Fedora 43
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit a94df4bb8f)
2026-01-05 16:37:42 +01:00
Jakub Jelen
6230b24ff5 tests: Test proxyjump configuration parsing
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 82db6a7ab3)
2026-01-05 13:40:22 +01:00
Jakub Jelen
e668b03dd7 tests: Reproducer for missing value to LogLevel
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 163373c9d9)
2026-01-05 13:40:13 +01:00
Jakub Jelen
77ce02d809 config: Allow setting username from configuration
... file, even if it was already set before. The options
level handles what was already set.

The proxyJump implementation sets the username from the proxyjump, which
is setting it to NULL, effectively writing the current username to the
new session, which was not possible to override due to the following check.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 320844669a)
2026-01-05 13:38:49 +01:00
Jakub Jelen
d61b0dc7cc tests: Improve test coverage of comparing certificates
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 701a2155a7)
2026-01-05 13:19:20 +01:00
Jakub Jelen
d12eb770ac pki: Fix comparing public key of certificate
When the first key object is a certificate object, this match will
fall through to the generic key comparison that is unable to handle
the ed25519 keys and fails.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 38f3d158f6)
2026-01-05 13:15:53 +01:00
Jakub Jelen
03b29a6874 pki: Avoild false positive matches when comparing certificates in mbedtls and gcrypt
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 0d5a2652b4)
2026-01-05 13:15:09 +01:00
Jakub Jelen
99957fb561 ssh_known_hosts_get_algorithms: Simplify cleanup ...
...  and prevent memory leak of host_port on memory allocation failure.

Thanks Xiaoke Wang for the report!

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 9d6df9d0fa)
2026-01-05 13:10:19 +01:00
Jakub Jelen
3e9175e66a server: Check strdup allocation failure
Thanks Xiaoke Wang for the report!

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit ee180c660e)
2026-01-05 13:09:59 +01:00
Jakub Jelen
bf295abb5b tests: Remove the -E which is overridden by followed -E on ctest CLI
The threads_pki_rsa was running and working under valgrind for some
time already without anyone noticing this syntax does not work.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 950abbbd81)
2026-01-05 13:07:08 +01:00
Jakub Jelen
7f14df3eac tests: Avoid needless pthread_exit()
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit b9c6701c68)
2026-01-05 13:06:56 +01:00
Pavol Žáčik
c206e5d84e client: Reset session packet state on disconnect
When reusing session structures for multiple
connections, the packet state could be SIZE_READ
before disconnect, causing initial packets of the
next connection to be misinterpreted.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit 41b8b3326c)
2026-01-05 13:06:31 +01:00
Jakub Jelen
274b8f19b3 connector: Fix default connector flags
Originally reported by Jeremy Cross <jcross@beyondtrust.com> in #461

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit c932790b82)
2026-01-05 13:05:30 +01:00
Jakub Jelen
39a88d62c9 connector: Reformat
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
(cherry picked from commit 8a0aa17bca)
2026-01-05 13:05:02 +01:00
Francesco Rollo
94f12090b5 fix(bind): Remove code duplication in ssh_bind_listen
Signed-off-by: Francesco Rollo <eferollo@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit c94e2efcf1)
2026-01-05 13:04:10 +01:00
44 changed files with 1676 additions and 377 deletions

View File

@@ -147,7 +147,7 @@ centos9s/openssl_3.5.x/x86_64:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS9_BUILD
extends: .tests extends: .tests
variables: variables:
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
script: script:
- export OPENSSL_ENABLE_SHA1_SIGNATURES=1 - export OPENSSL_ENABLE_SHA1_SIGNATURES=1
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. && - cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&

View File

@@ -1,6 +1,20 @@
CHANGELOG CHANGELOG
========= =========
version 0.11.4 (released 2026-02-10)
* Security:
* CVE-2025-14821: libssh loads configuration files from the C:\etc directory
on Windows
* CVE-2026-0964: SCP Protocol Path Traversal in ssh_scp_pull_request()
* CVE-2026-0965: Possible Denial of Service when parsing unexpected
configuration files
* CVE-2026-0966: Buffer underflow in ssh_get_hexa() on invalid input
* CVE-2026-0967: Specially crafted patterns could cause DoS
* CVE-2026-0968: OOB Read in sftp_parse_longname()
* libssh-2026-sftp-extensions: Read buffer overrun when handling SFTP
extensions
* Stability and compatibility improvements of ProxyJump
version 0.11.3 (released 2025-09-09) version 0.11.3 (released 2025-09-09)
* Security: * Security:
* CVE-2025-8114: Fix NULL pointer dereference after allocation failure * CVE-2025-8114: Fix NULL pointer dereference after allocation failure

View File

@@ -9,7 +9,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
include(DefineCMakeDefaults) include(DefineCMakeDefaults)
include(DefineCompilerFlags) include(DefineCompilerFlags)
project(libssh VERSION 0.11.3 LANGUAGES C) project(libssh VERSION 0.11.4 LANGUAGES C)
# global needed variable # global needed variable
set(APPLICATION_NAME ${PROJECT_NAME}) set(APPLICATION_NAME ${PROJECT_NAME})
@@ -21,7 +21,7 @@ set(APPLICATION_NAME ${PROJECT_NAME})
# 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.10.3") set(LIBRARY_VERSION "4.10.4")
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
@@ -77,7 +77,7 @@ endif (WITH_NACL)
# Disable symbol versioning in non UNIX platforms # Disable symbol versioning in non UNIX platforms
if (UNIX) if (UNIX)
find_package(ABIMap 0.3.1) find_package(ABIMap 0.4.0)
else (UNIX) else (UNIX)
set(WITH_SYMBOL_VERSIONING OFF) set(WITH_SYMBOL_VERSIONING OFF)
endif (UNIX) endif (UNIX)
@@ -168,6 +168,10 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
set(ALLOW_ABI_BREAK "BREAK_ABI") set(ALLOW_ABI_BREAK "BREAK_ABI")
endif() endif()
if (WITH_FINAL)
set(FINAL "FINAL")
endif()
# Target we can depend on in 'make dist' # Target we can depend on in 'make dist'
set(_SYMBOL_TARGET "${PROJECT_NAME}.map") set(_SYMBOL_TARGET "${PROJECT_NAME}.map")
@@ -180,7 +184,7 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
RELEASE_NAME_VERSION ${PROJECT_NAME}_${LIBRARY_VERSION} RELEASE_NAME_VERSION ${PROJECT_NAME}_${LIBRARY_VERSION}
CURRENT_MAP ${MAP_PATH} CURRENT_MAP ${MAP_PATH}
COPY_TO ${MAP_PATH} COPY_TO ${MAP_PATH}
FINAL ${FINAL}
${ALLOW_ABI_BREAK}) ${ALLOW_ABI_BREAK})
# Write the current version to the source # Write the current version to the source

View File

@@ -12,20 +12,35 @@ option(WITH_PCAP "Compile with Pcap generation support" ON)
option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF) option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF)
option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(BUILD_SHARED_LIBS "Build shared libraries" ON)
option(WITH_PKCS11_URI "Build with PKCS#11 URI support" OFF) option(WITH_PKCS11_URI "Build with PKCS#11 URI support" OFF)
option(WITH_PKCS11_PROVIDER "Use the PKCS#11 provider for accessing pkcs11 objects" OFF) option(WITH_PKCS11_PROVIDER
"Use the PKCS#11 provider for accessing pkcs11 objects" OFF)
option(UNIT_TESTING "Build with unit tests" OFF) option(UNIT_TESTING "Build with unit tests" OFF)
option(CLIENT_TESTING "Build with client tests; requires openssh" OFF) option(CLIENT_TESTING "Build with client tests; requires openssh" OFF)
option(SERVER_TESTING "Build with server tests; requires openssh and dropbear" OFF) option(SERVER_TESTING "Build with server tests; requires openssh and dropbear"
option(GSSAPI_TESTING "Build with GSSAPI tests; requires krb5-server,krb5-libs and krb5-workstation" OFF) OFF)
option(WITH_BENCHMARKS "Build benchmarks tools; enables unit testing and client tests" OFF) option(
GSSAPI_TESTING
"Build with GSSAPI tests; requires krb5-server,krb5-libs and krb5-workstation"
OFF)
option(WITH_BENCHMARKS
"Build benchmarks tools; enables unit testing and client tests" OFF)
option(WITH_EXAMPLES "Build examples" ON) option(WITH_EXAMPLES "Build examples" ON)
option(WITH_NACL "Build with libnacl (curve25519)" ON) option(WITH_NACL "Build with libnacl (curve25519)" ON)
option(WITH_SYMBOL_VERSIONING "Build with symbol versioning" ON) option(WITH_SYMBOL_VERSIONING "Build with symbol versioning" ON)
option(WITH_ABI_BREAK "Allow ABI break" OFF) option(WITH_ABI_BREAK "Allow ABI break" OFF)
option(WITH_GEX "Enable DH Group exchange mechanisms" ON) option(WITH_GEX "Enable DH Group exchange mechanisms" ON)
option(WITH_INSECURE_NONE "Enable insecure none cipher and MAC algorithms (not suitable for production!)" OFF) option(
option(WITH_EXEC "Enable libssh to execute arbitrary commands from configuration files or options (match exec, proxy commands and OpenSSH-based proxy-jumps)." ON) WITH_INSECURE_NONE
option(FUZZ_TESTING "Build with fuzzer for the server and client (automatically enables none cipher!)" OFF) "Enable insecure none cipher and MAC algorithms (not suitable for production!)"
OFF)
option(
WITH_EXEC
"Enable libssh to execute arbitrary commands from configuration files or options (match exec, proxy commands and OpenSSH-based proxy-jumps)."
ON)
option(
FUZZ_TESTING
"Build with fuzzer for the server and client (automatically enables none cipher!)"
OFF)
option(PICKY_DEVELOPER "Build with picky developer flags" OFF) option(PICKY_DEVELOPER "Build with picky developer flags" OFF)
if (WITH_ZLIB) if (WITH_ZLIB)
@@ -35,34 +50,47 @@ else (WITH_ZLIB)
endif (WITH_ZLIB) endif (WITH_ZLIB)
if (WITH_BENCHMARKS) if (WITH_BENCHMARKS)
set(UNIT_TESTING ON) set(UNIT_TESTING ON)
set(CLIENT_TESTING ON) set(CLIENT_TESTING ON)
endif() endif ()
if (UNIT_TESTING OR CLIENT_TESTING OR SERVER_TESTING OR GSSAPI_TESTING) if (UNIT_TESTING
set(BUILD_STATIC_LIB ON) OR CLIENT_TESTING
endif() OR SERVER_TESTING
OR GSSAPI_TESTING)
set(BUILD_STATIC_LIB ON)
endif ()
if (WITH_NACL) if (WITH_NACL)
set(WITH_NACL ON) set(WITH_NACL ON)
endif (WITH_NACL) endif (WITH_NACL)
if (WITH_ABI_BREAK) if (WITH_ABI_BREAK)
set(WITH_SYMBOL_VERSIONING ON) set(WITH_SYMBOL_VERSIONING ON)
endif (WITH_ABI_BREAK) endif (WITH_ABI_BREAK)
set(GLOBAL_CONF_DIR "/etc/ssh")
if (WIN32)
# Use PROGRAMDATA on Windows
if (DEFINED ENV{PROGRAMDATA})
set(GLOBAL_CONF_DIR "$ENV{PROGRAMDATA}/ssh")
else ()
set(GLOBAL_CONF_DIR "C:/ProgramData/ssh")
endif ()
endif ()
if (NOT GLOBAL_BIND_CONFIG) if (NOT GLOBAL_BIND_CONFIG)
set(GLOBAL_BIND_CONFIG "/etc/ssh/libssh_server_config") set(GLOBAL_BIND_CONFIG "${GLOBAL_CONF_DIR}/libssh_server_config")
endif (NOT GLOBAL_BIND_CONFIG) endif (NOT GLOBAL_BIND_CONFIG)
if (NOT GLOBAL_CLIENT_CONFIG) if (NOT GLOBAL_CLIENT_CONFIG)
set(GLOBAL_CLIENT_CONFIG "/etc/ssh/ssh_config") set(GLOBAL_CLIENT_CONFIG "${GLOBAL_CONF_DIR}/ssh_config")
endif (NOT GLOBAL_CLIENT_CONFIG) endif (NOT GLOBAL_CLIENT_CONFIG)
if (FUZZ_TESTING) if (FUZZ_TESTING)
set(WITH_INSECURE_NONE ON) set(WITH_INSECURE_NONE ON)
endif (FUZZ_TESTING) endif (FUZZ_TESTING)
if (WIN32) if (WIN32)
set(WITH_EXEC 0) set(WITH_EXEC 0)
endif(WIN32) endif (WIN32)

View File

@@ -8,6 +8,10 @@
#cmakedefine BINARYDIR "${BINARYDIR}" #cmakedefine BINARYDIR "${BINARYDIR}"
#cmakedefine SOURCEDIR "${SOURCEDIR}" #cmakedefine SOURCEDIR "${SOURCEDIR}"
/* Global configuration directory */
#cmakedefine USR_GLOBAL_CONF_DIR "${USR_GLOBAL_CONF_DIR}"
#cmakedefine GLOBAL_CONF_DIR "${GLOBAL_CONF_DIR}"
/* Global bind configuration file path */ /* Global bind configuration file path */
#cmakedefine GLOBAL_BIND_CONFIG "${GLOBAL_BIND_CONFIG}" #cmakedefine GLOBAL_BIND_CONFIG "${GLOBAL_BIND_CONFIG}"

View File

@@ -190,7 +190,6 @@ int verify_knownhost(ssh_session session)
ssh_key srv_pubkey = NULL; ssh_key srv_pubkey = NULL;
size_t hlen; size_t hlen;
char buf[10]; char buf[10];
char *hexa = NULL;
char *p = NULL; char *p = NULL;
int cmp; int cmp;
int rc; int rc;
@@ -201,7 +200,7 @@ int verify_knownhost(ssh_session session)
} }
rc = ssh_get_publickey_hash(srv_pubkey, rc = ssh_get_publickey_hash(srv_pubkey,
SSH_PUBLICKEY_HASH_SHA1, SSH_PUBLICKEY_HASH_SHA256,
&hash, &hash,
&hlen); &hlen);
ssh_key_free(srv_pubkey); ssh_key_free(srv_pubkey);
@@ -217,7 +216,7 @@ int verify_knownhost(ssh_session session)
break; break;
case SSH_KNOWN_HOSTS_CHANGED: case SSH_KNOWN_HOSTS_CHANGED:
fprintf(stderr, "Host key for server changed: it is now:\n"); fprintf(stderr, "Host key for server changed: it is now:\n");
ssh_print_hexa("Public key hash", hash, hlen); ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
fprintf(stderr, "For security reasons, connection will be stopped\n"); fprintf(stderr, "For security reasons, connection will be stopped\n");
ssh_clean_pubkey_hash(&hash); ssh_clean_pubkey_hash(&hash);
@@ -238,10 +237,9 @@ int verify_knownhost(ssh_session session)
/* FALL THROUGH to SSH_SERVER_NOT_KNOWN behavior */ /* FALL THROUGH to SSH_SERVER_NOT_KNOWN behavior */
case SSH_KNOWN_HOSTS_UNKNOWN: case SSH_KNOWN_HOSTS_UNKNOWN:
hexa = ssh_get_hexa(hash, hlen);
fprintf(stderr,"The server is unknown. Do you trust the host key?\n"); fprintf(stderr,"The server is unknown. Do you trust the host key?\n");
fprintf(stderr, "Public key hash: %s\n", hexa); fprintf(stderr, "Public key hash: ");
ssh_string_free_char(hexa); ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
ssh_clean_pubkey_hash(&hash); ssh_clean_pubkey_hash(&hash);
p = fgets(buf, sizeof(buf), stdin); p = fgets(buf, sizeof(buf), stdin);
if (p == NULL) { if (p == NULL) {

View File

@@ -36,6 +36,7 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdbool.h> #include <stdbool.h>
#endif /* _WIN32 */ #endif /* _WIN32 */
#include <stdio.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -136,6 +137,8 @@ int ssh_check_username_syntax(const char *username);
void ssh_proxyjumps_free(struct ssh_list *proxy_jump_list); void ssh_proxyjumps_free(struct ssh_list *proxy_jump_list);
bool ssh_libssh_proxy_jumps(void); bool ssh_libssh_proxy_jumps(void);
FILE *ssh_strict_fopen(const char *filename, size_t max_file_size);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -473,6 +473,9 @@ char *ssh_strerror(int err_num, char *buf, size_t buflen);
#define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1) #define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1)
int encode_current_tty_opts(unsigned char *buf, size_t buflen); int encode_current_tty_opts(unsigned char *buf, size_t buflen);
/** The default maximum file size for a configuration file */
#define SSH_MAX_CONFIG_FILE_SIZE 16 * 1024 * 1024
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -1 +1 @@
4.10.3 4.10.4

View File

@@ -0,0 +1,445 @@
_ssh_log
buffer_free
buffer_get
buffer_get_len
buffer_new
channel_accept_x11
channel_change_pty_size
channel_close
channel_forward_accept
channel_forward_cancel
channel_forward_listen
channel_free
channel_get_exit_status
channel_get_session
channel_is_closed
channel_is_eof
channel_is_open
channel_new
channel_open_forward
channel_open_session
channel_poll
channel_read
channel_read_buffer
channel_read_nonblocking
channel_request_env
channel_request_exec
channel_request_pty
channel_request_pty_size
channel_request_send_signal
channel_request_sftp
channel_request_shell
channel_request_subsystem
channel_request_x11
channel_select
channel_send_eof
channel_set_blocking
channel_write
channel_write_stderr
privatekey_free
privatekey_from_file
publickey_free
publickey_from_file
publickey_from_privatekey
publickey_to_string
sftp_aio_begin_read
sftp_aio_begin_write
sftp_aio_free
sftp_aio_wait_read
sftp_aio_wait_write
sftp_async_read
sftp_async_read_begin
sftp_attributes_free
sftp_canonicalize_path
sftp_channel_default_data_callback
sftp_channel_default_subsystem_request
sftp_chmod
sftp_chown
sftp_client_message_free
sftp_client_message_get_data
sftp_client_message_get_filename
sftp_client_message_get_flags
sftp_client_message_get_submessage
sftp_client_message_get_type
sftp_client_message_set_filename
sftp_close
sftp_closedir
sftp_dir_eof
sftp_expand_path
sftp_extension_supported
sftp_extensions_get_count
sftp_extensions_get_data
sftp_extensions_get_name
sftp_file_set_blocking
sftp_file_set_nonblocking
sftp_free
sftp_fstat
sftp_fstatvfs
sftp_fsync
sftp_get_client_message
sftp_get_error
sftp_handle
sftp_handle_alloc
sftp_handle_remove
sftp_hardlink
sftp_home_directory
sftp_init
sftp_limits
sftp_limits_free
sftp_lsetstat
sftp_lstat
sftp_mkdir
sftp_new
sftp_new_channel
sftp_open
sftp_opendir
sftp_read
sftp_readdir
sftp_readlink
sftp_rename
sftp_reply_attr
sftp_reply_data
sftp_reply_handle
sftp_reply_name
sftp_reply_names
sftp_reply_names_add
sftp_reply_status
sftp_rewind
sftp_rmdir
sftp_seek
sftp_seek64
sftp_send_client_message
sftp_server_free
sftp_server_init
sftp_server_new
sftp_server_version
sftp_setstat
sftp_stat
sftp_statvfs
sftp_statvfs_free
sftp_symlink
sftp_tell
sftp_tell64
sftp_unlink
sftp_utimes
sftp_write
ssh_accept
ssh_add_channel_callbacks
ssh_auth_list
ssh_basename
ssh_bind_accept
ssh_bind_accept_fd
ssh_bind_fd_toaccept
ssh_bind_free
ssh_bind_get_fd
ssh_bind_listen
ssh_bind_new
ssh_bind_options_parse_config
ssh_bind_options_set
ssh_bind_set_blocking
ssh_bind_set_callbacks
ssh_bind_set_fd
ssh_blocking_flush
ssh_buffer_add_data
ssh_buffer_free
ssh_buffer_get
ssh_buffer_get_data
ssh_buffer_get_len
ssh_buffer_new
ssh_buffer_reinit
ssh_channel_accept_forward
ssh_channel_accept_x11
ssh_channel_cancel_forward
ssh_channel_change_pty_size
ssh_channel_close
ssh_channel_free
ssh_channel_get_exit_state
ssh_channel_get_exit_status
ssh_channel_get_session
ssh_channel_is_closed
ssh_channel_is_eof
ssh_channel_is_open
ssh_channel_listen_forward
ssh_channel_new
ssh_channel_open_auth_agent
ssh_channel_open_forward
ssh_channel_open_forward_port
ssh_channel_open_forward_unix
ssh_channel_open_reverse_forward
ssh_channel_open_session
ssh_channel_open_x11
ssh_channel_poll
ssh_channel_poll_timeout
ssh_channel_read
ssh_channel_read_nonblocking
ssh_channel_read_timeout
ssh_channel_request_auth_agent
ssh_channel_request_env
ssh_channel_request_exec
ssh_channel_request_pty
ssh_channel_request_pty_size
ssh_channel_request_pty_size_modes
ssh_channel_request_send_break
ssh_channel_request_send_exit_signal
ssh_channel_request_send_exit_status
ssh_channel_request_send_signal
ssh_channel_request_sftp
ssh_channel_request_shell
ssh_channel_request_subsystem
ssh_channel_request_x11
ssh_channel_select
ssh_channel_send_eof
ssh_channel_set_blocking
ssh_channel_set_counter
ssh_channel_window_size
ssh_channel_write
ssh_channel_write_stderr
ssh_clean_pubkey_hash
ssh_connect
ssh_connector_free
ssh_connector_new
ssh_connector_set_in_channel
ssh_connector_set_in_fd
ssh_connector_set_out_channel
ssh_connector_set_out_fd
ssh_copyright
ssh_dirname
ssh_disconnect
ssh_dump_knownhost
ssh_event_add_connector
ssh_event_add_fd
ssh_event_add_session
ssh_event_dopoll
ssh_event_free
ssh_event_new
ssh_event_remove_connector
ssh_event_remove_fd
ssh_event_remove_session
ssh_execute_message_callbacks
ssh_finalize
ssh_forward_accept
ssh_forward_cancel
ssh_forward_listen
ssh_free
ssh_get_cipher_in
ssh_get_cipher_out
ssh_get_clientbanner
ssh_get_disconnect_message
ssh_get_error
ssh_get_error_code
ssh_get_fd
ssh_get_fingerprint_hash
ssh_get_hexa
ssh_get_hmac_in
ssh_get_hmac_out
ssh_get_issue_banner
ssh_get_kex_algo
ssh_get_log_callback
ssh_get_log_level
ssh_get_log_userdata
ssh_get_openssh_version
ssh_get_poll_flags
ssh_get_pubkey
ssh_get_pubkey_hash
ssh_get_publickey
ssh_get_publickey_hash
ssh_get_random
ssh_get_server_publickey
ssh_get_serverbanner
ssh_get_status
ssh_get_version
ssh_getpass
ssh_gssapi_get_creds
ssh_gssapi_set_creds
ssh_handle_key_exchange
ssh_init
ssh_is_blocking
ssh_is_connected
ssh_is_server_known
ssh_key_cmp
ssh_key_dup
ssh_key_free
ssh_key_is_private
ssh_key_is_public
ssh_key_new
ssh_key_type
ssh_key_type_from_name
ssh_key_type_to_char
ssh_known_hosts_parse_line
ssh_knownhosts_entry_free
ssh_log
ssh_message_auth_interactive_request
ssh_message_auth_kbdint_is_response
ssh_message_auth_password
ssh_message_auth_pubkey
ssh_message_auth_publickey
ssh_message_auth_publickey_state
ssh_message_auth_reply_pk_ok
ssh_message_auth_reply_pk_ok_simple
ssh_message_auth_reply_success
ssh_message_auth_set_methods
ssh_message_auth_user
ssh_message_channel_request_channel
ssh_message_channel_request_command
ssh_message_channel_request_env_name
ssh_message_channel_request_env_value
ssh_message_channel_request_open_destination
ssh_message_channel_request_open_destination_port
ssh_message_channel_request_open_originator
ssh_message_channel_request_open_originator_port
ssh_message_channel_request_open_reply_accept
ssh_message_channel_request_open_reply_accept_channel
ssh_message_channel_request_pty_height
ssh_message_channel_request_pty_pxheight
ssh_message_channel_request_pty_pxwidth
ssh_message_channel_request_pty_term
ssh_message_channel_request_pty_width
ssh_message_channel_request_reply_success
ssh_message_channel_request_subsystem
ssh_message_channel_request_x11_auth_cookie
ssh_message_channel_request_x11_auth_protocol
ssh_message_channel_request_x11_screen_number
ssh_message_channel_request_x11_single_connection
ssh_message_free
ssh_message_get
ssh_message_global_request_address
ssh_message_global_request_port
ssh_message_global_request_reply_success
ssh_message_reply_default
ssh_message_retrieve
ssh_message_service_reply_success
ssh_message_service_service
ssh_message_subtype
ssh_message_type
ssh_mkdir
ssh_new
ssh_options_copy
ssh_options_get
ssh_options_get_port
ssh_options_getopt
ssh_options_parse_config
ssh_options_set
ssh_pcap_file_close
ssh_pcap_file_free
ssh_pcap_file_new
ssh_pcap_file_open
ssh_pki_copy_cert_to_privkey
ssh_pki_export_privkey_base64
ssh_pki_export_privkey_base64_format
ssh_pki_export_privkey_file
ssh_pki_export_privkey_file_format
ssh_pki_export_privkey_to_pubkey
ssh_pki_export_pubkey_base64
ssh_pki_export_pubkey_file
ssh_pki_generate
ssh_pki_import_cert_base64
ssh_pki_import_cert_file
ssh_pki_import_privkey_base64
ssh_pki_import_privkey_file
ssh_pki_import_pubkey_base64
ssh_pki_import_pubkey_file
ssh_pki_key_ecdsa_name
ssh_print_hash
ssh_print_hexa
ssh_privatekey_type
ssh_publickey_to_file
ssh_remove_channel_callbacks
ssh_request_no_more_sessions
ssh_scp_accept_request
ssh_scp_close
ssh_scp_deny_request
ssh_scp_free
ssh_scp_init
ssh_scp_leave_directory
ssh_scp_new
ssh_scp_pull_request
ssh_scp_push_directory
ssh_scp_push_file
ssh_scp_push_file64
ssh_scp_read
ssh_scp_request_get_filename
ssh_scp_request_get_permissions
ssh_scp_request_get_size
ssh_scp_request_get_size64
ssh_scp_request_get_warning
ssh_scp_write
ssh_select
ssh_send_debug
ssh_send_ignore
ssh_send_issue_banner
ssh_send_keepalive
ssh_server_init_kex
ssh_service_request
ssh_session_export_known_hosts_entry
ssh_session_get_known_hosts_entry
ssh_session_has_known_hosts_entry
ssh_session_is_known_server
ssh_session_set_disconnect_message
ssh_session_update_known_hosts
ssh_set_agent_channel
ssh_set_agent_socket
ssh_set_auth_methods
ssh_set_blocking
ssh_set_callbacks
ssh_set_channel_callbacks
ssh_set_counters
ssh_set_fd_except
ssh_set_fd_toread
ssh_set_fd_towrite
ssh_set_log_callback
ssh_set_log_level
ssh_set_log_userdata
ssh_set_message_callback
ssh_set_pcap_file
ssh_set_server_callbacks
ssh_silent_disconnect
ssh_string_burn
ssh_string_copy
ssh_string_data
ssh_string_fill
ssh_string_free
ssh_string_free_char
ssh_string_from_char
ssh_string_get_char
ssh_string_len
ssh_string_new
ssh_string_to_char
ssh_threads_get_default
ssh_threads_get_noop
ssh_threads_get_pthread
ssh_threads_set_callbacks
ssh_try_publickey_from_file
ssh_userauth_agent
ssh_userauth_agent_pubkey
ssh_userauth_autopubkey
ssh_userauth_gssapi
ssh_userauth_kbdint
ssh_userauth_kbdint_getanswer
ssh_userauth_kbdint_getinstruction
ssh_userauth_kbdint_getname
ssh_userauth_kbdint_getnanswers
ssh_userauth_kbdint_getnprompts
ssh_userauth_kbdint_getprompt
ssh_userauth_kbdint_setanswer
ssh_userauth_list
ssh_userauth_none
ssh_userauth_offer_pubkey
ssh_userauth_password
ssh_userauth_privatekey_file
ssh_userauth_pubkey
ssh_userauth_publickey
ssh_userauth_publickey_auto
ssh_userauth_publickey_auto_get_current_identity
ssh_userauth_try_publickey
ssh_version
ssh_vlog
ssh_write_knownhost
string_burn
string_copy
string_data
string_fill
string_free
string_from_char
string_len
string_new
string_to_char

View File

@@ -1294,6 +1294,11 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (session == NULL) { if (session == NULL) {
return SSH_AUTH_ERROR; return SSH_AUTH_ERROR;
} }
SSH_LOG(SSH_LOG_INFO,
"Starting authentication as a user %s",
username ? username : session->opts.username);
if (! (session->opts.flags & SSH_OPT_FLAG_PUBKEY_AUTH)) { if (! (session->opts.flags & SSH_OPT_FLAG_PUBKEY_AUTH)) {
session->auth.supported_methods &= ~SSH_AUTH_METHOD_PUBLICKEY; session->auth.supported_methods &= ~SSH_AUTH_METHOD_PUBLICKEY;
return SSH_AUTH_DENIED; return SSH_AUTH_DENIED;

View File

@@ -218,28 +218,12 @@ static int ssh_bind_import_keys(ssh_bind sshbind) {
return SSH_OK; return SSH_OK;
} }
int ssh_bind_listen(ssh_bind sshbind) { int ssh_bind_listen(ssh_bind sshbind)
{
const char *host = NULL; const char *host = NULL;
socket_t fd; socket_t fd;
int rc; int rc;
/* Apply global bind configurations, if it hasn't been applied before */
rc = ssh_bind_options_parse_config(sshbind, NULL);
if (rc != 0) {
ssh_set_error(sshbind, SSH_FATAL,"Could not parse global config");
return SSH_ERROR;
}
/* Set default hostkey paths if no hostkey was found before */
if (sshbind->ecdsakey == NULL &&
sshbind->rsakey == NULL &&
sshbind->ed25519key == NULL) {
sshbind->ecdsakey = strdup("/etc/ssh/ssh_host_ecdsa_key");
sshbind->rsakey = strdup("/etc/ssh/ssh_host_rsa_key");
sshbind->ed25519key = strdup("/etc/ssh/ssh_host_ed25519_key");
}
/* Apply global bind configurations, if it hasn't been applied before */ /* Apply global bind configurations, if it hasn't been applied before */
rc = ssh_bind_options_parse_config(sshbind, NULL); rc = ssh_bind_options_parse_config(sshbind, NULL);
if (rc != 0) { if (rc != 0) {
@@ -289,10 +273,10 @@ int ssh_bind_listen(ssh_bind sshbind) {
} }
sshbind->bindfd = fd; sshbind->bindfd = fd;
} else { } else {
SSH_LOG(SSH_LOG_DEBUG, "Using app-provided bind socket"); SSH_LOG(SSH_LOG_DEBUG, "Using app-provided bind socket");
} }
return 0; return 0;
} }
int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, void *userdata) int ssh_bind_set_callbacks(ssh_bind sshbind, ssh_bind_callbacks callbacks, void *userdata)

View File

@@ -212,7 +212,7 @@ local_parse_file(ssh_bind bind,
return; return;
} }
f = fopen(filename, "r"); f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (f == NULL) { if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load", SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
filename); filename);
@@ -636,7 +636,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
* option to be redefined later by another file. */ * option to be redefined later by another file. */
uint8_t seen[BIND_CFG_MAX] = {0}; uint8_t seen[BIND_CFG_MAX] = {0};
f = fopen(filename, "r"); f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (f == NULL) { if (f == NULL) {
return 0; return 0;
} }

View File

@@ -836,6 +836,7 @@ error:
session->opts.fd = SSH_INVALID_SOCKET; session->opts.fd = SSH_INVALID_SOCKET;
session->session_state = SSH_SESSION_STATE_DISCONNECTED; session->session_state = SSH_SESSION_STATE_DISCONNECTED;
session->pending_call_state = SSH_PENDING_CALL_NONE; session->pending_call_state = SSH_PENDING_CALL_NONE;
session->packet_state = PACKET_STATE_INIT;
while ((it = ssh_list_get_iterator(session->channels)) != NULL) { while ((it = ssh_list_get_iterator(session->channels)) != NULL) {
ssh_channel_do_free(ssh_iterator_value(ssh_channel, it)); ssh_channel_do_free(ssh_iterator_value(ssh_channel, it));

View File

@@ -223,10 +223,9 @@ local_parse_file(ssh_session session,
return; return;
} }
f = fopen(filename, "r"); f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (f == NULL) { if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load", /* The underlying function logs the reasons */
filename);
return; return;
} }
@@ -1066,13 +1065,11 @@ ssh_config_parse_line(ssh_session session,
} }
break; break;
case SOC_USERNAME: case SOC_USERNAME:
if (session->opts.username == NULL) { p = ssh_config_get_str_tok(&s, NULL);
p = ssh_config_get_str_tok(&s, NULL); if (p && *parsing) {
if (p && *parsing) {
ssh_options_set(session, SSH_OPTIONS_USER, p); ssh_options_set(session, SSH_OPTIONS_USER, p);
} }
} break;
break;
case SOC_IDENTITY: case SOC_IDENTITY:
p = ssh_config_get_str_tok(&s, NULL); p = ssh_config_get_str_tok(&s, NULL);
if (p && *parsing) { if (p && *parsing) {
@@ -1466,8 +1463,9 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
int parsing, rv; int parsing, rv;
bool global = 0; bool global = 0;
f = fopen(filename, "r"); f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (f == NULL) { if (f == NULL) {
/* The underlying function logs the reasons */
return 0; return 0;
} }

View File

@@ -281,6 +281,9 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
* connection, otherwise return the first address without error or error */ * connection, otherwise return the first address without error or error */
if (s == -1) { if (s == -1) {
s = first; s = first;
} else if (s != first && first != -1) {
/* Clean up the saved socket if any */
ssh_connect_socket_close(first);
} }
return s; return s;

View File

@@ -166,7 +166,7 @@ int ssh_connector_set_out_channel(ssh_connector connector,
/* Fallback to default value for invalid flags */ /* Fallback to default value for invalid flags */
if (!(flags & SSH_CONNECTOR_STDOUT) && !(flags & SSH_CONNECTOR_STDERR)) { if (!(flags & SSH_CONNECTOR_STDOUT) && !(flags & SSH_CONNECTOR_STDERR)) {
connector->in_flags = SSH_CONNECTOR_STDOUT; connector->out_flags = SSH_CONNECTOR_STDOUT;
} }
return ssh_add_channel_callbacks(channel, &connector->out_channel_cb); return ssh_add_channel_callbacks(channel, &connector->out_channel_cb);
@@ -225,6 +225,87 @@ static void ssh_connector_reset_pollevents(ssh_connector connector)
} }
} }
/**
* @internal
*
* @brief Update the connector's flags after a read-write io
* operation
*
* This should be called after some data is successfully read from
* connector's input and written to connector's output.
*
* @param[in, out] connector Connector for which the io operation occured.
*
* @warning This does not consider the case when the io indicated failure
*
* @warning This does not consider the case when the input indicated that
* EOF was encountered.
*/
static void ssh_connector_update_flags_after_io(ssh_connector connector)
{
/*
* With fds we can afford to mark:
* - in_available as 0 after an fd read (even if more pending data can be
* immediately read from the fd)
*
* - out_wontblock as 0 after an fd write (even if more data can
* be written to the fd without blocking)
*
* since poll events set on the fd will get raised to indicate
* possibility of read/write in case existing situation is apt
* (i.e can read/write occur right now) or if situation becomes
* apt in future (read data becomes available, write becomes
* possible)
*/
/*
* On the other hand, with channels we need to be more careful
* before claiming read/write not possible because channel callbacks
* are called in limited scenarios.
*
* (e.g. connector callback to indicate read data available on input
* channel is called only when new data is received on channel. It is
* not called when we have some pending data in channel's buffers but
* don't receive any new data on the channel)
*
* Hence, in case of channels, blindly setting flag associated with
* read/write input/output to 0 after a read/write may not be a good
* idea as the callback that sets it back to 1 again may not be ever
* called again.
*/
uint32_t window_size;
/* update in_available based on input source (fd or channel) */
if (connector->in_fd != SSH_INVALID_SOCKET) {
connector->in_available = 0;
} else if (connector->in_channel != NULL) {
if (ssh_channel_poll_timeout(connector->in_channel, 0, 0) > 0) {
connector->in_available = 1;
} else {
connector->in_available = 0;
}
} else {
/* connector input is invalid ! */
return;
}
/* update out_wontblock based on output source (fd or channel) */
if (connector->out_fd != SSH_INVALID_SOCKET) {
connector->out_wontblock = 0;
} else if (connector->out_channel != NULL) {
window_size = ssh_channel_window_size(connector->out_channel);
if (window_size > 0) {
connector->out_wontblock = 1;
} else {
connector->out_wontblock = 0;
}
} else {
/* connector output is invalid ! */
return;
}
}
/** /**
* @internal * @internal
* *
@@ -306,8 +387,8 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
ssh_set_error(connector->session, SSH_FATAL, "output socket or channel closed"); ssh_set_error(connector->session, SSH_FATAL, "output socket or channel closed");
return; return;
} }
connector->out_wontblock = 0;
connector->in_available = 0; ssh_connector_update_flags_after_io(connector);
} else { } else {
connector->in_available = 1; connector->in_available = 1;
} }
@@ -359,8 +440,8 @@ ssh_connector_fd_out_cb(ssh_connector connector)
"Output socket or channel closed"); "Output socket or channel closed");
return; return;
} }
connector->in_available = 0;
connector->out_wontblock = 0; ssh_connector_update_flags_after_io(connector);
} else { } else {
connector->out_wontblock = 1; connector->out_wontblock = 1;
} }
@@ -381,15 +462,13 @@ ssh_connector_fd_out_cb(ssh_connector connector)
* *
* @returns 0 * @returns 0
*/ */
static int ssh_connector_fd_cb(ssh_poll_handle p, static int ssh_connector_fd_cb(UNUSED_PARAM(ssh_poll_handle p),
socket_t fd, socket_t fd,
int revents, int revents,
void *userdata) void *userdata)
{ {
ssh_connector connector = userdata; ssh_connector connector = userdata;
(void)p;
if (revents & POLLERR) { if (revents & POLLERR) {
ssh_connector_except(connector, fd); ssh_connector_except(connector, fd);
} else if((revents & (POLLIN|POLLHUP)) && fd == connector->in_fd) { } else if((revents & (POLLIN|POLLHUP)) && fd == connector->in_fd) {
@@ -419,7 +498,7 @@ static int ssh_connector_fd_cb(ssh_poll_handle p,
* @returns Amount of data bytes consumed * @returns Amount of data bytes consumed
*/ */
static int ssh_connector_channel_data_cb(ssh_session session, static int ssh_connector_channel_data_cb(ssh_session session,
ssh_channel channel, UNUSED_PARAM(ssh_channel channel),
void *data, void *data,
uint32_t len, uint32_t len,
int is_stderr, int is_stderr,
@@ -429,10 +508,6 @@ static int ssh_connector_channel_data_cb(ssh_session session,
int w; int w;
uint32_t window; uint32_t window;
(void) session;
(void) channel;
(void) is_stderr;
SSH_LOG(SSH_LOG_TRACE,"connector data on channel"); SSH_LOG(SSH_LOG_TRACE,"connector data on channel");
if (is_stderr && !(connector->in_flags & SSH_CONNECTOR_STDERR)) { if (is_stderr && !(connector->in_flags & SSH_CONNECTOR_STDERR)) {
@@ -484,11 +559,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
return SSH_ERROR; return SSH_ERROR;
} }
connector->out_wontblock = 0; ssh_connector_update_flags_after_io(connector);
connector->in_available = 0;
if ((unsigned int)w < len) {
connector->in_available = 1;
}
ssh_connector_reset_pollevents(connector); ssh_connector_reset_pollevents(connector);
return w; return w;
@@ -510,10 +581,11 @@ static int ssh_connector_channel_data_cb(ssh_session session,
* *
* @returns Amount of data bytes consumed * @returns Amount of data bytes consumed
*/ */
static int ssh_connector_channel_write_wontblock_cb(ssh_session session, static int
ssh_channel channel, ssh_connector_channel_write_wontblock_cb(ssh_session session,
uint32_t bytes, UNUSED_PARAM(ssh_channel channel),
void *userdata) uint32_t bytes,
void *userdata)
{ {
ssh_connector connector = userdata; ssh_connector connector = userdata;
uint8_t buffer[CHUNKSIZE]; uint8_t buffer[CHUNKSIZE];
@@ -553,8 +625,8 @@ static int ssh_connector_channel_write_wontblock_cb(ssh_session session,
return 0; return 0;
} }
connector->in_available = 0;
connector->out_wontblock = 0; ssh_connector_update_flags_after_io(connector);
} else { } else {
connector->out_wontblock = 1; connector->out_wontblock = 1;
} }

View File

@@ -519,9 +519,9 @@ static int ssh_retrieve_dhgroup(char *moduli_file,
} }
if (moduli_file != NULL) if (moduli_file != NULL)
moduli = fopen(moduli_file, "r"); moduli = ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SIZE);
else else
moduli = fopen(MODULI_FILE, "r"); moduli = ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE);
if (moduli == NULL) { if (moduli == NULL) {
char err_msg[SSH_ERRNO_MSG_MAX] = {0}; char err_msg[SSH_ERRNO_MSG_MAX] = {0};

View File

@@ -83,7 +83,7 @@ static struct ssh_tokens_st *ssh_get_knownhost_line(FILE **file,
struct ssh_tokens_st *tokens = NULL; struct ssh_tokens_st *tokens = NULL;
if (*file == NULL) { if (*file == NULL) {
*file = fopen(filename,"r"); *file = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (*file == NULL) { if (*file == NULL) {
return NULL; return NULL;
} }

View File

@@ -216,11 +216,22 @@ ssh_known_hosts_entries_compare(struct ssh_knownhosts_entry *k1,
return 0; return 0;
} }
/* This method reads the known_hosts file referenced by the path /**
* @internal
*
* @brief Read entries from filename to provided list
*
* This method reads the known_hosts file referenced by the path
* in filename argument, and entries matching the match argument * in filename argument, and entries matching the match argument
* will be added to the list in entries argument. * will be added to the list in entries argument.
* If the entries list is NULL, it will allocate a new list. Caller * If the entries list is NULL, it will allocate a new list. Caller
* is responsible to free it even if an error occurs. * is responsible to free it even if an error occurs.
*
* @param match[in] The host name (with port) to match against
* @param filename[in] The known hosts file to parse
* @param entries[in,out] The list of entries to append matching ones
* @return `SSH_OK` on missing file or success parsing,
* `SSH_ERROR` on error
*/ */
static int ssh_known_hosts_read_entries(const char *match, static int ssh_known_hosts_read_entries(const char *match,
const char *filename, const char *filename,
@@ -232,7 +243,7 @@ static int ssh_known_hosts_read_entries(const char *match,
FILE *fp = NULL; FILE *fp = NULL;
int rc; int rc;
fp = fopen(filename, "r"); fp = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (fp == NULL) { if (fp == NULL) {
char err_msg[SSH_ERRNO_MSG_MAX] = {0}; char err_msg[SSH_ERRNO_MSG_MAX] = {0};
SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s", SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s",
@@ -346,6 +357,33 @@ static char *ssh_session_get_host_port(ssh_session session)
/** /**
* @internal * @internal
*
* @brief Free known hosts entries list
*
* @param[in] entry_list The list of ssh_knownhosts_entry items
*/
static void ssh_knownhosts_entries_free(struct ssh_list *entry_list)
{
struct ssh_iterator *it = NULL;
if (entry_list == NULL) {
return;
}
for (it = ssh_list_get_iterator(entry_list);
it != NULL;
it = ssh_list_get_iterator(entry_list)) {
struct ssh_knownhosts_entry *entry = NULL;
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
ssh_knownhosts_entry_free(entry);
ssh_list_remove(entry_list, it);
}
ssh_list_free(entry_list);
}
/**
* @internal
*
* @brief Check which host keys should be preferred for the session. * @brief Check which host keys should be preferred for the session.
* *
* This checks the known_hosts file to find out which algorithms should be * This checks the known_hosts file to find out which algorithms should be
@@ -376,25 +414,23 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
} }
} }
host_port = ssh_session_get_host_port(session);
if (host_port == NULL) {
return NULL;
}
list = ssh_list_new(); list = ssh_list_new();
if (list == NULL) { if (list == NULL) {
ssh_set_error_oom(session); ssh_set_error_oom(session);
SAFE_FREE(host_port);
return NULL; return NULL;
} }
host_port = ssh_session_get_host_port(session);
if (host_port == NULL) {
goto error;
}
rc = ssh_known_hosts_read_entries(host_port, rc = ssh_known_hosts_read_entries(host_port,
session->opts.knownhosts, session->opts.knownhosts,
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); SAFE_FREE(host_port);
ssh_list_free(list); goto error;
return NULL;
} }
rc = ssh_known_hosts_read_entries(host_port, rc = ssh_known_hosts_read_entries(host_port,
@@ -402,21 +438,16 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
&entry_list); &entry_list);
SAFE_FREE(host_port); SAFE_FREE(host_port);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); goto error;
ssh_list_free(list);
return NULL;
} }
if (entry_list == NULL) { if (entry_list == NULL) {
ssh_list_free(list); goto error;
return NULL;
} }
count = ssh_list_count(entry_list); count = ssh_list_count(entry_list);
if (count == 0) { if (count == 0) {
ssh_list_free(list); goto error;
ssh_list_free(entry_list);
return NULL;
} }
for (it = ssh_list_get_iterator(entry_list); for (it = ssh_list_get_iterator(entry_list);
@@ -460,6 +491,7 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
return list; return list;
error: error:
ssh_knownhosts_entries_free(entry_list);
ssh_list_free(list); ssh_list_free(list);
return NULL; return NULL;
} }
@@ -511,6 +543,7 @@ static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e typ
/** /**
* @internal * @internal
*
* @brief Get the host keys algorithms identifiers from the known_hosts files * @brief Get the host keys algorithms identifiers from the known_hosts files
* *
* This expands the signatures types that can be generated from the keys types * This expands the signatures types that can be generated from the keys types
@@ -555,7 +588,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return NULL; return NULL;
} }
@@ -564,7 +597,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
&entry_list); &entry_list);
SAFE_FREE(host_port); SAFE_FREE(host_port);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return NULL; return NULL;
} }
@@ -805,7 +838,6 @@ out:
enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
{ {
struct ssh_list *entry_list = NULL; struct ssh_list *entry_list = NULL;
struct ssh_iterator *it = NULL;
char *host_port = NULL; char *host_port = NULL;
bool global_known_hosts_found = false; bool global_known_hosts_found = false;
bool known_hosts_found = false; bool known_hosts_found = false;
@@ -866,7 +898,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_ERROR; return SSH_KNOWN_HOSTS_ERROR;
} }
} }
@@ -877,7 +909,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_ERROR; return SSH_KNOWN_HOSTS_ERROR;
} }
} }
@@ -889,16 +921,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
for (it = ssh_list_get_iterator(entry_list); ssh_knownhosts_entries_free(entry_list);
it != NULL;
it = ssh_list_get_iterator(entry_list)) {
struct ssh_knownhosts_entry *entry = NULL;
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
ssh_knownhosts_entry_free(entry);
ssh_list_remove(entry_list, it);
}
ssh_list_free(entry_list);
return SSH_KNOWN_HOSTS_OK; return SSH_KNOWN_HOSTS_OK;
} }
@@ -1085,13 +1108,13 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
filename, filename,
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
it = ssh_list_get_iterator(entry_list); it = ssh_list_get_iterator(entry_list);
if (it == NULL) { if (it == NULL) {
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
@@ -1121,16 +1144,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
} }
} }
for (it = ssh_list_get_iterator(entry_list); ssh_knownhosts_entries_free(entry_list);
it != NULL;
it = ssh_list_get_iterator(entry_list)) {
struct ssh_knownhosts_entry *entry = NULL;
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
ssh_knownhosts_entry_free(entry);
ssh_list_remove(entry_list, it);
}
ssh_list_free(entry_list);
return found; return found;
} }
@@ -1202,6 +1216,8 @@ ssh_session_get_known_hosts_entry(ssh_session session,
} }
/** /**
* @internal
*
* @brief Get the known_hosts entry for the current connected session * @brief Get the known_hosts entry for the current connected session
* from the given known_hosts file. * from the given known_hosts file.
* *

View File

@@ -53,85 +53,70 @@
#include "libssh/priv.h" #include "libssh/priv.h"
#define MAX_MATCH_RECURSION 16 /**
* @brief Compare a string with a pattern containing wildcards `*` and `?`
/* *
* Returns true if the given string matches the pattern (which may contain ? * This function is an iterative replacement for the previously recursive
* and * as wildcards), and zero if it does not match. * implementation to avoid exponential complexity (DoS) with specific patterns.
*
* @param[in] s The string to match.
* @param[in] pattern The pattern to match against.
*
* @return 1 if the pattern matches, 0 otherwise.
*/ */
static int match_pattern(const char *s, const char *pattern, size_t limit) static int match_pattern(const char *s, const char *pattern)
{ {
bool had_asterisk = false; const char *s_star = NULL; /* Position in s when last `*` was met */
const char *p_star = NULL; /* Position in pattern after last `*` */
if (s == NULL || pattern == NULL || limit <= 0) { if (s == NULL || pattern == NULL) {
return 0; return 0;
} }
for (;;) { while (*s) {
/* If at end of pattern, accept if also at end of string. */ /* Case 1: Exact match or '?' wildcard */
if (*pattern == '\0') { if (*pattern == *s || *pattern == '?') {
return (*s == '\0'); s++;
}
/* Skip all the asterisks and adjacent question marks */
while (*pattern == '*' || (had_asterisk && *pattern == '?')) {
if (*pattern == '*') {
had_asterisk = true;
}
pattern++; pattern++;
continue;
} }
if (had_asterisk) { /* Case 2: '*' wildcard */
/* If at end of pattern, accept immediately. */ if (*pattern == '*') {
if (!*pattern) /* Record the position of the star and the current string position.
return 1; * We optimistically assume * matches 0 characters first.
/* If next character in pattern is known, optimize. */
if (*pattern != '?') {
/*
* Look instances of the next character in
* pattern, and try to match starting from
* those.
*/
for (; *s; s++)
if (*s == *pattern && match_pattern(s + 1, pattern + 1, limit - 1)) {
return 1;
}
/* Failed. */
return 0;
}
/*
* Move ahead one character at a time and try to
* match at each position.
*/ */
for (; *s; s++) { p_star = ++pattern;
if (match_pattern(s, pattern, limit - 1)) { s_star = s;
return 1; continue;
}
}
/* Failed. */
return 0;
}
/*
* There must be at least one more character in the string.
* If we are at the end, fail.
*/
if (!*s) {
return 0;
} }
/* Check if the next character of the string is acceptable. */ /* Case 3: Mismatch */
if (*pattern != '?' && *pattern != *s) { if (p_star) {
return 0; /* If we have seen a star previously, backtrack.
* We restore the pattern to just after the star,
* but advance the string position (consume one more char for the
* star).
* No need to backtrack to previous stars as any match of the last
* star could be eaten the same way by the previous star.
*/
pattern = p_star;
s = ++s_star;
continue;
} }
/* Move to the next character, both in string and in pattern. */ /* Case 4: Mismatch and no star to backtrack to */
s++; return 0;
}
/* Handle trailing stars in the pattern
* (e.g., pattern "abc*" matching "abc") */
while (*pattern == '*') {
pattern++; pattern++;
} }
/* NOTREACHED */ /* If we reached the end of the pattern, it's a match */
return 0; return (*pattern == '\0');
} }
/* /*
@@ -182,7 +167,7 @@ int match_pattern_list(const char *string, const char *pattern,
sub[subi] = '\0'; sub[subi] = '\0';
/* Try to match the subpattern against the string. */ /* Try to match the subpattern against the string. */
if (match_pattern(string, sub, MAX_MATCH_RECURSION)) { if (match_pattern(string, sub)) {
if (negated) { if (negated) {
return -1; /* Negative */ return -1; /* Negative */
} else { } else {

View File

@@ -37,6 +37,7 @@
#endif /* _WIN32 */ #endif /* _WIN32 */
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
@@ -459,7 +460,7 @@ char *ssh_get_hexa(const unsigned char *what, size_t len)
size_t i; size_t i;
size_t hlen = len * 3; size_t hlen = len * 3;
if (len > (UINT_MAX - 1) / 3) { if (what == NULL || len < 1 || len > (UINT_MAX - 1) / 3) {
return NULL; return NULL;
} }
@@ -2244,4 +2245,77 @@ ssh_libssh_proxy_jumps(void)
return !(t != NULL && t[0] == '1'); return !(t != NULL && t[0] == '1');
} }
/**
* @internal
*
* @brief Safely open a file containing some configuration.
*
* Runs checks if the file can be used as some configuration file (is regular
* file and is not too large). If so, returns the opened file (for reading).
* Otherwise logs error and returns `NULL`.
*
* @param filename The path to the file to open.
* @param max_file_size Maximum file size that is accepted.
*
* @returns the opened file or `NULL` on error.
*/
FILE *ssh_strict_fopen(const char *filename, size_t max_file_size)
{
FILE *f = NULL;
struct stat sb;
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
int r, fd;
/* open first to avoid TOCTOU */
fd = open(filename, O_RDONLY);
if (fd == -1) {
SSH_LOG(SSH_LOG_RARE,
"Failed to open a file %s for reading: %s",
filename,
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return NULL;
}
/* Check the file is sensible for a configuration file */
r = fstat(fd, &sb);
if (r != 0) {
SSH_LOG(SSH_LOG_RARE,
"Failed to stat %s: %s",
filename,
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
close(fd);
return NULL;
}
if ((sb.st_mode & S_IFMT) != S_IFREG) {
SSH_LOG(SSH_LOG_RARE,
"The file %s is not a regular file: skipping",
filename);
close(fd);
return NULL;
}
if ((size_t)sb.st_size > max_file_size) {
SSH_LOG(SSH_LOG_RARE,
"The file %s is too large (%jd MB > %zu MB): skipping",
filename,
(intmax_t)sb.st_size / 1024 / 1024,
max_file_size / 1024 / 1024);
close(fd);
return NULL;
}
f = fdopen(fd, "r");
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE,
"Failed to open a file %s for reading: %s",
filename,
ssh_strerror(r, err_msg, SSH_ERRNO_MSG_MAX));
close(fd);
return NULL;
}
/* the flcose() will close also the underlying fd */
return f;
}
/** @} */ /** @} */

View File

@@ -861,7 +861,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
SAFE_FREE(session->opts.global_knownhosts); SAFE_FREE(session->opts.global_knownhosts);
if (v == NULL) { if (v == NULL) {
session->opts.global_knownhosts = session->opts.global_knownhosts =
strdup("/etc/ssh/ssh_known_hosts"); strdup(GLOBAL_CONF_DIR "/ssh_known_hosts");
if (session->opts.global_knownhosts == NULL) { if (session->opts.global_knownhosts == NULL) {
ssh_set_error_oom(session); ssh_set_error_oom(session);
return -1; return -1;
@@ -1900,7 +1900,7 @@ int ssh_options_apply(ssh_session session)
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS) == 0) { if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS) == 0) {
if (session->opts.global_knownhosts == NULL) { if (session->opts.global_knownhosts == NULL) {
tmp = strdup("/etc/ssh/ssh_known_hosts"); tmp = strdup(GLOBAL_CONF_DIR "/ssh_known_hosts");
} else { } else {
tmp = ssh_path_expand_escape(session, tmp = ssh_path_expand_escape(session,
session->opts.global_knownhosts); session->opts.global_knownhosts);

View File

@@ -701,8 +701,8 @@ int ssh_key_cmp(const ssh_key k1,
ssh_buffer_get_len(k1->cert)); ssh_buffer_get_len(k1->cert));
} }
if (k1->type == SSH_KEYTYPE_ED25519 || if (ssh_key_type_plain(k1->type) == SSH_KEYTYPE_ED25519 ||
k1->type == SSH_KEYTYPE_SK_ED25519) { ssh_key_type_plain(k1->type) == SSH_KEYTYPE_SK_ED25519) {
return pki_ed25519_key_cmp(k1, k2, what); return pki_ed25519_key_cmp(k1, k2, what);
} }
@@ -1621,14 +1621,16 @@ fail:
/** /**
* @brief Import a base64 formatted public key from a memory c-string. * @brief Import a base64 formatted public key from a memory c-string.
* *
* @param[in] b64_key The base64 key to format. * Note that the public key is just the base64 part (without the key
* * type prefix and comment suffix you can find in the OpenSSH public
* @param[in] type The type of the key to format. * key file or known_hosts file).
* *
* @param[in] b64_key The base64 key to import.
* @param[in] type The type of the key to import.
* @param[out] pkey A pointer where the allocated key can be stored. You * @param[out] pkey A pointer where the allocated key can be stored. You
* need to free the memory using ssh_key_free(). * need to free the memory using ssh_key_free().
* *
* @return SSH_OK on success, SSH_ERROR on error. * @return `SSH_OK` on success, `SSH_ERROR` on error.
* *
* @see ssh_key_free() * @see ssh_key_free()
*/ */
@@ -1929,14 +1931,16 @@ int ssh_pki_import_pubkey_file(const char *filename, ssh_key *pkey)
/** /**
* @brief Import a base64 formatted certificate from a memory c-string. * @brief Import a base64 formatted certificate from a memory c-string.
* *
* @param[in] b64_cert The base64 cert to format. * Note that the certificate is just the base64 part (without the key
* type prefix and comment suffix you can find in the OpenSSH certificate
* file).
* *
* @param[in] type The type of the cert to format. * @param[in] b64_cert The base64 cert to import.
* @param[in] type The type of the cert to import.
* @param[out] pkey A pointer where the allocated certificate can be stored.
* You need to free the memory using ssh_key_free().
* *
* @param[out] pkey A pointer where the allocated key can be stored. You * @return `SSH_OK` on success, `SSH_ERROR` on error.
* need to free the memory using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
* *
* @see ssh_key_free() * @see ssh_key_free()
*/ */

View File

@@ -1355,7 +1355,7 @@ int pki_key_compare(const ssh_key k1,
case SSH_KEYTYPE_SK_ED25519: case SSH_KEYTYPE_SK_ED25519:
case SSH_KEYTYPE_SK_ED25519_CERT01: case SSH_KEYTYPE_SK_ED25519_CERT01:
/* ed25519 keys handled globally */ /* ed25519 keys handled globally */
return 0; return 1;
case SSH_KEYTYPE_ECDSA_P256: case SSH_KEYTYPE_ECDSA_P256:
case SSH_KEYTYPE_ECDSA_P256_CERT01: case SSH_KEYTYPE_ECDSA_P256_CERT01:
case SSH_KEYTYPE_ECDSA_P384: case SSH_KEYTYPE_ECDSA_P384:

View File

@@ -782,7 +782,7 @@ int pki_key_compare(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
case SSH_KEYTYPE_ED25519: case SSH_KEYTYPE_ED25519:
case SSH_KEYTYPE_SK_ED25519: case SSH_KEYTYPE_SK_ED25519:
/* ed25519 keys handled globally */ /* ed25519 keys handled globally */
rc = 0; rc = 1;
break; break;
default: default:
rc = 1; rc = 1;

View File

@@ -862,6 +862,22 @@ int ssh_scp_pull_request(ssh_scp scp)
size = strtoull(tmp, NULL, 10); size = strtoull(tmp, NULL, 10);
p++; p++;
name = strdup(p); name = strdup(p);
/* Catch invalid name:
* - empty ones
* - containing any forward slash -- directory traversal handled
* differently
* - special names "." and ".." referring to the current and parent
* directories -- they are not expected either
*/
if (name == NULL || name[0] == '\0' || strchr(name, '/') ||
strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
ssh_set_error(scp->session,
SSH_FATAL,
"Received invalid filename: %s",
name == NULL ? "<NULL>" : name);
SAFE_FREE(name);
goto error;
}
SAFE_FREE(scp->request_name); SAFE_FREE(scp->request_name);
scp->request_name = name; scp->request_name = name;
if (buffer[0] == 'C') { if (buffer[0] == 'C') {

View File

@@ -495,6 +495,11 @@ static size_t callback_receive_banner(const void *data, size_t len, void *user)
buffer[i] = '\0'; buffer[i] = '\0';
str = strdup(buffer); str = strdup(buffer);
if (str == NULL) {
session->session_state = SSH_SESSION_STATE_ERROR;
ssh_set_error_oom(session);
return 0;
}
/* number of bytes read */ /* number of bytes read */
processed = i + 1; processed = i + 1;
session->clientbanner = str; session->clientbanner = str;

View File

@@ -567,44 +567,51 @@ int sftp_init(sftp_session sftp)
return 0; return 0;
} }
unsigned int sftp_extensions_get_count(sftp_session sftp) { unsigned int sftp_extensions_get_count(sftp_session sftp)
if (sftp == NULL || sftp->ext == NULL) { {
return 0; if (sftp == NULL || sftp->ext == NULL) {
} return 0;
}
return sftp->ext->count; return sftp->ext->count;
} }
const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx) { const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx)
if (sftp == NULL) {
return NULL; if (sftp == NULL) {
if (sftp->ext == NULL || sftp->ext->name == NULL) { return NULL;
ssh_set_error_invalid(sftp->session); }
return NULL;
}
if (idx > sftp->ext->count) { if (sftp->ext == NULL || sftp->ext->name == NULL) {
ssh_set_error_invalid(sftp->session); ssh_set_error_invalid(sftp->session);
return NULL; return NULL;
} }
return sftp->ext->name[idx]; if (idx >= sftp->ext->count) {
ssh_set_error_invalid(sftp->session);
return NULL;
}
return sftp->ext->name[idx];
} }
const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx) { const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx)
if (sftp == NULL) {
return NULL; if (sftp == NULL) {
if (sftp->ext == NULL || sftp->ext->name == NULL) { return NULL;
ssh_set_error_invalid(sftp->session); }
return NULL;
}
if (idx > sftp->ext->count) { if (sftp->ext == NULL || sftp->ext->name == NULL) {
ssh_set_error_invalid(sftp->session); ssh_set_error_invalid(sftp->session);
return NULL; return NULL;
} }
return sftp->ext->data[idx]; if (idx >= sftp->ext->count) {
ssh_set_error_invalid(sftp->session);
return NULL;
}
return sftp->ext->data[idx];
} }
int sftp_extension_supported(sftp_session sftp, const char *name, int sftp_extension_supported(sftp_session sftp, const char *name,

View File

@@ -458,19 +458,24 @@ enum sftp_longname_field_e {
static char * sftp_parse_longname(const char *longname, static char * sftp_parse_longname(const char *longname,
enum sftp_longname_field_e longname_field) enum sftp_longname_field_e longname_field)
{ {
const char *p, *q; const char *p = NULL, *q = NULL;
size_t len, field = 0; size_t len, field = 0;
if (longname == NULL || longname_field < SFTP_LONGNAME_PERM ||
longname_field > SFTP_LONGNAME_NAME) {
return NULL;
}
p = longname; p = longname;
/* /*
* Find the beginning of the field which is specified * Find the beginning of the field which is specified
* by sftp_longname_field_e. * by sftp_longname_field_e.
*/ */
while (field != longname_field) { while (*p != '\0' && field != longname_field) {
if (isspace(*p)) { if (isspace(*p)) {
field++; field++;
p++; p++;
while (*p && isspace(*p)) { while (*p != '\0' && isspace(*p)) {
p++; p++;
} }
} else { } else {
@@ -478,8 +483,13 @@ static char * sftp_parse_longname(const char *longname,
} }
} }
/* If we reached NULL before we got our field fail */
if (field != longname_field) {
return NULL;
}
q = p; q = p;
while (! isspace(*q)) { while (*q != '\0' && !isspace(*q)) {
q++; q++;
} }
@@ -546,17 +556,14 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp,
if (rc != SSH_OK){ if (rc != SSH_OK){
goto error; goto error;
} }
SSH_LOG(SSH_LOG_DEBUG, SSH_LOG(SSH_LOG_DEBUG, "Flags: %.8" PRIx32, attr->flags);
"Flags: %.8" PRIx32 "\n", attr->flags);
if (attr->flags & SSH_FILEXFER_ATTR_SIZE) { if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
rc = ssh_buffer_unpack(buf, "q", &attr->size); rc = ssh_buffer_unpack(buf, "q", &attr->size);
if(rc != SSH_OK) { if(rc != SSH_OK) {
goto error; goto error;
} }
SSH_LOG(SSH_LOG_DEBUG, SSH_LOG(SSH_LOG_DEBUG, "Size: %" PRIu64, (uint64_t)attr->size);
"Size: %" PRIu64 "\n",
(uint64_t) attr->size);
} }
if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) { if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {

View File

@@ -1096,7 +1096,7 @@ jump_thread_func(void *arg)
cb = ssh_list_pop_head(struct ssh_jump_callbacks_struct *, cb = ssh_list_pop_head(struct ssh_jump_callbacks_struct *,
jump_session->opts.proxy_jumps_user_cb); jump_session->opts.proxy_jumps_user_cb);
if (cb != NULL) { if (cb != NULL && cb->before_connection != NULL) {
rc = cb->before_connection(jump_session, cb->userdata); rc = cb->before_connection(jump_session, cb->userdata);
if (rc != SSH_OK) { if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session)); SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
@@ -1205,6 +1205,8 @@ exit:
ssh_event_free(event); ssh_event_free(event);
ssh_free(jump_session); ssh_free(jump_session);
shutdown(jump_thread_data->fd, SHUT_RDWR);
close(jump_thread_data->fd);
SAFE_FREE(jump_thread_data); SAFE_FREE(jump_thread_data);
pthread_exit(NULL); pthread_exit(NULL);

View File

@@ -373,10 +373,10 @@ if (FUZZ_TESTING)
endif() endif()
add_custom_target(test_memcheck add_custom_target(test_memcheck
# FIXME: The threads_pki_rsa test is skipped under valgrind as it times out # FIXME: The pkd_hello_i1 test is skipped under valgrind as it times out
# Passing suppression file is also stupid so lets go with override here: # Passing suppression file is also stupid so lets go with override here:
# https://stackoverflow.com/a/56116311 # https://stackoverflow.com/a/56116311
COMMAND ${CMAKE_CTEST_COMMAND} -E torture_threads_pki_rsa -E pkd_hello_i1 COMMAND ${CMAKE_CTEST_COMMAND} -E pkd_hello_i1
--output-on-failure --force-new-ctest-process --test-action memcheck --output-on-failure --force-new-ctest-process --test-action memcheck
--overwrite MemoryCheckSuppressionFile=${CMAKE_SOURCE_DIR}/tests/valgrind.supp --overwrite MemoryCheckSuppressionFile=${CMAKE_SOURCE_DIR}/tests/valgrind.supp
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")

View File

@@ -72,6 +72,63 @@ static void session_setup_channel(void **state)
assert_non_null(s->ssh.tsftp); assert_non_null(s->ssh.tsftp);
} }
static void session_setup_extensions(void **state)
{
struct torture_state *s = *state;
struct passwd *pwd = NULL;
int rc, count;
const char *name = NULL, *data = NULL;
sftp_session sftp = NULL;
pwd = getpwnam("bob");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = torture_ssh_session(s,
TORTURE_SSH_SERVER,
NULL,
TORTURE_SSH_USER_ALICE,
NULL);
assert_non_null(s->ssh.session);
s->ssh.tsftp = torture_sftp_session(s->ssh.session);
assert_non_null(s->ssh.tsftp);
sftp = s->ssh.tsftp->sftp;
/* null parameter */
count = sftp_extensions_get_count(NULL);
assert_int_equal(count, 0);
count = sftp_extensions_get_count(sftp);
assert_int_not_equal(count, 0);
/* first null parameter */
name = sftp_extensions_get_name(NULL, 0);
assert_null(name);
data = sftp_extensions_get_data(NULL, 0);
assert_null(data);
/* First extension */
name = sftp_extensions_get_name(sftp, 0);
assert_non_null(name);
data = sftp_extensions_get_data(sftp, 0);
assert_non_null(data);
/* Last extension */
name = sftp_extensions_get_name(sftp, count - 1);
assert_non_null(name);
data = sftp_extensions_get_data(sftp, count - 1);
assert_non_null(data);
/* Overrun */
name = sftp_extensions_get_name(sftp, count);
assert_null(name);
data = sftp_extensions_get_data(sftp, count);
assert_null(data);
}
static int session_teardown(void **state) static int session_teardown(void **state)
{ {
struct torture_state *s = *state; struct torture_state *s = *state;
@@ -92,7 +149,10 @@ int torture_run_tests(void) {
session_teardown), session_teardown),
cmocka_unit_test_setup_teardown(session_setup_channel, cmocka_unit_test_setup_teardown(session_setup_channel,
NULL, NULL,
session_teardown) session_teardown),
cmocka_unit_test_setup_teardown(session_setup_extensions,
NULL,
session_teardown),
}; };
ssh_init(); ssh_init();

View File

@@ -28,6 +28,9 @@ if [ ! -d "$TESTDIR/db" ]; then
directories.tokendir = $TESTDIR/db directories.tokendir = $TESTDIR/db
objectstore.backend = file objectstore.backend = file
log.level = DEBUG log.level = DEBUG
# # The hashed ECDSA mechanisms wrongly do not support multi-part operations
# https://github.com/softhsm/SoftHSMv2/issues/842
slots.mechanisms = -CKM_ECDSA_SHA1,CKM_ECDSA_SHA224,CKM_ECDSA_SHA256,CKM_ECDSA_SHA384,CKM_ECDSA_SHA512
EOF EOF
cat "$TESTDIR/softhsm.conf" cat "$TESTDIR/softhsm.conf"

View File

@@ -98,6 +98,13 @@ if (UNIX AND NOT WIN32)
endif (WITH_SERVER) endif (WITH_SERVER)
endif (UNIX AND NOT WIN32) endif (UNIX AND NOT WIN32)
if (WITH_SFTP)
set(LIBSSH_UNIT_TESTS
${LIBSSH_UNIT_TESTS}
torture_unit_sftp
)
endif (WITH_SFTP)
foreach(_UNIT_TEST ${LIBSSH_UNIT_TESTS}) foreach(_UNIT_TEST ${LIBSSH_UNIT_TESTS})
add_cmocka_test(${_UNIT_TEST} add_cmocka_test(${_UNIT_TEST}
SOURCES ${_UNIT_TEST}.c SOURCES ${_UNIT_TEST}.c

View File

@@ -53,6 +53,8 @@ extern LIBSSH_THREAD int ssh_log_level;
#define LIBSSH_TEST_NONEWLINEONELINE "libssh_test_NoNewLineOneline.tmp" #define LIBSSH_TEST_NONEWLINEONELINE "libssh_test_NoNewLineOneline.tmp"
#define LIBSSH_TEST_RECURSIVE_INCLUDE "libssh_test_recursive_include.tmp" #define LIBSSH_TEST_RECURSIVE_INCLUDE "libssh_test_recursive_include.tmp"
#define LIBSSH_TESTCONFIG_MATCH_COMPLEX "libssh_test_match_complex.tmp" #define LIBSSH_TESTCONFIG_MATCH_COMPLEX "libssh_test_match_complex.tmp"
#define LIBSSH_TESTCONFIG_LOGLEVEL_MISSING "libssh_test_loglevel_missing.tmp"
#define LIBSSH_TESTCONFIG_JUMP "libssh_test_jump.tmp"
#define LIBSSH_TESTCONFIG_STRING1 \ #define LIBSSH_TESTCONFIG_STRING1 \
"User "USERNAME"\nInclude "LIBSSH_TESTCONFIG2"\n\n" "User "USERNAME"\nInclude "LIBSSH_TESTCONFIG2"\n\n"
@@ -243,6 +245,26 @@ extern LIBSSH_THREAD int ssh_log_level;
"\tForwardAgent yes\n" \ "\tForwardAgent yes\n" \
"\tHostName complex-match\n" "\tHostName complex-match\n"
#define LIBSSH_TESTCONFIG_LOGLEVEL_MISSING_STRING "LogLevel\n"
#define LIBSSH_TESTCONFIG_JUMP_STRING \
"# The jump host\n" \
"Host ub-jumphost\n" \
" HostName 1xxxxxx\n" \
" User ubuntu\n" \
" IdentityFile ~/of/temp-libssh.pem\n" \
" Port 23\n" \
" LogLevel DEBUG3\n" \
"\n" \
"# Cisco Router through Jump Host\n" \
"Host cisco-router\n" \
" HostName xx.xxxxxxxxx\n" \
" User username\n" \
" ProxyJump ub-jumphost\n" \
" Port 5555\n" \
" #RequiredRSASize 512\n" \
" PasswordAuthentication yes\n" \
" LogLevel DEBUG3\n"
/** /**
* @brief helper function loading configuration from either file or string * @brief helper function loading configuration from either file or string
*/ */
@@ -293,6 +315,8 @@ static int setup_config_files(void **state)
unlink(LIBSSH_TEST_NONEWLINEEND); unlink(LIBSSH_TEST_NONEWLINEEND);
unlink(LIBSSH_TEST_NONEWLINEONELINE); unlink(LIBSSH_TEST_NONEWLINEONELINE);
unlink(LIBSSH_TESTCONFIG_MATCH_COMPLEX); unlink(LIBSSH_TESTCONFIG_MATCH_COMPLEX);
unlink(LIBSSH_TESTCONFIG_LOGLEVEL_MISSING);
unlink(LIBSSH_TESTCONFIG_JUMP);
torture_write_file(LIBSSH_TESTCONFIG1, torture_write_file(LIBSSH_TESTCONFIG1,
LIBSSH_TESTCONFIG_STRING1); LIBSSH_TESTCONFIG_STRING1);
@@ -361,6 +385,10 @@ static int setup_config_files(void **state)
/* Match complex combinations */ /* Match complex combinations */
torture_write_file(LIBSSH_TESTCONFIG_MATCH_COMPLEX, torture_write_file(LIBSSH_TESTCONFIG_MATCH_COMPLEX,
LIBSSH_TESTCONFIG_MATCH_COMPLEX_STRING); LIBSSH_TESTCONFIG_MATCH_COMPLEX_STRING);
torture_write_file(LIBSSH_TESTCONFIG_LOGLEVEL_MISSING,
LIBSSH_TESTCONFIG_LOGLEVEL_MISSING_STRING);
torture_write_file(LIBSSH_TESTCONFIG_JUMP,
LIBSSH_TESTCONFIG_JUMP_STRING);
return 0; return 0;
} }
@@ -390,6 +418,8 @@ static int teardown_config_files(void **state)
unlink(LIBSSH_TEST_NONEWLINEEND); unlink(LIBSSH_TEST_NONEWLINEEND);
unlink(LIBSSH_TEST_NONEWLINEONELINE); unlink(LIBSSH_TEST_NONEWLINEONELINE);
unlink(LIBSSH_TESTCONFIG_MATCH_COMPLEX); unlink(LIBSSH_TESTCONFIG_MATCH_COMPLEX);
unlink(LIBSSH_TESTCONFIG_LOGLEVEL_MISSING);
unlink(LIBSSH_TESTCONFIG_JUMP);
return 0; return 0;
} }
@@ -2342,80 +2372,138 @@ static void torture_config_match_pattern(void **state)
(void) state; (void) state;
/* Simple test "a" matches "a" */ /* Simple test "a" matches "a" */
rv = match_pattern("a", "a", MAX_MATCH_RECURSION); rv = match_pattern("a", "a");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
/* Simple test "a" does not match "b" */ /* Simple test "a" does not match "b" */
rv = match_pattern("a", "b", MAX_MATCH_RECURSION); rv = match_pattern("a", "b");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
/* NULL arguments are correctly handled */ /* NULL arguments are correctly handled */
rv = match_pattern("a", NULL, MAX_MATCH_RECURSION); rv = match_pattern("a", NULL);
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
rv = match_pattern(NULL, "a", MAX_MATCH_RECURSION); rv = match_pattern(NULL, "a");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
/* Simple wildcard ? is handled in pattern */ /* Simple wildcard ? is handled in pattern */
rv = match_pattern("a", "?", MAX_MATCH_RECURSION); rv = match_pattern("a", "?");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("aa", "?", MAX_MATCH_RECURSION); rv = match_pattern("aa", "?");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
/* Wildcard in search string */ /* Wildcard in search string */
rv = match_pattern("?", "a", MAX_MATCH_RECURSION); rv = match_pattern("?", "a");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
rv = match_pattern("?", "?", MAX_MATCH_RECURSION); rv = match_pattern("?", "?");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
/* Simple wildcard * is handled in pattern */ /* Simple wildcard * is handled in pattern */
rv = match_pattern("a", "*", MAX_MATCH_RECURSION); rv = match_pattern("a", "*");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("aa", "*", MAX_MATCH_RECURSION); rv = match_pattern("aa", "*");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
/* Wildcard in search string */ /* Wildcard in search string */
rv = match_pattern("*", "a", MAX_MATCH_RECURSION); rv = match_pattern("*", "a");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
rv = match_pattern("*", "*", MAX_MATCH_RECURSION); rv = match_pattern("*", "*");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
/* More complicated patterns */ /* More complicated patterns */
rv = match_pattern("a", "*a", MAX_MATCH_RECURSION); rv = match_pattern("a", "*a");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("a", "a*", MAX_MATCH_RECURSION); rv = match_pattern("a", "a*");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("abababc", "*abc", MAX_MATCH_RECURSION); rv = match_pattern("abababc", "*abc");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("ababababca", "*abc", MAX_MATCH_RECURSION); rv = match_pattern("ababababca", "*abc");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
rv = match_pattern("ababababca", "*abc*", MAX_MATCH_RECURSION); rv = match_pattern("ababababca", "*abc*");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
/* Multiple wildcards in row */ /* Multiple wildcards in row */
rv = match_pattern("aa", "??", MAX_MATCH_RECURSION); rv = match_pattern("aa", "??");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("bba", "??a", MAX_MATCH_RECURSION); rv = match_pattern("bba", "??a");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("aaa", "**a", MAX_MATCH_RECURSION); rv = match_pattern("aaa", "**a");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("bbb", "**a", MAX_MATCH_RECURSION); rv = match_pattern("bbb", "**a");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
/* Consecutive asterisks do not make sense and do not need to recurse */ /* Consecutive asterisks do not make sense and do not need to recurse */
rv = match_pattern("hostname", "**********pattern", 5); rv = match_pattern("hostname", "**********pattern");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
rv = match_pattern("hostname", "pattern**********", 5); rv = match_pattern("hostname", "pattern**********");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
rv = match_pattern("pattern", "***********pattern", 5); rv = match_pattern("pattern", "***********pattern");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
rv = match_pattern("pattern", "pattern***********", 5); rv = match_pattern("pattern", "pattern***********");
assert_int_equal(rv, 1); assert_int_equal(rv, 1);
/* Limit the maximum recursion */ rv = match_pattern("hostname", "*p*a*t*t*e*r*n*");
rv = match_pattern("hostname", "*p*a*t*t*e*r*n*", 5);
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
/* Too much recursion */ rv = match_pattern("pattern", "*p*a*t*t*e*r*n*");
rv = match_pattern("pattern", "*p*a*t*t*e*r*n*", 5); assert_int_equal(rv, 1);
/* Regular Expression Denial of Service */
rv = match_pattern("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a");
assert_int_equal(rv, 1);
rv = match_pattern("ababababababababababababababababababababab",
"*a*b*a*b*a*b*a*b*a*b*a*b*a*b*a*b");
assert_int_equal(rv, 1);
/* A lot of backtracking */
rv = match_pattern("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax",
"a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*ax");
assert_int_equal(rv, 1);
/* Test backtracking: *a matches first 'a', fails on 'b', must backtrack */
rv = match_pattern("axaxaxb", "*a*b");
assert_int_equal(rv, 1);
/* Test greedy consumption with suffix */
rv = match_pattern("foo_bar_baz_bar", "*bar");
assert_int_equal(rv, 1);
/* Test exact suffix requirement (ensure no partial match acceptance) */
rv = match_pattern("foobar_extra", "*bar");
assert_int_equal(rv, 0); assert_int_equal(rv, 0);
/* Test multiple distinct wildcards */
rv = match_pattern("a_very_long_string_with_a_pattern", "*long*pattern");
assert_int_equal(rv, 1);
/* ? inside a * sequence */
rv = match_pattern("abcdefg", "a*c?e*g");
assert_int_equal(rv, 1);
/* Consecutive mixed wildcards */
rv = match_pattern("abc", "*?c");
assert_int_equal(rv, 1);
/* ? at the very end after * */
rv = match_pattern("abc", "ab?");
assert_int_equal(rv, 1);
rv = match_pattern("abc", "ab*?");
assert_int_equal(rv, 1);
/* Consecutive stars should be collapsed or handled gracefully */
rv = match_pattern("abc", "a**c");
assert_int_equal(rv, 1);
rv = match_pattern("abc", "***");
assert_int_equal(rv, 1);
/* Empty string handling */
rv = match_pattern("", "*");
assert_int_equal(rv, 1);
rv = match_pattern("", "?");
assert_int_equal(rv, 0);
rv = match_pattern("", "");
assert_int_equal(rv, 1);
/* Pattern longer than string */
rv = match_pattern("short", "short_but_longer");
assert_int_equal(rv, 0);
} }
/* Identity file can be specified multiple times in the configuration /* Identity file can be specified multiple times in the configuration
@@ -2617,6 +2705,129 @@ static void torture_config_match_complex(void **state)
ssh_string_free_char(v); ssh_string_free_char(v);
} }
/* Missing value to LogLevel configuration option
*/
static void torture_config_loglevel_missing_value(void **state)
{
ssh_session session = *state;
ssh_options_set(session, SSH_OPTIONS_HOST, "Bar");
_parse_config(session, LIBSSH_TESTCONFIG_LOGLEVEL_MISSING, NULL, SSH_OK);
}
static int before_connection(ssh_session jump_session, void *user)
{
char *v = NULL;
int ret;
(void)user;
/* During the connection, we force parsing the same configuration file
* (would be normally parsed automatically during the connection itself)
*/
ret = ssh_config_parse_file(jump_session, LIBSSH_TESTCONFIG_JUMP);
assert_return_code(ret, errno);
/* Test the variable presence */
ret = ssh_options_get(jump_session, SSH_OPTIONS_HOST, &v);
assert_return_code(ret, errno);
assert_string_equal(v, "1xxxxxx");
ssh_string_free_char(v);
ret = ssh_options_get(jump_session, SSH_OPTIONS_USER, &v);
assert_return_code(ret, errno);
assert_string_equal(v, "ubuntu");
ssh_string_free_char(v);
assert_int_equal(jump_session->opts.port, 23);
/* Fail the connection -- we are in unit tests so it would fail anyway */
return 1;
}
static int verify_knownhost(ssh_session jump_session, void *user)
{
(void)jump_session;
(void)user;
return 0;
}
static int authenticate(ssh_session jump_session, void *user)
{
(void)jump_session;
(void)user;
return 0;
}
/* Reproducer for complex proxy jump
*/
static void torture_config_jump(void **state)
{
ssh_session session = *state;
struct ssh_jump_callbacks_struct c = {
.before_connection = before_connection,
.verify_knownhost = verify_knownhost,
.authenticate = authenticate,
};
char *v = NULL;
int ret;
ssh_options_set(session, SSH_OPTIONS_HOST, "cisco-router");
_parse_config(session, LIBSSH_TESTCONFIG_JUMP, NULL, SSH_OK);
/* Test the variable presence */
ret = ssh_options_get(session, SSH_OPTIONS_HOST, &v);
assert_return_code(ret, errno);
assert_string_equal(v, "xx.xxxxxxxxx");
ssh_string_free_char(v);
ret = ssh_options_get(session, SSH_OPTIONS_USER, &v);
assert_return_code(ret, errno);
assert_string_equal(v, "username");
ssh_string_free_char(v);
assert_int_equal(session->opts.port, 5555);
/* At this point, the configuration file is not parsed for the jump host so
* we are getting just the the hostname -- the port and username will get
* pulled during the session connecting to this host */
assert_int_equal(ssh_list_count(session->opts.proxy_jumps), 1);
helper_proxy_jump_check(session->opts.proxy_jumps->root,
"ub-jumphost",
NULL,
NULL);
/* Set up the callbacks -- they should verify we are going to connect to the
* right host */
ret = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, &c);
assert_ssh_return_code(session, ret);
ret = ssh_connect(session);
assert_ssh_return_code_equal(session, ret, SSH_ERROR);
printf("%s: EOF\n", __func__);
}
/* Invalid configuration files
*/
static void torture_config_invalid(void **state)
{
ssh_session session = *state;
ssh_options_set(session, SSH_OPTIONS_HOST, "Bar");
/* non-regular file -- ignored (or missing on non-unix) so OK */
_parse_config(session, "/dev/random", NULL, SSH_OK);
#ifndef _WIN32
/* huge file -- ignored (or missing on non-unix) so OK */
_parse_config(session, "/proc/kcore", NULL, SSH_OK);
#endif
}
int torture_run_tests(void) int torture_run_tests(void)
{ {
int rc; int rc;
@@ -2713,6 +2924,15 @@ int torture_run_tests(void)
setup, teardown), setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_match_complex, cmocka_unit_test_setup_teardown(torture_config_match_complex,
setup, teardown), setup, teardown),
cmocka_unit_test_setup_teardown(torture_config_loglevel_missing_value,
setup,
teardown),
cmocka_unit_test_setup_teardown(torture_config_jump,
setup,
teardown),
cmocka_unit_test_setup_teardown(torture_config_invalid,
setup,
teardown),
}; };

View File

@@ -696,6 +696,82 @@ static void torture_knownhosts_algorithms_global(void **state)
ssh_free(session); ssh_free(session);
} }
static int setup_bad_knownhosts_file(void **state)
{
char *tmp_file = NULL;
size_t nwritten;
FILE *fp = NULL;
int rc = 0;
tmp_file = torture_create_temp_file(TMP_FILE_NAME);
assert_non_null(tmp_file);
*state = tmp_file;
fp = fopen(tmp_file, "w");
assert_non_null(fp);
nwritten = fwrite(LOCALHOST_DEFAULT_ED25519,
sizeof(char),
strlen(LOCALHOST_DEFAULT_ED25519),
fp);
if (nwritten != strlen(LOCALHOST_DEFAULT_ED25519)) {
rc = -1;
goto close_fp;
}
nwritten = fwrite("\n", sizeof(char), 1, fp);
if (nwritten != 1) {
rc = -1;
goto close_fp;
}
#define LOCALHOST_BAD_LINE "localhost \n"
nwritten = fwrite(LOCALHOST_BAD_LINE,
sizeof(char),
strlen(LOCALHOST_BAD_LINE),
fp);
if (nwritten != strlen(LOCALHOST_BAD_LINE)) {
rc = -1;
goto close_fp;
}
close_fp:
fclose(fp);
return rc;
}
static void torture_knownhosts_has_entry(void **state)
{
const char *knownhosts_file = *state;
enum ssh_known_hosts_e found;
ssh_session session;
bool process_config = false;
struct ssh_knownhosts_entry *entry = NULL;
session = ssh_new();
assert_non_null(session);
/* This makes sure the global configuration file is not processed */
ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config);
ssh_options_set(session, SSH_OPTIONS_HOST, "localhost");
/* This makes sure the current-user's known hosts are not used */
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "/dev/null");
ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, knownhosts_file);
/* Error is expected -- this tests the memory is not leaked from this
* test case */
found = ssh_session_has_known_hosts_entry(session);
assert_int_equal(found, SSH_KNOWN_HOSTS_ERROR);
found = ssh_session_get_known_hosts_entry(session, &entry);
assert_int_equal(found, SSH_KNOWN_HOSTS_ERROR);
assert_null(entry);
ssh_free(session);
}
#endif /* _WIN32 There is no /dev/null on Windows */ #endif /* _WIN32 There is no /dev/null on Windows */
int torture_run_tests(void) { int torture_run_tests(void) {
@@ -738,6 +814,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_knownhosts_algorithms_global, cmocka_unit_test_setup_teardown(torture_knownhosts_algorithms_global,
setup_knownhosts_file, setup_knownhosts_file,
teardown_knownhosts_file), teardown_knownhosts_file),
cmocka_unit_test_setup_teardown(torture_knownhosts_has_entry,
setup_bad_knownhosts_file,
teardown_knownhosts_file),
#endif #endif
}; };

View File

@@ -1129,6 +1129,36 @@ static void torture_ssh_is_ipaddr(void **state) {
assert_int_equal(rc, 0); assert_int_equal(rc, 0);
} }
static void torture_ssh_get_hexa(void **state)
{
const unsigned char *bin = NULL;
char *hex = NULL;
(void)state;
/* Null pointer should not crash */
bin = NULL;
hex = ssh_get_hexa(bin, 0);
assert_null(hex);
/* Null pointer should not crash regardless the length */
bin = NULL;
hex = ssh_get_hexa(bin, 99);
assert_null(hex);
/* Zero length input is not much useful. Just expect NULL too */
bin = (const unsigned char *)"";
hex = ssh_get_hexa(bin, 0);
assert_null(hex);
/* Valid inputs */
bin = (const unsigned char *)"\x00\xFF";
hex = ssh_get_hexa(bin, 2);
assert_non_null(hex);
assert_string_equal(hex, "00:ff");
ssh_string_free_char(hex);
}
int torture_run_tests(void) { int torture_run_tests(void) {
int rc; int rc;
struct CMUnitTest tests[] = { struct CMUnitTest tests[] = {
@@ -1158,6 +1188,7 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_ssh_check_hostname_syntax), cmocka_unit_test(torture_ssh_check_hostname_syntax),
cmocka_unit_test(torture_ssh_check_username_syntax), cmocka_unit_test(torture_ssh_check_username_syntax),
cmocka_unit_test(torture_ssh_is_ipaddr), cmocka_unit_test(torture_ssh_is_ipaddr),
cmocka_unit_test(torture_ssh_get_hexa),
}; };
ssh_init(); ssh_init();

View File

@@ -367,10 +367,14 @@ static void torture_pki_ecdsa_publickey_from_privatekey(void **state)
static void torture_pki_ecdsa_import_cert_file(void **state) static void torture_pki_ecdsa_import_cert_file(void **state)
{ {
int rc; int rc;
ssh_key pubkey = NULL;
ssh_key privkey = NULL;
ssh_key cert = NULL; ssh_key cert = NULL;
enum ssh_keytypes_e type; enum ssh_keytypes_e type, exp_cert_type;
struct pki_st *test_state = *((struct pki_st **)state); struct pki_st *test_state = *((struct pki_st **)state);
exp_cert_type = test_state->type + 3;
/* Importing public key as cert should fail */ /* Importing public key as cert should fail */
rc = ssh_pki_import_cert_file(LIBSSH_ECDSA_TESTKEY ".pub", &cert); rc = ssh_pki_import_cert_file(LIBSSH_ECDSA_TESTKEY ".pub", &cert);
assert_int_equal(rc, SSH_ERROR); assert_int_equal(rc, SSH_ERROR);
@@ -380,13 +384,78 @@ static void torture_pki_ecdsa_import_cert_file(void **state)
assert_int_equal(rc, 0); assert_int_equal(rc, 0);
assert_non_null(cert); assert_non_null(cert);
rc = ssh_pki_import_pubkey_file(LIBSSH_ECDSA_TESTKEY ".pub", &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
type = ssh_key_type(cert); type = ssh_key_type(cert);
assert_int_equal(type, test_state->type+3); assert_int_equal(type, exp_cert_type);
rc = ssh_key_is_public(cert); rc = ssh_key_is_public(cert);
assert_int_equal(rc, 1); assert_int_equal(rc, 1);
/* Import matching private key file and verify the pubkey matches */
rc = ssh_pki_import_privkey_file(LIBSSH_ECDSA_TESTKEY,
NULL,
NULL,
NULL,
&privkey);
assert_return_code(rc, errno);
assert_non_null(privkey);
type = ssh_key_type(privkey);
assert_true(type == test_state->type);
/* Basic sanity. */
rc = ssh_pki_copy_cert_to_privkey(NULL, privkey);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(pubkey, NULL);
assert_int_equal(rc, SSH_ERROR);
/* A public key doesn't have a cert, copy should fail. */
assert_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(pubkey, privkey);
assert_int_equal(rc, SSH_ERROR);
/* Copying the cert to non-cert keys should work fine. */
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_return_code(rc, errno);
assert_non_null(privkey->cert);
assert_true(privkey->cert_type == exp_cert_type);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 0);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 0);
/* The private key's cert is already set, another copy should fail. */
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_ERROR);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
/* Generate different key and try to assign it this certificate */
rc = ssh_pki_generate(test_state->type, 256, &privkey);
assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_int_equal(rc, SSH_ERROR);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 1);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 1);
SSH_KEY_FREE(cert); SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
} }
static void torture_pki_ecdsa_publickey_base64(void **state) static void torture_pki_ecdsa_publickey_base64(void **state)

View File

@@ -312,6 +312,8 @@ static void torture_pki_ed25519_publickey_from_privatekey(void **state)
static void torture_pki_ed25519_import_cert_file(void **state) static void torture_pki_ed25519_import_cert_file(void **state)
{ {
int rc; int rc;
ssh_key pubkey = NULL;
ssh_key privkey = NULL;
ssh_key cert = NULL; ssh_key cert = NULL;
enum ssh_keytypes_e type; enum ssh_keytypes_e type;
@@ -323,16 +325,88 @@ static void torture_pki_ed25519_import_cert_file(void **state)
assert_null(cert); assert_null(cert);
rc = ssh_pki_import_cert_file(LIBSSH_ED25519_TESTKEY "-cert.pub", &cert); rc = ssh_pki_import_cert_file(LIBSSH_ED25519_TESTKEY "-cert.pub", &cert);
assert_true(rc == 0); assert_return_code(rc, errno);
assert_non_null(cert); assert_non_null(cert);
rc = ssh_pki_import_pubkey_file(LIBSSH_ED25519_TESTKEY ".pub", &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
type = ssh_key_type(cert); type = ssh_key_type(cert);
assert_true(type == SSH_KEYTYPE_ED25519_CERT01); assert_true(type == SSH_KEYTYPE_ED25519_CERT01);
rc = ssh_key_is_public(cert); rc = ssh_key_is_public(cert);
assert_true(rc == 1); assert_int_equal(rc, 1);
/* Skip test if in FIPS mode */
if (ssh_fips_mode()) {
SSH_KEY_FREE(cert);
SSH_KEY_FREE(pubkey);
skip();
}
/* Import matching private key file and verify the pubkey matches */
rc = ssh_pki_import_privkey_file(LIBSSH_ED25519_TESTKEY,
NULL,
NULL,
NULL,
&privkey);
assert_return_code(rc, errno);
assert_non_null(privkey);
type = ssh_key_type(privkey);
assert_true(type == SSH_KEYTYPE_ED25519);
/* Basic sanity. */
rc = ssh_pki_copy_cert_to_privkey(NULL, privkey);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(pubkey, NULL);
assert_int_equal(rc, SSH_ERROR);
/* A public key doesn't have a cert, copy should fail. */
assert_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(pubkey, privkey);
assert_int_equal(rc, SSH_ERROR);
/* Copying the cert to non-cert keys should work fine. */
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey->cert);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_return_code(rc, errno);
assert_non_null(privkey->cert);
assert_true(privkey->cert_type == SSH_KEYTYPE_ED25519_CERT01);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 0);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 0);
/* The private key's cert is already set, another copy should fail. */
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_ERROR);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
/* Generate different key and try to assign it this certificate */
rc = ssh_pki_generate(SSH_KEYTYPE_ED25519, 0, &privkey);
assert_return_code(rc, errno);
assert_non_null(privkey);
rc = ssh_pki_export_privkey_to_pubkey(privkey, &pubkey);
assert_return_code(rc, errno);
assert_non_null(pubkey);
rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_int_equal(rc, SSH_ERROR);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 1);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 1);
SSH_KEY_FREE(cert); SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey);
} }
static void torture_pki_ed25519_publickey_base64(void **state) static void torture_pki_ed25519_publickey_base64(void **state)

View File

@@ -373,6 +373,7 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
ssh_key pubkey = NULL; ssh_key pubkey = NULL;
ssh_key privkey = NULL; ssh_key privkey = NULL;
ssh_key cert = NULL; ssh_key cert = NULL;
enum ssh_keytypes_e type;
(void)state; /* unused */ (void)state; /* unused */
@@ -389,6 +390,13 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
assert_return_code(rc, errno); assert_return_code(rc, errno);
assert_non_null(pubkey); assert_non_null(pubkey);
type = ssh_key_type(cert);
assert_true(type == SSH_KEYTYPE_RSA_CERT01);
rc = ssh_key_is_public(cert);
assert_int_equal(rc, 1);
/* Import matching private key file and verify the pubkey matches */
rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0), rc = ssh_pki_import_privkey_base64(torture_get_testkey(SSH_KEYTYPE_RSA, 0),
passphrase, passphrase,
NULL, NULL,
@@ -397,6 +405,9 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
assert_return_code(rc, errno); assert_return_code(rc, errno);
assert_non_null(privkey); assert_non_null(privkey);
type = ssh_key_type(privkey);
assert_true(type == SSH_KEYTYPE_RSA);
/* Basic sanity. */ /* Basic sanity. */
rc = ssh_pki_copy_cert_to_privkey(NULL, privkey); rc = ssh_pki_copy_cert_to_privkey(NULL, privkey);
assert_int_equal(rc, SSH_ERROR); assert_int_equal(rc, SSH_ERROR);
@@ -416,6 +427,10 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
rc = ssh_pki_copy_cert_to_privkey(cert, privkey); rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
assert_return_code(rc, errno); assert_return_code(rc, errno);
assert_non_null(privkey->cert); assert_non_null(privkey->cert);
assert_true(privkey->cert_type == SSH_KEYTYPE_RSA_CERT01);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 0);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 0);
/* The private key's cert is already set, another copy should fail. */ /* The private key's cert is already set, another copy should fail. */
rc = ssh_pki_copy_cert_to_privkey(cert, privkey); rc = ssh_pki_copy_cert_to_privkey(cert, privkey);
@@ -437,6 +452,9 @@ static void torture_pki_rsa_copy_cert_to_privkey(void **state)
rc = ssh_pki_copy_cert_to_privkey(cert, pubkey); rc = ssh_pki_copy_cert_to_privkey(cert, pubkey);
assert_int_equal(rc, SSH_ERROR); assert_int_equal(rc, SSH_ERROR);
assert_int_equal(ssh_key_cmp(privkey, cert, SSH_KEY_CMP_PUBLIC), 1);
assert_int_equal(ssh_key_cmp(cert, privkey, SSH_KEY_CMP_PUBLIC), 1);
SSH_KEY_FREE(cert); SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey); SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey); SSH_KEY_FREE(pubkey);

View File

@@ -58,14 +58,8 @@ static int run_on_threads(void *(*func)(void *))
} }
for (i = 0; i < NUM_THREADS; ++i) { for (i = 0; i < NUM_THREADS; ++i) {
void *p = NULL; rc = pthread_join(threads[i], NULL);
uint64_t *result;
rc = pthread_join(threads[i], &p);
assert_int_equal(rc, 0); assert_int_equal(rc, 0);
result = (uint64_t *)p;
assert_null(result);
} }
return rc; return rc;
@@ -164,7 +158,7 @@ static void *thread_pki_rsa_import_pubkey_file(void *threadid)
SSH_KEY_FREE(pubkey); SSH_KEY_FREE(pubkey);
pthread_exit(NULL); return NULL;
} }
static void torture_pki_rsa_import_pubkey_file(void **state) static void torture_pki_rsa_import_pubkey_file(void **state)
@@ -197,8 +191,7 @@ static void *thread_pki_rsa_import_privkey_base64_NULL_key(void *threadid)
NULL, NULL,
NULL); NULL);
assert_true(rc == -1); assert_true(rc == -1);
return NULL;
pthread_exit(NULL);
} }
static void torture_pki_rsa_import_privkey_base64_NULL_key(void **state){ static void torture_pki_rsa_import_privkey_base64_NULL_key(void **state){
@@ -225,7 +218,8 @@ static void *thread_pki_rsa_import_privkey_base64_NULL_str(void *threadid)
assert_true(rc == -1); assert_true(rc == -1);
SSH_KEY_FREE(key); SSH_KEY_FREE(key);
pthread_exit(NULL);
return NULL;
} }
static void torture_pki_rsa_import_privkey_base64_NULL_str(void **state){ static void torture_pki_rsa_import_privkey_base64_NULL_str(void **state){
@@ -267,7 +261,7 @@ static void *thread_pki_rsa_import_privkey_base64(void *threadid)
free(key_str); free(key_str);
SSH_KEY_FREE(key); SSH_KEY_FREE(key);
pthread_exit(NULL); return NULL;
} }
static void torture_pki_rsa_import_privkey_base64(void **state) static void torture_pki_rsa_import_privkey_base64(void **state)
@@ -310,7 +304,8 @@ static void *thread_pki_rsa_publickey_from_privatekey(void *threadid)
SSH_KEY_FREE(key); SSH_KEY_FREE(key);
SSH_KEY_FREE(pubkey); SSH_KEY_FREE(pubkey);
pthread_exit(NULL);
return NULL;
} }
static void torture_pki_rsa_publickey_from_privatekey(void **state) static void torture_pki_rsa_publickey_from_privatekey(void **state)
@@ -383,7 +378,8 @@ static void *thread_pki_rsa_copy_cert_to_privkey(void *threadid)
SSH_KEY_FREE(cert); SSH_KEY_FREE(cert);
SSH_KEY_FREE(privkey); SSH_KEY_FREE(privkey);
SSH_KEY_FREE(pubkey); SSH_KEY_FREE(pubkey);
pthread_exit(NULL);
return NULL;
} }
static void torture_pki_rsa_copy_cert_to_privkey(void **state) static void torture_pki_rsa_copy_cert_to_privkey(void **state)
@@ -416,7 +412,8 @@ static void *thread_pki_rsa_import_cert_file(void *threadid)
assert_true(rc == 1); assert_true(rc == 1);
SSH_KEY_FREE(cert); SSH_KEY_FREE(cert);
pthread_exit(NULL);
return NULL;
} }
static void torture_pki_rsa_import_cert_file(void **state) static void torture_pki_rsa_import_cert_file(void **state)
@@ -467,7 +464,8 @@ static void *thread_pki_rsa_publickey_base64(void *threadid)
free(b64_key); free(b64_key);
free(key_buf); free(key_buf);
SSH_KEY_FREE(key); SSH_KEY_FREE(key);
pthread_exit(NULL);
return NULL;
} }
static void torture_pki_rsa_publickey_base64(void **state) static void torture_pki_rsa_publickey_base64(void **state)
@@ -531,7 +529,8 @@ static void *thread_pki_rsa_duplicate_key(void *threadid)
SSH_KEY_FREE(privkey_dup); SSH_KEY_FREE(privkey_dup);
SSH_STRING_FREE_CHAR(b64_key); SSH_STRING_FREE_CHAR(b64_key);
SSH_STRING_FREE_CHAR(b64_key_gen); SSH_STRING_FREE_CHAR(b64_key_gen);
pthread_exit(NULL);
return NULL;
} }
static void torture_pki_rsa_duplicate_key(void **state) static void torture_pki_rsa_duplicate_key(void **state)
@@ -614,7 +613,8 @@ static void *thread_pki_rsa_generate_key(void *threadid)
SSH_KEY_FREE(pubkey); SSH_KEY_FREE(pubkey);
ssh_free(session); ssh_free(session);
pthread_exit(NULL);
return NULL;
} }
static void torture_pki_rsa_generate_key(void **state) static void torture_pki_rsa_generate_key(void **state)
@@ -672,7 +672,8 @@ static void *thread_pki_rsa_import_privkey_base64_passphrase(void *threadid)
assert_true(rc == -1); assert_true(rc == -1);
SSH_KEY_FREE(key); SSH_KEY_FREE(key);
#endif #endif
pthread_exit(NULL);
return NULL;
} }
static void torture_pki_rsa_import_privkey_base64_passphrase(void **state) static void torture_pki_rsa_import_privkey_base64_passphrase(void **state)
@@ -723,14 +724,8 @@ static void torture_mixed(void **state)
for (f = 0; f < NUM_TESTS; f++) { for (f = 0; f < NUM_TESTS; f++) {
for (i = 0; i < NUM_THREADS; ++i) { for (i = 0; i < NUM_THREADS; ++i) {
void *p = NULL; rc = pthread_join(threads[f][i], NULL);
uint64_t *result = NULL;
rc = pthread_join(threads[f][i], &p);
assert_int_equal(rc, 0); assert_int_equal(rc, 0);
result = (uint64_t *)p;
assert_null(result);
} }
} }
} }

View File

@@ -0,0 +1,86 @@
#include "config.h"
#include "sftp_common.c"
#include "torture.h"
#define LIBSSH_STATIC
static void test_sftp_parse_longname(void **state)
{
const char *lname = NULL;
char *value = NULL;
/* state not used */
(void)state;
/* Valid example from SFTP draft, page 18:
* https://datatracker.ietf.org/doc/draft-spaghetti-sshm-filexfer/
*/
lname = "-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer";
value = sftp_parse_longname(lname, SFTP_LONGNAME_PERM);
assert_string_equal(value, "-rwxr-xr-x");
free(value);
value = sftp_parse_longname(lname, SFTP_LONGNAME_OWNER);
assert_string_equal(value, "mjos");
free(value);
value = sftp_parse_longname(lname, SFTP_LONGNAME_GROUP);
assert_string_equal(value, "staff");
free(value);
value = sftp_parse_longname(lname, SFTP_LONGNAME_SIZE);
assert_string_equal(value, "348911");
free(value);
/* This function is broken further as the date contains space which breaks
* the parsing altogether */
value = sftp_parse_longname(lname, SFTP_LONGNAME_DATE);
assert_string_equal(value, "Mar");
free(value);
value = sftp_parse_longname(lname, SFTP_LONGNAME_TIME);
assert_string_equal(value, "25");
free(value);
value = sftp_parse_longname(lname, SFTP_LONGNAME_NAME);
assert_string_equal(value, "14:29");
free(value);
}
static void test_sftp_parse_longname_invalid(void **state)
{
const char *lname = NULL;
char *value = NULL;
/* state not used */
(void)state;
/* Invalid inputs should not crash
*/
lname = NULL;
value = sftp_parse_longname(lname, SFTP_LONGNAME_PERM);
assert_null(value);
value = sftp_parse_longname(lname, SFTP_LONGNAME_NAME);
assert_null(value);
lname = "";
value = sftp_parse_longname(lname, SFTP_LONGNAME_PERM);
assert_string_equal(value, "");
free(value);
value = sftp_parse_longname(lname, SFTP_LONGNAME_NAME);
assert_null(value);
lname = "-rwxr-xr-x 1";
value = sftp_parse_longname(lname, SFTP_LONGNAME_PERM);
assert_string_equal(value, "-rwxr-xr-x");
free(value);
value = sftp_parse_longname(lname, SFTP_LONGNAME_NAME);
assert_null(value);
}
int torture_run_tests(void)
{
int rc;
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_sftp_parse_longname),
cmocka_unit_test(test_sftp_parse_longname_invalid),
};
rc = cmocka_run_group_tests(tests, NULL, NULL);
return rc;
}

View File

@@ -140,6 +140,40 @@
fun:FIPS_mode_set fun:FIPS_mode_set
fun:OPENSSL_init_library fun:OPENSSL_init_library
} }
{
Threads + Failed PEM decoder do not play well openssl/openssl#29077
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
fun:CRYPTO_malloc
fun:CRYPTO_zalloc
fun:ossl_rcu_read_lock
fun:module_find
fun:module_run
fun:CONF_modules_load
fun:CONF_modules_load_file_ex
fun:ossl_config_int
fun:ossl_config_int
fun:ossl_init_config
fun:ossl_init_config_ossl_
fun:__pthread_once_slow.isra.0
fun:pthread_once@@GLIBC_2.34
fun:CRYPTO_THREAD_run_once
fun:OPENSSL_init_crypto
fun:ossl_provider_doall_activated
fun:ossl_algorithm_do_all
fun:ossl_method_construct.constprop.0
fun:inner_evp_generic_fetch.constprop.0
fun:evp_generic_do_all
fun:EVP_KEYMGMT_do_all_provided
fun:ossl_decoder_ctx_setup_for_pkey
fun:OSSL_DECODER_CTX_new_for_pkey
fun:pem_read_bio_key_decoder
fun:pem_read_bio_key
fun:PEM_read_bio_PrivateKey_ex
fun:pki_private_key_from_base64
...
}
# Cmocka # Cmocka
{ {
This looks like leak from cmocka when the forked server is not properly terminated This looks like leak from cmocka when the forked server is not properly terminated
@@ -207,65 +241,12 @@
Memcheck:Leak Memcheck:Leak
match-leak-kinds: reachable match-leak-kinds: reachable
fun:malloc fun:malloc
fun:malloc
fun:strdup
fun:_dl_load_cache_lookup
fun:_dl_map_object
fun:dl_open_worker_begin
fun:_dl_catch_exception
fun:dl_open_worker
fun:_dl_catch_exception
fun:_dl_open
fun:do_dlopen
fun:_dl_catch_exception
fun:_dl_catch_error
fun:dlerror_run
fun:__libc_dlopen_mode
fun:module_load
fun:__nss_module_get_function
fun:getaddrinfo
...
fun:krb5_sname_to_principal
...
fun:gss_init_sec_context
fun:ssh_packet_userauth_gssapi_response
fun:ssh_packet_process
fun:ssh_packet_socket_callback
fun:ssh_socket_pollcallback
fun:ssh_poll_ctx_dopoll
fun:ssh_handle_packets
fun:ssh_handle_packets_termination
fun:ssh_userauth_get_response
fun:ssh_userauth_gssapi
fun:torture_gssapi_auth_server_identity
...
fun:_cmocka_run_group_tests
fun:torture_run_tests
fun:main
}
{
Reachable memory from getaddrinfo
Memcheck:Leak
match-leak-kinds: reachable
... ...
fun:__nss_module_get_function fun:__nss_module_get_function
... ...
fun:getaddrinfo fun:getaddrinfo
... ...
fun:krb5_sname_to_principal fun:torture_*
...
fun:gss_init_sec_context
fun:ssh_packet_userauth_gssapi_response
fun:ssh_packet_process
fun:ssh_packet_socket_callback
fun:ssh_socket_pollcallback
fun:ssh_poll_ctx_dopoll
fun:ssh_handle_packets
fun:ssh_handle_packets_termination
fun:ssh_userauth_get_response
fun:ssh_userauth_gssapi
fun:torture_gssapi_auth_server_identity
... ...
fun:_cmocka_run_group_tests fun:_cmocka_run_group_tests
fun:torture_run_tests fun:torture_run_tests
@@ -290,13 +271,11 @@
fun:malloc fun:malloc
... ...
fun:krb5_gss_save_error_string fun:krb5_gss_save_error_string
fun:UnknownInlinedFun ...
fun:acquire_cred_context.isra.0 fun:acquire_cred_context.isra.0
fun:acquire_cred_from.isra.0 fun:acquire_cred_from.isra.0
fun:gss_add_cred_from fun:gss_add_cred_from
fun:gss_acquire_cred_from fun:gss_acquire_cred_from
...
fun:gss_acquire_cred
} }
{ {
error string from gss init sec context error string from gss init sec context
@@ -305,7 +284,7 @@
fun:malloc fun:malloc
... ...
fun:krb5_gss_save_error_string fun:krb5_gss_save_error_string
fun:UnknownInlinedFun ...
fun:krb5_gss_init_sec_context_ext fun:krb5_gss_init_sec_context_ext
fun:krb5_gss_init_sec_context fun:krb5_gss_init_sec_context
fun:gss_init_sec_context fun:gss_init_sec_context