Compare commits

..

47 Commits

Author SHA1 Message Date
Arthur Chan
f13a8d7ced OSS-Fuzz Add fuzzer and corpora for sftp attr parsing
Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-10 15:34:39 +01:00
Shreyas Mahajan
c0963b3417 SSH2 NONE authentication
Signed-off-by: Shreyas Mahajan <shreyasmahajan05@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-10 15:16:58 +01:00
Jakub Jelen
50313883f3 libssh-0.12.0
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-10 10:35:23 +01:00
Jakub Jelen
7e02580dff Copy changelog + ABI files from last minor releases
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-10 10:35:23 +01:00
Jakub Jelen
3232d72812 fix typos
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Sahana Prasad <sahana@redhat.com>
2026-02-10 10:35:23 +01:00
Jakub Jelen
60ad19c2c8 misc: Fix possible NULL dereference
thanks oss-fuzz

https://issues.oss-fuzz.com/u/1/issues/482613826
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Sahana Prasad <sahana@redhat.com>
2026-02-10 10:35:23 +01:00
Andreas Schneider
6a7f19ec34 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>
2026-02-05 22:31:48 +01:00
Andreas Schneider
12ccea8dd8 cmake: Reformat DefineOptions.cmake with cmake-format
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 22:31:48 +01:00
Jakub Jelen
daa80818f8 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>
2026-02-05 22:31:48 +01:00
Jakub Jelen
a5eb30dbfd 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>
2026-02-05 22:31:48 +01:00
Jakub Jelen
1b2a4f760b 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>
2026-02-05 22:31:48 +01:00
Jakub Jelen
9be83584a5 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>
2026-02-05 22:31:48 +01:00
Jakub Jelen
417a095e67 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>
2026-02-05 22:31:43 +01:00
Jakub Jelen
a411de5ce8 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>
2026-02-05 17:19:01 +01:00
Jakub Jelen
90a5d8f473 CVE-2026-0968 tests: Reproducer for invalid longname data
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 17:19:01 +01:00
Jakub Jelen
20856f44c1 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>
2026-02-05 17:19:01 +01:00
Jakub Jelen
28d6d10ddc sftp: Initialize pointers
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 17:19:01 +01:00
Jakub Jelen
b3e13b7f0b pki: Discourage comparing private keys
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-05 17:19:01 +01:00
Jakub Jelen
5654c593df ed25519: Avoid timing leak when comparing private keys
This affects libgcrypt and mbedTLS backends. The OpenSSL backend is
using OpenSSL implementation of the Ed25519 which is compared correctly.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-05 17:19:01 +01:00
Jakub Jelen
b90b7f2451 Reproducer for out of bounds read of SFTP extensions
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-05 17:19:01 +01:00
Jakub Jelen
855a0853ad sftp: Fix out-of-bound read from sftp extensions
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-05 17:19:00 +01:00
Jakub Jelen
f0fdfd4f49 sftp: Reformat sftp_extensions_* API
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-05 17:19:00 +01:00
Eshan Kelkar
dc39902006 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>
2026-02-05 17:10:51 +01:00
Eshan Kelkar
29dd7874cd sftp_aio: Test sftp aio with libssh proxyjump
Signed-off-by: Eshan Kelkar <eshankelkar@galorithm.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 17:10:51 +01:00
Jakub Jelen
8a134e03db tests: Check SSH_OPTIONS_NEXT_IDENTITY functionality
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
39d931f7e5 options: Allow listing all identity files
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
b4f6d8b800 misc: Rewrite custom getting of port to use options API
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
c78d2bb8fb test: Verify expand characters work as expected
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
5b0cee7c1b misc: Add support for %j and %C percent expand
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
59ed66b684 New ssh_get_local_hostname()
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
ce0b616bc6 Fix percent expand character %d to home directory
Fixes: #349

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
31ceec02fe misc: Cache user home directory in session
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
a7cf4bb37b misc: Reformat ssh_get_user_home_dir()
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
3dfaa70fcf misc: Reformat ssh_path_expand_escape
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-05 15:43:11 +01:00
Jakub Jelen
76b14eaed7 Update list of implemented RFCs and drafts
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
8e8f091aba connector: Simplify handling of out/err channels
Based on stale MR !461.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
d75a54e206 tests: Log SFTP server messages to stderr
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
efb7a7c4e0 tests: Cover three steps jump parsing
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
7342e73d10 sftp: Remove needless newline from log messages
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
832f92e35f socket: Refactor proxyJump connection and log more events and information
The jump thread was touching the main session object, which is
really not guaranteed to be thread safe.

The moving of the proxyjump strucutre was quite ineffective
as it involved moving the whole list to new list and then removing
the first item. This could be done easily by popping the head and
moving the whole remaining lists without any allocations.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
ea3464532e test: Tighten testing to make sure right user and key is used with proxyjumps
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
7e235f8748 auth: Log the username used for authentication
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
052c8217b7 misc: Document ssh_list_append()
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Eshan Kelkar
26b9ba5f8c 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>
2026-02-04 18:08:25 +01:00
Jakub Jelen
1b3c061aae Reproducer for memory leak from parsing knonw hosts
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <norbertpocs0@gmail.com>
2026-02-03 18:01:36 +01:00
Jakub Jelen
1525ea3dda 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>
2026-02-03 18:01:35 +01:00
Jakub Jelen
a189c2ef4d gssapi: Sanitize input parameters
Originally reported with this patch by Brian Carpenter from Deep Fork Cyber.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-03 12:09:17 +01:00
66 changed files with 4442 additions and 551 deletions

View File

@@ -1,6 +1,96 @@
CHANGELOG
=========
version 0.12.0 (released 2026-02-10)
* Deprecations and removals:
* Bumped minimal RSA key size to 1024 bits
* New functionality:
* Add support for hybrid key exchange mechanisms using Quantum Resistant
cryptography for all backends. These are now preferred:
* sntrup761x25519-sha512, sntrup761x25519-sha512@openssh.com
* mlkem768nistp256-sha256
* mlkem768x25519-sha256
* mlkem1024nistp384-sha384 (only OpenSSL 3.5+ and libgcrypt)
* New cmake option WITH_HERMETIC_USR
* Added support for Ed25519 keys through PKCS#11
* Support for host-bound public key authentication
(publickey-hostbound-v00@openssh.com)
* Use curve25519 implementation from mbedTLS and libgcrypt
* New functions for signing arbitrary data (commits) with SSH keys
* sshsig_sign()
* sshsig_verify()
* Support for FIDO/U2F keys (internal implementation using libfido2)
* Compatible with OpenSSH: should work out of the box
* Extensible with callbacks
* Add support for GSSAPI Key Exchange (RFC 4462, RFC 8732)
* Add support for new configuration options (client and server):
* RequiredRsaSize
* AddressFamily (client)
* GSSAPIKeyExchange
* GSSAPIKexAlgorithms
* New option to get list of configured identities (SSH_OPTIONS_NEXT_IDENTITY)
* More OpenSSH compatible percent expansion characters
* Add new server auth_kbdint_function() callback
* New PKI Context structure for key operations
* Stability and compatibility improvements of ProxyJump
* SFTP
* Prevent failures when SFTP status message does not contain error message
* Fix possible timeouts while waiting for SFTP messages
* Support for users-groups-by-id@openssh.com extension in client
* Support for SSH_FXF_TRUNC in server
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)
* Security:
* CVE-2025-8114: Fix NULL pointer dereference after allocation failure
* CVE-2025-8277: Fix memory leak of ephemeral key pair during repeated wrong KEX
* Potential UAF when send() fails during key exchange
* Fix possible timeout during KEX if client sends authentication too early (#311)
* Cleanup OpenSSL PKCS#11 provider when loaded
* Zeroize buffers containing private key blobs during export
version 0.11.2 (released 2025-06-24)
* Security:
* CVE-2025-4877 - Write beyond bounds in binary to base64 conversion
* CVE-2025-4878 - Use of uninitialized variable in privatekey_from_file()
* CVE-2025-5318 - Likely read beyond bounds in sftp server handle management
* CVE-2025-5351 - Double free in functions exporting keys
* CVE-2025-5372 - ssh_kdf() returns a success code on certain failures
* CVE-2025-5449 - Likely read beyond bounds in sftp server message decoding
* CVE-2025-5987 - Invalid return code for chacha20 poly1305 with OpenSSL
* Compatibility
* Fixed compatibility with CPM.cmake
* Compatibility with OpenSSH 10.0
* Tests compatibility with new Dropbear releases
* Removed p11-kit remoting from the pkcs11 testsuite
* Bugfixes
* Implement missing packet filter for DH GEX
* Properly process the SSH2_MSG_DEBUG message
* Allow escaping quotes in quoted arguments to ssh configuration
* Do not fail with unknown match keywords in ssh configuration
* Process packets before selecting signature algorithm during authentication
* Do not fail hard when the SFTP status message is not sent by noncompliant
servers
version 0.11.1 (released 2024-08-30)
* Fixed default TTY modes that are set when stdin is not connected to tty (#270)
* Fixed zlib cleanup procedure, which could crash on i386
* Various test fixes improving their stability
* Fixed cygwin build
version 0.11.0 (released 2024-07-31)
* Deprecations and Removals:
* Dropped support for DSA

View File

@@ -9,7 +9,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules")
include(DefineCMakeDefaults)
include(DefineCompilerFlags)
project(libssh VERSION 0.11.00 LANGUAGES C)
project(libssh VERSION 0.12.00 LANGUAGES C)
# global needed variable
set(APPLICATION_NAME ${PROJECT_NAME})
@@ -21,7 +21,7 @@ set(APPLICATION_NAME ${PROJECT_NAME})
# Increment AGE. Set REVISION to 0
# If the source code was changed, but there were no interface changes:
# Increment REVISION.
set(LIBRARY_VERSION "4.10.0")
set(LIBRARY_VERSION "4.11.0")
set(LIBRARY_SOVERSION "4")
# where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked

View File

@@ -12,21 +12,36 @@ option(WITH_PCAP "Compile with Pcap generation support" ON)
option(WITH_INTERNAL_DOC "Compile doxygen internal documentation" OFF)
option(BUILD_SHARED_LIBS "Build shared libraries" ON)
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(WITH_FIDO2 "Build with FIDO2/U2F support" OFF)
option(UNIT_TESTING "Build with unit tests" OFF)
option(CLIENT_TESTING "Build with client tests; requires openssh" OFF)
option(SERVER_TESTING "Build with server tests; requires openssh and dropbear" 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(SERVER_TESTING "Build with server tests; requires openssh and dropbear"
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_NACL "Build with libnacl (curve25519)" ON)
option(WITH_SYMBOL_VERSIONING "Build with symbol versioning" ON)
option(WITH_ABI_BREAK "Allow ABI break" OFF)
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(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(
WITH_INSECURE_NONE
"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(WITH_HERMETIC_USR "Build with support for hermetic /usr/" OFF)
@@ -37,39 +52,58 @@ else (WITH_ZLIB)
endif (WITH_ZLIB)
if (WITH_BENCHMARKS)
set(UNIT_TESTING ON)
set(CLIENT_TESTING ON)
endif()
set(UNIT_TESTING ON)
set(CLIENT_TESTING ON)
endif ()
if (UNIT_TESTING OR CLIENT_TESTING OR SERVER_TESTING OR GSSAPI_TESTING)
set(BUILD_STATIC_LIB ON)
endif()
if (UNIT_TESTING
OR CLIENT_TESTING
OR SERVER_TESTING
OR GSSAPI_TESTING)
set(BUILD_STATIC_LIB ON)
endif ()
if (WITH_NACL)
set(WITH_NACL ON)
set(WITH_NACL ON)
endif (WITH_NACL)
if (WITH_ABI_BREAK)
set(WITH_SYMBOL_VERSIONING ON)
set(WITH_SYMBOL_VERSIONING ON)
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 ()
if (WITH_HERMETIC_USR)
set(USR_GLOBAL_CONF_DIR "/usr${GLOBAL_CONF_DIR}")
endif ()
endif ()
if (NOT GLOBAL_BIND_CONFIG)
set(GLOBAL_BIND_CONFIG "/etc/ssh/libssh_server_config")
set(GLOBAL_BIND_CONFIG "${GLOBAL_CONF_DIR}/libssh_server_config")
if (WITH_HERMETIC_USR)
set(USR_GLOBAL_BIND_CONFIG "/usr${GLOBAL_BIND_CONFIG}")
endif ()
endif (NOT GLOBAL_BIND_CONFIG)
if (NOT GLOBAL_CLIENT_CONFIG)
set(GLOBAL_CLIENT_CONFIG "/etc/ssh/ssh_config")
set(GLOBAL_CLIENT_CONFIG "${GLOBAL_CONF_DIR}/ssh_config")
if (WITH_HERMETIC_USR)
set(USR_GLOBAL_CLIENT_CONFIG "/usr${GLOBAL_CLIENT_CONFIG}")
endif ()
endif (NOT GLOBAL_CLIENT_CONFIG)
if (WITH_HERMETIC_USR)
set(USR_GLOBAL_BIND_CONFIG "/usr${GLOBAL_BIND_CONFIG}")
set(USR_GLOBAL_CLIENT_CONFIG "/usr${GLOBAL_CLIENT_CONFIG}")
endif (WITH_HERMETIC_USR)
if (FUZZ_TESTING)
set(WITH_INSECURE_NONE ON)
set(WITH_INSECURE_NONE ON)
endif (FUZZ_TESTING)
if (WIN32)
set(WITH_EXEC 0)
endif(WIN32)
endif (WIN32)

View File

@@ -8,6 +8,10 @@
#cmakedefine BINARYDIR "${BINARYDIR}"
#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 */
#cmakedefine USR_GLOBAL_BIND_CONFIG "${USR_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;
size_t hlen;
char buf[10];
char *hexa = NULL;
char *p = NULL;
int cmp;
int rc;
@@ -201,7 +200,7 @@ int verify_knownhost(ssh_session session)
}
rc = ssh_get_publickey_hash(srv_pubkey,
SSH_PUBLICKEY_HASH_SHA1,
SSH_PUBLICKEY_HASH_SHA256,
&hash,
&hlen);
ssh_key_free(srv_pubkey);
@@ -217,7 +216,7 @@ int verify_knownhost(ssh_session session)
break;
case SSH_KNOWN_HOSTS_CHANGED:
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");
ssh_clean_pubkey_hash(&hash);
@@ -238,10 +237,9 @@ int verify_knownhost(ssh_session session)
/* FALL THROUGH to SSH_SERVER_NOT_KNOWN behavior */
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, "Public key hash: %s\n", hexa);
ssh_string_free_char(hexa);
fprintf(stderr, "Public key hash: ");
ssh_print_hash(SSH_PUBLICKEY_HASH_SHA256, hash, hlen);
ssh_clean_pubkey_hash(&hash);
p = fgets(buf, sizeof(buf), stdin);
if (p == NULL) {

View File

@@ -171,21 +171,15 @@ The following RFC documents described SSH-2 protocol as an Internet standard.
The Secure Shell (SSH) Session Channel Break Extension
- <a href="https://tools.ietf.org/html/rfc4344" target="_blank">RFC 4344</a>,
The Secure Shell (SSH) Transport Layer Encryption Modes
- <a href="https://tools.ietf.org/html/rfc4345" target="_blank">RFC 4345</a>,
Improved Arcfour Modes for the Secure Shell (SSH) Transport Layer Protocol
It was later modified and expanded by the following RFCs.
- <a href="https://tools.ietf.org/html/rfc4419" target="_blank">RFC 4419</a>,
Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer
Protocol
- <a href="https://tools.ietf.org/html/rfc4432" target="_blank">RFC 4432</a>,
RSA Key Exchange for the Secure Shell (SSH) Transport Layer Protocol
(not implemented in libssh)
- <a href="https://tools.ietf.org/html/rfc4462" target="_blank">RFC 4462</a>,
Generic Security Service Application Program Interface (GSS-API)
Authentication and Key Exchange for the Secure Shell (SSH) Protocol
(only the authentication implemented in libssh)
- <a href="https://tools.ietf.org/html/rfc4716" target="_blank">RFC 4716</a>,
The Secure Shell (SSH) Public Key File Format
(not implemented in libssh)
@@ -204,7 +198,6 @@ It was later modified and expanded by the following RFCs.
(not implemented in libssh)
- <a href="https://tools.ietf.org/html/rfc8160" target="_blank">RFC 8160</a>,
IUTF8 Terminal Mode in Secure Shell (SSH)
(not handled in libssh)
- <a href="https://tools.ietf.org/html/rfc8270" target="_blank">RFC 8270</a>,
Increase the Secure Shell Minimum Recommended Diffie-Hellman Modulus Size to 2048 Bits
- <a href="https://tools.ietf.org/html/rfc8308" target="_blank">RFC 8308</a>,
@@ -223,6 +216,14 @@ There are also drafts that are being currently developed and followed.
- <a href="https://tools.ietf.org/html/draft-miller-ssh-agent-03" target="_blank">draft-miller-ssh-agent-08</a>
SSH Agent Protocol
- <a href="https://tools.ietf.org/html/draft-ietf-sshm-mlkem-hybrid-kex-09" target="_blank">draft-ietf-sshm-mlkem-hybrid-kex-09</a>
PQ/T Hybrid Key Exchange with ML-KEM in SSH
- <a href="https://tools.ietf.org/html/draft-ietf-sshm-ntruprime-ssh-06" target="_blank">draft-ietf-sshm-ntruprime-ssh-06</a>
Secure Shell (SSH) Key Exchange Method Using Hybrid Streamlined NTRU Prime sntrup761 and X25519 with SHA-512: sntrup761x25519-sha512
- <a href="https://tools.ietf.org/html/draft-ietf-sshm-chacha20-poly1305-02" target="_blank">draft-ietf-sshm-chacha20-poly1305-02</a>
Secure Shell (SSH) authenticated encryption cipher: chacha20-poly1305
- <a href="https://tools.ietf.org/html/draft-ietf-sshm-strict-kex-01" target="_blank">draft-ietf-sshm-strict-kex-01</a>
SSH Strict KEX extension
Interesting cryptography documents:
@@ -247,8 +248,6 @@ them like the statvfs calls in SFTP or the ssh-agent.
OpenSSH's deviations and extensions</a>
- <a href="https://api.libssh.org/rfc/PROTOCOL.certkeys" target="_blank">
OpenSSH's pubkey certificate authentication</a>
- <a href="https://api.libssh.org/rfc/PROTOCOL.chacha20poly1305" target="_blank">
chacha20-poly1305@openssh.com authenticated encryption mode</a>
- <a href="https://api.libssh.org/rfc/PROTOCOL.key" target="_blank">
OpenSSH private key format (openssh-key-v1)</a>

View File

@@ -337,6 +337,7 @@ static void batch_shell(ssh_session session)
static int client(ssh_session session)
{
int auth = 0;
int authenticated = 0;
char *banner = NULL;
int state;
@@ -369,16 +370,28 @@ static int client(ssh_session session)
return -1;
}
ssh_userauth_none(session, NULL);
banner = ssh_get_issue_banner(session);
if (banner) {
printf("%s\n", banner);
free(banner);
}
auth = authenticate_console(session);
if (auth != SSH_AUTH_SUCCESS) {
auth = ssh_userauth_none(session, NULL);
if (auth == SSH_AUTH_SUCCESS) {
authenticated = 1;
} else if (auth == SSH_AUTH_ERROR) {
fprintf(stderr,
"Authentication error during none auth: %s\n",
ssh_get_error(session));
return -1;
}
if (!authenticated) {
auth = authenticate_console(session);
if (auth != SSH_AUTH_SUCCESS) {
return -1;
}
}
if (cmds[0] == NULL) {
shell(session);
} else {

View File

@@ -432,6 +432,7 @@ enum ssh_options_e {
SSH_OPTIONS_ADDRESS_FAMILY,
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE,
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
SSH_OPTIONS_NEXT_IDENTITY,
};
enum {

View File

@@ -36,6 +36,7 @@
#include <sys/types.h>
#include <stdbool.h>
#endif /* _WIN32 */
#include <stdio.h>
#ifdef __cplusplus
extern "C" {
@@ -43,8 +44,9 @@ extern "C" {
/* in misc.c */
/* gets the user home dir. */
char *ssh_get_user_home_dir(void);
char *ssh_get_user_home_dir(ssh_session session);
char *ssh_get_local_username(void);
char *ssh_get_local_hostname(void);
int ssh_file_readaccess_ok(const char *file);
int ssh_dir_writeable(const char *path);
@@ -136,6 +138,8 @@ int ssh_check_username_syntax(const char *username);
void ssh_proxyjumps_free(struct ssh_list *proxy_jump_list);
bool ssh_libssh_proxy_jumps(void);
FILE *ssh_strict_fopen(const char *filename, size_t max_file_size);
#ifdef __cplusplus
}
#endif

View File

@@ -508,6 +508,9 @@ char *ssh_strerror(int err_num, char *buf, size_t buflen);
#define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1)
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
}
#endif

View File

@@ -246,13 +246,16 @@ struct ssh_session_struct {
struct {
struct ssh_list *identity;
struct ssh_list *identity_non_exp;
struct ssh_iterator *identity_it;
struct ssh_list *certificate;
struct ssh_list *certificate_non_exp;
struct ssh_list *proxy_jumps;
struct ssh_list *proxy_jumps_user_cb;
char *proxy_jumps_str;
char *username;
char *host;
char *bindaddr; /* bind the client to an ip addr */
char *homedir;
char *sshdir;
char *knownhosts;
char *global_knownhosts;

View File

@@ -1 +1 @@
4.10.0
4.11.0

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

@@ -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

@@ -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

@@ -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

@@ -0,0 +1,465 @@
_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_get_users_groups_by_id
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_name_id_map_free
sftp_name_id_map_new
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_supported_methods
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_get_sk_application
ssh_key_get_sk_flags
ssh_key_get_sk_user_id
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_ctx_free
ssh_pki_ctx_get_sk_attestation_buffer
ssh_pki_ctx_new
ssh_pki_ctx_options_set
ssh_pki_ctx_set_sk_pin_callback
ssh_pki_ctx_sk_callbacks_option_set
ssh_pki_ctx_sk_callbacks_options_clear
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_generate_key
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_sk_resident_keys_load
ssh_string_burn
ssh_string_cmp
ssh_string_copy
ssh_string_data
ssh_string_fill
ssh_string_free
ssh_string_free_char
ssh_string_from_char
ssh_string_from_data
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
sshsig_sign
sshsig_verify
string_burn
string_copy
string_data
string_fill
string_free
string_from_char
string_len
string_new
string_to_char

View File

@@ -1396,6 +1396,11 @@ int ssh_userauth_publickey_auto(ssh_session session,
if (session == NULL) {
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)) {
session->auth.supported_methods &= ~SSH_AUTH_METHOD_PUBLICKEY;
return SSH_AUTH_DENIED;

View File

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

View File

@@ -256,10 +256,9 @@ local_parse_file(ssh_session session,
return;
}
f = fopen(filename, "r");
f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (f == NULL) {
SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
filename);
/* The underlying function logs the reasons */
return;
}
@@ -493,6 +492,10 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
bool parse_entry = do_parsing;
bool libssh_proxy_jump = ssh_libssh_proxy_jumps();
if (do_parsing) {
SAFE_FREE(session->opts.proxy_jumps_str);
ssh_proxyjumps_free(session->opts.proxy_jumps);
}
/* Special value none disables the proxy */
cmp = strcasecmp(s, "none");
if (cmp == 0) {
@@ -509,6 +512,17 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
return SSH_ERROR;
}
if (do_parsing) {
/* Store the whole string in session */
SAFE_FREE(session->opts.proxy_jumps_str);
session->opts.proxy_jumps_str = strdup(s);
if (session->opts.proxy_jumps_str == NULL) {
free(c);
ssh_set_error_oom(session);
return SSH_ERROR;
}
}
cp = c;
do {
endp = strchr(cp, ',');
@@ -1693,8 +1707,9 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
int rv;
bool global = 0;
fp = fopen(filename, "r");
fp = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (fp == NULL) {
/* The underlying function logs the reasons */
return 0;
}

View File

@@ -305,6 +305,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 occurred.
*
* @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
*
@@ -390,8 +471,8 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
ssh_set_error(connector->session, SSH_FATAL, "output socket or channel closed");
return;
}
connector->out_wontblock = 0;
connector->in_available = 0;
ssh_connector_update_flags_after_io(connector);
} else {
connector->in_available = 1;
}
@@ -444,8 +525,8 @@ ssh_connector_fd_out_cb(ssh_connector connector)
"Output socket or channel closed");
return;
}
connector->in_available = 0;
connector->out_wontblock = 0;
ssh_connector_update_flags_after_io(connector);
} else {
connector->out_wontblock = 1;
}
@@ -542,16 +623,8 @@ static int ssh_connector_channel_data_cb(ssh_session session,
window_len = MIN(window, len);
/* Route the data to the right exception channel */
if (is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR)) {
w = ssh_channel_write_stderr(connector->out_channel,
data,
window_len);
} else if (!is_stderr &&
(connector->out_flags & SSH_CONNECTOR_STDOUT)) {
w = ssh_channel_write(connector->out_channel,
data,
window_len);
} else if (connector->out_flags & SSH_CONNECTOR_STDOUT) {
if (connector->out_flags & SSH_CONNECTOR_STDOUT &&
!(is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR))) {
w = ssh_channel_write(connector->out_channel,
data,
window_len);
@@ -574,11 +647,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
return SSH_ERROR;
}
connector->out_wontblock = 0;
connector->in_available = 0;
if ((unsigned int)w < len) {
connector->in_available = 1;
}
ssh_connector_update_flags_after_io(connector);
ssh_connector_reset_pollevents(connector);
return w;
@@ -650,8 +719,8 @@ ssh_connector_channel_write_wontblock_cb(ssh_session session,
return 0;
}
connector->in_available = 0;
connector->out_wontblock = 0;
ssh_connector_update_flags_after_io(connector);
} else {
connector->out_wontblock = 1;
}

View File

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

View File

@@ -198,7 +198,7 @@ int
ssh_gssapi_handle_userauth(ssh_session session, const char *user,
uint32_t n_oid, ssh_string *oids)
{
char hostname[NI_MAXHOST] = {0};
char *hostname = NULL;
OM_uint32 maj_stat, min_stat;
size_t i;
gss_OID_set supported; /* oids supported by server */
@@ -210,14 +210,6 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
int rc;
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
rc = gethostname(hostname, 64);
if (rc != 0) {
SSH_LOG(SSH_LOG_TRACE,
"Error getting hostname: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
/* Destroy earlier GSSAPI context if any */
ssh_gssapi_free(session);
rc = ssh_gssapi_init(session);
@@ -284,7 +276,16 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
return SSH_OK;
}
hostname = ssh_get_local_hostname();
if (hostname == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Error getting hostname: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
rc = ssh_gssapi_import_name(session->gssapi, hostname);
SAFE_FREE(hostname);
if (rc != SSH_OK) {
ssh_auth_reply_default(session, 0);
gss_release_oid_set(&min_stat, &both_supported);
@@ -850,6 +851,10 @@ int ssh_gssapi_client_identity(ssh_session session, gss_OID_set *valid_oids)
char *ptr = NULL;
int ret;
if (session == NULL || session->gssapi == NULL) {
return SSH_ERROR;
}
if (session->gssapi->client.client_deleg_creds == NULL) {
if (session->opts.gss_client_identity != NULL) {
namebuf.value = (void *)session->opts.gss_client_identity;

View File

@@ -421,7 +421,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
gss_name_t client_name = GSS_C_NO_NAME;
OM_uint32 ret_flags = 0;
gss_buffer_desc mic = GSS_C_EMPTY_BUFFER, msg = GSS_C_EMPTY_BUFFER;
char hostname[NI_MAXHOST] = {0};
char *hostname = NULL;
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
rc = ssh_buffer_unpack(packet, "S", &otoken);
@@ -538,8 +538,8 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
goto error;
}
rc = gethostname(hostname, 64);
if (rc != 0) {
hostname = ssh_get_local_hostname();
if (hostname == NULL) {
SSH_LOG(SSH_LOG_TRACE,
"Error getting hostname: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
@@ -547,6 +547,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
}
rc = ssh_gssapi_import_name(session->gssapi, hostname);
SAFE_FREE(hostname);
if (rc != SSH_OK) {
goto error;
}

View File

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

View File

@@ -216,11 +216,22 @@ ssh_known_hosts_entries_compare(struct ssh_knownhosts_entry *k1,
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
* will be added to the list in entries argument.
* If the entries list is NULL, it will allocate a new list. Caller
* 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,
const char *filename,
@@ -232,7 +243,7 @@ static int ssh_known_hosts_read_entries(const char *match,
FILE *fp = NULL;
int rc;
fp = fopen(filename, "r");
fp = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
if (fp == NULL) {
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
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
*
* @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.
*
* This checks the known_hosts file to find out which algorithms should be
@@ -453,7 +491,7 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
return list;
error:
ssh_list_free(entry_list);
ssh_knownhosts_entries_free(entry_list);
ssh_list_free(list);
return NULL;
}
@@ -505,6 +543,7 @@ static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e typ
/**
* @internal
*
* @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
@@ -549,7 +588,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
&entry_list);
if (rc != 0) {
SAFE_FREE(host_port);
ssh_list_free(entry_list);
ssh_knownhosts_entries_free(entry_list);
return NULL;
}
@@ -558,7 +597,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
&entry_list);
SAFE_FREE(host_port);
if (rc != 0) {
ssh_list_free(entry_list);
ssh_knownhosts_entries_free(entry_list);
return NULL;
}
@@ -799,7 +838,6 @@ out:
enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
{
struct ssh_list *entry_list = NULL;
struct ssh_iterator *it = NULL;
char *host_port = NULL;
bool global_known_hosts_found = false;
bool known_hosts_found = false;
@@ -860,7 +898,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
&entry_list);
if (rc != 0) {
SAFE_FREE(host_port);
ssh_list_free(entry_list);
ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_ERROR;
}
}
@@ -871,7 +909,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
&entry_list);
if (rc != 0) {
SAFE_FREE(host_port);
ssh_list_free(entry_list);
ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_ERROR;
}
}
@@ -883,16 +921,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
return SSH_KNOWN_HOSTS_UNKNOWN;
}
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);
ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_OK;
}
@@ -1079,13 +1108,13 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
filename,
&entry_list);
if (rc != 0) {
ssh_list_free(entry_list);
ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN;
}
it = ssh_list_get_iterator(entry_list);
if (it == NULL) {
ssh_list_free(entry_list);
ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN;
}
@@ -1115,16 +1144,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
}
}
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);
ssh_knownhosts_entries_free(entry_list);
return found;
}
@@ -1196,6 +1216,8 @@ ssh_session_get_known_hosts_entry(ssh_session session,
}
/**
* @internal
*
* @brief Get the known_hosts entry for the current connected session
* from the given known_hosts file.
*

View File

@@ -615,10 +615,10 @@ int ssh_publickey_to_file(ssh_session session,
FILE *fp = NULL;
char *user = NULL;
char buffer[1024];
char host[256];
char *host = NULL;
unsigned char *pubkey_64 = NULL;
size_t len;
int rc;
if(session==NULL)
return SSH_ERROR;
if(file==NULL || pubkey==NULL){
@@ -636,8 +636,8 @@ int ssh_publickey_to_file(ssh_session session,
return SSH_ERROR;
}
rc = gethostname(host, sizeof(host));
if (rc < 0) {
host = ssh_get_local_hostname();
if (host == NULL) {
SAFE_FREE(user);
SAFE_FREE(pubkey_64);
return SSH_ERROR;
@@ -651,6 +651,7 @@ int ssh_publickey_to_file(ssh_session session,
SAFE_FREE(pubkey_64);
SAFE_FREE(user);
SAFE_FREE(host);
SSH_LOG(SSH_LOG_RARE, "Trying to write public key file: %s", file);
SSH_LOG(SSH_LOG_PACKET, "public key file content: %s", buffer);

View File

@@ -1,4 +1,4 @@
# This map file was updated with abimap-0.3.2
# This map file was updated with abimap-0.4.0
LIBSSH_4_5_0 # Released
{
@@ -482,27 +482,28 @@ LIBSSH_4_10_0 # Released
ssh_request_no_more_sessions;
} LIBSSH_4_9_0;
LIBSSH_AFTER_4_10_0
LIBSSH_4_11_0 # Released
{
global:
sftp_get_users_groups_by_id;
sftp_name_id_map_free;
sftp_name_id_map_new;
ssh_get_supported_methods;
sshsig_sign;
sshsig_verify;
ssh_string_cmp;
ssh_string_from_data;
ssh_pki_ctx_new;
ssh_key_get_sk_application;
ssh_key_get_sk_flags;
ssh_key_get_sk_user_id;
ssh_pki_ctx_free;
ssh_pki_ctx_get_sk_attestation_buffer;
ssh_pki_ctx_new;
ssh_pki_ctx_options_set;
ssh_pki_ctx_set_sk_pin_callback;
ssh_pki_ctx_sk_callbacks_option_set;
ssh_pki_ctx_sk_callbacks_options_clear;
ssh_pki_ctx_get_sk_attestation_buffer;
ssh_key_get_sk_flags;
ssh_key_get_sk_application;
ssh_key_get_sk_user_id;
ssh_pki_generate_key;
ssh_sk_resident_keys_load;
ssh_string_cmp;
ssh_string_from_data;
sshsig_sign;
sshsig_verify;
} LIBSSH_4_10_0;

View File

@@ -53,85 +53,70 @@
#include "libssh/priv.h"
#define MAX_MATCH_RECURSION 16
/*
* Returns true if the given string matches the pattern (which may contain ?
* and * as wildcards), and zero if it does not match.
/**
* @brief Compare a string with a pattern containing wildcards `*` and `?`
*
* This function is an iterative replacement for the previously recursive
* 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;
}
for (;;) {
/* If at end of pattern, accept if also at end of string. */
if (*pattern == '\0') {
return (*s == '\0');
}
/* Skip all the asterisks and adjacent question marks */
while (*pattern == '*' || (had_asterisk && *pattern == '?')) {
if (*pattern == '*') {
had_asterisk = true;
}
while (*s) {
/* Case 1: Exact match or '?' wildcard */
if (*pattern == *s || *pattern == '?') {
s++;
pattern++;
continue;
}
if (had_asterisk) {
/* If at end of pattern, accept immediately. */
if (!*pattern)
return 1;
/* 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.
/* Case 2: '*' wildcard */
if (*pattern == '*') {
/* Record the position of the star and the current string position.
* We optimistically assume * matches 0 characters first.
*/
for (; *s; s++) {
if (match_pattern(s, pattern, limit - 1)) {
return 1;
}
}
/* 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;
p_star = ++pattern;
s_star = s;
continue;
}
/* Check if the next character of the string is acceptable. */
if (*pattern != '?' && *pattern != *s) {
return 0;
/* Case 3: Mismatch */
if (p_star) {
/* 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. */
s++;
/* Case 4: Mismatch and no star to backtrack to */
return 0;
}
/* Handle trailing stars in the pattern
* (e.g., pattern "abc*" matching "abc") */
while (*pattern == '*') {
pattern++;
}
/* NOTREACHED */
return 0;
/* If we reached the end of the pattern, it's a match */
return (*pattern == '\0');
}
/*
@@ -182,7 +167,7 @@ int match_pattern_list(const char *string, const char *pattern,
sub[subi] = '\0';
/* Try to match the subpattern against the string. */
if (match_pattern(string, sub, MAX_MATCH_RECURSION)) {
if (match_pattern(string, sub)) {
if (negated) {
return -1; /* Negative */
} else {

View File

@@ -37,6 +37,7 @@
#endif /* _WIN32 */
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
@@ -108,22 +109,22 @@
*/
#ifdef _WIN32
char *ssh_get_user_home_dir(void)
static char *ssh_get_user_home_dir_internal(void)
{
char tmp[PATH_MAX] = {0};
char *szPath = NULL;
char tmp[PATH_MAX] = {0};
char *szPath = NULL;
if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
szPath = malloc(strlen(tmp) + 1);
if (szPath == NULL) {
return NULL;
if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
szPath = malloc(strlen(tmp) + 1);
if (szPath == NULL) {
return NULL;
}
strcpy(szPath, tmp);
return szPath;
}
strcpy(szPath, tmp);
return szPath;
}
return NULL;
return NULL;
}
/* we have read access on file */
@@ -298,7 +299,7 @@ int ssh_is_ipaddr(const char *str)
#define NSS_BUFLEN_PASSWD 4096
#endif /* NSS_BUFLEN_PASSWD */
char *ssh_get_user_home_dir(void)
static char *ssh_get_user_home_dir_internal(void)
{
char *szPath = NULL;
struct passwd pwd;
@@ -313,7 +314,6 @@ char *ssh_get_user_home_dir(void)
return NULL;
}
snprintf(buf, sizeof(buf), "%s", szPath);
return strdup(buf);
}
@@ -428,6 +428,29 @@ int ssh_is_ipaddr(const char *str)
#endif /* _WIN32 */
char *ssh_get_user_home_dir(ssh_session session)
{
char *szPath = NULL;
/* If used previously, reuse cached value */
if (session != NULL && session->opts.homedir != NULL) {
return strdup(session->opts.homedir);
}
szPath = ssh_get_user_home_dir_internal();
if (szPath == NULL) {
return NULL;
}
if (session != NULL) {
/* cache it:
* failure is not fatal -- at worst we will just not cache it */
session->opts.homedir = strdup(szPath);
}
return szPath;
}
char *ssh_lowercase(const char* str)
{
char *new = NULL, *p = NULL;
@@ -468,6 +491,38 @@ char *ssh_hostport(const char *host, int port)
return dest;
}
static char *
ssh_get_hexa_internal(const unsigned char *what, size_t len, bool colons)
{
const char h[] = "0123456789abcdef";
char *hexa = NULL;
size_t i;
size_t bytes_per_byte = 2 + (colons ? 1 : 0);
size_t hlen = len * bytes_per_byte;
if (what == NULL || len < 1 || len > (UINT_MAX - 1) / bytes_per_byte) {
return NULL;
}
hexa = calloc(hlen + 1, sizeof(char));
if (hexa == NULL) {
return NULL;
}
for (i = 0; i < len; i++) {
hexa[i * bytes_per_byte] = h[(what[i] >> 4) & 0xF];
hexa[i * bytes_per_byte + 1] = h[what[i] & 0xF];
if (colons) {
hexa[i * bytes_per_byte + 2] = ':';
}
}
if (colons) {
hexa[hlen - 1] = '\0';
}
return hexa;
}
/**
* @brief Convert a buffer into a colon separated hex string.
* The caller has to free the memory.
@@ -483,28 +538,7 @@ char *ssh_hostport(const char *host, int port)
*/
char *ssh_get_hexa(const unsigned char *what, size_t len)
{
const char h[] = "0123456789abcdef";
char *hexa = NULL;
size_t i;
size_t hlen = len * 3;
if (len > (UINT_MAX - 1) / 3) {
return NULL;
}
hexa = malloc(hlen + 1);
if (hexa == NULL) {
return NULL;
}
for (i = 0; i < len; i++) {
hexa[i * 3] = h[(what[i] >> 4) & 0xF];
hexa[i * 3 + 1] = h[what[i] & 0xF];
hexa[i * 3 + 2] = ':';
}
hexa[hlen - 1] = '\0';
return hexa;
return ssh_get_hexa_internal(what, len, true);
}
/**
@@ -814,7 +848,17 @@ static struct ssh_iterator *ssh_iterator_new(const void *data)
return iterator;
}
int ssh_list_append(struct ssh_list *list,const void *data)
/**
* @internal
*
* @brief Appends an element to the end of the list.
*
* @param[in] list The list to append the element
* @param[in] data The element to append
*
* @return `SSH_OK` on success, `SSH_ERROR` on error
*/
int ssh_list_append(struct ssh_list *list, const void *data)
{
struct ssh_iterator *iterator = NULL;
@@ -1179,7 +1223,7 @@ char *ssh_path_expand_tilde(const char *d)
} else {
ld = strlen(d);
p = (char *) d;
h = ssh_get_user_home_dir();
h = ssh_get_user_home_dir(NULL);
}
if (h == NULL) {
return NULL;
@@ -1201,15 +1245,118 @@ char *ssh_path_expand_tilde(const char *d)
return r;
}
char *ssh_get_local_hostname(void)
{
char host[NI_MAXHOST] = {0};
int rc;
rc = gethostname(host, sizeof(host));
if (rc != 0) {
return NULL;
}
return strdup(host);
}
static char *get_connection_hash(ssh_session session)
{
unsigned char conn_hash[SHA_DIGEST_LENGTH];
char *local_hostname = NULL;
SHACTX ctx = sha1_init();
char strport[10] = {0};
unsigned int port;
char *username = NULL;
int rc;
if (session == NULL) {
return NULL;
}
if (ctx == NULL) {
goto err;
}
/* Local hostname %l */
local_hostname = ssh_get_local_hostname();
if (local_hostname == NULL) {
goto err;
}
rc = sha1_update(ctx, local_hostname, strlen(local_hostname));
if (rc != SSH_OK) {
goto err;
}
SAFE_FREE(local_hostname);
/* Remote hostname %h */
if (session->opts.host == NULL) {
goto err;
}
rc = sha1_update(ctx, session->opts.host, strlen(session->opts.host));
if (rc != SSH_OK) {
goto err;
}
/* Remote port %p */
ssh_options_get_port(session, &port);
snprintf(strport, sizeof(strport), "%d", port);
rc = sha1_update(ctx, strport, strlen(strport));
if (rc != SSH_OK) {
goto err;
}
/* The remote username %r */
username = session->opts.username;
if (username == NULL) {
/* fallback to local username: it will be used if not explicitly set */
username = ssh_get_local_username();
if (username == NULL) {
goto err;
}
}
rc = sha1_update(ctx, username, strlen(username));
if (username != session->opts.username) {
free(username);
}
if (rc != SSH_OK) {
goto err;
}
/* ProxyJump */
if (session->opts.proxy_jumps_str != NULL) {
rc = sha1_update(ctx,
session->opts.proxy_jumps_str,
strlen(session->opts.proxy_jumps_str));
}
if (rc != SSH_OK) {
goto err;
}
/* Frees context */
rc = sha1_final(conn_hash, ctx);
if (rc != SSH_OK) {
goto err;
}
return ssh_get_hexa_internal(conn_hash, SHA_DIGEST_LENGTH, false);
err:
free(local_hostname);
sha1_ctx_free(ctx);
return NULL;
}
/** @internal
* @brief expands a string in function of session options
*
* @param[in] s Format string to expand. Known parameters:
* %d SSH configuration directory (~/.ssh)
* %h target host name
* %u local username
* %l local hostname
* %r remote username
* %p remote port
* - %d user home directory (~)
* - %h target host name
* - %u local username
* - %l local hostname
* - %r remote username
* - %p remote port
* - %j proxyjump string
* - %C Hash of %l%h%p%r%j
*
* @returns Expanded string. The caller needs to free the memory using
* ssh_string_free_char().
*
@@ -1217,7 +1364,6 @@ char *ssh_path_expand_tilde(const char *d)
*/
char *ssh_path_expand_escape(ssh_session session, const char *s)
{
char host[NI_MAXHOST] = {0};
char *buf = NULL;
char *r = NULL;
char *x = NULL;
@@ -1266,65 +1412,67 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
}
switch (*p) {
case '%':
goto escape;
case 'd':
if (session->opts.sshdir) {
x = strdup(session->opts.sshdir);
} else {
ssh_set_error(session, SSH_FATAL,
"Cannot expand sshdir");
free(buf);
free(r);
return NULL;
}
break;
case 'u':
x = ssh_get_local_username();
break;
case 'l':
if (gethostname(host, sizeof(host) == 0)) {
x = strdup(host);
}
break;
case 'h':
if (session->opts.host) {
x = strdup(session->opts.host);
} else {
ssh_set_error(session, SSH_FATAL,
"Cannot expand host");
free(buf);
free(r);
return NULL;
}
break;
case 'r':
if (session->opts.username) {
x = strdup(session->opts.username);
} else {
ssh_set_error(session, SSH_FATAL,
"Cannot expand username");
free(buf);
free(r);
return NULL;
}
break;
case 'p':
{
char tmp[6];
snprintf(tmp, sizeof(tmp), "%hu",
(uint16_t)(session->opts.port > 0 ? session->opts.port
: 22));
x = strdup(tmp);
}
break;
default:
ssh_set_error(session, SSH_FATAL,
"Wrong escape sequence detected");
case '%':
goto escape;
case 'd':
x = ssh_get_user_home_dir(session);
if (x == NULL) {
ssh_set_error(session, SSH_FATAL, "Cannot expand homedir");
free(buf);
free(r);
return NULL;
}
break;
case 'u':
x = ssh_get_local_username();
break;
case 'l':
x = ssh_get_local_hostname();
break;
case 'h':
if (session->opts.host) {
x = strdup(session->opts.host);
} else {
ssh_set_error(session, SSH_FATAL, "Cannot expand host");
free(buf);
free(r);
return NULL;
}
break;
case 'r':
if (session->opts.username) {
x = strdup(session->opts.username);
} else {
ssh_set_error(session, SSH_FATAL, "Cannot expand username");
free(buf);
free(r);
return NULL;
}
break;
case 'p': {
char tmp[6];
unsigned int port;
ssh_options_get_port(session, &port);
snprintf(tmp, sizeof(tmp), "%u", port);
x = strdup(tmp);
break;
}
case 'j':
if (session->opts.proxy_jumps_str != NULL) {
x = strdup(session->opts.proxy_jumps_str);
} else {
x = strdup("");
}
break;
case 'C':
x = get_connection_hash(session);
break;
default:
ssh_set_error(session, SSH_FATAL, "Wrong escape sequence detected");
free(buf);
free(r);
return NULL;
}
if (x == NULL) {
@@ -1336,8 +1484,7 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
i += strlen(x);
if (i >= MAX_BUF_SIZE) {
ssh_set_error(session, SSH_FATAL,
"String too long");
ssh_set_error(session, SSH_FATAL, "String too long");
free(buf);
free(x);
free(r);
@@ -2283,4 +2430,77 @@ ssh_libssh_proxy_jumps(void)
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

@@ -907,7 +907,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
SAFE_FREE(session->opts.global_knownhosts);
if (v == NULL) {
session->opts.global_knownhosts =
strdup("/etc/ssh/ssh_known_hosts");
strdup(GLOBAL_CONF_DIR "/ssh_known_hosts");
if (session->opts.global_knownhosts == NULL) {
ssh_set_error_oom(session);
return -1;
@@ -1197,7 +1197,6 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session);
return -1;
} else {
ssh_proxyjumps_free(session->opts.proxy_jumps);
rc = ssh_config_parse_proxy_jump(session, v, true);
if (rc != SSH_OK) {
return SSH_ERROR;
@@ -1562,7 +1561,16 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
* - SSH_OPTIONS_IDENTITY:
* Get the first identity file name (const char *).\n
* \n
* By default id_rsa, id_ecdsa and id_ed25519 files are used.
* By default `id_rsa`, `id_ecdsa`, `id_ed25519`, `id_ecdsa_sk`
* and `id_ed25519_sk` (when SK support is built in) files are
* used.
*
* - SSH_OPTIONS_NEXT_IDENTITY:
* Get the next identity file name (const char *).\n
* \n
* Repeat calls to get all key paths. SSH_EOF is returned when
* the end of list is reached. Another call will start another
* iteration over the same list.
*
* - SSH_OPTIONS_PROXYCOMMAND:
* Get the proxycommand necessary to log into the
@@ -1658,6 +1666,30 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
break;
}
case SSH_OPTIONS_NEXT_IDENTITY: {
if (session->opts.identity_it != NULL) {
/* Move to the next item */
session->opts.identity_it = session->opts.identity_it->next;
if (session->opts.identity_it == NULL) {
*value = NULL;
return SSH_EOF;
}
} else {
/* Get iterator from opts */
struct ssh_iterator *it = NULL;
it = ssh_list_get_iterator(session->opts.identity);
if (it == NULL) {
it = ssh_list_get_iterator(session->opts.identity_non_exp);
}
if (it == NULL) {
return SSH_ERROR;
}
session->opts.identity_it = it;
}
src = ssh_iterator_value(char *, session->opts.identity_it);
break;
}
case SSH_OPTIONS_PROXYCOMMAND:
src = session->opts.ProxyCommand;
break;
@@ -1963,7 +1995,7 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
/* set default filename */
if (filename == NULL) {
expanded_filename = ssh_path_expand_escape(session, "%d/config");
expanded_filename = ssh_path_expand_escape(session, "%d/.ssh/config");
} else {
expanded_filename = ssh_path_expand_escape(session, filename);
}
@@ -1976,11 +2008,16 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
goto out;
}
if (filename == NULL) {
if ((fp = fopen(GLOBAL_CLIENT_CONFIG, "r")) != NULL) {
fp = ssh_strict_fopen(GLOBAL_CLIENT_CONFIG, SSH_MAX_CONFIG_FILE_SIZE);
if (fp != NULL) {
filename = GLOBAL_CLIENT_CONFIG;
#ifdef USR_GLOBAL_CLIENT_CONFIG
} else if ((fp = fopen(USR_GLOBAL_CLIENT_CONFIG, "r")) != NULL) {
filename = USR_GLOBAL_CLIENT_CONFIG;
} else {
fp = ssh_strict_fopen(USR_GLOBAL_CLIENT_CONFIG,
SSH_MAX_CONFIG_FILE_SIZE);
if (fp != NULL) {
filename = USR_GLOBAL_CLIENT_CONFIG;
}
#endif
}
@@ -2021,7 +2058,7 @@ int ssh_options_apply(ssh_session session)
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) {
if (session->opts.knownhosts == NULL) {
tmp = ssh_path_expand_escape(session, "%d/known_hosts");
tmp = ssh_path_expand_escape(session, "%d/.ssh/known_hosts");
} else {
tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
}
@@ -2035,7 +2072,7 @@ int ssh_options_apply(ssh_session session)
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_GLOBAL_KNOWNHOSTS) == 0) {
if (session->opts.global_knownhosts == NULL) {
tmp = strdup("/etc/ssh/ssh_known_hosts");
tmp = strdup(GLOBAL_CONF_DIR "/ssh_known_hosts");
} else {
tmp = ssh_path_expand_escape(session,
session->opts.global_knownhosts);

View File

@@ -834,6 +834,10 @@ int ssh_key_is_private(const ssh_key k) {
/**
* @brief Compare keys if they are equal.
*
* Note that comparing private keys is almost never needed. The private key
* is cryptographically bound to the public key and comparing public keys should
* always be preferred.
*
* @param[in] k1 The first key to compare.
*
* @param[in] k2 The second key to compare.
@@ -2684,7 +2688,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
const char *filename)
{
char key_buf[MAX_LINE_SIZE];
char host[256];
char *host = NULL;
char *b64_key = NULL;
char *user = NULL;
FILE *fp = NULL;
@@ -2699,8 +2703,8 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
return SSH_ERROR;
}
rc = gethostname(host, sizeof(host));
if (rc < 0) {
host = ssh_get_local_hostname();
if (host == NULL) {
free(user);
return SSH_ERROR;
}
@@ -2708,6 +2712,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
rc = ssh_pki_export_pubkey_base64(key, &b64_key);
if (rc < 0) {
free(user);
free(host);
return SSH_ERROR;
}
@@ -2718,6 +2723,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
user,
host);
free(user);
free(host);
free(b64_key);
if (rc < 0) {
return SSH_ERROR;

View File

@@ -104,9 +104,9 @@ pki_ed25519_key_cmp(const ssh_key k1, const ssh_key k2, enum ssh_keycmp_e what)
}
/* In the internal implementation, the private key is the concatenation
* of the private seed with the public key. */
cmp = memcmp(k1->ed25519_privkey,
k2->ed25519_privkey,
2 * ED25519_KEY_LEN);
cmp = secure_memcmp(k1->ed25519_privkey,
k2->ed25519_privkey,
2 * ED25519_KEY_LEN);
if (cmp != 0) {
return 1;
}

View File

@@ -874,6 +874,22 @@ int ssh_scp_pull_request(ssh_scp scp)
size = strtoull(tmp, NULL, 10);
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);
scp->request_name = name;
if (buffer[0] == 'C') {

View File

@@ -168,7 +168,7 @@ ssh_session ssh_new(void)
}
#endif /* WITH_GSSAPI */
id = strdup("%d/id_ed25519");
id = strdup("%d/.ssh/id_ed25519");
if (id == NULL) {
goto err;
}
@@ -179,7 +179,7 @@ ssh_session ssh_new(void)
}
#ifdef HAVE_ECC
id = strdup("%d/id_ecdsa");
id = strdup("%d/.ssh/id_ecdsa");
if (id == NULL) {
goto err;
}
@@ -189,7 +189,7 @@ ssh_session ssh_new(void)
}
#endif
id = strdup("%d/id_rsa");
id = strdup("%d/.ssh/id_rsa");
if (id == NULL) {
goto err;
}
@@ -200,7 +200,7 @@ ssh_session ssh_new(void)
#ifdef WITH_FIDO2
/* Add security key identities */
id = strdup("%d/id_ed25519_sk");
id = strdup("%d/.ssh/id_ed25519_sk");
if (id == NULL) {
goto err;
}
@@ -210,7 +210,7 @@ ssh_session ssh_new(void)
}
#ifdef HAVE_ECC
id = strdup("%d/id_ecdsa_sk");
id = strdup("%d/.ssh/id_ecdsa_sk");
if (id == NULL) {
goto err;
}
@@ -384,6 +384,7 @@ void ssh_free(ssh_session session)
ssh_proxyjumps_free(session->opts.proxy_jumps);
SSH_LIST_FREE(session->opts.proxy_jumps);
SSH_LIST_FREE(session->opts.proxy_jumps_user_cb);
SAFE_FREE(session->opts.proxy_jumps_str);
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
session->out_queue)) != NULL) {
@@ -405,6 +406,7 @@ void ssh_free(ssh_session session)
SAFE_FREE(session->opts.bindaddr);
SAFE_FREE(session->opts.username);
SAFE_FREE(session->opts.host);
SAFE_FREE(session->opts.homedir);
SAFE_FREE(session->opts.sshdir);
SAFE_FREE(session->opts.knownhosts);
SAFE_FREE(session->opts.global_knownhosts);

View File

@@ -583,44 +583,51 @@ int sftp_init(sftp_session sftp)
return 0;
}
unsigned int sftp_extensions_get_count(sftp_session sftp) {
if (sftp == NULL || sftp->ext == NULL) {
return 0;
}
unsigned int sftp_extensions_get_count(sftp_session sftp)
{
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) {
if (sftp == NULL)
return NULL;
if (sftp->ext == NULL || sftp->ext->name == NULL) {
ssh_set_error_invalid(sftp->session);
return NULL;
}
const char *sftp_extensions_get_name(sftp_session sftp, unsigned int idx)
{
if (sftp == NULL) {
return NULL;
}
if (idx > sftp->ext->count) {
ssh_set_error_invalid(sftp->session);
return NULL;
}
if (sftp->ext == NULL || sftp->ext->name == NULL) {
ssh_set_error_invalid(sftp->session);
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) {
if (sftp == NULL)
return NULL;
if (sftp->ext == NULL || sftp->ext->name == NULL) {
ssh_set_error_invalid(sftp->session);
return NULL;
}
const char *sftp_extensions_get_data(sftp_session sftp, unsigned int idx)
{
if (sftp == NULL) {
return NULL;
}
if (idx > sftp->ext->count) {
ssh_set_error_invalid(sftp->session);
return NULL;
}
if (sftp->ext == NULL || sftp->ext->name == NULL) {
ssh_set_error_invalid(sftp->session);
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

View File

@@ -461,19 +461,24 @@ enum sftp_longname_field_e {
static char * sftp_parse_longname(const char *longname,
enum sftp_longname_field_e longname_field)
{
const char *p, *q;
const char *p = NULL, *q = NULL;
size_t len, field = 0;
if (longname == NULL || longname_field < SFTP_LONGNAME_PERM ||
longname_field > SFTP_LONGNAME_NAME) {
return NULL;
}
p = longname;
/*
* Find the beginning of the field which is specified
* by sftp_longname_field_e.
*/
while (field != longname_field) {
while (*p != '\0' && field != longname_field) {
if (isspace(*p)) {
field++;
p++;
while (*p && isspace(*p)) {
while (*p != '\0' && isspace(*p)) {
p++;
}
} else {
@@ -481,8 +486,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;
while (! isspace(*q)) {
while (*q != '\0' && !isspace(*q)) {
q++;
}
@@ -549,17 +559,14 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp,
if (rc != SSH_OK){
goto error;
}
SSH_LOG(SSH_LOG_DEBUG,
"Flags: %.8" PRIx32 "\n", attr->flags);
SSH_LOG(SSH_LOG_DEBUG, "Flags: %.8" PRIx32, attr->flags);
if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
rc = ssh_buffer_unpack(buf, "q", &attr->size);
if(rc != SSH_OK) {
goto error;
}
SSH_LOG(SSH_LOG_DEBUG,
"Size: %" PRIu64 "\n",
(uint64_t) attr->size);
SSH_LOG(SSH_LOG_DEBUG, "Size: %" PRIu64, (uint64_t)attr->size);
}
if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {

View File

@@ -97,6 +97,10 @@ struct ssh_socket_struct {
struct jump_thread_data_struct {
ssh_session session;
socket_t fd;
char *next_hostname;
uint16_t next_port;
struct ssh_jump_info_struct *next_jump;
struct ssh_jump_callbacks_struct *next_cb;
};
int proxy_disconnect = 0;
@@ -1249,6 +1253,22 @@ verify_knownhost(ssh_session session)
return SSH_OK;
}
static void free_jump_thread_data(struct jump_thread_data_struct *data)
{
if (data == NULL) {
return;
}
ssh_free(data->session);
SAFE_FREE(data->next_hostname);
if (data->next_jump != NULL) {
SAFE_FREE(data->next_jump->hostname);
SAFE_FREE(data->next_jump->username);
}
SAFE_FREE(data->next_jump);
SAFE_FREE(data);
}
static void *
jump_thread_func(void *arg)
{
@@ -1260,72 +1280,30 @@ jump_thread_func(void *arg)
int rc;
ssh_event event = NULL;
ssh_connector connector_in = NULL, connector_out = NULL;
ssh_session session = NULL;
int next_port;
uint16_t next_port;
char *next_hostname = NULL;
jump_thread_data = (struct jump_thread_data_struct *)arg;
session = jump_thread_data->session;
jump_session = jump_thread_data->session;
next_port = session->opts.port;
next_hostname = strdup(session->opts.host);
/* First thing we need to do is to set the right level as its kept in
* thread local variable, therefore reset to 0 after spawning new thread.
*/
ssh_set_log_level(jump_session->common.log_verbosity);
jump_session = ssh_new();
if (jump_session == NULL) {
goto exit;
}
cb = jump_thread_data->next_cb;
jis = jump_thread_data->next_jump;
jump_session->proxy_root = false;
/* Reset the global variable if it was previously 1 */
if (session->proxy_root) {
proxy_disconnect = 0;
}
for (jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
session->opts.proxy_jumps);
jis != NULL;
jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
session->opts.proxy_jumps)) {
rc = ssh_list_append(jump_session->opts.proxy_jumps, jis);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
goto exit;
}
}
for (jis =
ssh_list_pop_head(struct ssh_jump_info_struct *,
session->opts.proxy_jumps_user_cb);
jis != NULL;
jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
session->opts.proxy_jumps_user_cb)) {
rc = ssh_list_append(jump_session->opts.proxy_jumps_user_cb, jis);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
goto exit;
}
}
ssh_options_set(jump_session,
SSH_OPTIONS_LOG_VERBOSITY,
&session->common.log_verbosity);
/* Pop the information about the current jump */
jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
jump_session->opts.proxy_jumps);
if (jis == NULL) {
SSH_LOG(SSH_LOG_WARN, "Inconsistent list of proxy jumps received");
goto exit;
}
/* This is the calling thread target where we will eventually initialize
* forwarding */
next_port = jump_thread_data->next_port;
next_hostname = jump_thread_data->next_hostname;
ssh_options_set(jump_session, SSH_OPTIONS_HOST, jis->hostname);
ssh_options_set(jump_session, SSH_OPTIONS_USER, jis->username);
ssh_options_set(jump_session, SSH_OPTIONS_PORT, &jis->port);
/* Pop the callbacks for the current jump */
cb = ssh_list_pop_head(struct ssh_jump_callbacks_struct *,
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);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
@@ -1333,6 +1311,13 @@ jump_thread_func(void *arg)
}
}
SSH_LOG(SSH_LOG_PACKET,
"Proxy connecting to host %s port %d user %s, callbacks=%p",
jis->hostname,
jis->port,
jis->username,
(void *)cb);
/* If there are more jumps then this will make a new thread and call the
* current function again, until there are no jumps. When there are no jumps
* it connects normally. */
@@ -1423,36 +1408,42 @@ exit:
ssh_event_remove_connector(event, connector_out);
ssh_connector_free(connector_out);
}
SAFE_FREE(next_hostname);
if (jis != NULL) {
SAFE_FREE(jis->hostname);
SAFE_FREE(jis->username);
}
SAFE_FREE(jis);
ssh_disconnect(jump_session);
ssh_event_free(event);
ssh_free(jump_session);
shutdown(jump_thread_data->fd, SHUT_RDWR);
close(jump_thread_data->fd);
SAFE_FREE(jump_thread_data);
free_jump_thread_data(jump_thread_data);
pthread_exit(NULL);
}
int
ssh_socket_connect_proxyjump(ssh_socket s)
{
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ssh_poll_handle h = NULL;
int rc;
pthread_t jump_thread;
struct ssh_jump_info_struct *jis = NULL;
struct ssh_jump_callbacks_struct *cb = NULL;
struct jump_thread_data_struct *jump_thread_data = NULL;
socket_t pair[2];
ssh_session jump_session = NULL, session = NULL;
struct ssh_list *empty_list = NULL;
socket_t pair[2] = {SSH_INVALID_SOCKET, SSH_INVALID_SOCKET};
session = s->session;
SSH_LOG(SSH_LOG_INFO,
"Connecting to host %s port %d user %s through ProxyJump",
session->opts.host,
session->opts.port,
session->opts.username);
if (s->state != SSH_SOCKET_NONE) {
ssh_set_error(
s->session,
session,
SSH_FATAL,
"ssh_socket_connect_proxyjump called on socket not unconnected");
return SSH_ERROR;
@@ -1460,50 +1451,100 @@ ssh_socket_connect_proxyjump(ssh_socket s)
jump_thread_data = calloc(1, sizeof(struct jump_thread_data_struct));
if (jump_thread_data == NULL) {
ssh_set_error_oom(s->session);
ssh_set_error_oom(session);
return SSH_ERROR;
}
rc = socketpair(PF_UNIX, SOCK_STREAM, 0, pair);
if (rc == -1) {
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ssh_set_error(s->session,
ssh_set_error(session,
SSH_FATAL,
"Creating socket pair failed: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
SAFE_FREE(jump_thread_data);
return SSH_ERROR;
goto fail;
}
jump_thread_data->session = s->session;
jump_session = ssh_new();
if (jump_session == NULL) {
ssh_set_error_oom(session);
goto fail;
}
jump_session->proxy_root = false;
/* Reset the global variable if it was previously 1 */
if (session->proxy_root) {
proxy_disconnect = 0;
}
/* Pop first jump that will be used by the following thread */
jis = ssh_list_pop_head(struct ssh_jump_info_struct *,
session->opts.proxy_jumps);
if (jis == NULL) {
SSH_LOG(SSH_LOG_WARN, "Inconsistent list of proxy jumps received");
ssh_free(jump_session);
goto fail;
}
jump_thread_data->next_jump = jis;
/* Move remaining to the jump session without reallocation.
* The list in the new jump_session is just allocated so empty */
empty_list = jump_session->opts.proxy_jumps;
jump_session->opts.proxy_jumps = session->opts.proxy_jumps;
session->opts.proxy_jumps = empty_list;
/* Pop the callbacks for the first jump */
cb = ssh_list_pop_head(struct ssh_jump_callbacks_struct *,
session->opts.proxy_jumps_user_cb);
/* empty is ok */
jump_thread_data->next_cb = cb;
/* Move remaining to the jump session without reallocation.
* The list in the new jump_session is just allocated so empty */
empty_list = jump_session->opts.proxy_jumps_user_cb;
jump_session->opts.proxy_jumps_user_cb = session->opts.proxy_jumps_user_cb;
session->opts.proxy_jumps_user_cb = empty_list;
ssh_options_set(jump_session,
SSH_OPTIONS_LOG_VERBOSITY,
&session->common.log_verbosity);
jump_thread_data->next_port = session->opts.port;
jump_thread_data->next_hostname = strdup(session->opts.host);
jump_thread_data->fd = pair[0];
pair[0] = SSH_INVALID_SOCKET;
jump_thread_data->session = jump_session;
/* transferred to the jump_thread_data */
jump_session = NULL;
SSH_LOG(SSH_LOG_INFO,
"Starting proxy thread to host %s port %d user %s, callbacks=%p",
jump_thread_data->next_jump->hostname,
jump_thread_data->next_jump->port,
jump_thread_data->next_jump->username,
(void *)jump_thread_data->next_cb);
rc = pthread_create(&jump_thread, NULL, jump_thread_func, jump_thread_data);
if (rc != 0) {
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ssh_set_error(s->session,
ssh_set_error(session,
SSH_FATAL,
"Creating new thread failed: %s",
ssh_strerror(rc, err_msg, SSH_ERRNO_MSG_MAX));
SAFE_FREE(jump_thread_data);
return SSH_ERROR;
goto fail;
}
/* ownership passed to the thread */
jump_thread_data = NULL;
rc = pthread_detach(jump_thread);
if (rc != 0) {
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
ssh_set_error(s->session,
ssh_set_error(session,
SSH_FATAL,
"Failed to detach thread: %s",
ssh_strerror(rc, err_msg, SSH_ERRNO_MSG_MAX));
SAFE_FREE(jump_thread_data);
return SSH_ERROR;
goto fail;
}
SSH_LOG(SSH_LOG_DEBUG,
"ProxyJump connection pipe: [%d,%d]",
"ProxyJump connection thread %lu started pipe: [%d,%d]",
(unsigned long)jump_thread,
pair[0],
pair[1]);
@@ -1511,6 +1552,7 @@ ssh_socket_connect_proxyjump(ssh_socket s)
if (rc != SSH_OK) {
return rc;
}
pair[1] = SSH_INVALID_SOCKET;
s->fd_is_socket = 1;
h = ssh_socket_get_poll_handle(s);
@@ -1525,6 +1567,16 @@ ssh_socket_connect_proxyjump(ssh_socket s)
}
return SSH_OK;
fail:
if (pair[0] != SSH_INVALID_SOCKET) {
close(pair[0]);
}
if (pair[1] != SSH_INVALID_SOCKET) {
close(pair[1]);
}
free_jump_thread_data(jump_thread_data);
return SSH_ERROR;
}
#endif /* HAVE_PTHREAD */

View File

@@ -394,6 +394,7 @@ if (CLIENT_TESTING OR SERVER_TESTING)
# Allow to auth with bob's public keys on alice and doe account
configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/alice/.ssh/authorized_keys @ONLY)
configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/authorized_keys @ONLY)
configure_file(keys/id_ecdsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/frank/.ssh/authorized_keys @ONLY)
# append ECDSA public key
file(READ keys/id_ecdsa.pub CONTENTS)
@@ -418,6 +419,9 @@ if (CLIENT_TESTING OR SERVER_TESTING)
file(READ keys/pkcs11/id_pkcs11_ecdsa_256_openssh.pub CONTENTS)
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
# Create home directory for noneuser (for "none" authentication test)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/home/noneuser/.ssh)
file(READ keys/pkcs11/id_pkcs11_ecdsa_384_openssh.pub CONTENTS)
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")

View File

@@ -228,6 +228,44 @@ static void torture_auth_none_max_tries(void **state) {
torture_update_sshd_config(state, "");
}
static void torture_auth_none_success(void **state)
{
struct torture_state *s = *state;
const char *additional_config = "PermitEmptyPasswords yes\n"
"PasswordAuthentication yes\n"
"KbdInteractiveAuthentication no\n"
"PubkeyAuthentication no\n"
"AuthenticationMethods none\n";
ssh_session session = s->ssh.session;
int rc;
torture_update_sshd_config(state, additional_config);
/* Use noneuser which has an empty password set in shadow.in
* When PermitEmptyPasswords is yes and PasswordAuthentication is yes,
* OpenSSH's userauth_none() internally calls mm_auth_password() with
* an empty password, which succeeds for users with empty passwords.
*/
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_NONEUSER);
if (rc != SSH_OK) {
goto cleanup;
}
rc = ssh_connect(session);
if (rc != SSH_OK) {
goto cleanup;
}
rc = ssh_userauth_none(session, NULL);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
cleanup:
torture_update_sshd_config(state, "");
if (rc != SSH_OK && rc != SSH_AUTH_SUCCESS) {
assert_int_equal(rc, SSH_OK);
}
}
static void torture_auth_pubkey(void **state) {
struct torture_state *s = *state;
@@ -376,7 +414,7 @@ torture_auth_autopubkey_protected_auth_function (const char *prompt, char *buf,
assert_int_equal(echo, 0);
assert_int_equal(verify, 0);
expected_id = ssh_path_expand_escape(data->session, "%d/id_rsa_protected");
expected_id = ssh_path_expand_escape(data->session, "%d/.ssh/id_rsa_protected");
assert_true(expected_id != NULL);
rc = ssh_userauth_publickey_auto_get_current_identity(data->session, &id);
@@ -429,7 +467,7 @@ static void torture_auth_autopubkey_protected(void **state) {
/* Try id_rsa_protected first.
*/
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "%d/id_rsa_protected");
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "%d/.ssh/id_rsa_protected");
assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session);
@@ -1373,6 +1411,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_none_nonblocking,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_none_success,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_none_max_tries,
session_setup,
session_teardown),
@@ -1424,9 +1465,10 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_agent_identities_only,
agent_setup,
agent_teardown),
cmocka_unit_test_setup_teardown(torture_auth_agent_identities_only_protected,
agent_setup,
agent_teardown),
cmocka_unit_test_setup_teardown(
torture_auth_agent_identities_only_protected,
agent_setup,
agent_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types,
pubkey_setup,
session_teardown),
@@ -1436,15 +1478,17 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ecdsa,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ecdsa_nonblocking,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(
torture_auth_pubkey_types_ecdsa_nonblocking,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ed25519,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_ed25519_nonblocking,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(
torture_auth_pubkey_types_ed25519_nonblocking,
pubkey_setup,
session_teardown),
#ifdef WITH_FIDO2
cmocka_unit_test_setup_teardown(torture_auth_pubkey_types_sk_ecdsa,
pubkey_setup,
@@ -1456,9 +1500,10 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_pubkey_rsa_key_size,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_rsa_key_size_nonblocking,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(
torture_auth_pubkey_rsa_key_size_nonblocking,
pubkey_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_auth_pubkey_skip_none,
pubkey_setup,
session_teardown),

View File

@@ -100,13 +100,10 @@ static int session_setup(void **state)
static int session_setup_ssh_dir(void **state)
{
struct torture_state *s = *state;
const char *no_home = "~/.no_ssh";
int rc;
session_setup(state);
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_SSH_DIR, no_home);
assert_ssh_return_code(s->ssh.session, rc);
s->ssh.session->opts.homedir = strdup("~/.no_ssh");
return 0;
}

View File

@@ -6,6 +6,7 @@
#include <errno.h>
#include "torture.h"
#include "libssh/session.h"
#include "libssh/options.h"
#include "libssh/misc.h"
#define LIBSSH_SSH_CONFIG "libssh_config"
@@ -59,6 +60,23 @@ static int setup_config_files(void **state)
return 0;
}
static int setup_session(void **state)
{
struct torture_state *s = *state;
int verbosity;
s->ssh.session = ssh_new();
assert_non_null(s->ssh.session);
verbosity = torture_libssh_verbosity();
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
setenv("NSS_WRAPPER_HOSTNAME", "client.libssh.site", 1);
return 0;
}
static int teardown(void **state)
{
struct torture_state *s = *state;
@@ -80,6 +98,16 @@ static int teardown(void **state)
return 0;
}
static int teardown_session(void **state)
{
struct torture_state *s = *state;
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
return 0;
}
/* This tests makes sure that parsing both system-wide and per-user
* configuration files retains OpenSSH semantics (the per-user overrides
* the system-wide values).
@@ -210,10 +238,233 @@ static void torture_client_config_suppress(void **state)
assert_string_equal(s->ssh.session->opts.username, "bob");
}
static void torture_client_config_expand_bad(void **state)
{
ssh_session session = ssh_new();
int ret = 0;
(void)state;
assert_non_null(session);
/* The hash without host fails, but does not crash */
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "%C");
ret = ssh_options_apply(session);
assert_ssh_return_code_equal(session, ret, SSH_ERROR);
/* The hash without host fails, but does not crash */
ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "%C");
ret = ssh_options_apply(session);
assert_ssh_return_code_equal(session, ret, SSH_OK);
ssh_free(session);
}
static void torture_client_config_expand(void **state)
{
struct torture_state *s = *state;
int ret = 0;
/* TEST: user home directory */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%d");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
assert_string_equal(s->ssh.session->opts.knownhosts,
BINARYDIR "/tests/home");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: target host name */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%h");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
assert_string_equal(s->ssh.session->opts.knownhosts, TORTURE_SSH_SERVER);
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: local username */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%u");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
assert_string_equal(s->ssh.session->opts.knownhosts, "root");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: local hostname */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
assert_string_equal(s->ssh.session->opts.knownhosts, "client.libssh.site");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: remote username */
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, "alice");
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%r");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
assert_string_equal(s->ssh.session->opts.knownhosts, "alice");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: remote port */
ssh_options_set(s->ssh.session, SSH_OPTIONS_PORT_STR, "2222");
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%p");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
assert_string_equal(s->ssh.session->opts.knownhosts, "2222");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: empty proxyjump */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%j");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
/* No proxyjump string should not explode */
assert_string_equal(s->ssh.session->opts.knownhosts, "");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: proxyjump string present */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%j");
ssh_options_set(s->ssh.session,
SSH_OPTIONS_PROXYJUMP,
"user@" TORTURE_SSH_SERVER ":22");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
assert_string_equal(s->ssh.session->opts.knownhosts,
"user@" TORTURE_SSH_SERVER ":22");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: separate list %l-%h-%p-%r-%j with empty ProxyJump */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l-%h-%p-%r-%j");
ssh_options_set(s->ssh.session, SSH_OPTIONS_PROXYJUMP, "none");
ssh_options_set(s->ssh.session, SSH_OPTIONS_PORT_STR, "22");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
// Tested by
// ret = system(SSH_EXECUTABLE
// " -p 22 -o UserKnownHostsFile=/dev/null"
// " -o KnownHostsCommand='/bin/touch \"/tmp/%l-%h-%p-%r-%j\"'"
// " alice@" TORTURE_SSH_SERVER);
// assert_return_code(ret, errno);
assert_string_equal(s->ssh.session->opts.knownhosts,
"client.libssh.site-127.0.0.10-22-alice-");
/* TEST: hash of %l%h%p%r%j with empty ProxyJump */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%C");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
// Tested by
// ret = system(SSH_EXECUTABLE
// " -p 22 -o UserKnownHostsFile=/dev/null"
// " -o KnownHostsCommand='/bin/touch \"/tmp/%C\"'"
// " alice@" TORTURE_SSH_SERVER);
// assert_return_code(ret, errno);
assert_string_equal(s->ssh.session->opts.knownhosts,
"133e3957ff9d01fdcf1f6c7f83325a8ce49bf850");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: separate list %l-%h-%p-%r-%j */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l-%h-%p-%r-%j");
ssh_options_set(s->ssh.session,
SSH_OPTIONS_PROXYJUMP,
"user@" TORTURE_SSH_SERVER ":22");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
// Tested by
// ret = system(SSH_EXECUTABLE
// " -p 22 -oProxyJump=user@" TORTURE_SSH_SERVER ":22"
// " -o UserKnownHostsFile=/dev/null"
// " -o KnownHostsCommand='/bin/touch \"/tmp/%l-%h-%p-%r-%j\"'"
// " alice@" TORTURE_SSH_SERVER);
// assert_return_code(ret, errno);
assert_string_equal(s->ssh.session->opts.knownhosts,
"client.libssh.site-127.0.0.10-22-alice-user@"
TORTURE_SSH_SERVER ":22");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
/* TEST: hash of %l%h%p%r%j */
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%C");
ret = ssh_options_apply(s->ssh.session);
assert_ssh_return_code(s->ssh.session, ret);
// Tested by
// ret = system(SSH_EXECUTABLE
// " -p 22 -oProxyJump=user@" TORTURE_SSH_SERVER ":22"
// " -o UserKnownHostsFile=/dev/null"
// " -o KnownHostsCommand='/bin/touch \"/tmp/%C\"'"
// " alice@" TORTURE_SSH_SERVER);
// assert_return_code(ret, errno);
assert_string_equal(s->ssh.session->opts.knownhosts,
"adf0b7c4e71a0fee85fd97506507ba8591f3663b");
/* Reset the flag so we can repeat the test */
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
}
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
/* Keep these first -- following setup is changing user to bob, which we
* do not want */
cmocka_unit_test(torture_client_config_expand_bad),
cmocka_unit_test_setup_teardown(torture_client_config_expand,
setup_session,
teardown_session),
cmocka_unit_test_setup_teardown(torture_client_config_system,
setup_config_files,
teardown),
@@ -228,7 +479,6 @@ int torture_run_tests(void) {
teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);

View File

@@ -121,6 +121,50 @@ static int authenticate(ssh_session jump_session, void *user)
return ssh_userauth_publickey_auto(jump_session, NULL, NULL);
}
static int authenticate_doe(ssh_session jump_session, void *user)
{
ssh_key pkey = NULL;
char bob_ssh_key[1024];
struct passwd *pwd = NULL;
int rc;
(void)user;
pwd = getpwnam("bob");
assert_non_null(pwd);
snprintf(bob_ssh_key, sizeof(bob_ssh_key), "%s/.ssh/id_rsa", pwd->pw_dir);
rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &pkey);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_publickey(jump_session, NULL, pkey);
ssh_key_free(pkey);
return rc;
}
static int authenticate_frank(ssh_session jump_session, void *user)
{
ssh_key pkey = NULL;
char bob_ssh_key[1024];
struct passwd *pwd = NULL;
int rc;
(void)user;
pwd = getpwnam("bob");
assert_non_null(pwd);
snprintf(bob_ssh_key, sizeof(bob_ssh_key), "%s/.ssh/id_ecdsa", pwd->pw_dir);
rc = ssh_pki_import_privkey_file(bob_ssh_key, NULL, NULL, NULL, &pkey);
assert_int_equal(rc, SSH_OK);
rc = ssh_userauth_publickey(jump_session, NULL, pkey);
ssh_key_free(pkey);
return rc;
}
static void torture_proxyjump_multiple_jump(void **state)
{
struct torture_state *s = *state;
@@ -129,11 +173,10 @@ static void torture_proxyjump_multiple_jump(void **state)
const char *address = torture_server_address(AF_INET);
int rc;
socket_t fd;
struct ssh_jump_callbacks_struct c = {
.before_connection = before_connection,
.verify_knownhost = verify_knownhost,
.authenticate = authenticate
.authenticate = authenticate,
};
rc = snprintf(proxyjump_buf,
@@ -177,14 +220,14 @@ static void torture_proxyjump_multiple_sshd_jump(void **state)
struct ssh_jump_callbacks_struct c = {
.before_connection = before_connection,
.verify_knownhost = verify_knownhost,
.authenticate = authenticate,
.authenticate = authenticate_doe,
};
torture_setup_sshd_servers(state, false);
rc = snprintf(proxyjump_buf,
sizeof(proxyjump_buf),
"alice@%s:22,alice@%s:22",
"doe@%s:22,doe@%s:22",
address,
address1);
if (rc < 0 || rc >= (int)sizeof(proxyjump_buf)) {
@@ -222,17 +265,22 @@ static void torture_proxyjump_multiple_sshd_users_jump(void **state)
int rc;
socket_t fd;
struct ssh_jump_callbacks_struct c = {
struct ssh_jump_callbacks_struct c1 = {
.before_connection = before_connection,
.verify_knownhost = verify_knownhost,
.authenticate = authenticate,
.authenticate = authenticate_doe,
};
struct ssh_jump_callbacks_struct c2 = {
.before_connection = before_connection,
.verify_knownhost = verify_knownhost,
.authenticate = authenticate_frank,
};
torture_setup_sshd_servers(state, false);
rc = snprintf(proxyjump_buf,
sizeof(proxyjump_buf),
"doe@%s:22,alice@%s:22",
"doe@%s:22,frank@%s:22",
address,
address1);
if (rc < 0 || rc >= (int)sizeof(proxyjump_buf)) {
@@ -240,9 +288,9 @@ static void torture_proxyjump_multiple_sshd_users_jump(void **state)
}
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP, proxyjump_buf);
assert_ssh_return_code(session, rc);
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, &c);
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, &c1);
assert_ssh_return_code(session, rc);
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, &c);
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, &c2);
assert_ssh_return_code(session, rc);
rc = ssh_connect(session);

View File

@@ -11,19 +11,37 @@
#define MAX_XFER_BUF_SIZE 16384
#define DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(TEST_NAME) \
{ \
#TEST_NAME, \
TEST_NAME, \
session_setup, \
session_teardown, \
NULL \
}, \
{ \
#TEST_NAME"_proxyjump", \
TEST_NAME, \
session_proxyjump_setup, \
session_teardown, \
NULL \
}
static int sshd_setup(void **state)
{
torture_setup_sshd_server(state, false);
torture_setup_sshd_servers(state, false);
return 0;
}
static int sshd_teardown(void **state)
{
/* this will take care of the server1 teardown too */
torture_teardown_sshd_server(state);
return 0;
}
static int session_setup(void **state)
static int session_setup_helper(void **state, bool with_proxyjump)
{
struct torture_state *s = *state;
struct passwd *pwd = NULL;
@@ -35,11 +53,15 @@ static int session_setup(void **state)
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);
if (with_proxyjump) {
s->ssh.session = torture_ssh_session_proxyjump();
} else {
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);
@@ -48,6 +70,16 @@ static int session_setup(void **state)
return 0;
}
static int session_setup(void **state)
{
return session_setup_helper(state, false);
}
static int session_proxyjump_setup(void **state)
{
return session_setup_helper(state, true);
}
static int session_teardown(void **state)
{
struct torture_state *s = *state;
@@ -722,37 +754,18 @@ int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_file,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_more_than_cap,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_file,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_more_than_cap,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_negative,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_negative,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_unordered_wait,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_unordered_wait,
session_setup,
session_teardown),
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_read_file),
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
torture_sftp_aio_read_more_than_cap),
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_write_file),
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
torture_sftp_aio_write_more_than_cap),
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_read_negative),
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_write_negative),
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
torture_sftp_aio_read_unordered_wait),
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
torture_sftp_aio_write_unordered_wait),
};
ssh_init();

View File

@@ -72,6 +72,63 @@ static void session_setup_channel(void **state)
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)
{
struct torture_state *s = *state;
@@ -92,7 +149,10 @@ int torture_run_tests(void) {
session_teardown),
cmocka_unit_test_setup_teardown(session_setup_channel,
NULL,
session_teardown)
session_teardown),
cmocka_unit_test_setup_teardown(session_setup_extensions,
NULL,
session_teardown),
};
ssh_init();

View File

@@ -2,4 +2,4 @@ users:x:9000:
sshd:x:65531:
nobody:x:65533:
nogroup:x:65534:nobody
root:x:65532:
root:x:0:

View File

@@ -2,3 +2,4 @@ bob:secret:sshd
alice:secret:sshd
charlie:secret:sshd
doe:secret:sshd
noneuser::sshd

View File

@@ -2,7 +2,9 @@ bob:x:5000:9000:bob gecos:@HOMEDIR@/bob:/bin/sh
alice:x:5001:9000:alice gecos:@HOMEDIR@/alice:/bin/sh
charlie:x:5002:9000:charlie gecos:@HOMEDIR@/charlie:/bin/sh
doe:x:5003:9000:doe gecos:@HOMEDIR@/doe:/bin/sh
frank:x:5003:9000:doe gecos:@HOMEDIR@/frank:/bin/sh
noneuser:x:5004:9000:noneuser gecos:@HOMEDIR@/noneuser:/bin/sh
sshd:x:65530:65531:sshd:@HOMEDIR@:/sbin/nologin
nobody:x:65533:65534:nobody gecos:@HOMEDIR@:/bin/false
root:x:65534:65532:root gecos:@HOMEDIR@:/bin/false
root:x:0:0:root gecos:@HOMEDIR@:/bin/false
@LOCAL_USER@:x:@LOCAL_UID@:9000:local user:@HOMEDIR@:/bin/false

View File

@@ -2,3 +2,4 @@ alice:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFl
bob:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
charlie:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
doe:$6$0jWkA8VP$MvBUvtGy38jWCZ5KtqnZEKQWXvvImDkDhDQII1kTqtAp3/xH31b71c.AjGkBFle.2QwCJQH7OzB/NXiMprusr/::0:::::
noneuser:::0:::::

View File

@@ -33,6 +33,7 @@ fuzzer(ssh_client_config_fuzzer)
fuzzer(ssh_known_hosts_fuzzer)
fuzzer(ssh_privkey_fuzzer)
fuzzer(ssh_pubkey_fuzzer)
fuzzer(ssh_sftp_attr_fuzzer)
fuzzer(ssh_sshsig_fuzzer)
if (WITH_SERVER)
fuzzer(ssh_server_fuzzer)

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2026 libssh authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LIBSSH_STATIC 1
#include "libssh/libssh.h"
#include "libssh/sftp.h"
#include "libssh/sftp_priv.h"
#include "nallocinc.c"
/* SFTP protocol version constants */
#define SFTP_PROTOCOL_VERSION_3 3
#define SFTP_PROTOCOL_VERSION_4 4
/* Flags for sftp_parse_attr expectname parameter */
#define SFTP_EXPECT_NAME 1
#define SFTP_NO_NAME 0
/*
* Helper to create a minimal sftp_session for fuzzing.
* We don't use sftp_new() as it requires a real SSH connection.
*/
static sftp_session create_minimal_sftp_session(ssh_session session)
{
sftp_session sftp;
sftp = calloc(1, sizeof(struct sftp_session_struct));
if (sftp == NULL) {
return NULL;
}
sftp->session = session;
return sftp;
}
static void _fuzz_finalize(void)
{
ssh_finalize();
}
int LLVMFuzzerInitialize(int *argc, char ***argv)
{
(void)argc;
nalloc_init(*argv[0]);
ssh_init();
atexit(_fuzz_finalize);
return 0;
}
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
{
ssh_session session = NULL;
sftp_session sftp = NULL;
ssh_buffer buffer = NULL;
sftp_attributes attr = NULL;
int versions[] = {
SFTP_PROTOCOL_VERSION_3, SFTP_PROTOCOL_VERSION_3,
SFTP_PROTOCOL_VERSION_4, SFTP_PROTOCOL_VERSION_4
};
int expectnames[] = {SFTP_NO_NAME, SFTP_EXPECT_NAME, SFTP_NO_NAME, SFTP_EXPECT_NAME};
size_t i;
/* Minimum bytes for a valid SFTP message */
if (size == 0) {
return 0;
}
assert(nalloc_start(data, size) > 0);
/* Allocate shared resources once for all test iterations */
session = ssh_new();
if (session == NULL) {
goto cleanup;
}
sftp = create_minimal_sftp_session(session);
if (sftp == NULL) {
goto cleanup;
}
buffer = ssh_buffer_new();
if (buffer == NULL) {
goto cleanup;
}
/* Main fuzzing target: sftp_parse_attr */
/* Parses untrusted SFTP messages from client */
/* Test all combinations (v3/v4, with/without name) */
for (i = 0; i < (sizeof(versions) / sizeof(versions[0])); i++) {
sftp->version = versions[i];
/* Reset and repopulate buffer for each iteration */
ssh_buffer_reinit(buffer);
if (ssh_buffer_add_data(buffer, data, size) == SSH_OK) {
attr = sftp_parse_attr(sftp, buffer, expectnames[i]);
sftp_attributes_free(attr);
attr = NULL;
}
}
cleanup:
ssh_buffer_free(buffer);
free(sftp);
ssh_free(session);
nalloc_end();
return 0;
}

View File

@@ -389,6 +389,143 @@ failed:
return NULL;
}
/* always return verification successful */
static int verify_knownhost_trust_all(UNUSED_PARAM(ssh_session jump_session),
UNUSED_PARAM(void *user))
{
return SSH_OK;
}
/**
* @brief Create a session connected to server via proxyjump
*
* @param[in] state A pointer to a pointer to an initialized torture_state
* structure
*
* @warning It is expected that both sshd servers are setup before calling
* this, see torture_setup_sshd_server() and
* torture_setup_sshd_servers()
*
* TODO: If needed, in future, we can extend this function to:
* - allow caller to pass server host port, user, password similar to
* torture_ssh_session() or club this with that function
*
* - allow caller to customize jump hosts and callbacks for each of them
*/
ssh_session torture_ssh_session_proxyjump(void)
{
/*
* We'll setup the connection chain:
* - client
* - jump host 1: doe (sshd server, IPV4)
* - jump host 2: alice (sshd server1, IPV6)
* - server: alice (sshd server, IPV4)
*/
char jump_host_list[1024] = {0};
int jump_host_count = 2;
const char *jump_host_1_address = torture_server_address(AF_INET);
const char *jump_host_2_address = torture_server1_address(AF_INET6);
struct ssh_jump_callbacks_struct jump_host_callbacks = {
.before_connection = NULL,
.verify_knownhost = verify_knownhost_trust_all,
.authenticate = NULL,
};
ssh_session session = NULL;
bool process_config = false;
int rc, i;
session = ssh_new();
if (session == NULL) {
fprintf(stderr, "Failed to create new ssh session\n");
goto failed;
}
rc = ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
if (rc < 0) {
fprintf(stderr,
"Failed to set session host: %s\n",
ssh_get_error(session));
goto failed;
}
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
if (rc < 0) {
fprintf(stderr,
"Failed to set session user: %s\n",
ssh_get_error(session));
goto failed;
}
rc = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config);
if (rc < 0) {
fprintf(stderr,
"Failed to set process config option: %s\n",
ssh_get_error(session));
goto failed;
}
rc = snprintf(jump_host_list,
sizeof(jump_host_list),
"doe@%s:22,alice@%s:22",
jump_host_1_address,
jump_host_2_address);
if (rc < 0) {
fprintf(stderr, "snprintf failed: %s\n", strerror(errno));
goto failed;
}
if (rc >= (int)sizeof(jump_host_list)) {
fprintf(stderr, "Insufficient jump host list buffer size\n");
goto failed;
}
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP, jump_host_list);
if (rc < 0) {
fprintf(stderr,
"Failed to set jump hosts for the session: %s\n",
ssh_get_error(session));
goto failed;
}
for (i = 0; i < jump_host_count; ++i) {
rc = ssh_options_set(session,
SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND,
&jump_host_callbacks);
if (rc < 0) {
fprintf(stderr,
"Failed to set jump callbacks for jump host %d: %s\n",
i + 1,
ssh_get_error(session));
goto failed;
}
}
rc = ssh_connect(session);
if (rc != SSH_OK) {
fprintf(stderr,
"Failed to connect to ssh server: %s\n",
ssh_get_error(session));
goto failed;
}
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
if (rc != SSH_AUTH_SUCCESS) {
fprintf(stderr, "Public key authentication did not succeed\n");
goto failed;
}
return session;
failed:
if (ssh_is_connected(session)) {
ssh_disconnect(session);
}
ssh_free(session);
return NULL;
}
#ifdef WITH_SERVER
ssh_bind torture_ssh_bind(const char *addr,
@@ -833,7 +970,7 @@ torture_setup_create_sshd_config(void **state, bool pam, bool second_sshd)
"TrustedUserCAKeys %s\n"
"\n"
"LogLevel DEBUG3\n"
"Subsystem sftp %s -l DEBUG2\n"
"Subsystem sftp %s -l DEBUG3 -e\n"
"\n"
"PasswordAuthentication yes\n"
"PubkeyAuthentication yes\n"
@@ -873,7 +1010,7 @@ torture_setup_create_sshd_config(void **state, bool pam, bool second_sshd)
"TrustedUserCAKeys %s\n" /* Trusted CA */
"\n"
"LogLevel DEBUG3\n"
"Subsystem sftp %s -l DEBUG2\n" /* SFTP server */
"Subsystem sftp %s -l DEBUG3 -e\n" /* SFTP server */
"\n"
"PasswordAuthentication yes\n"
"PubkeyAuthentication yes\n"

View File

@@ -51,6 +51,7 @@
#define TORTURE_SSH_USER_ALICE "alice"
#define TORTURE_SSH_USER_CHARLIE "charlie"
#define TORTURE_SSH_USER_NONEUSER "noneuser"
/* Used by main to communicate with parse_opt. */
struct argument_s {
@@ -115,6 +116,8 @@ ssh_session torture_ssh_session(struct torture_state *s,
const char *user,
const char *password);
ssh_session torture_ssh_session_proxyjump(void);
ssh_bind torture_ssh_bind(const char *addr,
const unsigned int port,
enum ssh_keytypes_e key_type,

View File

@@ -126,6 +126,13 @@ if (HAVE_LIBFIDO2)
)
endif (HAVE_LIBFIDO2)
if (WITH_SFTP)
set(LIBSSH_UNIT_TESTS
${LIBSSH_UNIT_TESTS}
torture_unit_sftp
)
endif (WITH_SFTP)
foreach(_UNIT_TEST ${LIBSSH_UNIT_TESTS})
add_cmocka_test(${_UNIT_TEST}
SOURCES ${_UNIT_TEST}.c

View File

@@ -25,7 +25,7 @@ extern LIBSSH_THREAD int ssh_log_level;
#define HOSTKEYALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp521,ssh-rsa"
#define PUBKEYACCEPTEDTYPES "rsa-sha2-512,ssh-rsa,ecdsa-sha2-nistp521"
#define MACS "hmac-sha1,hmac-sha2-256,hmac-sha2-512,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com"
#define USER_KNOWN_HOSTS "%d/my_known_hosts"
#define USER_KNOWN_HOSTS "%d/.ssh/my_known_hosts"
#define GLOBAL_KNOWN_HOSTS "/etc/ssh/my_ssh_known_hosts"
#define BIND_ADDRESS "::1"
@@ -159,6 +159,8 @@ extern LIBSSH_THREAD int ssh_log_level;
"\tProxyJump jumpbox:2222\n" \
"Host two-step\n" \
"\tProxyJump u1@first:222,u2@second:33\n" \
"Host three-step\n" \
"\tProxyJump u1@first:222,u2@second:33,u3@third:444\n" \
"Host none\n" \
"\tProxyJump none\n" \
"Host only-command\n" \
@@ -1172,6 +1174,23 @@ static void torture_config_proxyjump(void **state,
"u1",
"222");
/* Three step jump */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "three-step");
_parse_config(session, file, string, SSH_OK);
helper_proxy_jump_check(session->opts.proxy_jumps->root,
"third",
"u3",
"444");
helper_proxy_jump_check(session->opts.proxy_jumps->root->next,
"second",
"u2",
"33");
helper_proxy_jump_check(session->opts.proxy_jumps->root->next->next,
"first",
"u1",
"222");
/* none */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "none");
@@ -1237,6 +1256,13 @@ static void torture_config_proxyjump(void **state,
assert_string_equal(session->opts.ProxyCommand,
"ssh -l u1 -p 222 -J u2@second:33 -W '[%h]:%p' first");
/* Three step jump */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "three-step");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
"ssh -l u1 -p 222 -J u2@second:33,u3@third:444 -W '[%h]:%p' first");
/* none */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "none");
@@ -2468,80 +2494,138 @@ static void torture_config_match_pattern(void **state)
(void) state;
/* Simple test "a" matches "a" */
rv = match_pattern("a", "a", MAX_MATCH_RECURSION);
rv = match_pattern("a", "a");
assert_int_equal(rv, 1);
/* 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);
/* NULL arguments are correctly handled */
rv = match_pattern("a", NULL, MAX_MATCH_RECURSION);
rv = match_pattern("a", NULL);
assert_int_equal(rv, 0);
rv = match_pattern(NULL, "a", MAX_MATCH_RECURSION);
rv = match_pattern(NULL, "a");
assert_int_equal(rv, 0);
/* Simple wildcard ? is handled in pattern */
rv = match_pattern("a", "?", MAX_MATCH_RECURSION);
rv = match_pattern("a", "?");
assert_int_equal(rv, 1);
rv = match_pattern("aa", "?", MAX_MATCH_RECURSION);
rv = match_pattern("aa", "?");
assert_int_equal(rv, 0);
/* Wildcard in search string */
rv = match_pattern("?", "a", MAX_MATCH_RECURSION);
rv = match_pattern("?", "a");
assert_int_equal(rv, 0);
rv = match_pattern("?", "?", MAX_MATCH_RECURSION);
rv = match_pattern("?", "?");
assert_int_equal(rv, 1);
/* Simple wildcard * is handled in pattern */
rv = match_pattern("a", "*", MAX_MATCH_RECURSION);
rv = match_pattern("a", "*");
assert_int_equal(rv, 1);
rv = match_pattern("aa", "*", MAX_MATCH_RECURSION);
rv = match_pattern("aa", "*");
assert_int_equal(rv, 1);
/* Wildcard in search string */
rv = match_pattern("*", "a", MAX_MATCH_RECURSION);
rv = match_pattern("*", "a");
assert_int_equal(rv, 0);
rv = match_pattern("*", "*", MAX_MATCH_RECURSION);
rv = match_pattern("*", "*");
assert_int_equal(rv, 1);
/* More complicated patterns */
rv = match_pattern("a", "*a", MAX_MATCH_RECURSION);
rv = match_pattern("a", "*a");
assert_int_equal(rv, 1);
rv = match_pattern("a", "a*", MAX_MATCH_RECURSION);
rv = match_pattern("a", "a*");
assert_int_equal(rv, 1);
rv = match_pattern("abababc", "*abc", MAX_MATCH_RECURSION);
rv = match_pattern("abababc", "*abc");
assert_int_equal(rv, 1);
rv = match_pattern("ababababca", "*abc", MAX_MATCH_RECURSION);
rv = match_pattern("ababababca", "*abc");
assert_int_equal(rv, 0);
rv = match_pattern("ababababca", "*abc*", MAX_MATCH_RECURSION);
rv = match_pattern("ababababca", "*abc*");
assert_int_equal(rv, 1);
/* Multiple wildcards in row */
rv = match_pattern("aa", "??", MAX_MATCH_RECURSION);
rv = match_pattern("aa", "??");
assert_int_equal(rv, 1);
rv = match_pattern("bba", "??a", MAX_MATCH_RECURSION);
rv = match_pattern("bba", "??a");
assert_int_equal(rv, 1);
rv = match_pattern("aaa", "**a", MAX_MATCH_RECURSION);
rv = match_pattern("aaa", "**a");
assert_int_equal(rv, 1);
rv = match_pattern("bbb", "**a", MAX_MATCH_RECURSION);
rv = match_pattern("bbb", "**a");
assert_int_equal(rv, 0);
/* 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);
rv = match_pattern("hostname", "pattern**********", 5);
rv = match_pattern("hostname", "pattern**********");
assert_int_equal(rv, 0);
rv = match_pattern("pattern", "***********pattern", 5);
rv = match_pattern("pattern", "***********pattern");
assert_int_equal(rv, 1);
rv = match_pattern("pattern", "pattern***********", 5);
rv = match_pattern("pattern", "pattern***********");
assert_int_equal(rv, 1);
/* Limit the maximum recursion */
rv = match_pattern("hostname", "*p*a*t*t*e*r*n*", 5);
rv = match_pattern("hostname", "*p*a*t*t*e*r*n*");
assert_int_equal(rv, 0);
/* Too much recursion */
rv = match_pattern("pattern", "*p*a*t*t*e*r*n*", 5);
rv = match_pattern("pattern", "*p*a*t*t*e*r*n*");
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);
/* 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
@@ -2849,6 +2933,23 @@ static void torture_config_jump(void **state)
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 rc;
@@ -3003,6 +3104,9 @@ int torture_run_tests(void)
cmocka_unit_test_setup_teardown(torture_config_jump,
setup,
teardown),
cmocka_unit_test_setup_teardown(torture_config_invalid,
setup,
teardown),
};
ssh_init();

View File

@@ -696,6 +696,82 @@ static void torture_knownhosts_algorithms_global(void **state)
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 */
int torture_run_tests(void) {
@@ -738,6 +814,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_knownhosts_algorithms_global,
setup_knownhosts_file,
teardown_knownhosts_file),
cmocka_unit_test_setup_teardown(torture_knownhosts_has_entry,
setup_bad_knownhosts_file,
teardown_knownhosts_file),
#endif
};

View File

@@ -52,7 +52,7 @@ static void torture_get_user_home_dir(void **state) {
(void) state;
user = ssh_get_user_home_dir();
user = ssh_get_user_home_dir(NULL);
assert_non_null(user);
#ifndef _WIN32
assert_string_equal(user, pwd->pw_dir);
@@ -288,7 +288,8 @@ static void torture_path_expand_escape(void **state) {
const char *s = "%d/%h/%p/by/%r";
char *e;
session->opts.sshdir = strdup("guru");
/* Set the homedir here to prevent querying the NSS DB */
session->opts.homedir = strdup("guru");
session->opts.host = strdup("meditation");
session->opts.port = 0;
session->opts.username = strdup("root");
@@ -310,9 +311,10 @@ static void torture_path_expand_known_hosts(void **state) {
ssh_session session = *state;
char *tmp;
session->opts.sshdir = strdup("/home/guru/.ssh");
/* Set the homedir here to prevent querying the NSS DB */
session->opts.homedir = strdup("/home/guru");
tmp = ssh_path_expand_escape(session, "%d/known_hosts");
tmp = ssh_path_expand_escape(session, "%d/.ssh/known_hosts");
assert_non_null(tmp);
assert_string_equal(tmp, "/home/guru/.ssh/known_hosts");
free(tmp);
@@ -322,9 +324,10 @@ static void torture_path_expand_percent(void **state) {
ssh_session session = *state;
char *tmp;
session->opts.sshdir = strdup("/home/guru/.ssh");
/* Set the homedir here to prevent querying the NSS DB */
session->opts.homedir = strdup("/home/guru");
tmp = ssh_path_expand_escape(session, "%d/config%%1");
tmp = ssh_path_expand_escape(session, "%d/.ssh/config%%1");
assert_non_null(tmp);
assert_string_equal(tmp, "/home/guru/.ssh/config%1");
free(tmp);
@@ -1249,6 +1252,36 @@ static void torture_ssh_is_ipaddr(void **state) {
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 rc;
struct CMUnitTest tests[] = {
@@ -1285,6 +1318,7 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_ssh_check_hostname_syntax),
cmocka_unit_test(torture_ssh_check_username_syntax),
cmocka_unit_test(torture_ssh_is_ipaddr),
cmocka_unit_test(torture_ssh_get_hexa),
};
ssh_init();

View File

@@ -839,6 +839,7 @@ static void torture_options_get_identity(void **state)
char *identity = NULL;
int rc;
/* This adds an identity to the head of the list and returns */
rc = ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, "identity1");
assert_true(rc == 0);
rc = ssh_options_get(session, SSH_OPTIONS_IDENTITY, &identity);
@@ -856,6 +857,48 @@ static void torture_options_get_identity(void **state)
assert_non_null(identity);
assert_string_equal(identity, "identity2");
ssh_string_free_char(identity);
/* Iterate over all of the identities */
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_string_equal(identity, "identity2");
ssh_string_free_char(identity);
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_string_equal(identity, "identity1");
SAFE_FREE(identity);
/* here are the default identities */
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_string_equal(identity, "%d/.ssh/id_ed25519");
ssh_string_free_char(identity);
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_string_equal(identity, "%d/.ssh/id_ecdsa");
ssh_string_free_char(identity);
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_string_equal(identity, "%d/.ssh/id_rsa");
ssh_string_free_char(identity);
#ifdef WITH_FIDO2
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_string_equal(identity, "%d/.ssh/id_ed25519_sk");
ssh_string_free_char(identity);
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
assert_int_equal(rc, SSH_OK);
assert_string_equal(identity, "%d/.ssh/id_ecdsa_sk");
ssh_string_free_char(identity);
#endif /* WITH_FIDO2 */
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
assert_int_equal(rc, SSH_EOF);
}
static void torture_options_set_global_knownhosts(void **state)
@@ -2067,25 +2110,25 @@ static void torture_options_apply (void **state)
rc = ssh_list_append(awaited_list, id);
assert_int_equal(rc, SSH_OK);
/* append the defaults; this list is copied from ssh_new@src/session.c */
id = ssh_path_expand_escape(session, "%d/id_ed25519");
id = ssh_path_expand_escape(session, "%d/.ssh/id_ed25519");
rc = ssh_list_append(awaited_list, id);
assert_int_equal(rc, SSH_OK);
#ifdef HAVE_ECC
id = ssh_path_expand_escape(session, "%d/id_ecdsa");
id = ssh_path_expand_escape(session, "%d/.ssh/id_ecdsa");
rc = ssh_list_append(awaited_list, id);
assert_int_equal(rc, SSH_OK);
#endif
id = ssh_path_expand_escape(session, "%d/id_rsa");
id = ssh_path_expand_escape(session, "%d/.ssh/id_rsa");
rc = ssh_list_append(awaited_list, id);
assert_int_equal(rc, SSH_OK);
#ifdef WITH_FIDO2
/* Add security key identities */
id = ssh_path_expand_escape(session, "%d/id_ed25519_sk");
id = ssh_path_expand_escape(session, "%d/.ssh/id_ed25519_sk");
rc = ssh_list_append(awaited_list, id);
assert_int_equal(rc, SSH_OK);
#ifdef HAVE_ECC
id = ssh_path_expand_escape(session, "%d/id_ecdsa_sk");
id = ssh_path_expand_escape(session, "%d/.ssh/id_ecdsa_sk");
rc = ssh_list_append(awaited_list, id);
assert_int_equal(rc, SSH_OK);
#endif /* HAVE_ECC */

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;
}