Compare commits

..

89 Commits

Author SHA1 Message Date
Jakub Jelen
729a44e121 ci: Skip macos jobs on forks
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-03-24 10:58:22 +01:00
Jakub Jelen
051ac812db examples: Add warning about example code
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-03-24 10:58:11 +01:00
Haythem666
01772c4f79 pki: add ssh_key_type_and_hash_from_signature_name()
Merge ssh_key_type_from_signature_name() and ssh_key_hash_from_name()
into a single function ssh_key_type_and_hash_from_signature_name() to:

- Avoid double string comparisons on the same algorithm name
- Return SSH_ERROR on unknown/NULL input instead of silently returning SSH_DIGEST_AUTO
- Use strlen() before strcmp() to short-circuit string comparisons.

Handle GSSAPI "null" hostkey case in wrapper.c.
Add unit tests for the new function.

Fixes: https://gitlab.com/libssh/libssh-mirror/-/issues/355
Signed-off-by: Haythem666 <haythem.farhat@epfl.ch>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-24 10:50:39 +01:00
Manas Trivedi
9f7c596ca5 tests: add coverage for NULL session in ssh_channel_is_open
Signed-off-by: Manas Trivedi <manas.trivedi.020@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-24 10:15:06 +01:00
Manas Trivedi
34bbb48561 channels: add NULL session check in ssh_channel_is_open
Prevent potential NULL pointer dereference when accessing
channel->session->alive.

Signed-off-by: Manas Trivedi <manas.trivedi.020@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-24 10:15:05 +01:00
Jakub Jelen
f060583d6f tests: Generate coverage for fuzzing tests
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-19 22:16:17 +01:00
Bulitha Kawushika De Zoysa
a05b2b76be tests: initialize sftp test pointers to NULL
Signed-off-by: Bulitha Kawushika De Zoysa <bulithakaushika99@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-19 17:06:05 +01:00
Bulitha Kawushika De Zoysa
c9f34ac55f sftp: Add support for the users-groups-by-id@openssh.com OpenSSH extension on the server side.
Signed-off-by: Bulitha Kawushika De Zoysa <bulithakaushika99@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-19 17:06:04 +01:00
Ahmed hossam
bc24bba176 docs: Add documentation for test_server functions
Signed-off-by: Ahmed hossam <ahmed.hossambahig@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-19 17:03:42 +01:00
Colin Baumgarten
3154a4ab8d sftpserver: Fix client messages being ignored if sent at a high rate
When using OpenSSH scp to read files larger than a few hundred
kilobytes, downloads stall and never finish. A workaround is to
pass -Xnrequests=1 to scp, which will cause scp to only do a
single concurrent SFTP read request at a time.

The cause for the problem is that if SFTP client messages are
received at a high rate, sftp_channel_default_data_callback() will
potentially be called with multiple messages in the incoming data
buffer, but only the first message will be extracted and handled.

So add a loop to extract as many SFTP client messages as available
from the incoming data buffer.

Signed-off-by: Colin Baumgarten <colin.baumgarten@hubersuhner.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-17 12:01:15 +01:00
Sudharshan Hegde
9478de8082 doc: add missing Doxygen comments and fix documentation style
- Add missing @brief, @param, and @return docs across src/ and include/
- Fix blank lines between doc comments and function definitions
- Move function docs from headers to corresponding .c files
- Use named constants (SSH_OK, SSH_ERROR, SSH_TIMEOUT_INFINITE) in docs
- Fix parameter ordering in error.c, buffer.c, log.c docs
- Place #ifdef-guarded docs inside their respective #ifdef blocks

Signed-off-by: Sudharshan Hegde <sudharshanhegde68@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-17 11:46:11 +01:00
Shreyas Mahajan
e927820082 Make headers self-contained and include-order independent
Signed-off-by: Shreyas Mahajan <shreyasmahajan05@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:07:16 +01:00
ShreyasMahajann
67950c620d misc: Reformat struct ssh_list members to use 4-space indentation
Signed-off-by: Shreyas Mahajan <shreyasmahajan05@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:07:16 +01:00
Jakub Jelen
31ea4d1213 tests: Negative tests for ssh_pki_ctx_options_set
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:04:57 +01:00
Jakub Jelen
29c503ed7c tests: Remove needless reset to NULL
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:04:57 +01:00
Jakub Jelen
b1a28f7987 tests: Use the new ssh_pki_generate_key() where possible
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:04:57 +01:00
Jakub Jelen
616d165f14 pki_context: Document 0 is valid for bit size (default)
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:04:57 +01:00
Jakub Jelen
b9ecb9283e pki_context: Allow using minimal RSA key size in new API
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:04:57 +01:00
Jakub Jelen
c38edb59f2 examples: Avoid using deprecated ssh_pki_generate
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:04:57 +01:00
Jakub Jelen
def7a679f8 examples: Use separate variable for exit code
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:04:57 +01:00
Jakub Jelen
6f671919ad examples: Use separate variable for fd
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 19:04:57 +01:00
Jakub Jelen
45b1d85fb0 fuzz: Add debugging hints to readme
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 18:44:55 +01:00
Jakub Jelen
e7f4cc9580 knownhosts: Avoid possible memory leak on failed malloc
Thanks oss-fuzz

https://issues.oss-fuzz.com/issues/489362256

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 18:44:55 +01:00
Jakub Jelen
5479b276b2 Use ARRAY_SIZE systematically
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 18:25:22 +01:00
Jakub Jelen
5d7fbcf22a Fix line endings in sftp_attr_fuzzer
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 18:25:22 +01:00
Jakub Jelen
123c442a56 tests: Reformat torture_buffer
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
2026-03-16 18:25:22 +01:00
Arthur Chan
4dfcdd96b8 OSS-Fuzz: Add fuzzer for scp functions
Signed-off-by: Arthur Chan <arthur.chan@adalogics.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-13 20:48:53 +01:00
Emmanuel Ugwu
9d36b9dd81 docs: add doxygen documentation and fix inconsistencies
- src/misc.c: added doxygen docs for ssh_get_local_username()
- src/auth.c: added doxygen docs for ssh_kbdint_new(), ssh_kbdint_free(), ssh_kbdint_clean()
- src/bind_config.c: fix @params -> @param, @returns -> @return
- src/bind.c, src/socket.c, src/threads.c: fix @returns -> @return
- include/libssh/callbacks.h: fix @returns -> @return

Signed-off-by: Emmanuel Ugwu <emmanuelugwu121@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-13 20:48:07 +01:00
Rui Li
afa21334b4 tests: Add tests for originalhost/host separation and Match support
Signed-off-by: Rui Li <ruili3422@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-13 20:46:35 +01:00
Rui Li
a2ebc7ea9b Implement originalhost/host separation and Match support
Signed-off-by: Rui Li <ruili3422@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-13 20:46:35 +01:00
Rui Li
1ab8a35c5d Add strict validation mode to ssh_config_parse_uri in config_parser
Signed-off-by: Rui Li <ruili3422@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-13 20:46:35 +01:00
Madhav Vasisth
8782fcec18 agent: Add support for SSH2_AGENTC_REMOVE_IDENTITY
Implement support for the SSH2_AGENTC_REMOVE_IDENTITY
agent protocol message.

The implementation mirrors ssh_agent_sign_data()
and reuses agent_talk(). A single cleanup path is
used to ensure proper resource handling.

Signed-off-by: Madhav Vasisth <mv2363@srmist.edu.in>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-09 18:27:10 +01:00
Pavol Žáčik
8d563f90f3 Add more krb5-related Valgrind suppressions
All newly reported leaks are categorized as
reachable and they mostly relate to global
variables in krb5 which are free'd before
each re-initialization.

Fixes #352.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-09 18:20:06 +01:00
Jakub Jelen
6a5e298cec Log more useful information to be able to troubleshoot sftp server
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-03-06 15:02:37 +01:00
Jan Pazdziora
163e1b059b Expansion of %s expansion is no longer happening.
The SSH_OPTIONS_SSH_DIR/session->opts.sshdir value
is passed through ssh_path_expand_tilde which does not expand %s.

Amending f643c34ee8.

Signed-off-by: Jan Pazdziora <jan.pazdziora@code.adelton.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-06 15:02:37 +01:00
Michael Hansen
e16018491e Add casts to a couple more pack size constants in hybrid_mlkem.c
Signed-off-by: Michael Hansen <zrax0111@gmail.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-06 15:02:37 +01:00
Michael Hansen
c26e9298e3 Fix parameter size mismatch in ssh_buffer_pack for hybrid_mlkem.c
Signed-off-by: Michael Hansen <zrax0111@gmail.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-06 15:02:37 +01:00
Shiva Kiran Koninty
3c0567cb67 docs: Fix struct field comment positioning for Doxygen
Doxygen interprets comments placed beside struct fields to belong
to the next field instead of the current field.

This could be fixed by moving the comments atop the fields,
or by using the `/**< COMMENT */` format.

Stay consistent with the comment format used for other structs
and move the comments atop the fields.

Signed-off-by: Shiva Kiran Koninty <shiva_kr@riseup.net>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-03-06 15:02:37 +01:00
Shiva Kiran Koninty
00d1903bf6 doc: Document sftp_attributes_struct
Fixes #333

Signed-off-by: Shiva Kiran Koninty <shiva_kr@riseup.net>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-06 15:02:37 +01:00
Himaneesh Mishra
bc2a483aa1 headers: add missing stdint/stddef includes
Signed-off-by: Himaneesh Mishra <himaneeshmishra@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-06 15:02:37 +01:00
Shiva Kiran Koninty
5ad8dda6f6 buffer: Remove support for format specifier 'F' in ssh_buffer_pack()
Eliminate dead code.

Signed-off-by: Shiva Kiran Koninty <shiva_kr@riseup.net>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-06 15:02:37 +01:00
Shiva Kiran Koninty
d680b8ea8a sntrup: Remove needless conversion of shared secret to bignum
The derived shared secret in SNTRUP761 is converted into a bignum,
only to be converted back to binary during use in kex.c.
Instead use field 'hybrid_shared_secret' in ssh_crypto_struct
to store it, just like the Hybrid MLKEM implementation.

Fixes #338

Signed-off-by: Shiva Kiran Koninty <shiva_kr@riseup.net>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-06 15:02:37 +01:00
Francesco Rollo
90b07e2c18 refactor(server): Warn about config override behavior in bind APIs
- Add a warning to ssh_bind_listen() clarifying that it implicitly
calls ssh_bind_options_parse_config(), which may override options
previously set via ssh_bind_options_set().

- Add a warning to ssh_bind_options_set() and ssh_bind_config_parse_string()
explaining that options may be overridden if configuration files are parsed
afterwards, either implicitly via ssh_bind_listen() or by an explicit call to
ssh_bind_options_parse_config().

Signed-off-by: Francesco <eferollo@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:36:59 +01:00
Francesco Rollo
edbd929fa2 feat(server): Add support for -o option argument in server example
Allow passing server configuration options via the -o flag and expose
ssh_bind_config_parse_string() as a public API.

Signed-off-by: Francesco <eferollo@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:36:58 +01:00
Himaneesh Mishra
38932b74c0 docs: reduce Doxygen warnings in libsshpp.hpp
Signed-off-by: Himaneesh Mishra <himaneeshmishra@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:35:41 +01:00
Mingyuan Li
60d6179eaa tests: Add opendir handle exhaustion test for sftpserver
Add torture_server_sftp_opendir_handles_exhaustion test that
exercises the error path in process_opendir() when all SFTP
handles are occupied. This covers the memory leak fix for
h->name that was missing in the sftp_handle_alloc() failure path.

The test exhausts all 256 handle slots with sftp_open(), then
verifies that sftp_opendir() fails gracefully without crashing
or leaking memory.

Signed-off-by: Mingyuan Li <2560359315@qq.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:33:36 +01:00
Mingyuan Li
0d9b2c68cc sftpserver: Fix memory leak of h->name in process_opendir error path
When sftp_handle_alloc() fails in process_opendir(), the error path
frees the handle struct h but does not free h->name which was
allocated by strdup(). This causes a memory leak every time the
server runs out of available SFTP handles while processing an
opendir request.

Also add a missing NULL check for the strdup() call itself to
handle out-of-memory conditions gracefully.

This is the same class of bug that was fixed in process_open() by
commit db7f101d (CVE-2025-5449), but was missed in process_opendir().

Signed-off-by: Mingyuan Li <2560359315@qq.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:33:35 +01:00
Madhav Vasisth
adc2462329 docs: clarify ssh-agent API usage and lifecycle
Clarify the session-coupled nature of the ssh-agent interface,
document lifecycle and ownership expectations of agent-related
objects, and describe common error cases and limitations.

No functional changes.

Signed-off-by: Madhav Vasisth <mv2363@srmist.edu.in>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:32:39 +01:00
Pavol Žáčik
0bff33c790 gss-kex: Fix memory leaks in ssh_gssapi_check_client_config
Upon unsuccessful alloc of the gssapi context, the function
would return early without freeing the supported OID set.

With opts->gss_client_identity enabled, the function would
not free the client_id allocated by gss_import_name.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:30:53 +01:00
Pavol Žáčik
47e9b5536a gss-kex: Release output_token and mic on error paths
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:30:53 +01:00
Pavol Žáčik
2f1f474e27 gssapi: Free both_supported on a new error path
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-24 10:30:52 +01:00
Bulitha Kawushika De Zoysa
18d7a3967c tests: Add interoperability tests against TinySSH
This adds a new test suite 'torture_tinyssh' that verifies interoperability with the TinySSH server using various key exchange methods.

Fixes #271

Signed-off-by: Bulitha Kawushika De Zoysa <bulithakaushika99@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-20 10:56:46 +01:00
Antoni Bertolin Monferrer
d45ce10c83 channels: Fix OOM error check after strdup
The fix allows the code to properly check if the strdup failed to allocate a
char buffer for the exit signal.

Signed-off-by: Antoni Bertolin Monferrer <antoni.monferrer@canonical.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-20 10:55:17 +01:00
Pavol Žáčik
a7fd80795e Update recently added logging to be less verbose
In 20d9642c and parent commits, log levels were
recategorized to be less verbose when using the
level INFO and lower. These levels should not
print any information redundant to the end user.

This commit fixes recently added uses of logging
that are not consistent with the abovementioned
categorization, in particular:

- logs in ssh_strict_fopen should not have
  the RARE/WARNING level since failing to open
  a file may not be an issue at all (e.g., when
  trying to open the knownhosts file).

- logging the username used in authentication
  or proxyjump-related information should be done
  at the DEBUG level, otherwise it could pollute
  the output of, e.g., curl.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-20 10:54:27 +01:00
Jakub Jelen
f8cba20859 Add back Security section to 0.12.0 changelog
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-12 14:54:06 +01:00
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
156 changed files with 8468 additions and 1042 deletions

View File

@@ -784,8 +784,6 @@ coverity:
- mkdir obj && cd obj
only:
- branches@libssh/libssh-mirror
- branches@cryptomilk/libssh-mirror
- branches@jjelen/libssh-mirror
# TODO add -DFUZZ_TESTING=ON clang cant find _LLVMFuzzerInitialize on arm64
macos-m1:

101
CHANGELOG
View File

@@ -1,6 +1,107 @@
CHANGELOG
=========
version 0.12.0 (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
* 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

@@ -108,7 +108,6 @@ if (DOXYGEN_FOUND)
packet_struct,
pem_get_password_struct,
ssh_tokens_st,
sftp_attributes_struct,
sftp_client_message_struct,
sftp_dir_struct,
sftp_ext_struct,

View File

@@ -49,4 +49,4 @@ ALL_FUNC=$(echo "$FUNC_LINES" | sed -e "s/$F_CUT_BEFORE//g" -e "s/$F_CUT_AFTER//
ALL_FUNC=$(echo "$ALL_FUNC" | sort - | uniq | wc -l)
# percentage of the documented functions
awk "BEGIN {printf \"Documentation coverage is %.2f%\n\", 100 - (${UNDOC_FUNC}/${ALL_FUNC}*100)}"
awk "BEGIN {printf \"Documentation coverage is %.2f%%\n\", 100 - (${UNDOC_FUNC}/${ALL_FUNC}*100)}"

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

@@ -24,7 +24,7 @@ int main(void)
int rv;
/* Generate a new ED25519 private key file */
rv = ssh_pki_generate(SSH_KEYTYPE_ED25519, 0, &key);
rv = ssh_pki_generate_key(SSH_KEYTYPE_ED25519, NULL, &key);
if (rv != SSH_OK) {
fprintf(stderr, "Failed to generate private key");
return -1;

View File

@@ -35,7 +35,7 @@
struct arguments_st {
enum ssh_keytypes_e type;
unsigned long bits;
int bits;
char *file;
char *passphrase;
char *format;
@@ -321,8 +321,9 @@ list_fingerprint(char *file)
int main(int argc, char *argv[])
{
ssh_pki_ctx ctx = NULL;
ssh_key key = NULL;
int rc = 0;
int ret = EXIT_FAILURE, rc, fd;
char overwrite[1024] = "";
char *pubkey_file = NULL;
@@ -361,15 +362,15 @@ int main(int argc, char *argv[])
}
errno = 0;
rc = open(arguments.file, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
if (rc < 0) {
fd = open(arguments.file, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR);
if (fd < 0) {
if (errno == EEXIST) {
printf("File \"%s\" exists. Overwrite it? (y|n) ", arguments.file);
rc = scanf("%1023s", overwrite);
if (rc > 0 && tolower(overwrite[0]) == 'y') {
rc = open(arguments.file, O_WRONLY);
if (rc > 0) {
close(rc);
fd = open(arguments.file, O_WRONLY);
if (fd > 0) {
close(fd);
errno = 0;
rc = chmod(arguments.file, S_IRUSR | S_IWUSR);
if (rc != 0) {
@@ -391,13 +392,30 @@ int main(int argc, char *argv[])
goto end;
}
} else {
close(rc);
close(fd);
}
/* Create a new PKI Context if needed -- for other types using NULL is ok */
if (arguments.type == SSH_KEYTYPE_RSA && arguments.bits != 0) {
ctx = ssh_pki_ctx_new();
if (ctx == NULL) {
fprintf(stderr, "Error: Failed to allocate PKI context\n");
goto end;
}
rc = ssh_pki_ctx_options_set(ctx,
SSH_PKI_OPTION_RSA_KEY_SIZE,
&arguments.bits);
if (rc != SSH_OK) {
fprintf(stderr, "Error: Failed to set RSA bit size\n");
goto end;
}
}
/* Generate a new private key */
rc = ssh_pki_generate(arguments.type, arguments.bits, &key);
rc = ssh_pki_generate_key(arguments.type, ctx, &key);
if (rc != SSH_OK) {
fprintf(stderr, "Error: Failed to generate keys");
fprintf(stderr, "Error: Failed to generate keys\n");
goto end;
}
@@ -451,24 +469,23 @@ int main(int argc, char *argv[])
pubkey_file = (char *)malloc(strlen(arguments.file) + 5);
if (pubkey_file == NULL) {
rc = ENOMEM;
goto end;
}
sprintf(pubkey_file, "%s.pub", arguments.file);
errno = 0;
rc = open(pubkey_file,
fd = open(pubkey_file,
O_CREAT | O_EXCL | O_WRONLY,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (rc < 0) {
if (fd < 0) {
if (errno == EEXIST) {
printf("File \"%s\" exists. Overwrite it? (y|n) ", pubkey_file);
rc = scanf("%1023s", overwrite);
if (rc > 0 && tolower(overwrite[0]) == 'y') {
rc = open(pubkey_file, O_WRONLY);
if (rc > 0) {
close(rc);
fd = open(pubkey_file, O_WRONLY);
if (fd > 0) {
close(fd);
errno = 0;
rc = chmod(pubkey_file,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
@@ -491,7 +508,7 @@ int main(int argc, char *argv[])
goto end;
}
} else {
close(rc);
close(fd);
}
/* Write the public key */
@@ -501,14 +518,12 @@ int main(int argc, char *argv[])
goto end;
}
end:
if (key != NULL) {
ssh_key_free(key);
}
ret = EXIT_SUCCESS;
if (arguments.file != NULL) {
free(arguments.file);
}
end:
ssh_pki_ctx_free(ctx);
ssh_key_free(key);
free(arguments.file);
if (arguments.passphrase != NULL) {
#ifdef HAVE_EXPLICIT_BZERO
@@ -519,8 +534,6 @@ end:
free(arguments.passphrase);
}
if (pubkey_file != NULL) {
free(pubkey_file);
}
return rc;
free(pubkey_file);
return ret;
}

View File

@@ -1,4 +1,4 @@
/* This is a sample implementation of a libssh based SSH server */
/* This is a sample implementation of a libssh based SFTP server */
/*
Copyright 2014 Audrius Butkevicius
@@ -9,6 +9,28 @@ domain. This does not apply to the rest of the library though, but it is
allowed to cut-and-paste working code from this file to any license of
program.
The goal is to show the API in action.
!!! WARNING / ACHTUNG !!!
This is not a production-ready SFTP server implementation. While it demonstrates
how an SFTP server can be implemented on the SFTP layer and integrated into
existing SSH server, it lacks many steps int the authentication and
session establishment!
It allows to log in any user with hardcoded credentials below or with public
key provided from authorized keys file.
The resulting SFTP session keeps running under original user who runs the
example server and therefore the SFTP session has access to all files that are
accessible to the user running the server.
Real-world servers should at very least switch the user to unprivileged one
after authentication using setuid(). If some more restrictions are needed,
generally limiting what files should and should not be accessible, it is
recommended to use chroot() as handling symlinks can be tricky in the SFTP
callbacks.
!!! WARNING / ACHTUNG !!!
*/
#include "config.h"

View File

@@ -10,6 +10,23 @@ allowed to cut-and-paste working code from this file to any license of
program.
The goal is to show the API in action. It's not a reference on how terminal
clients must be made or how a client should react.
!!! WARNING / ACHTUNG !!!
This is not a production-ready SSH server implementation. While it demonstrates
how an SSH server can be implemented, it lacks many steps during
the authentication and session establishment!
It allows to log in any user with hardcoded credentials below or with public
key provided from authorized keys file.
The resulting session keeps running under original user who runs the example
server and therefore it retains the same permissions.
Real-world servers should at very least switch the user to unprivileged one
after authentication using setuid().
!!! WARNING / ACHTUNG !!!
*/
#include "config.h"

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

@@ -9,6 +9,23 @@ domain. This does not apply to the rest of the library though, but it is
allowed to cut-and-paste working code from this file to any license of
program.
The goal is to show the API in action.
!!! WARNING / ACHTUNG !!!
This is not a production-ready SSH server implementation. While it demonstrates
how an SSH server can be implemented, it lacks many steps during
the authentication and session establishment!
It allows to log in any user with hardcoded credentials below or with public
key provided from authorized keys file.
The resulting session keeps running under original user who runs the example
server and therefore it retains the same permissions.
Real-world servers should at very least switch the user to unprivileged one
after authentication using setuid().
!!! WARNING / ACHTUNG !!!
*/
#include "config.h"
@@ -107,6 +124,14 @@ static struct argp_option options[] = {
.doc = "Set the authorized keys file.",
.group = 0
},
{
.name = "option",
.key = 'o',
.arg = "OPTION",
.flags = 0,
.doc = "Set server configuration option [-o OptionName=Value]",
.group = 0
},
{
.name = "user",
.key = 'u',
@@ -158,6 +183,9 @@ parse_opt(int key, char *arg, struct argp_state *state)
case 'a':
strncpy(authorizedkeys, arg, DEF_STR_SIZE - 1);
break;
case 'o':
ssh_bind_config_parse_string(sshbind, arg);
break;
case 'u':
strncpy(username, arg, sizeof(username) - 1);
break;
@@ -194,7 +222,7 @@ parse_opt(int argc, char **argv, ssh_bind sshbind)
{
int key;
while((key = getopt(argc, argv, "a:e:k:p:P:r:u:v")) != -1) {
while((key = getopt(argc, argv, "a:e:k:o:p:P:r:u:v")) != -1) {
if (key == 'p') {
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_BINDPORT_STR, optarg);
} else if (key == 'k') {
@@ -205,6 +233,8 @@ parse_opt(int argc, char **argv, ssh_bind sshbind)
ssh_bind_options_set(sshbind, SSH_BIND_OPTIONS_HOSTKEY, optarg);
} else if (key == 'a') {
strncpy(authorizedkeys, optarg, DEF_STR_SIZE-1);
} else if (key == 'o') {
ssh_bind_config_parse_string(sshbind, optarg);
} else if (key == 'u') {
strncpy(username, optarg, sizeof(username) - 1);
} else if (key == 'P') {
@@ -222,6 +252,7 @@ parse_opt(int argc, char **argv, ssh_bind sshbind)
"libssh %s -- a Secure Shell protocol implementation\n"
"\n"
" -a, --authorizedkeys=FILE Set the authorized keys file.\n"
" -o, --option=OPTION Set server configuration option (e.g., -o OptionName=Value).\n"
" -e, --ecdsakey=FILE Set the ecdsa key (deprecated alias for 'k').\n"
" -k, --hostkey=FILE Set a host key. Can be used multiple times.\n"
" Implies no default keys.\n"

View File

@@ -74,6 +74,13 @@
extern "C" {
#endif
/**
* @brief SSH agent connection context.
*
* This structure represents a connection to an SSH authentication agent.
* It is used by libssh to communicate with the agent for operations such
* as listing available identities and signing data during authentication.
*/
struct ssh_agent_struct {
struct ssh_socket_struct *sock;
ssh_buffer ident;
@@ -82,13 +89,24 @@ struct ssh_agent_struct {
};
/* agent.c */
/**
* @brief Create a new ssh agent structure.
*
* @return An allocated ssh agent structure or NULL on error.
* Creates and initializes an SSH agent context bound to the given
* SSH session. The agent connection relies on the session configuration
* (e.g. agent forwarding).
*
* @param session The SSH session the agent will be associated with.
*
* @return An allocated ssh agent structure on success, or NULL on error.
*
* @note This function does not start or manage an external agent
* process; it only connects to an already running agent.
*/
struct ssh_agent_struct *ssh_agent_new(struct ssh_session_struct *session);
void ssh_agent_close(struct ssh_agent_struct *agent);
/**
@@ -101,23 +119,75 @@ void ssh_agent_free(struct ssh_agent_struct *agent);
/**
* @brief Check if the ssh agent is running.
*
* @param session The ssh session to check for the agent.
* @param session The SSH session to check for agent availability.
*
* @return 1 if it is running, 0 if not.
* @return 1 if an agent is available, 0 otherwise.
*/
int ssh_agent_is_running(struct ssh_session_struct *session);
uint32_t ssh_agent_get_ident_count(struct ssh_session_struct *session);
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
char **comment);
/**
* @brief Retrieve the first identity provided by the SSH agent.
*
* @param session The SSH session associated with the agent.
* @param comment Optional pointer to receive the key comment.
*
* @return A public key on success, or NULL if no identities are available.
*
* @note The returned key is owned by the caller and must be freed
* using ssh_key_free().
*/
ssh_key ssh_agent_get_first_ident(struct ssh_session_struct *session,
char **comment);
/**
* @brief Retrieve the next identity provided by the SSH agent.
*
* @param session The SSH session associated with the agent.
* @param comment Optional pointer to receive the key comment.
*
* @return A public key on success, or NULL if no further identities exist.
*
* @note The returned key is owned by the caller and must be freed
* using ssh_key_free().
*/
ssh_key ssh_agent_get_next_ident(struct ssh_session_struct *session,
char **comment);
/**
* @brief Request the SSH agent to sign data using a public key.
*
* Asks the SSH agent to generate a signature over the provided data
* using the specified public key.
*
* @param session The SSH session associated with the agent.
* @param pubkey The public key identifying the signing identity.
* @param data The data to be signed.
*
* @return A newly allocated ssh_string containing the signature on
* success, or NULL on failure.
*
* @note This operation requires that the agent possesses the
* corresponding private key and may prompt the user for
* confirmation depending on agent configuration.
*/
ssh_string ssh_agent_sign_data(ssh_session session,
const ssh_key pubkey,
struct ssh_buffer_struct *data);
/**
* @brief Remove an identity from the SSH agent.
*
* @param session The SSH session.
* @param key The public key to remove.
*
* @return `SSH_OK` on success, `SSH_ERROR` on failure.
*/
int ssh_agent_remove_identity(ssh_session session,
const ssh_key key);
#ifdef __cplusplus
}

View File

@@ -57,25 +57,16 @@ enum ssh_bind_config_opcode_e {
BIND_CFG_MAX /* Keep this one last in the list */
};
/* @brief Parse configuration file and set the options to the given ssh_bind
/**
* @brief Parse configuration file and set the options to the given ssh_bind
*
* @params[in] sshbind The ssh_bind context to be configured
* @params[in] filename The path to the configuration file
* @param[in] sshbind The ssh_bind context to be configured
* @param[in] filename The path to the configuration file
*
* @returns 0 on successful parsing the configuration file, -1 on error
*/
int ssh_bind_config_parse_file(ssh_bind sshbind, const char *filename);
/* @brief Parse configuration string and set the options to the given bind session
*
* @params[in] bind The ssh bind session
* @params[in] input Null terminated string containing the configuration
*
* @returns SSH_OK on successful parsing the configuration string,
* SSH_ERROR on error
*/
int ssh_bind_config_parse_string(ssh_bind bind, const char *input);
#ifdef __cplusplus
}
#endif

View File

@@ -31,6 +31,8 @@
#ifndef _BLF_H_
#define _BLF_H_
#include <stdint.h>
//#include "includes.h"
#if !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H)

View File

@@ -20,6 +20,10 @@
#ifndef _BYTEARRAY_H
#define _BYTEARRAY_H
#include "config.h"
#include <stdint.h>
#define _DATA_BYTE_CONST(data, pos) \
((uint8_t)(((const uint8_t *)(data))[(pos)]))

View File

@@ -522,7 +522,7 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
* verifies that the callback pointer exists
* @param p callback pointer
* @param c callback name
* @returns nonzero if callback can be called
* @return nonzero if callback can be called
*/
#define ssh_callbacks_exists(p,c) (\
(p != NULL) && ( (char *)&((p)-> c) < (char *)(p) + (p)->size ) && \
@@ -538,12 +538,9 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
* automatically passed through.
*
* @param list list of callbacks
*
* @param cbtype type of the callback
*
* @param c callback name
*
* @param va_args parameters to be passed
* @param ... Parameters to be passed to the callback.
*/
#define ssh_callbacks_execute_list(list, cbtype, c, ...) \
do { \
@@ -585,9 +582,18 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
_cb = ssh_iterator_value(_cb_type, _cb_i); \
if (ssh_callbacks_exists(_cb, _cb_name))
/** @internal
* @brief Execute the current callback in an ssh_callbacks_iterate() loop.
*
* @param _cb_name The name of the callback field to invoke.
* @param ... Parameters to be passed to the callback.
*/
#define ssh_callbacks_iterate_exec(_cb_name, ...) \
_cb->_cb_name(__VA_ARGS__, _cb->userdata)
/** @internal
* @brief End an ssh_callbacks_iterate() loop.
*/
#define ssh_callbacks_iterate_end() \
} \
} while(0)
@@ -1060,8 +1066,18 @@ LIBSSH_API int ssh_remove_channel_callbacks(ssh_channel channel,
* @{
*/
/** @brief Callback for thread mutex operations (init, destroy, lock, unlock).
*
* @param lock Pointer to the mutex lock.
*
* @return 0 on success, non-zero on error.
*/
typedef int (*ssh_thread_callback) (void **lock);
/** @brief Callback to retrieve the current thread identifier.
*
* @return The unique identifier of the calling thread.
*/
typedef unsigned long (*ssh_thread_id_callback) (void);
struct ssh_threads_callbacks_struct {
const char *type;
@@ -1172,10 +1188,20 @@ typedef int (*ssh_jump_verify_knownhost_callback)(ssh_session session,
typedef int (*ssh_jump_authenticate_callback)(ssh_session session,
void *userdata);
/**
* @brief Callback collection for managing an SSH proxyjump connection.
*
* Set these callbacks to control knownhost verification and authentication
* on the jump host before the final destination is reached.
*/
struct ssh_jump_callbacks_struct {
/** Userdata passed to each callback. */
void *userdata;
/** Called before connecting to the jump host. */
ssh_jump_before_connection_callback before_connection;
/** Called to verify the jump host's identity. */
ssh_jump_verify_knownhost_callback verify_knownhost;
/** Called to authenticate on the jump host. */
ssh_jump_authenticate_callback authenticate;
};

View File

@@ -9,6 +9,8 @@ Public domain.
#ifndef CHACHA_H
#define CHACHA_H
#include <stdint.h>
struct chacha_ctx {
uint32_t input[16];
};

View File

@@ -29,6 +29,8 @@
#ifndef CHACHA20_POLY1305_H
#define CHACHA20_POLY1305_H
#include <stdint.h>
#define CHACHA20_BLOCKSIZE 64
#define CHACHA20_KEYLEN 32

View File

@@ -54,6 +54,11 @@ int ssh_config_get_yesno(char **str, int notfound);
* be stored or NULL if we do not care about the result.
* @param[in] ignore_port Set to true if we should not attempt to parse
* port number.
* @param[in] strict Set to true to validate hostname against RFC1035
* (for resolving to a real host).
* Set to false to only reject shell metacharacters
* (allowing config aliases with non-RFC1035 chars
* like underscores, resolved later via Hostname).
*
* @returns SSH_OK if the provided string is in format of SSH URI,
* SSH_ERROR on failure
@@ -62,7 +67,8 @@ int ssh_config_parse_uri(const char *tok,
char **username,
char **hostname,
char **port,
bool ignore_port);
bool ignore_port,
bool strict);
/**
* @brief: Parse the ProxyJump configuration line and if parsing,

View File

@@ -25,14 +25,19 @@
#ifndef _CRYPTO_H_
#define _CRYPTO_H_
#include <stdbool.h>
#include "config.h"
#include <stdbool.h>
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
#elif defined(HAVE_LIBMBEDCRYPTO)
#include <mbedtls/gcm.h>
#endif
#ifdef HAVE_OPENSSL_ECDH_H
#include <openssl/ecdh.h>
#endif
#include "libssh/wrapper.h"
#ifdef cbc_encrypt
@@ -42,9 +47,6 @@
#undef cbc_decrypt
#endif
#ifdef HAVE_OPENSSL_ECDH_H
#include <openssl/ecdh.h>
#endif
#include "libssh/curve25519.h"
#include "libssh/dh.h"
#include "libssh/ecdh.h"

View File

@@ -23,6 +23,10 @@
#ifndef SRC_DH_GEX_H_
#define SRC_DH_GEX_H_
#include "config.h"
#include "libssh/libssh.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -22,6 +22,10 @@
#ifndef SSH_KNOWNHOSTS_H_
#define SSH_KNOWNHOSTS_H_
#include "config.h"
#include "libssh/libssh.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -24,8 +24,9 @@
#include "config.h"
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
#include "libssh/libssh.h"
typedef gcry_md_hd_t SHACTX;
typedef gcry_md_hd_t SHA256CTX;
typedef gcry_md_hd_t SHA384CTX;

View File

@@ -27,7 +27,6 @@
#include "config.h"
#ifdef HAVE_LIBMBEDCRYPTO
#include <mbedtls/md.h>
#include <mbedtls/bignum.h>
#include <mbedtls/pk.h>
@@ -36,6 +35,8 @@
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/platform.h>
#include "libssh/libssh.h"
typedef mbedtls_md_context_t *SHACTX;
typedef mbedtls_md_context_t *SHA256CTX;
typedef mbedtls_md_context_t *SHA384CTX;

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

@@ -67,38 +67,60 @@ class Channel;
*/
#ifndef SSH_NO_CPP_EXCEPTIONS
/** @brief This class describes a SSH Exception object. This object can be thrown
* by several SSH functions that interact with the network, and may fail because of
* socket, protocol or memory errors.
/** @brief This class describes a SSH Exception object. This object can be
* thrown by several SSH functions that interact with the network, and
* may fail because of socket, protocol or memory errors.
*/
class SshException{
public:
SshException(ssh_session csession){
code=ssh_get_error_code(csession);
description=std::string(ssh_get_error(csession));
}
SshException(const SshException &e){
code=e.code;
description=e.description;
}
/** @brief returns the Error code
* @returns SSH_FATAL Fatal error happened (not recoverable)
* @returns SSH_REQUEST_DENIED Request was denied by remote host
* @see ssh_get_error_code
*/
int getCode(){
return code;
}
/** @brief returns the error message of the last exception
* @returns pointer to a c string containing the description of error
* @see ssh_get_error
*/
std::string getError(){
return description;
}
private:
int code;
std::string description;
class SshException {
public:
/** @brief Construct an exception from a libssh session error state.
* Captures the current error code and error string associated with
* the given session.
*
* @param[in] csession libssh session handle used to query error details.
*
* @see ssh_get_error_code
* @see ssh_get_error
*/
SshException(ssh_session csession)
{
code = ssh_get_error_code(csession);
description = std::string(ssh_get_error(csession));
}
/** @brief Copy-construct an exception.
*
* @param[in] e Source exception.
*/
SshException(const SshException &e)
{
code = e.code;
description = e.description;
}
/** @brief returns the Error code
*
* @returns `SSH_FATAL` Fatal error happened (not recoverable)
* @returns `SSH_REQUEST_DENIED` Request was denied by remote host
*
* @see ssh_get_error_code
*/
int getCode()
{
return code;
}
/** @brief returns the error message of the last exception
*
* @returns pointer to a c string containing the description of error
*
* @see ssh_get_error
*/
std::string getError()
{
return description;
}
private:
int code;
std::string description;
};
/** @internal
@@ -134,9 +156,12 @@ public:
c_session=NULL;
}
/** @brief sets an SSH session options
* @param type Type of option
* @param option cstring containing the value of option
*
* @param[in] type Type of option
* @param[in] option cstring containing the value of option
*
* @throws SshException on error
*
* @see ssh_options_set
*/
void_throwable setOption(enum ssh_options_e type, const char *option){
@@ -144,9 +169,12 @@ public:
return_throwable;
}
/** @brief sets an SSH session options
* @param type Type of option
* @param option long integer containing the value of option
*
* @param[in] type Type of option
* @param[in] option long integer containing the value of option
*
* @throws SshException on error
*
* @see ssh_options_set
*/
void_throwable setOption(enum ssh_options_e type, long int option){
@@ -154,9 +182,12 @@ public:
return_throwable;
}
/** @brief sets an SSH session options
* @param type Type of option
* @param option void pointer containing the value of option
*
* @param[in] type Type of option
* @param[in] option void pointer containing the value of option
*
* @throws SshException on error
*
* @see ssh_options_set
*/
void_throwable setOption(enum ssh_options_e type, void *option){
@@ -164,7 +195,9 @@ public:
return_throwable;
}
/** @brief connects to the remote host
*
* @throws SshException on error
*
* @see ssh_connect
*/
void_throwable connect(){
@@ -173,8 +206,11 @@ public:
return_throwable;
}
/** @brief Authenticates automatically using public key
*
* @throws SshException on error
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED
*
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`, `SSH_AUTH_DENIED`
*
* @see ssh_userauth_autopubkey
*/
int userauthPublickeyAuto(void){
@@ -184,9 +220,13 @@ public:
}
/** @brief Authenticates using the "none" method. Prefer using autopubkey if
* possible.
*
* @throws SshException on error
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED
*
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`, `SSH_AUTH_DENIED`
*
* @see ssh_userauth_none
*
* @see Session::userauthAutoPubkey
*/
int userauthNone(){
@@ -198,16 +238,16 @@ public:
/**
* @brief Authenticate through the "keyboard-interactive" method.
*
* @param[in] username The username to authenticate. You can specify NULL if
* ssh_option_set_username() has been used. You cannot
* try two different logins in a row.
*
* @param[in] username The username to authenticate. You can specify NULL
* If ssh_option_set_username()has been used. You cannot
* try two different logins in a row.
* @param[in] submethods Undocumented. Set it to NULL.
*
* @throws SshException on error
*
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED,
* SSH_AUTH_ERROR, SSH_AUTH_INFO, SSH_AUTH_AGAIN
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`,
* `SSH_AUTH_DENIED`, `SSH_AUTH_ERROR`, `SSH_AUTH_INFO`,
* `SSH_AUTH_AGAIN`
*
* @see ssh_userauth_kbdint
*/
@@ -218,6 +258,7 @@ public:
}
/** @brief Get the number of prompts (questions) the server has given.
*
* @returns The number of prompts.
* @see ssh_userauth_kbdint_getnprompts
*/
@@ -228,17 +269,16 @@ public:
/**
* @brief Set the answer for a question from a message block.
*
* @param[in] index The index number of the prompt.
* @param[in] answer The answer to give to the server. The answer MUST be
* encoded UTF-8. It is up to the server how to interpret
* the value and validate it. However, if you read the
* answer in some other encoding, you MUST convert it to
* UTF-8.
* @param[in] index The index number of the prompt.
* @param[in] answer The answer to give to the server. The answer MUST be
* encoded UTF-8.It is up to the server how to interpret
* the value and validate it. However, if you read the
* answer in some other encoding, you MUST convert it to
* UTF-8.
*
* @throws SshException on error
*
* @returns 0 on success, < 0 on error
*
* @see ssh_userauth_kbdint_setanswer
*/
int userauthKbdintSetAnswer(unsigned int index, const char *answer)
@@ -248,12 +288,13 @@ public:
return ret;
}
/** @brief Authenticates using the password method.
*
* @param[in] password password to use for authentication
*
* @throws SshException on error
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`, `SSH_AUTH_DENIED`
*
* @see ssh_userauth_password
*/
int userauthPassword(const char *password){
@@ -262,10 +303,14 @@ public:
return ret;
}
/** @brief Try to authenticate using the publickey method.
*
* @param[in] pubkey public key to use for authentication
*
* @throws SshException on error
* @returns SSH_AUTH_SUCCESS if the pubkey is accepted,
* @returns SSH_AUTH_DENIED if the pubkey is denied
*
* @returns `SSH_AUTH_SUCCESS` if the pubkey is accepted,
* @returns `SSH_AUTH_DENIED` if the pubkey is denied
*
* @see ssh_userauth_try_pubkey
*/
int userauthTryPublickey(ssh_key pubkey){
@@ -274,9 +319,12 @@ public:
return ret;
}
/** @brief Authenticates using the publickey method.
*
* @param[in] privkey private key to use for authentication
*
* @throws SshException on error
* @returns SSH_AUTH_SUCCESS, SSH_AUTH_PARTIAL, SSH_AUTH_DENIED
* @returns `SSH_AUTH_SUCCESS`, `SSH_AUTH_PARTIAL`, `SSH_AUTH_DENIED`
*
* @see ssh_userauth_pubkey
*/
int userauthPublickey(ssh_key privkey){
@@ -286,7 +334,9 @@ public:
}
/** @brief Returns the available authentication methods from the server
*
* @throws SshException on error
*
* @returns Bitfield of available methods.
* @see ssh_userauth_list
*/
@@ -302,8 +352,9 @@ public:
ssh_disconnect(c_session);
}
/** @brief Returns the disconnect message from the server, if any
* @returns pointer to the message, or NULL. Do not attempt to free
* the pointer.
*
* @returns pointer to the message, or NULL. Do not attempt to free the
* pointer.
*/
const char *getDisconnectMessage(){
const char *msg=ssh_get_disconnect_message(c_session);
@@ -312,25 +363,30 @@ public:
/** @internal
* @brief gets error message
*/
const char *getError(){
return ssh_get_error(c_session);
const char *getError()
{
return ssh_get_error(c_session);
}
/** @internal
* @brief returns error code
*/
int getErrorCode(){
return ssh_get_error_code(c_session);
int getErrorCode()
{
return ssh_get_error_code(c_session);
}
/** @brief returns the file descriptor used for the communication
*
* @returns the file descriptor
*
* @warning if a proxycommand is used, this function will only return
* one of the two file descriptors being used
* one of the two file descriptors being used.
* @see ssh_get_fd
*/
socket_t getSocket(){
return ssh_get_fd(c_session);
}
/** @brief gets the Issue banner from the ssh server
*
* @returns the issue banner. This is generally a MOTD from server
* @see ssh_get_issue_banner
*/
@@ -344,6 +400,7 @@ public:
return ret;
}
/** @brief returns the OpenSSH version (server) if possible
*
* @returns openssh version code
* @see ssh_get_openssh_version
*/
@@ -351,6 +408,7 @@ public:
return ssh_get_openssh_version(c_session);
}
/** @brief returns the version of the SSH protocol being used
*
* @returns the SSH protocol version
* @see ssh_get_version
*/
@@ -358,9 +416,10 @@ public:
return ssh_get_version(c_session);
}
/** @brief verifies that the server is known
*
* @throws SshException on error
* @returns Integer value depending on the knowledge of the
* server key
*
* @returns Integer value depending on the knowledge of the server key
* @see ssh_session_update_known_hosts
*/
int isServerKnown(){
@@ -377,6 +436,7 @@ public:
}
/** @brief copies options from a session to another
*
* @throws SshException on error
* @see ssh_options_copy
*/
@@ -385,8 +445,11 @@ public:
return_throwable;
}
/** @brief parses a configuration file for options
*
* @throws SshException on error
*
* @param[in] file configuration file name
*
* @see ssh_options_parse_config
*/
void_throwable optionsParseConfig(const char *file){
@@ -399,8 +462,8 @@ public:
void silentDisconnect(){
ssh_silent_disconnect(c_session);
}
/** @brief Writes the known host file with current
* host key
/** @brief Writes the known host file with current host key
*
* @throws SshException on error
* @see ssh_write_knownhost
*/
@@ -411,11 +474,15 @@ public:
}
/** @brief accept an incoming forward connection
*
* @param[in] timeout_ms timeout for waiting, in ms
*
* @returns new Channel pointer on the forward connection
* @returns NULL in case of error
*
* @warning you have to delete this pointer after use
* @see ssh_channel_forward_accept
*
* @see Session::listenForward
*/
inline Channel *acceptForward(int timeout_ms);
@@ -439,6 +506,9 @@ public:
}
protected:
/** @internal
* @brief Underlying libssh session handle.
*/
ssh_session c_session;
private:
@@ -447,8 +517,7 @@ private:
Session& operator=(const Session &);
};
/** @brief the ssh::Channel class describes the state of an SSH
* channel.
/** @brief the ssh::Channel class describes the state of an SSH channel.
* @see ssh_channel
*/
class Channel {
@@ -464,11 +533,15 @@ public:
}
/** @brief accept an incoming X11 connection
*
* @param[in] timeout_ms timeout for waiting, in ms
*
* @returns new Channel pointer on the X11 connection
* @returns NULL in case of error
*
* @warning you have to delete this pointer after use
* @see ssh_channel_accept_x11
*
* @see Channel::requestX11
*/
Channel *acceptX11(int timeout_ms){
@@ -478,8 +551,10 @@ public:
return newchan;
}
/** @brief change the size of a pseudoterminal
*
* @param[in] cols number of columns
* @param[in] rows number of rows
*
* @throws SshException on error
* @see ssh_channel_change_pty_size
*/
@@ -490,6 +565,7 @@ public:
}
/** @brief closes a channel
*
* @throws SshException on error
* @see ssh_channel_close
*/
@@ -536,6 +612,21 @@ public:
bool isOpen(){
return ssh_channel_is_open(channel) != 0;
}
/** @brief Open a TCP forward channel.
*
* @param[in] remotehost Remote host to connect to.
* @param[in] remoteport Remote port to connect to.
* @param[in] sourcehost Source address to report (can be NULL depending on
* server policy).
* @param[in] localport Local port to report (0 lets the server pick if
* applicable).
*
* @returns `SSH_OK` (0) on success.
* @returns `SSH_ERROR` on error (no-exception builds).
*
* @throws SshException on error (exception-enabled builds).
* @see ssh_channel_open_forward
*/
int openForward(const char *remotehost, int remoteport,
const char *sourcehost, int localport=0){
int err=ssh_channel_open_forward(channel,remotehost,remoteport,
@@ -549,20 +640,55 @@ public:
ssh_throw(err);
return_throwable;
}
int poll(bool is_stderr=false){
int err=ssh_channel_poll(channel,is_stderr);
ssh_throw(err);
return err;
/** @brief Poll the channel for available data.
*
* @param[in] is_stderr If true, poll stderr stream; otherwise stdout.
*
* @returns Number of bytes available to read (>= 0).
* @returns `SSH_ERROR` on error (no-exception builds).
*
* @throws SshException on error (exception-enabled builds).
* @see ssh_channel_poll
*/
int poll(bool is_stderr = false)
{
int err = ssh_channel_poll(channel, is_stderr);
ssh_throw(err);
return err;
}
int read(void *dest, size_t count){
int err;
/* handle int overflow */
if(count > 0x7fffffff)
count = 0x7fffffff;
err=ssh_channel_read_timeout(channel,dest,count,false,-1);
ssh_throw(err);
return err;
/** @brief Read data from the channel (blocking).
*
* @param[out] dest Destination buffer.
* @param[in] count Maximum number of bytes to read.
*
* @returns Number of bytes read (>= 0). A return of 0 indicates EOF/no data.
* @returns `SSH_ERROR` on error (no-exception builds).
*
* @throws SshException on error (exception-enabled builds).
* @see ssh_channel_read_timeout
*/
int read(void *dest, size_t count)
{
int err;
if (count > 0x7fffffff)
count = 0x7fffffff;
err = ssh_channel_read_timeout(channel, dest, count, false, -1);
ssh_throw(err);
return err;
}
/** @brief Read data from the channel with a timeout.
*
* @param[out] dest Destination buffer.
* @param[in] count Maximum number of bytes to read.
* @param[in] timeout Timeout in milliseconds. A negative value means
* infinite timeout.
*
* @returns Number of bytes read (>= 0). A return value of 0 indicates EOF.
* @returns `SSH_ERROR` on error (no-exception builds).
*
* @throws SshException on error (exception-enabled builds).
* @see ssh_channel_read_timeout
*/
int read(void *dest, size_t count, int timeout){
int err;
/* handle int overflow */
@@ -572,6 +698,22 @@ public:
ssh_throw(err);
return err;
}
/** @brief Read data from the channel with optional stderr selection and
* timeout.
*
* @param[out] dest Destination buffer.
* @param[in] count Maximum number of bytes to read.
* @param[in] is_stderr If true, read from the stderr stream; otherwise
* read from stdout.
* @param[in] timeout Timeout in milliseconds. A negative value means
* infinite timeout.
*
* @returns Number of bytes read (>= 0). A return value of 0 indicates EOF.
* @returns `SSH_ERROR` on error (no-exception builds).
*
* @throws SshException on error (exception-enabled builds).
* @see ssh_channel_read_timeout
*/
int read(void *dest, size_t count, bool is_stderr=false, int timeout=-1){
int err;
/* handle int overflow */
@@ -581,6 +723,19 @@ public:
ssh_throw(err);
return err;
}
/** @brief Read data from the channel without blocking.
*
* @param[out] dest Destination buffer.
* @param[in] count Maximum number of bytes to read.
* @param[in] is_stderr If true, read from the stderr stream; otherwise read
* from stdout.
*
* @returns Number of bytes read (>= 0). A return of 0 may indicate no data.
* @returns `SSH_ERROR` on error (no-exception builds).
*
* @throws SshException on error (exception-enabled builds).
* @see ssh_channel_read_nonblocking
*/
int readNonblocking(void *dest, size_t count, bool is_stderr=false){
int err;
/* handle int overflow */
@@ -629,6 +784,18 @@ public:
ssh_throw(err);
return_throwable;
}
/** @brief Request X11 forwarding for this channel.
*
* @param[in] single_connection If true, allow only a single X11 connection
* for this channel; further X11 connections are
* refused after the first is accepted.
* @param[in] protocol X11 authentication protocol.
* @param[in] cookie X11 authentication cookie.
* @param[in] screen_number X11 screen number.
*
* @returns `SSH_OK` on success.
* @returns `SSH_ERROR` on error (no-exception builds).
*/
int requestX11(bool single_connection,
const char *protocol, const char *cookie, int screen_number){
int err=ssh_channel_request_x11(channel,single_connection,
@@ -641,11 +808,16 @@ public:
ssh_throw(err);
return_throwable;
}
/** @brief Writes on a channel
* @param data data to write.
* @param len number of bytes to write.
* @param is_stderr write should be done on the stderr channel (server only)
/**
* @brief Writes on a channel
*
* @param[in] data data to write.
* @param[in] len number of bytes to write.
* @param[in] is_stderr write should be done on the stderr channel (server
* only)
*
* @returns number of bytes written
*
* @throws SshException in case of error
* @see ssh_channel_write
* @see ssh_channel_write_stderr
@@ -670,7 +842,13 @@ public:
}
protected:
/** @internal
* @brief Parent session owning this channel.
*/
Session *session;
/** @internal
* @brief Underlying libssh channel handle.
*/
ssh_channel channel;
private:
@@ -683,7 +861,6 @@ private:
Channel &operator=(const Channel &);
};
inline Channel *Session::acceptForward(int timeout_ms){
ssh_channel forward =
ssh_channel_open_forward_port(c_session, timeout_ms, NULL, NULL, NULL);

View File

@@ -23,6 +23,11 @@
#include "config.h"
#include <stdint.h>
#include "libssh/callbacks.h"
#include "libssh/libssh.h"
struct ssh_auth_request {
char *username;
int method;

View File

@@ -21,8 +21,9 @@
#ifndef MISC_H_
#define MISC_H_
#ifdef _WIN32
#include "config.h"
#ifdef _WIN32
# ifdef _MSC_VER
# ifndef _SSIZE_T_DEFINED
# undef ssize_t
@@ -31,11 +32,13 @@
# define _SSIZE_T_DEFINED
# endif /* _SSIZE_T_DEFINED */
# endif /* _MSC_VER */
#else
#include <sys/types.h>
#include <stdbool.h>
#endif /* _WIN32 */
#include <stdio.h>
#include "libssh/libssh.h"
#ifdef __cplusplus
extern "C" {
@@ -43,8 +46,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);
@@ -57,8 +61,8 @@ int ssh_is_ipaddr(const char *str);
/* list processing */
struct ssh_list {
struct ssh_iterator *root;
struct ssh_iterator *end;
struct ssh_iterator *root;
struct ssh_iterator *end;
};
struct ssh_iterator {
@@ -66,9 +70,15 @@ struct ssh_iterator {
const void *data;
};
/**
* @brief Holds connection details for an SSH proxyjump host.
*/
struct ssh_jump_info_struct {
/** Hostname or IP address of the jump host. */
char *hostname;
/** Username to authenticate with on the jump host. */
char *username;
/** Port number of the jump host. */
int port;
};
@@ -136,6 +146,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

@@ -21,6 +21,13 @@
#ifndef _OPTIONS_H
#define _OPTIONS_H
#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include "libssh/libssh.h"
#ifdef __cplusplus
extern "C" {
#endif

View File

@@ -21,6 +21,9 @@
#ifndef PACKET_H_
#define PACKET_H_
#include "config.h"
#include "libssh/callbacks.h"
#include "libssh/wrapper.h"
struct ssh_socket_struct;

View File

@@ -130,14 +130,15 @@ extern "C" {
/* SSH Key Functions */
void ssh_key_clean (ssh_key key);
const char *
ssh_key_get_signature_algorithm(ssh_session session,
enum ssh_keytypes_e type);
enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name);
const char *ssh_key_get_signature_algorithm(ssh_session session,
enum ssh_keytypes_e type);
enum ssh_keytypes_e ssh_key_type_plain(enum ssh_keytypes_e type);
enum ssh_digest_e ssh_key_type_to_hash(ssh_session session,
enum ssh_keytypes_e type);
enum ssh_digest_e ssh_key_hash_from_name(const char *name);
int ssh_key_type_and_hash_from_signature_name(const char *name,
enum ssh_keytypes_e *type,
enum ssh_digest_e *hash_type);
#define is_ecdsa_key_type(t) \
((t) >= SSH_KEYTYPE_ECDSA_P256 && (t) <= SSH_KEYTYPE_ECDSA_P521)

View File

@@ -24,8 +24,12 @@
#include "config.h"
#ifdef HAVE_POLL
#include <poll.h>
#endif
#include "libssh/libssh.h"
#ifdef HAVE_POLL
typedef struct pollfd ssh_pollfd_t;
#else /* HAVE_POLL */

View File

@@ -5,6 +5,10 @@
#ifndef POLY1305_H
#define POLY1305_H
#include <stddef.h>
#include <stdint.h>
#include "libssh/chacha20-poly1305-common.h"
#ifdef __cplusplus

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

@@ -9,6 +9,8 @@
#ifndef SC25519_H
#define SC25519_H
#include <stdint.h>
#define sc25519 crypto_sign_ed25519_ref_sc25519
#define shortsc25519 crypto_sign_ed25519_ref_shortsc25519
#define sc25519_from32bytes crypto_sign_ed25519_ref_sc25519_from32bytes

View File

@@ -21,6 +21,13 @@
#ifndef _SCP_H
#define _SCP_H
#include "config.h"
#include <stddef.h>
#include <stdint.h>
#include "libssh/libssh.h"
enum ssh_scp_states {
SSH_SCP_NEW, //Data structure just created
SSH_SCP_WRITE_INITED, //Gave our intention to write

View File

@@ -35,6 +35,11 @@
extern "C" {
#endif
/**
* @brief Options for configuring an SSH server bind session.
*
* Used with ssh_bind_options_set() to configure server-side options.
*/
enum ssh_bind_options_e {
SSH_BIND_OPTIONS_BINDADDR,
SSH_BIND_OPTIONS_BINDPORT,
@@ -102,12 +107,31 @@ LIBSSH_API int ssh_bind_options_set(ssh_bind sshbind,
LIBSSH_API int ssh_bind_options_parse_config(ssh_bind sshbind,
const char *filename);
LIBSSH_API int ssh_bind_config_parse_string(ssh_bind bind, const char *input);
/**
* @brief Start listening to the socket.
*
* @param ssh_bind_o The ssh server bind to use.
*
* @return 0 on success, < 0 on error.
*
* @warning This function implicitly calls ssh_bind_options_parse_config()
* to process system-wide and user configuration files unless
* configuration processing was already performed explicitly
* by the caller.\n
* As a result, any options previously set (e.g., manually via
* ssh_bind_options_set() or ssh_bind_config_parse_string()) may be
* overridden by values from the configuration files.\n
* To guarantee that explicitly set options take precedence,
* callers of this function should either:
* - call ssh_bind_options_parse_config() themselves before
* setting options, or
* - disable automatic config processing via
* SSH_BIND_OPTIONS_PROCESS_CONFIG (set to false).
*
* @see ssh_bind_options_parse_config()
* @see ssh_bind_options_set()
*/
LIBSSH_API int ssh_bind_listen(ssh_bind ssh_bind_o);

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;
@@ -283,6 +286,9 @@ struct ssh_session_struct {
int control_master;
char *control_path;
int address_family;
char *originalhost; /* user-supplied host for config matching */
bool config_hostname_only; /* config hostname path: update host only,
not originalhost */
} opts;
/* server options */

View File

@@ -64,6 +64,7 @@ extern "C" {
#endif /* _MSC_VER */
#endif /* _WIN32 */
/** @brief The SFTP protocol version implemented by this library. */
#define LIBSFTP_VERSION 3
typedef struct sftp_attributes_struct* sftp_attributes;
@@ -90,10 +91,76 @@ typedef struct sftp_request_queue_struct* sftp_request_queue;
* @see sftp_free
*/
typedef struct sftp_session_struct* sftp_session;
/**
* @brief Handle for an SFTP status message received from the server.
*
* This type represents a status response returned by the SFTP server in
* reply to a client request. It carries a numeric status code and an optional
* human-readable error message. This type is used internally by libssh and
* is not part of the public API.
*
* @see sftp_status_message_struct
*/
typedef struct sftp_status_message_struct* sftp_status_message;
/**
* @brief Handle for SFTP file system statistics.
*
* This type represents file system statistics as reported by the SFTP server,
* analogous to the POSIX @c statvfs structure. It is obtained via
* sftp_statvfs() or sftp_fstatvfs() and must be freed with
* sftp_statvfs_free().
*
* @see sftp_statvfs_struct
* @see sftp_statvfs
* @see sftp_fstatvfs
* @see sftp_statvfs_free
*/
typedef struct sftp_statvfs_struct* sftp_statvfs_t;
/**
* @brief Handle for SFTP server limits information.
*
* This type represents the server-reported SFTP limits such as the maximum
* packet, read, and write lengths and the maximum number of open handles.
* It is obtained via sftp_limits() and must be freed with sftp_limits_free().
*
* @see sftp_limits_struct
* @see sftp_limits
* @see sftp_limits_free
*/
typedef struct sftp_limits_struct* sftp_limits_t;
/**
* @brief Handle for an asynchronous SFTP I/O operation.
*
* This type represents an in-flight asynchronous SFTP read or write request.
* It is allocated by sftp_aio_begin_read() or sftp_aio_begin_write() and
* consumed by the corresponding sftp_aio_wait_read() or sftp_aio_wait_write()
* call. If the wait call is not reached, the handle must be freed explicitly
* with sftp_aio_free() to avoid memory leaks.
*
* @see sftp_aio_begin_read
* @see sftp_aio_wait_read
* @see sftp_aio_begin_write
* @see sftp_aio_wait_write
* @see sftp_aio_free
*/
typedef struct sftp_aio_struct* sftp_aio;
/**
* @brief Handle for an SFTP name-to-id mapping.
*
* This type stores a mapping between numeric user or group IDs and their
* corresponding names. It is allocated via sftp_name_id_map_new(), populated
* by sftp_get_users_groups_by_id(), and freed with sftp_name_id_map_free().
*
* @see sftp_name_id_map_struct
* @see sftp_name_id_map_new
* @see sftp_get_users_groups_by_id
* @see sftp_name_id_map_free
*/
typedef struct sftp_name_id_map_struct *sftp_name_id_map;
struct sftp_session_struct {
@@ -177,28 +244,113 @@ struct sftp_status_message_struct {
char *langmsg;
};
/**
* @brief SFTP file attributes structure.
*
* This type represents file attributes. It is used both for
* sending and receiving file attributes from the server.
*
* `flags` determines which of the struct fields are present.
*
* @see sftp_attributes_free()
*/
struct sftp_attributes_struct {
/** File name */
char *name;
char *longname; /* ls -l output on openssh, not reliable else */
/** Extended name i.e output of `ls -l` (requires SFTP v3 with OpenSSH) */
char *longname;
/** Determines which of the struct fields are present */
uint32_t flags;
/** File type */
uint8_t type;
/** File size (requires flag `SSH_FILEXFER_ATTR_SIZE`) */
uint64_t size;
/** User ID (requires SFTP v3 with flag `SSH_FILEXFER_ATTR_UIDGID`) */
uint32_t uid;
/** Group ID (requires SFTP v3 with flag `SSH_FILEXFER_ATTR_UIDGID`) */
uint32_t gid;
char *owner; /* set if openssh and version 4 */
char *group; /* set if openssh and version 4 */
/**
* File owner
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_OWNERGROUP`
* or SFTP v3 with OpenSSH)
*/
char *owner;
/**
* File group
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_OWNERGROUP`
* or SFTP v3 with OpenSSH)
*/
char *group;
/** File permissions (requires flag `SSH_FILEXFER_ATTR_PERMISSIONS`) */
uint32_t permissions;
/**
* Access time
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_ACCESSTIME`)
*/
uint64_t atime64;
/**
* Access time
* (requires SFTP v3 with flag `SSH_FILEXFER_ATTR_ACMODTIME`)
*/
uint32_t atime;
/**
* Access time nanoseconds
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_SUBSECOND_TIMES`)
*/
uint32_t atime_nseconds;
/**
* Creation time
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_CREATETIME`)
*/
uint64_t createtime;
/**
* Creation time nanoseconds
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_SUBSECOND_TIMES`)
*/
uint32_t createtime_nseconds;
/**
* Modification time
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_MODIFYTIME`)
*/
uint64_t mtime64;
/**
* Modification time
* (requires SFTP v3 with flag `SSH_FILEXFER_ATTR_ACMODTIME`)
*/
uint32_t mtime;
/**
* Modification time nanoseconds
* (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_SUBSECOND_TIMES`)
*/
uint32_t mtime_nseconds;
/** ACL data (requires SFTP v4 with flag `SSH_FILEXFER_ATTR_ACL`) */
ssh_string acl;
/** Unused */
uint32_t extended_count;
/** Unused */
ssh_string extended_type;
/** Unused */
ssh_string extended_data;
};
@@ -206,27 +358,55 @@ struct sftp_attributes_struct {
* @brief SFTP statvfs structure.
*/
struct sftp_statvfs_struct {
uint64_t f_bsize; /** file system block size */
uint64_t f_frsize; /** fundamental fs block size */
uint64_t f_blocks; /** number of blocks (unit f_frsize) */
uint64_t f_bfree; /** free blocks in file system */
uint64_t f_bavail; /** free blocks for non-root */
uint64_t f_files; /** total file inodes */
uint64_t f_ffree; /** free file inodes */
uint64_t f_favail; /** free file inodes for to non-root */
uint64_t f_fsid; /** file system id */
uint64_t f_flag; /** bit mask of f_flag values */
uint64_t f_namemax; /** maximum filename length */
/** file system block size */
uint64_t f_bsize;
/** fundamental fs block size */
uint64_t f_frsize;
/** number of blocks (unit f_frsize) */
uint64_t f_blocks;
/** free blocks in file system */
uint64_t f_bfree;
/** free blocks for non-root */
uint64_t f_bavail;
/** total file inodes */
uint64_t f_files;
/** free file inodes */
uint64_t f_ffree;
/** free file inodes for non-root */
uint64_t f_favail;
/** file system id */
uint64_t f_fsid;
/** bit mask of f_flag values */
uint64_t f_flag;
/** maximum filename length */
uint64_t f_namemax;
};
/**
* @brief SFTP limits structure.
*/
struct sftp_limits_struct {
uint64_t max_packet_length; /** maximum number of bytes in a single sftp packet */
uint64_t max_read_length; /** maximum length in a SSH_FXP_READ packet */
uint64_t max_write_length; /** maximum length in a SSH_FXP_WRITE packet */
uint64_t max_open_handles; /** maximum number of active handles allowed by server */
/** maximum number of bytes in a single sftp packet */
uint64_t max_packet_length;
/** maximum length in a SSH_FXP_READ packet */
uint64_t max_read_length;
/** maximum length in a SSH_FXP_WRITE packet */
uint64_t max_write_length;
/** maximum number of active handles allowed by server */
uint64_t max_open_handles;
};
/**
@@ -279,7 +459,6 @@ LIBSSH_API sftp_session sftp_new(ssh_session session);
*/
LIBSSH_API sftp_session sftp_new_channel(ssh_session session, ssh_channel channel);
/**
* @brief Close and deallocate a sftp session.
*
@@ -1469,7 +1648,9 @@ LIBSSH_API void sftp_handle_remove(sftp_session sftp, void *handle);
#define SFTP_EXTENDED SSH_FXP_EXTENDED
/* openssh flags */
/** @brief statvfs flag: file system is mounted read-only. */
#define SSH_FXE_STATVFS_ST_RDONLY 0x1 /* read-only */
/** @brief statvfs flag: file system does not support setuid/setgid. */
#define SSH_FXE_STATVFS_ST_NOSUID 0x2 /* no setuid */
#ifdef __cplusplus

View File

@@ -21,7 +21,12 @@
#ifndef SFTP_PRIV_H
#define SFTP_PRIV_H
#include "config.h"
#include <stdbool.h>
#include <stdint.h>
#include "libssh/sftp.h"
#ifdef __cplusplus
extern "C" {

View File

@@ -43,17 +43,35 @@ extern "C" {
* @{
*/
/**
* @brief Macro to declare an SFTP message callback function.
*
* @param name The name of the callback function to declare.
*/
#define SSH_SFTP_CALLBACK(name) \
static int name(sftp_client_message message)
/**
* @brief Callback for handling SFTP client messages.
*
* @param message The SFTP client message to handle.
*
* @return 0 on success, -1 on error.
*/
typedef int (*sftp_server_message_callback)(sftp_client_message message);
/**
* @brief Maps an SFTP message type to its handler callback.
*/
struct sftp_message_handler
{
/** The name of the SFTP operation (e.g. "read", "write"). */
const char *name;
/** The extended operation name for SSH_FXP_EXTENDED requests, or NULL. */
const char *extended_name;
/** The SFTP message type code (e.g. SSH_FXP_READ). */
uint8_t type;
/** The callback function to invoke for this message type. */
sftp_server_message_callback cb;
};
@@ -61,6 +79,7 @@ LIBSSH_API int sftp_channel_default_subsystem_request(ssh_session session,
ssh_channel channel,
const char *subsystem,
void *userdata);
LIBSSH_API int sftp_channel_default_data_callback(ssh_session session,
ssh_channel channel,
void *data,

View File

@@ -21,13 +21,14 @@
#ifndef WRAPPER_H_
#define WRAPPER_H_
#include "config.h"
#include <stdbool.h>
#include "config.h"
#include "libssh/libssh.h"
#include "libssh/libcrypto.h"
#include "libssh/libgcrypt.h"
#include "libssh/libmbedcrypto.h"
#include "libssh/libssh.h"
#ifdef __cplusplus
extern "C" {

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

@@ -635,3 +635,91 @@ ssh_string ssh_agent_sign_data(ssh_session session,
return sig_blob;
}
int ssh_agent_remove_identity(ssh_session session,
const ssh_key key)
{
ssh_buffer request = NULL;
ssh_buffer reply = NULL;
ssh_string key_blob = NULL;
uint8_t type = 0;
int rc = SSH_ERROR;
if (session == NULL || key == NULL) {
return SSH_ERROR;
}
if (session->agent == NULL) {
ssh_set_error(session,
SSH_FATAL,
"No agent connection available");
return SSH_ERROR;
}
/* Connect to the agent if not already connected */
if (!ssh_socket_is_open(session->agent->sock)) {
if (agent_connect(session) < 0) {
ssh_set_error(session,
SSH_FATAL,
"Could not connect to SSH agent");
return SSH_ERROR;
}
}
request = ssh_buffer_new();
if (request == NULL) {
ssh_set_error_oom(session);
goto fail;
}
if (ssh_buffer_add_u8(request, SSH2_AGENTC_REMOVE_IDENTITY) < 0) {
ssh_set_error_oom(session);
goto fail;
}
if (ssh_pki_export_pubkey_blob(key, &key_blob) < 0) {
ssh_set_error(session, SSH_FATAL, "Failed to export public key blob");
goto fail;
}
if (ssh_buffer_add_ssh_string(request, key_blob) < 0) {
ssh_set_error_oom(session);
goto fail;
}
reply = ssh_buffer_new();
if (reply == NULL) {
ssh_set_error_oom(session);
goto fail;
}
if (agent_talk(session, request, reply) < 0) {
goto fail;
}
if (ssh_buffer_get_u8(reply, &type) != sizeof(uint8_t)) {
ssh_set_error(session,
SSH_FATAL,
"Failed to read agent reply type");
goto fail;
}
if (agent_failed(type)) {
SSH_LOG(SSH_LOG_DEBUG, "Agent reports failure removing identity");
goto fail;
} else if (type != SSH_AGENT_SUCCESS) {
ssh_set_error(session,
SSH_FATAL,
"Agent refused to remove identity: reply type %u",
type);
goto fail;
}
rc = SSH_OK;
fail:
SSH_STRING_FREE(key_blob);
SSH_BUFFER_FREE(request);
SSH_BUFFER_FREE(reply);
return rc;
}

View File

@@ -1397,7 +1397,7 @@ int ssh_userauth_publickey_auto(ssh_session session,
return SSH_AUTH_ERROR;
}
SSH_LOG(SSH_LOG_INFO,
SSH_LOG(SSH_LOG_DEBUG,
"Starting authentication as a user %s",
username ? username : session->opts.username);
@@ -1835,6 +1835,14 @@ int ssh_userauth_agent_pubkey(ssh_session session,
return rc;
}
/**
* @internal
*
* @brief Allocates memory for keyboard interactive auth structure.
*
* @return A newly allocated ssh_kbdint structure `kbd` on success, NULL on failure.
* The caller is responsible for freeing allocated memory.
*/
ssh_kbdint ssh_kbdint_new(void)
{
ssh_kbdint kbd;
@@ -1847,7 +1855,11 @@ ssh_kbdint ssh_kbdint_new(void)
return kbd;
}
/**
* @brief Deallocate memory for keyboard interactive auth structure.
*
* @param[in] kbd The keyboard interactive structure to free.
*/
void ssh_kbdint_free(ssh_kbdint kbd)
{
size_t i, n;
@@ -1885,6 +1897,14 @@ void ssh_kbdint_free(ssh_kbdint kbd)
SAFE_FREE(kbd);
}
/**
* @brief Clean a keyboard interactive auth structure.
*
* Clears structure's fields and resets nanswers and nprompts to 0, allowing
* reuse.
*
* @param[in] kbd The keyboard interactive struct to clean
*/
void ssh_kbdint_clean(ssh_kbdint kbd)
{
size_t i, n;

View File

@@ -327,7 +327,7 @@ static int ssh_bind_poll_callback(ssh_poll_handle sshpoll, socket_t fd, int reve
/** @internal
* @brief returns the current poll handle, or creates it
* @param sshbind the ssh_bind object
* @returns a ssh_poll handle suitable for operation
* @return a ssh_poll handle suitable for operation
*/
ssh_poll_handle ssh_bind_get_poll(ssh_bind sshbind)
{

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;
}
@@ -676,12 +676,21 @@ int ssh_bind_config_parse_file(ssh_bind bind, const char *filename)
return 0;
}
/* @brief Parse configuration string and set the options to the given bind session
/**
* @brief Parse configuration string and set the options to the given bind
* session
*
* @params[in] bind The ssh bind session
* @params[in] input Null terminated string containing the configuration
* @param[in] bind The ssh bind session
* @param[in] input Null terminated string containing the configuration
*
* @returns SSH_OK on successful parsing the configuration string,
* @warning Options set via this function may be overridden if a configuration
* file is parsed afterwards (e.g., by an implicit call to
* ssh_bind_options_parse_config() inside ssh_bind_listen(), or by a
* manual call to the same function) and contains the same options.\n
* It is the callers responsibility to ensure the correct order of
* API calls if explicit options must take precedence.
*
* @return SSH_OK on successful parsing the configuration string,
* SSH_ERROR on error
*/
int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
@@ -713,21 +722,29 @@ int ssh_bind_config_parse_string(ssh_bind bind, const char *input)
}
if (c == NULL) {
/* should not happen, would mean a string without trailing '\0' */
SSH_LOG(SSH_LOG_WARN, "No trailing '\\0' in config string");
ssh_set_error(bind,
SSH_FATAL,
"No trailing '\\0' in config string");
return SSH_ERROR;
}
line_len = c - line_start;
if (line_len > MAX_LINE_SIZE - 1) {
SSH_LOG(SSH_LOG_WARN,
"Line %u too long: %zu characters",
line_num,
line_len);
ssh_set_error(bind,
SSH_FATAL,
"Line %u too long: %zu characters",
line_num,
line_len);
return SSH_ERROR;
}
memcpy(line, line_start, line_len);
line[line_len] = '\0';
SSH_LOG(SSH_LOG_DEBUG, "Line %u: %s", line_num, line);
rv = ssh_bind_config_parse_line(bind, line, line_num, &parser_flags, seen, 0);
rv = ssh_bind_config_parse_line(bind,
line,
line_num,
&parser_flags,
seen,
0);
if (rv < 0) {
return SSH_ERROR;
}

View File

@@ -109,6 +109,11 @@ static void buffer_verify(ssh_buffer buf)
}
#else
/** @internal
* @brief No-op stub for buffer_verify when debug checks are disabled.
*
* @param x The buffer to verify (ignored).
*/
#define buffer_verify(x)
#endif
@@ -921,18 +926,10 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
va_arg(ap, void *);
count++; /* increase argument count */
break;
case 'F':
case 'B':
b = va_arg(ap, bignum);
if (*p == 'F') {
/* For padded bignum, we know the exact length */
len = va_arg(ap, size_t);
count++; /* increase argument count */
needed_size += sizeof(uint32_t) + len;
} else {
/* The bignum bytes + 1 for possible padding */
needed_size += sizeof(uint32_t) + bignum_num_bytes(b) + 1;
}
/* The bignum bytes + 1 for possible padding */
needed_size += sizeof(uint32_t) + bignum_num_bytes(b) + 1;
break;
case 't':
cstring = va_arg(ap, char *);
@@ -972,9 +969,12 @@ static int ssh_buffer_pack_allocate_va(struct ssh_buffer_struct *buffer,
/** @internal
* @brief Add multiple values in a buffer on a single function call
*
* @param[in] buffer The buffer to add to
* @param[in] format A format string of arguments.
* @param[in] argc Number of arguments passed after format.
* @param[in] ap A va_list of arguments.
*
* @returns SSH_OK on success
* SSH_ERROR on error
* @see ssh_buffer_add_format() for format list values.
@@ -1062,16 +1062,9 @@ ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
rc = ssh_buffer_add_data(buffer, o.data, (uint32_t)len);
o.data = NULL;
break;
case 'F':
case 'B':
b = va_arg(ap, bignum);
if (*p == 'F') {
len = va_arg(ap, size_t);
count++; /* increase argument count */
o.string = ssh_make_padded_bignum_string(b, len);
} else {
o.string = ssh_make_bignum_string(b);
}
o.string = ssh_make_bignum_string(b);
if(o.string == NULL){
rc = SSH_ERROR;
break;
@@ -1127,8 +1120,9 @@ ssh_buffer_pack_va(struct ssh_buffer_struct *buffer,
* 'P': size_t, void * (len of data, pointer to data)
* only pushes data.
* 'B': bignum (pushed as SSH string)
* 'F': bignum, size_t (bignum, padded to fixed length,
* pushed as SSH string)
* @param[in] argc Number of arguments passed after format.
* @param[in] ... Arguments as described by the format string.
*
* @returns SSH_OK on success
* SSH_ERROR on error
* @warning when using 'P' with a constant size (e.g. 8), do not
@@ -1165,7 +1159,9 @@ int _ssh_buffer_pack(struct ssh_buffer_struct *buffer,
* @brief Get multiple values from a buffer on a single function call
* @param[in] buffer The buffer to get from
* @param[in] format A format string of arguments.
* @param[in] argc Number of arguments passed in the va_list.
* @param[in] ap A va_list of arguments.
*
* @returns SSH_OK on success
* SSH_ERROR on error
* @see ssh_buffer_get_format() for format list values.
@@ -1428,6 +1424,9 @@ cleanup:
* 'P': size_t, void ** (len of data, pointer to data)
* only pulls data.
* 'B': bignum * (pulled as SSH string)
* @param[in] argc Number of arguments passed after format.
* @param[in] ... Arguments as described by the format string.
*
* @returns SSH_OK on success
* SSH_ERROR on error
* @warning when using 'P' with a constant size (e.g. 8), do not

View File

@@ -1738,7 +1738,7 @@ int ssh_channel_write(ssh_channel channel, const void *data, uint32_t len)
*/
int ssh_channel_is_open(ssh_channel channel)
{
if (channel == NULL) {
if (channel == NULL || channel->session == NULL) {
return 0;
}
return (channel->state == SSH_CHANNEL_STATE_OPEN && channel->session->alive != 0);
@@ -3517,7 +3517,7 @@ int ssh_channel_get_exit_state(ssh_channel channel,
*pexit_signal = NULL;
if (channel->exit.signal != NULL) {
*pexit_signal = strdup(channel->exit.signal);
if (pexit_signal == NULL) {
if (*pexit_signal == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}

View File

@@ -582,9 +582,8 @@ int ssh_connect(ssh_session session)
session->client = 1;
if (session->opts.fd == SSH_INVALID_SOCKET &&
session->opts.host == NULL &&
session->opts.ProxyCommand == NULL)
{
session->opts.originalhost == NULL &&
session->opts.ProxyCommand == NULL) {
ssh_set_error(session, SSH_FATAL, "Hostname required");
return SSH_ERROR;
}

View File

@@ -256,9 +256,10 @@ 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",
SSH_LOG(SSH_LOG_RARE,
"Failed to open included configuration file %s",
filename);
return;
}
@@ -493,6 +494,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 +514,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, ',');
@@ -528,6 +544,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
&jump_host->username,
&jump_host->hostname,
&port,
false,
false);
if (rv != SSH_OK) {
ssh_set_error_invalid(session);
@@ -550,7 +567,12 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
} else if (parse_entry) {
/* We actually care only about the first item */
rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
rv = ssh_config_parse_uri(cp,
&username,
&hostname,
&port,
false,
false);
if (rv != SSH_OK) {
ssh_set_error_invalid(session);
goto out;
@@ -566,7 +588,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
}
} else {
/* The rest is just sanity-checked to avoid failures later */
rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false, false);
if (rv != SSH_OK) {
ssh_set_error_invalid(session);
goto out;
@@ -1024,20 +1046,20 @@ static int ssh_config_parse_line_internal(ssh_session session,
break;
case MATCH_ORIGINALHOST:
/* Skip one argument */
/* Here we match only one argument */
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL || p[0] == '\0') {
SSH_LOG(SSH_LOG_TRACE, "line %d: Match keyword "
"'%s' requires argument", count, p2);
ssh_set_error(session,
SSH_FATAL,
"line %d: ERROR - Match originalhost keyword "
"requires argument",
count);
SAFE_FREE(x);
return -1;
}
result &=
ssh_config_match(session->opts.originalhost, p, negate);
args++;
SSH_LOG(SSH_LOG_TRACE,
"line %d: Unsupported Match keyword '%s', ignoring",
count,
p2);
result = 0;
break;
case MATCH_HOST:
@@ -1050,7 +1072,11 @@ static int ssh_config_parse_line_internal(ssh_session session,
SAFE_FREE(x);
return -1;
}
result &= ssh_config_match(session->opts.host, p, negate);
result &= ssh_config_match(session->opts.host
? session->opts.host
: session->opts.originalhost,
p,
negate);
args++;
break;
@@ -1138,7 +1164,9 @@ static int ssh_config_parse_line_internal(ssh_session session,
int ok = 0, result = -1;
*parsing = 0;
lowerhost = (session->opts.host) ? ssh_lowercase(session->opts.host) : NULL;
lowerhost = (session->opts.originalhost)
? ssh_lowercase(session->opts.originalhost)
: NULL;
for (p = ssh_config_get_str_tok(&s, NULL);
p != NULL && p[0] != '\0';
p = ssh_config_get_str_tok(&s, NULL)) {
@@ -1165,7 +1193,9 @@ static int ssh_config_parse_line_internal(ssh_session session,
if (z == NULL) {
z = strdup(p);
}
session->opts.config_hostname_only = true;
ssh_options_set(session, SSH_OPTIONS_HOST, z);
session->opts.config_hostname_only = false;
free(z);
}
break;
@@ -1654,11 +1684,12 @@ int ssh_config_parse_line_cli(ssh_session session, const char *line)
true);
}
/* @brief Parse configuration from a file pointer
/**
* @brief Parse configuration from a file pointer
*
* @params[in] session The ssh session
* @params[in] fp A valid file pointer
* @params[in] global Whether the config is global or not
* @param[in] session The ssh session
* @param[in] fp A valid file pointer
* @param[in] global Whether the config is global or not
*
* @returns 0 on successful parsing the configuration file, -1 on error
*/
@@ -1680,10 +1711,11 @@ int ssh_config_parse(ssh_session session, FILE *fp, bool global)
return 0;
}
/* @brief Parse configuration file and set the options to the given session
/**
* @brief Parse configuration file and set the options to the given session
*
* @params[in] session The ssh session
* @params[in] filename The path to the ssh configuration file
* @param[in] session The ssh session
* @param[in] filename The path to the ssh configuration file
*
* @returns 0 on successful parsing the configuration file, -1 on error
*/
@@ -1693,8 +1725,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;
}
@@ -1717,10 +1750,11 @@ int ssh_config_parse_file(ssh_session session, const char *filename)
return rv;
}
/* @brief Parse configuration string and set the options to the given session
/**
* @brief Parse configuration string and set the options to the given session
*
* @params[in] session The ssh session
* @params[in] input Null terminated string containing the configuration
* @param[in] session The ssh session
* @param[in] input Null terminated string containing the configuration
*
* @returns SSH_OK on successful parsing the configuration string,
* SSH_ERROR on error

View File

@@ -172,7 +172,8 @@ int ssh_config_parse_uri(const char *tok,
char **username,
char **hostname,
char **port,
bool ignore_port)
bool ignore_port,
bool strict)
{
char *endp = NULL;
long port_n;
@@ -243,13 +244,31 @@ int ssh_config_parse_uri(const char *tok,
if (*hostname == NULL) {
goto error;
}
/* if not an ip, check syntax */
rc = ssh_is_ipaddr(*hostname);
if (rc == 0) {
rc = ssh_check_hostname_syntax(*hostname);
if (rc != SSH_OK) {
if (strict) {
/* if not an ip, check syntax */
rc = ssh_is_ipaddr(*hostname);
if (rc == 0) {
rc = ssh_check_hostname_syntax(*hostname);
if (rc != SSH_OK) {
goto error;
}
}
} else {
/* Reject shell metacharacters to allow config aliases with
* non-RFC1035 chars (e.g. %, _). Modeled on OpenSSH's
* valid_hostname() in ssh.c. */
const char *c = NULL;
if ((*hostname)[0] == '-') {
goto error;
}
for (c = *hostname; *c != '\0'; c++) {
char *is_meta = strchr("'`\"$\\;&<>|(){},", *c);
int is_space = isspace((unsigned char)*c);
int is_ctrl = iscntrl((unsigned char)*c);
if (is_meta != NULL || is_space || is_ctrl) {
goto error;
}
}
}
}
/* Skip also the closing bracket */

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;
}
@@ -566,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;
@@ -642,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

@@ -43,11 +43,9 @@
* @brief Registers an error with a description.
*
* @param error The place to store the error.
*
* @param code The class of error.
*
* @param function The name of the calling function.
* @param descr The description, which can be a format string.
*
* @param ... The arguments for the format string.
*/
void _ssh_set_error(void *error,
@@ -76,6 +74,7 @@ void _ssh_set_error(void *error,
* @brief Registers an out of memory error
*
* @param error The place to store the error.
* @param function The name of the calling function.
*
*/
void _ssh_set_error_oom(void *error, const char *function)

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);
@@ -251,6 +243,7 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
/* Get the server supported oids */
rc = ssh_gssapi_server_oids(&supported);
if (rc != SSH_OK) {
gss_release_oid_set(&min_stat, &both_supported);
return SSH_ERROR;
}
@@ -284,7 +277,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);
@@ -729,7 +731,8 @@ int ssh_gssapi_check_client_config(ssh_session session)
gssapi = calloc(1, sizeof(struct ssh_gssapi_struct));
if (gssapi == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
ret = SSH_ERROR;
break;
}
gssapi->server_creds = GSS_C_NO_CREDENTIAL;
gssapi->client_creds = GSS_C_NO_CREDENTIAL;
@@ -818,6 +821,11 @@ int ssh_gssapi_check_client_config(ssh_session session)
gss_release_buffer(&min_stat, &output_token);
gss_delete_sec_context(&min_stat, &gssapi->ctx, GSS_C_NO_BUFFER);
if (client_id != GSS_C_NO_NAME) {
gss_release_name(&min_stat, &client_id);
client_id = GSS_C_NO_NAME;
}
SAFE_FREE(gssapi->canonic_user);
SAFE_FREE(gssapi);

View File

@@ -152,7 +152,7 @@ static int derive_hybrid_secret(ssh_session session,
rc = ssh_buffer_pack(combined_secret,
"PP",
MLKEM_SHARED_SECRET_SIZE,
(size_t)MLKEM_SHARED_SECRET_SIZE,
mlkem_shared_secret,
ssh_string_len(ecdh_shared_secret),
ssh_string_data(ecdh_shared_secret));
@@ -244,7 +244,7 @@ int ssh_client_hybrid_mlkem_init(ssh_session session)
"PP",
ssh_string_len(crypto->mlkem_client_pubkey),
ssh_string_data(crypto->mlkem_client_pubkey),
CURVE25519_PUBKEY_SIZE,
(size_t)CURVE25519_PUBKEY_SIZE,
crypto->curve25519_client_pubkey);
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
@@ -768,7 +768,7 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
"PP",
ssh_string_len(crypto->mlkem_ciphertext),
ssh_string_data(crypto->mlkem_ciphertext),
CURVE25519_PUBKEY_SIZE,
(size_t)CURVE25519_PUBKEY_SIZE,
crypto->curve25519_server_pubkey);
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:

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;
}
@@ -590,6 +591,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
if (!(ret_flags & GSS_C_INTEG_FLAG) || !(ret_flags & GSS_C_MUTUAL_FLAG)) {
SSH_LOG(SSH_LOG_WARN,
"GSSAPI(accept) integrity and mutual flags were not set");
gss_release_buffer(&min_stat, &output_token);
goto error;
}
SSH_LOG(SSH_LOG_DEBUG, "token accepted");
@@ -606,6 +608,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
"creating mic failed",
maj_stat,
min_stat);
gss_release_buffer(&min_stat, &output_token);
goto error;
}
@@ -620,15 +623,14 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
output_token.length,
(size_t)output_token.length,
output_token.value);
gss_release_buffer(&min_stat, &output_token);
gss_release_buffer(&min_stat, &mic);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
ssh_buffer_reinit(session->out_buffer);
goto error;
}
gss_release_buffer(&min_stat, &output_token);
gss_release_buffer(&min_stat, &mic);
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
goto error;

View File

@@ -1688,11 +1688,6 @@ int ssh_make_sessionid(ssh_session session)
switch (session->next_crypto->kex_type) {
case SSH_KEX_SNTRUP761X25519_SHA512:
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
rc = ssh_buffer_pack(buf,
"F",
session->next_crypto->shared_secret,
SHA512_DIGEST_LEN);
break;
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
@@ -1919,9 +1914,6 @@ int ssh_generate_session_keys(ssh_session session)
switch (session->next_crypto->kex_type) {
case SSH_KEX_SNTRUP761X25519_SHA512:
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
k_string = ssh_make_padded_bignum_string(crypto->shared_secret,
crypto->digest_len);
break;
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024

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

@@ -243,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",
@@ -310,7 +310,11 @@ static int ssh_known_hosts_read_entries(const char *match,
}
}
if (entry != NULL) {
ssh_list_append(*entries, entry);
rc = ssh_list_append(*entries, entry);
if (rc != SSH_OK) {
ssh_knownhosts_entry_free(entry);
goto error;
}
}
}

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);
@@ -769,6 +770,16 @@ ssh_string ssh_get_pubkey(ssh_session session)
****************************************************************************/
#ifdef WITH_SERVER
/**
* @brief Accept an incoming SSH connection on a bind socket.
*
* @deprecated Use ssh_bind_accept() instead.
*
* @param session The SSH session to accept the connection on.
*
* @return SSH_OK on success, SSH_ERROR on error.
*/
int ssh_accept(ssh_session session) {
return ssh_handle_key_exchange(session);
}

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,34 @@ 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;
LIBSSH_AFTER_4_11_0
{
global:
ssh_bind_config_parse_string;
} LIBSSH_4_11_0;

View File

@@ -109,6 +109,16 @@ static void ssh_log_custom(ssh_logging_callback log_fn,
log_fn(verbosity, function, buf, ssh_get_log_userdata());
}
/** @internal
* @brief Dispatch a pre-formatted log message to the active logging backend.
*
* If a custom logging callback is registered, the message is passed to it.
* Otherwise, the message is written to stderr.
*
* @param verbosity The verbosity level of the message.
* @param function The name of the calling function.
* @param buffer The already-formatted log message string.
*/
void ssh_log_function(int verbosity,
const char *function,
const char *buffer)
@@ -123,6 +133,15 @@ void ssh_log_function(int verbosity,
ssh_log_stderr(verbosity, function, buffer);
}
/** @internal
* @brief Format a log message from a va_list and dispatch it for logging.
*
* @param verbosity The verbosity level of the message.
* @param function The name of the calling function.
* @param format A printf-style format string.
* @param va Pointer to the variable argument list
* for the format string.
*/
void ssh_vlog(int verbosity,
const char *function,
const char *format,
@@ -134,6 +153,15 @@ void ssh_vlog(int verbosity,
ssh_log_function(verbosity, function, buffer);
}
/** @internal
* @brief Log a message if the given verbosity does not
* exceed the global log level.
*
* @param verbosity The verbosity level of the message.
* @param function The name of the calling function.
* @param format A printf-style format string.
* @param ... Additional arguments corresponding to the format string.
*/
void _ssh_log(int verbosity,
const char *function,
const char *format, ...)
@@ -149,6 +177,15 @@ void _ssh_log(int verbosity,
/* LEGACY */
/** @brief Log a message using the verbosity level of the given session.
*
* @deprecated Use the SSH_LOG() macro instead.
*
* @param session The SSH session whose verbosity level is checked.
* @param verbosity The verbosity level of the message.
* @param format A printf-style format string.
* @param ... Arguments as described by the format string.
*/
void ssh_log(ssh_session session,
int verbosity,
const char *format, ...)
@@ -164,9 +201,14 @@ void ssh_log(ssh_session session,
/** @internal
* @brief log a SSH event with a common pointer
* @param common The SSH/bind session.
* @param verbosity The verbosity of the event.
* @param format The format string of the log entry.
*
* Works for both ssh_session and ssh_bind as both embed ssh_common_struct.
*
* @param common The SSH/bind session.
* @param verbosity The verbosity of the event.
* @param function The name of the calling function.
* @param format The format string of the log entry.
* @param ... Additional arguments corresponding to the format string.
*/
void ssh_log_common(struct ssh_common_struct *common,
int verbosity,
@@ -221,6 +263,9 @@ int ssh_set_log_callback(ssh_logging_callback cb) {
return SSH_OK;
}
/** @internal
* @brief Clear the thread-local logging callback, reverting to stderr logging.
*/
void
_ssh_reset_log_cb(void)
{

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,25 +109,31 @@
*/
#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 */
/** @internal
* @brief Check whether the current process has read access to a file.
*
* @param[in] file Path to the file to check.
*
* @return 1 if the file is readable, 0 otherwise.
*/
int ssh_file_readaccess_ok(const char *file)
{
if (_access(file, 4) < 0) {
@@ -207,6 +214,12 @@ struct tm *ssh_localtime(const time_t *timer, struct tm *result)
return result;
}
/** @internal
* @brief Get the username of the currently running process.
*
* @return A newly allocated string with the username, or NULL on error.
* The caller is responsible for freeing it.
*/
char *ssh_get_local_username(void)
{
DWORD size = 0;
@@ -233,6 +246,13 @@ char *ssh_get_local_username(void)
return NULL;
}
/** @internal
* @brief Check whether a string is a valid IPv4 address.
*
* @param[in] str The string to check.
*
* @return 1 if the string is a valid IPv4 address, 0 otherwise.
*/
int ssh_is_ipaddr_v4(const char *str)
{
struct sockaddr_storage ss;
@@ -256,6 +276,13 @@ int ssh_is_ipaddr_v4(const char *str)
return 0;
}
/** @internal
* @brief Check whether a string is a valid IPv4 or IPv6 address.
*
* @param[in] str The string to check.
*
* @return 1 if valid IP address, 0 if not, -1 on memory error.
*/
int ssh_is_ipaddr(const char *str)
{
int rc = SOCKET_ERROR;
@@ -298,7 +325,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 +340,6 @@ char *ssh_get_user_home_dir(void)
return NULL;
}
snprintf(buf, sizeof(buf), "%s", szPath);
return strdup(buf);
}
@@ -322,7 +348,13 @@ char *ssh_get_user_home_dir(void)
return szPath;
}
/* we have read access on file */
/** @internal
* @brief Check whether the current process has read access to a file.
*
* @param[in] file Path to the file to check.
*
* @return 1 if the file is readable, 0 otherwise.
*/
int ssh_file_readaccess_ok(const char *file)
{
if (access(file, R_OK) < 0) {
@@ -357,6 +389,12 @@ int ssh_dir_writeable(const char *path)
return 0;
}
/** @internal
* @brief Get the username of the currently running process.
*
* @return A newly allocated string with the username, or NULL on error.
* The caller is responsible for freeing it.
*/
char *ssh_get_local_username(void)
{
struct passwd pwd;
@@ -381,6 +419,13 @@ char *ssh_get_local_username(void)
return name;
}
/** @internal
* @brief Check whether a string is a valid IPv4 address.
*
* @param[in] str The string to check.
*
* @return 1 if the string is a valid IPv4 address, 0 otherwise.
*/
int ssh_is_ipaddr_v4(const char *str)
{
int rc = -1;
@@ -394,6 +439,13 @@ int ssh_is_ipaddr_v4(const char *str)
return 0;
}
/** @internal
* @brief Check whether a string is a valid IPv4 or IPv6 address.
*
* @param[in] str The string to check.
*
* @return 1 if valid IP address, 0 if not, -1 on memory error.
*/
int ssh_is_ipaddr(const char *str)
{
int rc = -1;
@@ -428,6 +480,48 @@ int ssh_is_ipaddr(const char *str)
#endif /* _WIN32 */
/** @internal
* @brief Get the home directory of the current user.
*
* If a session is provided and a cached value exists, it is returned directly.
* Otherwise the home directory is looked up and cached in the session.
*
* @param[in] session The SSH session to cache the result in, or NULL.
*
* @return A newly allocated string with the home directory path, or NULL
* on error. The caller is responsible for freeing it.
*/
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;
}
/** @internal
* @brief Convert a string to lowercase.
*
* @param[in] str The string to convert.
*
* @return A newly allocated lowercase copy of the string, or NULL on error.
* The caller is responsible for freeing it.
*/
char *ssh_lowercase(const char* str)
{
char *new = NULL, *p = NULL;
@@ -448,6 +542,15 @@ char *ssh_lowercase(const char* str)
return new;
}
/** @internal
* @brief Format a host and port into a "[host]:port" string.
*
* @param[in] host The hostname or IP address.
* @param[in] port The port number.
*
* @return A newly allocated string of the form "[host]:port", or NULL
* on error. The caller is responsible for freeing it.
*/
char *ssh_hostport(const char *host, int port)
{
char *dest = NULL;
@@ -468,6 +571,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 +618,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);
}
/**
@@ -742,6 +856,11 @@ const char *ssh_version(int req_version)
return NULL;
}
/** @internal
* @brief Create a new empty linked list.
*
* @return A newly allocated ssh_list, or NULL on memory error.
*/
struct ssh_list *ssh_list_new(void)
{
struct ssh_list *ret = malloc(sizeof(struct ssh_list));
@@ -752,6 +871,13 @@ struct ssh_list *ssh_list_new(void)
return ret;
}
/** @internal
* @brief Free a linked list and all its iterator nodes.
*
* The data pointed to by each node is not freed.
*
* @param[in] list The list to free.
*/
void ssh_list_free(struct ssh_list *list)
{
struct ssh_iterator *ptr = NULL, *next = NULL;
@@ -766,6 +892,13 @@ void ssh_list_free(struct ssh_list *list)
SAFE_FREE(list);
}
/** @internal
* @brief Get the first iterator of a linked list.
*
* @param[in] list The list to iterate.
*
* @return Pointer to the first iterator, or NULL if the list is empty.
*/
struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list)
{
if (!list)
@@ -773,6 +906,14 @@ struct ssh_iterator *ssh_list_get_iterator(const struct ssh_list *list)
return list->root;
}
/** @internal
* @brief Find the iterator pointing to a specific value in the list.
*
* @param[in] list The list to search.
* @param[in] value The data pointer to find.
*
* @return The iterator pointing to the value, or NULL if not found.
*/
struct ssh_iterator *ssh_list_find(const struct ssh_list *list, void *value)
{
struct ssh_iterator *it = NULL;
@@ -848,6 +989,14 @@ int ssh_list_append(struct ssh_list *list, const void *data)
return SSH_OK;
}
/** @internal
* @brief Prepend an element at the beginning of the list.
*
* @param[in] list The list to prepend to.
* @param[in] data The element to prepend.
*
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
int ssh_list_prepend(struct ssh_list *list, const void *data)
{
struct ssh_iterator *it = NULL;
@@ -873,6 +1022,12 @@ int ssh_list_prepend(struct ssh_list *list, const void *data)
return SSH_OK;
}
/** @internal
* @brief Remove an element from the list by its iterator.
*
* @param[in] list The list to remove from.
* @param[in] iterator The iterator pointing to the element to remove.
*/
void ssh_list_remove(struct ssh_list *list, struct ssh_iterator *iterator)
{
struct ssh_iterator *ptr = NULL, *prev = NULL;
@@ -1189,7 +1344,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;
@@ -1211,15 +1366,125 @@ char *ssh_path_expand_tilde(const char *d)
return r;
}
/** @internal
* @brief Get the hostname of the local machine.
*
* @return A newly allocated string with the hostname, or NULL on error.
* The caller is responsible for freeing it.
*/
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] session The SSH session providing option values for expansion.
* @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().
*
@@ -1227,7 +1492,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;
@@ -1276,65 +1540,69 @@ 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 if (session->opts.originalhost) {
x = strdup(session->opts.originalhost);
} 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) {
@@ -1346,8 +1614,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);
@@ -1640,6 +1907,15 @@ void burn_free(void *ptr, size_t len)
}
#if !defined(HAVE_STRNDUP)
/** @internal
* @brief Compatibility implementation of strndup for systems that lack it.
*
* @param[in] s The string to duplicate.
* @param[in] n Maximum number of characters to copy.
*
* @return A newly allocated null-terminated string, or NULL on error.
*/
char *strndup(const char *s, size_t n)
{
char *x = NULL;
@@ -1660,7 +1936,11 @@ char *strndup(const char *s, size_t n)
}
#endif /* ! HAVE_STRNDUP */
/* Increment 64b integer in network byte order */
/** @internal
* @brief Increment a 64-bit counter stored in network byte order.
*
* @param[in,out] counter Pointer to an 8-byte buffer holding the counter.
*/
void
uint64_inc(unsigned char *counter)
{
@@ -2293,4 +2573,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_TRACE,
"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_TRACE,
"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_TRACE,
"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_TRACE,
"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_TRACE,
"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

@@ -23,6 +23,7 @@
*/
#include "config.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -106,6 +107,14 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
}
}
if (src->opts.originalhost != NULL) {
new->opts.originalhost = strdup(src->opts.originalhost);
if (new->opts.originalhost == NULL) {
ssh_free(new);
return -1;
}
}
if (src->opts.bindaddr != NULL) {
new->opts.bindaddr = strdup(src->opts.bindaddr);
if (new->opts.bindaddr == NULL) {
@@ -279,6 +288,20 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
return 0;
}
/** @internal
* @brief Set a key exchange algorithm list option on the session.
*
* Supports prefix modifiers: '+' to append, '-' to remove, '^' to prepend
* to the default algorithm list.
*
* @param[in] session The SSH session.
* @param[in] algo The algorithm type to configure.
* @param[in] list The algorithm list string.
* @param[out] place Pointer to the string to store
* the resulting algorithm list.
*
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
int ssh_options_set_algo(ssh_session session,
enum ssh_kex_types_e algo,
const char *list,
@@ -369,8 +392,8 @@ int ssh_options_set_algo(ssh_session session,
* default ssh directory.\n
* \n
* The ssh directory is used for files like known_hosts
* and identity (private and public key). It may include
* "%s" which will be replaced by the user home
* and identity (private and public key). It may start
* with ~ which will be replaced by the user home
* directory.
*
* - SSH_OPTIONS_KNOWNHOSTS:
@@ -718,18 +741,52 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
return -1;
} else {
char *username = NULL, *hostname = NULL;
rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true);
if (rc != SSH_OK) {
char *strict_hostname = NULL;
/* Non-strict parse: reject shell metacharacters */
rc = ssh_config_parse_uri(value,
&username,
&hostname,
NULL,
true,
false);
if (rc != SSH_OK || hostname == NULL) {
SAFE_FREE(username);
SAFE_FREE(hostname);
ssh_set_error_invalid(session);
return -1;
}
/* Non-strict passed: set username and originalhost */
if (username != NULL) {
SAFE_FREE(session->opts.username);
session->opts.username = username;
}
if (hostname != NULL) {
if (!session->opts.config_hostname_only) {
SAFE_FREE(session->opts.originalhost);
session->opts.originalhost = hostname;
} else {
SAFE_FREE(hostname);
}
/* Strict parse: set host only if valid hostname or IP */
rc = ssh_config_parse_uri(value,
NULL,
&strict_hostname,
NULL,
true,
true);
if (rc != SSH_OK || strict_hostname == NULL) {
SAFE_FREE(session->opts.host);
session->opts.host = hostname;
SAFE_FREE(strict_hostname);
if (session->opts.config_hostname_only) {
/* Config path: Hostname must be valid */
ssh_set_error_invalid(session);
return -1;
}
} else {
SAFE_FREE(session->opts.host);
session->opts.host = strict_hostname;
}
}
break;
@@ -907,7 +964,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 +1254,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 +1618,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
@@ -1638,7 +1703,8 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
switch(type)
{
case SSH_OPTIONS_HOST:
src = session->opts.host;
src = session->opts.host ? session->opts.host
: session->opts.originalhost;
break;
case SSH_OPTIONS_USER:
@@ -1658,6 +1724,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;
@@ -1948,7 +2038,7 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
if (session == NULL) {
return -1;
}
if (session->opts.host == NULL) {
if (session->opts.originalhost == NULL) {
ssh_set_error_invalid(session);
return -1;
}
@@ -1963,7 +2053,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 +2066,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
}
@@ -2000,6 +2095,16 @@ out:
return r;
}
/** @internal
* @brief Apply default values for unset session options.
*
* Sets default SSH directory and username if not already configured,
* and resolves any remaining option expansions.
*
* @param[in] session The SSH session to apply defaults to.
*
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
int ssh_options_apply(ssh_session session)
{
char *tmp = NULL;
@@ -2021,7 +2126,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 +2140,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);
@@ -2364,6 +2469,15 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* not a pointer when it should have been a pointer, or if
* its a pointer to a pointer when it should have just been
* a pointer), then the behaviour is undefined.
*
* @warning Options set via this function may be overridden if a
* configuration file is parsed afterwards (e.g., by an
* implicit call to ssh_bind_options_parse_config() inside
* ssh_bind_listen(), or by a manual call to the same
* function) and contains the same options.\n
* It is the callers responsibility to ensure the correct
* order of API calls if explicit options must take
* precedence.
*/
int
ssh_bind_options_set(ssh_bind sshbind,

192
src/pki.c
View File

@@ -449,40 +449,137 @@ const char *ssh_key_type_to_char(enum ssh_keytypes_e type) {
/* We should never reach this */
return NULL;
}
enum ssh_digest_e ssh_key_hash_from_name(const char *name)
/**
* @brief Convert a signature algorithm name to a key type and hash type.
*
* Looks up the given signature algorithm name and returns both the
* corresponding key type and digest algorithm in a single call,
* avoiding double string comparisons on the same input.
*
* @param[in] name The signature algorithm name to convert (e.g.
* "ssh-rsa", "rsa-sha2-256", "ecdsa-sha2-nistp256").
*
* @param[out] type A pointer to store the resulting key type.
*
* @param[out] hash_type A pointer to store the resulting hash/digest type.
*
* @return SSH_OK on success, SSH_ERROR if name is NULL or
* unknown.
*/
int ssh_key_type_and_hash_from_signature_name(const char *name,
enum ssh_keytypes_e *type,
enum ssh_digest_e *hash_type)
{
if (name == NULL) {
/* TODO we should rather fail */
return SSH_DIGEST_AUTO;
size_t len;
if (name == NULL || type == NULL || hash_type == NULL) {
return SSH_ERROR;
}
if (strcmp(name, "ssh-rsa") == 0) {
return SSH_DIGEST_SHA1;
} else if (strcmp(name, "rsa-sha2-256") == 0) {
return SSH_DIGEST_SHA256;
} else if (strcmp(name, "rsa-sha2-512") == 0) {
return SSH_DIGEST_SHA512;
} else if (strcmp(name, "ecdsa-sha2-nistp256") == 0) {
return SSH_DIGEST_SHA256;
} else if (strcmp(name, "ecdsa-sha2-nistp384") == 0) {
return SSH_DIGEST_SHA384;
} else if (strcmp(name, "ecdsa-sha2-nistp521") == 0) {
return SSH_DIGEST_SHA512;
} else if (strcmp(name, "ssh-ed25519") == 0) {
return SSH_DIGEST_AUTO;
} else if (strcmp(name, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) {
return SSH_DIGEST_SHA256;
} else if (strcmp(name, "sk-ssh-ed25519@openssh.com") == 0) {
return SSH_DIGEST_AUTO;
len = strlen(name);
if (len == 7 && strcmp(name, "ssh-rsa") == 0) {
*type = SSH_KEYTYPE_RSA;
*hash_type = SSH_DIGEST_SHA1;
return SSH_OK;
}
if (len == 11 && strcmp(name, "ssh-ed25519") == 0) {
*type = SSH_KEYTYPE_ED25519;
*hash_type = SSH_DIGEST_AUTO;
return SSH_OK;
}
if (len == 12) {
if (strcmp(name, "rsa-sha2-256") == 0) {
*type = SSH_KEYTYPE_RSA;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (strcmp(name, "rsa-sha2-512") == 0) {
*type = SSH_KEYTYPE_RSA;
*hash_type = SSH_DIGEST_SHA512;
return SSH_OK;
}
}
if (len == 19) {
if (strcmp(name, "ecdsa-sha2-nistp256") == 0) {
*type = SSH_KEYTYPE_ECDSA_P256;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (strcmp(name, "ecdsa-sha2-nistp384") == 0) {
*type = SSH_KEYTYPE_ECDSA_P384;
*hash_type = SSH_DIGEST_SHA384;
return SSH_OK;
}
if (strcmp(name, "ecdsa-sha2-nistp521") == 0) {
*type = SSH_KEYTYPE_ECDSA_P521;
*hash_type = SSH_DIGEST_SHA512;
return SSH_OK;
}
}
if (len == 26 && strcmp(name, "sk-ssh-ed25519@openssh.com") == 0) {
*type = SSH_KEYTYPE_SK_ED25519;
*hash_type = SSH_DIGEST_AUTO;
return SSH_OK;
}
if (len == 28 && strcmp(name, "ssh-rsa-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_RSA_CERT01;
*hash_type = SSH_DIGEST_SHA1;
return SSH_OK;
}
if (len == 32 && strcmp(name, "ssh-ed25519-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_ED25519_CERT01;
*hash_type = SSH_DIGEST_AUTO;
return SSH_OK;
}
if (len == 33) {
if (strcmp(name, "rsa-sha2-256-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_RSA_CERT01;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (strcmp(name, "rsa-sha2-512-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_RSA_CERT01;
*hash_type = SSH_DIGEST_SHA512;
return SSH_OK;
}
}
if (len == 34 && strcmp(name, "sk-ecdsa-sha2-nistp256@openssh.com") == 0) {
*type = SSH_KEYTYPE_SK_ECDSA;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (len == 40) {
if (strcmp(name, "ecdsa-sha2-nistp256-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_ECDSA_P256_CERT01;
*hash_type = SSH_DIGEST_SHA256;
return SSH_OK;
}
if (strcmp(name, "ecdsa-sha2-nistp384-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_ECDSA_P384_CERT01;
*hash_type = SSH_DIGEST_SHA384;
return SSH_OK;
}
if (strcmp(name, "ecdsa-sha2-nistp521-cert-v01@openssh.com") == 0) {
*type = SSH_KEYTYPE_ECDSA_P521_CERT01;
*hash_type = SSH_DIGEST_SHA512;
return SSH_OK;
}
}
SSH_LOG(SSH_LOG_TRACE, "Unknown signature name %s", name);
/* TODO we should rather fail */
return SSH_DIGEST_AUTO;
return SSH_ERROR;
}
/**
* @brief Checks the given key against the configured allowed
* public key algorithm types
@@ -700,27 +797,6 @@ ssh_key_get_signature_algorithm(ssh_session session,
return ssh_key_signature_to_char(type, hash_type);
}
/**
* @brief Convert a ssh key algorithm name to a ssh key algorithm type.
*
* @param[in] name The name to convert.
*
* @return The enum ssh key algorithm type.
*/
enum ssh_keytypes_e ssh_key_type_from_signature_name(const char *name) {
if (name == NULL) {
return SSH_KEYTYPE_UNKNOWN;
}
if ((strcmp(name, "rsa-sha2-256") == 0) ||
(strcmp(name, "rsa-sha2-512") == 0)) {
return SSH_KEYTYPE_RSA;
}
/* Otherwise the key type matches the signature type */
return ssh_key_type_from_name(name);
}
/**
* @brief Convert a ssh key name to a ssh key type.
*
@@ -834,6 +910,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 +2764,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 +2779,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 +2788,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 +2799,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;
@@ -2893,8 +2975,12 @@ int ssh_pki_import_signature_blob(const ssh_string sig_blob,
}
alg = ssh_string_get_char(algorithm);
type = ssh_key_type_from_signature_name(alg);
hash_type = ssh_key_hash_from_name(alg);
rc = ssh_key_type_and_hash_from_signature_name(alg, &type, &hash_type);
if (rc != SSH_OK) {
SSH_BUFFER_FREE(buf);
SSH_STRING_FREE(algorithm);
return SSH_ERROR;
}
SSH_STRING_FREE(algorithm);
blob = ssh_buffer_get_ssh_string(buf);

View File

@@ -110,7 +110,7 @@ void ssh_pki_ctx_free(ssh_pki_ctx context)
* Set the RSA key size in bits for key generation.
* Typically 2048, 3072, or 4096 bits. Must be greater
* than or equal to 1024, as anything below is considered
* insecure.
* insecure. Use 0 (default) to use default key size (3072).
*
* - SSH_PKI_OPTION_SK_APPLICATION (const char *):
* The Relying Party identifier (application string) that
@@ -191,7 +191,7 @@ int ssh_pki_ctx_options_set(ssh_pki_ctx context,
if (value == NULL) {
SSH_LOG(SSH_LOG_WARN, "RSA key size pointer must not be NULL");
return SSH_ERROR;
} else if (*(int *)value != 0 && *(int *)value <= RSA_MIN_KEY_SIZE) {
} else if (*(int *)value != 0 && *(int *)value < RSA_MIN_KEY_SIZE) {
SSH_LOG(
SSH_LOG_WARN,
"RSA key size must be greater than %d bits or 0 for default",

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

@@ -84,16 +84,33 @@ struct ssh_poll_ctx_struct {
#ifdef HAVE_POLL
#include <poll.h>
/** @internal
* @brief Initialize the poll subsystem. No-op when native poll is available.
*/
void ssh_poll_init(void)
{
return;
}
/** @internal
* @brief Clean up the poll subsystem. No-op when native poll is available.
*/
void ssh_poll_cleanup(void)
{
return;
}
/** @internal
* @brief Wait for events on a set of file descriptors.
*
* @param fds Array of pollfd structures specifying the file descriptors.
* @param nfds Number of file descriptors in the array.
* @param timeout Timeout in milliseconds, `SSH_TIMEOUT_INFINITE`
* to block indefinitely.
*
* @return Number of file descriptors with events, 0 on timeout,
* -1 on error.
*/
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
{
return poll((struct pollfd *)fds, nfds, timeout);
@@ -321,16 +338,33 @@ static int bsd_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
return rc;
}
/** @internal
* @brief Initialize the poll subsystem using the BSD poll emulation.
*/
void ssh_poll_init(void)
{
ssh_poll_emu = bsd_poll;
}
/** @internal
* @brief Clean up the poll subsystem, resetting to the BSD poll emulation.
*/
void ssh_poll_cleanup(void)
{
ssh_poll_emu = bsd_poll;
}
/** @internal
* @brief Wait for events on a set of file descriptors.
*
* @param fds Array of pollfd structures specifying the file descriptors.
* @param nfds Number of file descriptors in the array.
* @param timeout Timeout in milliseconds, `SSH_TIMEOUT_INFINITE`
* to block indefinitely.
*
* @return Number of file descriptors with events, 0 on timeout,
* -1 on error.
*/
int ssh_poll(ssh_pollfd_t *fds, nfds_t nfds, int timeout)
{
return (ssh_poll_emu)(fds, nfds, timeout);

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,8 @@ 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.originalhost);
SAFE_FREE(session->opts.homedir);
SAFE_FREE(session->opts.sshdir);
SAFE_FREE(session->opts.knownhosts);
SAFE_FREE(session->opts.global_knownhosts);
@@ -1007,7 +1010,9 @@ int ssh_get_version(ssh_session session) {
/**
* @internal
* @brief Callback to be called when the socket received an exception code.
* @param user is a pointer to session
* @param code The exception code from the socket layer.
* @param errno_code The errno value associated with the exception.
* @param user Pointer to the SSH session.
*/
void ssh_socket_exception_callback(int code, int errno_code, void *user){
ssh_session session = (ssh_session)user;

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

View File

@@ -24,11 +24,12 @@
#include "config.h"
#ifndef _WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <grp.h>
#include <netinet/in.h>
#include <pwd.h>
#include <sys/statvfs.h>
#endif
@@ -239,9 +240,7 @@ sftp_make_client_message(sftp_session sftp, sftp_packet packet)
}
break;
case SSH_FXP_EXTENDED:
rc = ssh_buffer_unpack(payload,
"s",
&msg->submessage);
rc = ssh_buffer_unpack(payload, "s", &msg->submessage);
if (rc != SSH_OK) {
goto error;
}
@@ -256,9 +255,13 @@ sftp_make_client_message(sftp_session sftp, sftp_packet packet)
goto error;
}
} else if (strcmp(msg->submessage, "statvfs@openssh.com") == 0 ){
rc = ssh_buffer_unpack(payload,
"s",
&msg->filename);
rc = ssh_buffer_unpack(payload, "s", &msg->filename);
if (rc != SSH_OK) {
goto error;
}
} else if (strcmp(msg->submessage,
"users-groups-by-id@openssh.com") == 0) {
rc = ssh_buffer_unpack(payload, "SS", &msg->data, &msg->handle);
if (rc != SSH_OK) {
goto error;
}
@@ -489,7 +492,10 @@ sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr)
return -1;
}
SSH_LOG(SSH_LOG_PROTOCOL, "Sending name %s", ssh_string_get_char(file));
SSH_LOG(SSH_LOG_PROTOCOL,
"Sending name %s, ID %" PRIu32,
ssh_string_get_char(file),
msg->id);
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
ssh_buffer_add_u32(out, htonl(1)) < 0 ||
@@ -532,6 +538,7 @@ int sftp_reply_handle(sftp_client_message msg, ssh_string handle)
ssh_log_hexdump("Sending handle:",
(const unsigned char *)ssh_string_get_char(handle),
ssh_string_len(handle));
SSH_LOG(SSH_LOG_PROTOCOL, "packet ID %" PRIu32, msg->id);
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
ssh_buffer_add_ssh_string(out, handle) < 0 ||
@@ -565,7 +572,7 @@ int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr)
return -1;
}
SSH_LOG(SSH_LOG_PROTOCOL, "Sending attr");
SSH_LOG(SSH_LOG_PROTOCOL, "Sending attr, ID %" PRIu32, msg->id);
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
buffer_add_attributes(out, attr) < 0 ||
@@ -653,7 +660,10 @@ int sftp_reply_names(sftp_client_message msg)
return -1;
}
SSH_LOG(SSH_LOG_PROTOCOL, "Sending %d names", msg->attr_num);
SSH_LOG(SSH_LOG_PROTOCOL,
"Sending %d names, ID %" PRIu32,
msg->attr_num,
msg->id);
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
ssh_buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
@@ -705,8 +715,11 @@ sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message)
return -1;
}
SSH_LOG(SSH_LOG_PROTOCOL, "Sending status %d, message: %s", status,
ssh_string_get_char(s));
SSH_LOG(SSH_LOG_PROTOCOL,
"Sending status %d, message: %s, ID %" PRIu32,
status,
ssh_string_get_char(s),
msg->id);
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
ssh_buffer_add_u32(out, htonl(status)) < 0 ||
@@ -745,7 +758,10 @@ int sftp_reply_data(sftp_client_message msg, const void *data, int len)
return -1;
}
SSH_LOG(SSH_LOG_PROTOCOL, "Sending data, length: %d", len);
SSH_LOG(SSH_LOG_PROTOCOL,
"Sending data, length: %d, ID %" PRIu32,
len,
msg->id);
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
ssh_buffer_add_u32(out, ntohl(len)) < 0 ||
@@ -780,7 +796,7 @@ sftp_reply_statvfs(sftp_client_message msg, sftp_statvfs_t st)
return -1;
}
SSH_LOG(SSH_LOG_PROTOCOL, "Sending statvfs reply");
SSH_LOG(SSH_LOG_PROTOCOL, "Sending statvfs reply, ID %" PRIu32, msg->id);
if (ssh_buffer_add_u32(out, msg->id) < 0 ||
ssh_buffer_add_u64(out, ntohll(st->f_bsize)) < 0 ||
@@ -810,7 +826,9 @@ int sftp_reply_version(sftp_client_message client_msg)
ssh_buffer reply;
int rc;
SSH_LOG(SSH_LOG_PROTOCOL, "Sending version packet");
SSH_LOG(SSH_LOG_PROTOCOL,
"Sending version packet, ID %" PRIu32,
client_msg->id);
version = sftp->client_version;
reply = ssh_buffer_new();
@@ -819,14 +837,17 @@ int sftp_reply_version(sftp_client_message client_msg)
return -1;
}
rc = ssh_buffer_pack(reply, "dssssss",
rc = ssh_buffer_pack(reply,
"dssssssss",
LIBSFTP_VERSION,
"posix-rename@openssh.com",
"1",
"hardlink@openssh.com",
"1",
"statvfs@openssh.com",
"2");
"2",
"users-groups-by-id@openssh.com",
"1");
if (rc != SSH_OK) {
ssh_set_error_oom(session);
SSH_BUFFER_FREE(reply);
@@ -933,6 +954,14 @@ void *sftp_handle(sftp_session sftp, ssh_string handle)
return sftp->handles[val];
}
/**
* @brief Remove an SFTP file or directory handle from the session.
*
* @param sftp The SFTP session.
* @param handle The handle to remove.
*
* @see sftp_handle_alloc()
*/
void sftp_handle_remove(sftp_session sftp, void *handle)
{
int i;
@@ -1044,6 +1073,352 @@ struct sftp_handle
char *name;
};
/**
* @internal
*
* @brief Parse a blob of IDs into a sftp_name_id_map.
*
* This function extracts numeric IDs from the binary blob and populates
* the 'ids' array of the map. Note that each element of the 'names'
* array in the map is initialized to NULL by this function.
*
* @param[in] ids_blob The binary string blob containing uint32_t IDs.
*
* @return A newly allocated sftp_name_id_map on success, or NULL on error.
*/
static sftp_name_id_map sftp_name_id_map_from_ids_blob(ssh_string ids_blob)
{
sftp_name_id_map map = NULL;
ssh_buffer buf = NULL;
size_t len;
uint32_t count, i;
int rc;
if (ids_blob == NULL) {
SSH_LOG(SSH_LOG_WARNING, "IDs blob is NULL");
return NULL;
}
len = ssh_string_len(ids_blob);
if (len % sizeof(uint32_t) != 0) {
SSH_LOG(SSH_LOG_WARNING,
"IDs blob length is not a multiple of 4 bytes");
return NULL;
}
count = len / sizeof(uint32_t);
map = sftp_name_id_map_new(count);
if (map == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate sftp_name_id_map");
return NULL;
}
buf = ssh_buffer_new();
if (buf == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate ssh_buffer");
sftp_name_id_map_free(map);
return NULL;
}
rc = ssh_buffer_add_data(buf, ssh_string_data(ids_blob), len);
if (rc < 0) {
SSH_LOG(SSH_LOG_WARNING, "Failed to copy blob data to buffer");
SSH_BUFFER_FREE(buf);
sftp_name_id_map_free(map);
return NULL;
}
for (i = 0; i < count; i++) {
uint32_t val;
rc = ssh_buffer_unpack(buf, "d", &val);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING, "Failed to unpack ID from buffer");
SSH_BUFFER_FREE(buf);
sftp_name_id_map_free(map);
return NULL;
}
map->ids[i] = val;
}
SSH_BUFFER_FREE(buf);
return map;
}
/**
* @internal
*
* @brief Fill the names array using UIDs in the map.
*/
static int sftp_fill_names_using_uids(sftp_name_id_map users_map)
{
struct passwd pwd_struct;
struct passwd *pwd_res = NULL;
long pwd_buf_size = -1;
char *pwd_lookup_buf = NULL;
uint32_t i;
int rc = SSH_OK;
if (users_map == NULL) {
return SSH_ERROR;
}
#ifdef _SC_GETPW_R_SIZE_MAX
pwd_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
#endif
if (pwd_buf_size <= 0) {
pwd_buf_size = 16384;
}
pwd_lookup_buf = calloc(1, pwd_buf_size);
if (pwd_lookup_buf == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate pwd lookup buffer");
return SSH_ERROR;
}
for (i = 0; i < users_map->count; i++) {
int ret = getpwuid_r(users_map->ids[i],
&pwd_struct,
pwd_lookup_buf,
pwd_buf_size,
&pwd_res);
SAFE_FREE(users_map->names[i]);
if (ret == 0 && pwd_res != NULL) {
users_map->names[i] = strdup(pwd_res->pw_name);
} else {
users_map->names[i] = strdup("");
}
if (users_map->names[i] == NULL) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to allocate memory for username string");
rc = SSH_ERROR;
break;
}
}
SAFE_FREE(pwd_lookup_buf);
return rc;
}
/**
* @internal
*
* @brief Fill the names array using GIDs in the map.
*/
static int sftp_fill_names_using_gids(sftp_name_id_map groups_map)
{
struct group grp_struct;
struct group *grp_res = NULL;
long grp_buf_size = -1;
char *grp_lookup_buf = NULL;
uint32_t i;
int rc = SSH_OK;
if (groups_map == NULL) {
return SSH_ERROR;
}
#ifdef _SC_GETGR_R_SIZE_MAX
grp_buf_size = sysconf(_SC_GETGR_R_SIZE_MAX);
#endif
if (grp_buf_size <= 0) {
grp_buf_size = 16384;
}
grp_lookup_buf = calloc(1, grp_buf_size);
if (grp_lookup_buf == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate grp lookup buffer");
return SSH_ERROR;
}
for (i = 0; i < groups_map->count; i++) {
int ret = getgrgid_r(groups_map->ids[i],
&grp_struct,
grp_lookup_buf,
grp_buf_size,
&grp_res);
SAFE_FREE(groups_map->names[i]);
if (ret == 0 && grp_res != NULL) {
groups_map->names[i] = strdup(grp_res->gr_name);
} else {
groups_map->names[i] = strdup("");
}
if (groups_map->names[i] == NULL) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to allocate memory for group name string");
rc = SSH_ERROR;
break;
}
}
SAFE_FREE(grp_lookup_buf);
return rc;
}
/**
* @internal
*
* @brief Pack the resolved names from a map into the output buffer.
*
* This function formats the multiple names according to the
* users-groups-by-id@openssh.com extension specification. Each name in
* the map is individually encoded as an SSH string. The entire concatenated
* sequence of these encoded names is then wrapped and appended to the
* output buffer as one single, large SSH string.
*
* @param[out] out_buffer The destination buffer for the final packed string.
* @param[in] map The map containing the resolved names.
*
* @return SSH_OK on success, or SSH_ERROR on memory allocation failure.
*/
static int sftp_buffer_add_names(ssh_buffer out_buffer, sftp_name_id_map map)
{
ssh_buffer temp_buffer = NULL;
uint32_t i;
int rc;
if (out_buffer == NULL || map == NULL) {
return SSH_ERROR;
}
temp_buffer = ssh_buffer_new();
if (temp_buffer == NULL) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to allocate temporary buffer for names");
return SSH_ERROR;
}
for (i = 0; i < map->count; i++) {
const char *name = map->names[i] != NULL ? map->names[i] : "";
rc = ssh_buffer_pack(temp_buffer, "s", name);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING, "Failed to pack name into buffer");
SSH_BUFFER_FREE(temp_buffer);
return SSH_ERROR;
}
}
rc = ssh_buffer_pack(out_buffer,
"dP",
(uint32_t)ssh_buffer_get_len(temp_buffer),
(size_t)ssh_buffer_get_len(temp_buffer),
ssh_buffer_get(temp_buffer));
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to add names string blob to output buffer");
}
SSH_BUFFER_FREE(temp_buffer);
return (rc != SSH_OK) ? SSH_ERROR : SSH_OK;
}
/**
* @internal
*
* @brief Handle users-groups-by-id@openssh.com extension request.
*
* Resolves numeric user IDs (UIDs) and group IDs (GIDs) to their
* corresponding username and group name strings. Returns empty strings
* for IDs that cannot be resolved.
*
* @param[in] client_msg The SFTP client message containing the request.
* client_msg->data contains the UIDs blob.
* client_msg->handle contains the GIDs blob.
*
* @return SSH_OK on success (reply sent to client). On error, an error
* status is sent to the client and SSH_ERROR is returned.
*/
static int process_users_groups_by_id(sftp_client_message client_msg)
{
ssh_buffer out = NULL;
sftp_name_id_map users_map = NULL;
sftp_name_id_map groups_map = NULL;
int rc;
SSH_LOG(SSH_LOG_PROTOCOL, "Processing users-groups-by-id extension");
if (client_msg->data == NULL || client_msg->handle == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Missing UIDs or GIDs blob");
goto error;
}
users_map = sftp_name_id_map_from_ids_blob(client_msg->data);
if (users_map == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Failed to parse UIDs blob");
goto error;
}
groups_map = sftp_name_id_map_from_ids_blob(client_msg->handle);
if (groups_map == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Failed to parse GIDs blob");
goto error;
}
rc = sftp_fill_names_using_uids(users_map);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING, "Failed to resolve UIDs");
goto error;
}
rc = sftp_fill_names_using_gids(groups_map);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING, "Failed to resolve GIDs");
goto error;
}
out = ssh_buffer_new();
if (out == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Failed to allocate output buffer");
goto error;
}
rc = ssh_buffer_add_u32(out, client_msg->id);
if (rc < 0) {
SSH_LOG(SSH_LOG_WARNING, "Failed to add request ID to buffer");
goto error;
}
rc = sftp_buffer_add_names(out, users_map);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING, "Failed to add users to buffer");
goto error;
}
rc = sftp_buffer_add_names(out, groups_map);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING, "Failed to add groups to buffer");
goto error;
}
rc = sftp_packet_write(client_msg->sftp, SSH_FXP_EXTENDED_REPLY, out);
if (rc < 0) {
SSH_LOG(SSH_LOG_WARNING, "Failed to send extended reply");
goto error;
}
sftp_name_id_map_free(users_map);
sftp_name_id_map_free(groups_map);
SSH_BUFFER_FREE(out);
SSH_LOG(SSH_LOG_PROTOCOL, "Successfully processed request");
return SSH_OK;
error:
sftp_name_id_map_free(users_map);
sftp_name_id_map_free(groups_map);
SSH_BUFFER_FREE(out);
SSH_LOG(SSH_LOG_WARNING, "Sending error response");
sftp_reply_status(client_msg, SSH_FX_FAILURE, "Internal processing error");
return SSH_ERROR;
}
SSH_SFTP_CALLBACK(process_unsupported);
SSH_SFTP_CALLBACK(process_open);
SSH_SFTP_CALLBACK(process_read);
@@ -1088,6 +1463,10 @@ const struct sftp_message_handler message_handlers[] = {
const struct sftp_message_handler extended_handlers[] = {
/* here are some extended type handlers */
{"statvfs", "statvfs@openssh.com", 0, process_extended_statvfs},
{"users-groups-by-id",
"users-groups-by-id@openssh.com",
0,
process_users_groups_by_id},
{NULL, NULL, 0, NULL},
};
@@ -1183,6 +1562,10 @@ process_read(sftp_client_message client_msg)
ssh_log_hexdump("Processing read: handle:",
(const unsigned char *)ssh_string_get_char(handle),
ssh_string_len(handle));
SSH_LOG(SSH_LOG_PROTOCOL,
"Offset %" PRIu64", length %" PRIu32,
client_msg->offset,
client_msg->len);
h = sftp_handle(sftp, handle);
if (h != NULL && h->type == SFTP_FILE_HANDLE) {
@@ -1241,6 +1624,10 @@ process_write(sftp_client_message client_msg)
ssh_log_hexdump("Processing write: handle",
(const unsigned char *)ssh_string_get_char(handle),
ssh_string_len(handle));
SSH_LOG(SSH_LOG_PROTOCOL,
"Offset %" PRIu64", length %" PRIu32,
client_msg->offset,
client_msg->len);
h = sftp_handle(sftp, handle);
if (h != NULL && h->type == SFTP_FILE_HANDLE) {
@@ -1343,6 +1730,15 @@ process_opendir(sftp_client_message client_msg)
}
h->dirp = dir;
h->name = strdup(dir_name);
if (h->name == NULL) {
free(h);
closedir(dir);
SSH_LOG(SSH_LOG_PROTOCOL, "failed to duplicate directory name");
sftp_reply_status(client_msg,
SSH_FX_FAILURE,
"Failed to allocate new handle");
return SSH_ERROR;
}
h->type = SFTP_DIR_HANDLE;
handle_s = sftp_handle_alloc(client_msg->sftp, h);
@@ -1350,6 +1746,7 @@ process_opendir(sftp_client_message client_msg)
sftp_reply_handle(client_msg, handle_s);
ssh_string_free(handle_s);
} else {
SAFE_FREE(h->name);
free(h);
closedir(dir);
sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available");
@@ -1958,7 +2355,10 @@ dispatch_sftp_request(sftp_client_message sftp_msg)
sftp_server_message_callback handler = NULL;
uint8_t type = sftp_client_message_get_type(sftp_msg);
SSH_LOG(SSH_LOG_PROTOCOL, "processing request type: %u", type);
SSH_LOG(SSH_LOG_PROTOCOL,
"processing request type: %" PRIu8 ", ID %" PRIu32,
type,
sftp_msg->id);
for (int i = 0; message_handlers[i].cb != NULL; i++) {
if (type == message_handlers[i].type) {
@@ -2035,16 +2435,19 @@ sftp_channel_default_subsystem_request(ssh_session session,
}
/**
* @brief Default data callback for sftp server
* @brief Default channel data callback for an SFTP server subsystem.
*
* @param[in] session The ssh session
* @param[in] channel The ssh channel with SFTP session opened
* @param[in] data The data to be processed.
* @param[in] len The length of input data to be processed
* @param[in] is_stderr Unused channel flag for stderr flagging
* @param[in] userdata The pointer to sftp_session
* Processes incoming data on the channel and dispatches it to the SFTP
* server message handler.
*
* @return number of bytes processed, -1 when error occurs.
* @param[in] session The SSH session.
* @param[in] channel The SSH channel carrying the SFTP data.
* @param[in] data The received data buffer.
* @param[in] len The length of the data buffer.
* @param[in] is_stderr Unused; SFTP does not use the stderr stream.
* @param[in] userdata Pointer to the sftp_session handle.
*
* @return Number of bytes processed on success, `SSH_ERROR` on error.
*/
int
sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
@@ -2057,7 +2460,7 @@ sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
sftp_session *sftpp = (sftp_session *)userdata;
sftp_session sftp = NULL;
sftp_client_message msg;
int decode_len;
uint32_t undecoded_len = len;
int rc;
if (sftpp == NULL) {
@@ -2066,17 +2469,25 @@ sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
}
sftp = *sftpp;
decode_len = sftp_decode_channel_data_to_packet(sftp, data, len);
if (decode_len == SSH_ERROR)
return SSH_ERROR;
do {
int decode_len =
sftp_decode_channel_data_to_packet(sftp, data, undecoded_len);
if (decode_len == SSH_ERROR) {
return SSH_ERROR;
}
msg = sftp_get_client_message_from_packet(sftp);
rc = process_client_message(msg);
sftp_client_message_free(msg);
if (rc != SSH_OK)
SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
msg = sftp_get_client_message_from_packet(sftp);
rc = process_client_message(msg);
sftp_client_message_free(msg);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
}
return decode_len;
undecoded_len -= decode_len;
data = (uint8_t *)data + decode_len;
} while (undecoded_len > 0);
return len;
}
#else
/* Not available on Windows for now */

View File

@@ -28,7 +28,6 @@
#include "libssh/sntrup761.h"
#ifdef HAVE_SNTRUP761
#include "libssh/bignum.h"
#include "libssh/buffer.h"
#include "libssh/crypto.h"
#include "libssh/dh.h"
@@ -141,7 +140,7 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
{
unsigned char ssk[SNTRUP761_SIZE + CURVE25519_PUBKEY_SIZE];
unsigned char *k = ssk + SNTRUP761_SIZE;
unsigned char hss[SHA512_DIGEST_LEN];
void *shared_secret_data = NULL;
int rc;
rc = ssh_curve25519_create_k(session, k);
@@ -216,22 +215,27 @@ static int ssh_sntrup761x25519_build_k(ssh_session session)
ssh_log_hexdump("kem key", ssk, SNTRUP761_SIZE);
#endif
sha512(ssk, sizeof ssk, hss);
bignum_bin2bn(hss, sizeof hss, &session->next_crypto->shared_secret);
if (session->next_crypto->shared_secret == NULL) {
ssh_string_burn(session->next_crypto->hybrid_shared_secret);
ssh_string_free(session->next_crypto->hybrid_shared_secret);
session->next_crypto->hybrid_shared_secret =
ssh_string_new(SHA512_DIGEST_LEN);
if (session->next_crypto->hybrid_shared_secret == NULL) {
ssh_set_error_oom(session);
rc = SSH_ERROR;
goto cleanup;
}
shared_secret_data =
ssh_string_data(session->next_crypto->hybrid_shared_secret);
sha512(ssk, sizeof ssk, shared_secret_data);
#ifdef DEBUG_CRYPTO
ssh_print_bignum("Shared secret key", session->next_crypto->shared_secret);
ssh_log_hexdump("Shared secret key", shared_secret_data, SHA512_DIGEST_LEN);
#endif
return 0;
cleanup:
ssh_burn(ssk, sizeof ssk);
ssh_burn(hss, sizeof hss);
return rc;
}

View File

@@ -65,6 +65,9 @@ struct sockaddr_un {
* @{
*/
/** @internal
* @brief Represents the possible states of an SSH socket connection.
*/
enum ssh_socket_states_e {
SSH_SOCKET_NONE,
SSH_SOCKET_CONNECTING,
@@ -444,7 +447,8 @@ int ssh_socket_pollcallback(struct ssh_poll_handle_struct *p,
/** @internal
* @brief returns the poll handle corresponding to the socket,
* creates it if it does not exist.
* @returns allocated and initialized ssh_poll_handle object
*
* @return allocated and initialized ssh_poll_handle object
*/
ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
{
@@ -1062,12 +1066,26 @@ int ssh_socket_get_poll_flags(ssh_socket s)
}
#ifdef _WIN32
/** @internal
* @brief Set a socket file descriptor to non-blocking mode.
*
* @param fd The socket file descriptor to configure.
*
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
int ssh_socket_set_nonblocking(socket_t fd)
{
u_long nonblocking = 1;
return ioctlsocket(fd, FIONBIO, &nonblocking);
}
/** @internal
* @brief Set a socket file descriptor to blocking mode.
*
* @param fd The socket file descriptor to configure.
*
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
int ssh_socket_set_blocking(socket_t fd)
{
u_long nonblocking = 0;
@@ -1075,11 +1093,25 @@ int ssh_socket_set_blocking(socket_t fd)
}
#else /* _WIN32 */
/** @internal
* @brief Set a socket file descriptor to non-blocking mode.
*
* @param fd The socket file descriptor to configure.
*
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
int ssh_socket_set_nonblocking(socket_t fd)
{
return fcntl(fd, F_SETFL, O_NONBLOCK);
}
/** @internal
* @brief Set a socket file descriptor to blocking mode.
*
* @param fd The socket file descriptor to configure.
*
* @return 0 on success, -1 on error.
*/
int ssh_socket_set_blocking(socket_t fd)
{
return fcntl(fd, F_SETFL, 0);
@@ -1435,7 +1467,7 @@ ssh_socket_connect_proxyjump(ssh_socket s)
session = s->session;
SSH_LOG(SSH_LOG_INFO,
SSH_LOG(SSH_LOG_DEBUG,
"Connecting to host %s port %d user %s through ProxyJump",
session->opts.host,
session->opts.port,
@@ -1515,7 +1547,7 @@ ssh_socket_connect_proxyjump(ssh_socket s)
/* transferred to the jump_thread_data */
jump_session = NULL;
SSH_LOG(SSH_LOG_INFO,
SSH_LOG(SSH_LOG_DEBUG,
"Starting proxy thread to host %s port %d user %s, callbacks=%p",
jump_thread_data->next_jump->hostname,
jump_thread_data->next_jump->port,

View File

@@ -38,7 +38,6 @@ static struct ssh_threads_callbacks_struct *user_callbacks = NULL;
/** @internal
* @brief inits the threading with the backend cryptographic libraries
*/
int ssh_threads_init(void)
{
static int threads_initialized = 0;
@@ -63,6 +62,9 @@ int ssh_threads_init(void)
return rc;
}
/** @internal
* @brief Finalize and clean up the threading backend of the crypto libraries.
*/
void ssh_threads_finalize(void)
{
crypto_thread_finalize();
@@ -84,6 +86,12 @@ int ssh_threads_set_callbacks(struct ssh_threads_callbacks_struct *cb)
return rc;
}
/** @internal
* @brief Get the type identifier of the currently active threading callbacks.
*
* @return A string identifying the threading backend, or NULL if no
* callbacks are registered.
*/
const char *ssh_threads_get_type(void)
{
if (user_callbacks != NULL) {

View File

@@ -437,6 +437,7 @@ int crypt_set_algorithms_server(ssh_session session){
struct ssh_cipher_struct *ssh_ciphertab=ssh_get_ciphertab();
struct ssh_hmac_struct *ssh_hmactab=ssh_get_hmactab();
int cmp;
int rc;
if (session == NULL) {
return SSH_ERROR;
@@ -580,8 +581,24 @@ int crypt_set_algorithms_server(ssh_session session){
}
method = session->next_crypto->kex_methods[SSH_HOSTKEYS];
session->srv.hostkey = ssh_key_type_from_signature_name(method);
session->srv.hostkey_digest = ssh_key_hash_from_name(method);
/* For GSSAPI key exchange, hostkey algorithm may be "null" */
if (strcmp(method, "null") == 0) {
session->srv.hostkey = SSH_KEYTYPE_UNKNOWN;
session->srv.hostkey_digest = SSH_DIGEST_AUTO;
} else {
rc = ssh_key_type_and_hash_from_signature_name(
method,
&session->srv.hostkey,
&session->srv.hostkey_digest);
if (rc != SSH_OK) {
ssh_set_error(session,
SSH_FATAL,
"unknown hostkey algorithm %s",
method);
return SSH_ERROR;
}
}
/* setup DH key exchange type */
switch (session->next_crypto->kex_type) {

View File

@@ -259,6 +259,20 @@ else()
set(PUTTYGEN_EXECUTABLE "/bin/puttygen-not-found")
endif()
find_program(TINYSSHD_EXECUTABLE
NAMES
tinysshd
PATHS
/sbin
/usr/sbin
/usr/local/sbin)
if (TINYSSHD_EXECUTABLE)
message(STATUS "Found TinySSH server: ${TINYSSHD_EXECUTABLE}")
else()
message(STATUS "Could NOT find TinySSH server")
endif()
find_program(SSHD_EXECUTABLE
NAME
sshd
@@ -391,6 +405,14 @@ if (CLIENT_TESTING OR SERVER_TESTING)
file(COPY keys/id_ed25519_sk DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
file(COPY keys/id_ed25519_sk.pub DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/home/bob/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)
# TINYSSH Key Setup
set(TINYSSH_KEY_DIR "${CMAKE_CURRENT_BINARY_DIR}/etc/tinyssh")
file(MAKE_DIRECTORY ${TINYSSH_KEY_DIR})
# Copy the TINYSSH hostkeys
file(COPY keys/ed25519.pk DESTINATION ${TINYSSH_KEY_DIR})
file(COPY keys/.ed25519.sk DESTINATION ${TINYSSH_KEY_DIR})
# 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)
@@ -419,6 +441,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

@@ -36,6 +36,10 @@ if (WITH_PKCS11_URI)
torture_auth_pkcs11)
endif()
if (TINYSSHD_EXECUTABLE AND NCAT_EXECUTABLE)
set(LIBSSH_CLIENT_TESTS ${LIBSSH_CLIENT_TESTS} torture_tinyssh)
endif()
if (HAVE_PTHREAD)
set(LIBSSH_CLIENT_TESTS
${LIBSSH_CLIENT_TESTS}
@@ -92,6 +96,12 @@ foreach(_CLI_TEST ${LIBSSH_CLIENT_TESTS})
LINK_LIBRARIES ${TORTURE_LIBRARY} util
)
if (_CLI_TEST STREQUAL "torture_tinyssh")
target_compile_definitions(${_CLI_TEST} PRIVATE
TINYSSH_KEYS_DIR="${TINYSSH_KEY_DIR}"
)
endif()
if (OSX)
set_property(
TEST

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

@@ -10,6 +10,7 @@
#include "libssh/callbacks.h"
#include "libssh/libssh.h"
#include <libssh/agent.h>
#include "libssh/priv.h"
#include <errno.h>
@@ -286,6 +287,96 @@ static void torture_auth_agent_forwarding(void **state)
}
}
static int agent_session_setup(void **state)
{
struct torture_state *s = *state;
int verbosity = torture_libssh_verbosity();
int rc;
s->ssh.ssh.session = ssh_new();
assert_non_null(s->ssh.ssh.session);
rc = ssh_options_set(s->ssh.ssh.session,
SSH_OPTIONS_LOG_VERBOSITY,
&verbosity);
assert_int_equal(rc, SSH_OK);
/* No callbacks needed — only talking to the local agent.
* The group setup already started the agent and loaded keys.
* Do NOT call torture_setup_ssh_agent here — that would spawn
* a second agent and overwrite SSH_AUTH_SOCK. */
s->ssh.ssh.cb_state = NULL;
s->ssh.ssh.callbacks = NULL;
return 0;
}
static void torture_agent_remove_identity(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.ssh.session;
ssh_key key = NULL;
char *comment = NULL;
uint32_t count_before = 0;
uint32_t count_after = 0;
int rc;
assert_non_null(session);
assert_true(ssh_agent_is_running(session));
count_before = ssh_agent_get_ident_count(session);
assert_true(count_before > 0);
key = ssh_agent_get_first_ident(session, &comment);
assert_non_null(key);
assert_non_null(comment);
rc = ssh_agent_remove_identity(session, key);
assert_int_equal(rc, SSH_OK);
count_after = ssh_agent_get_ident_count(session);
assert_int_equal(count_after, count_before - 1);
ssh_key_free(key);
ssh_string_free_char(comment);
}
static void torture_agent_remove_identity_negative(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.ssh.session;
int rc;
assert_non_null(session);
/* NULL key should return SSH_ERROR */
rc = ssh_agent_remove_identity(session, NULL);
assert_int_equal(rc, SSH_ERROR);
}
static void torture_agent_remove_identity_nonexistent(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.ssh.session;
ssh_key key = NULL;
int rc;
assert_non_null(session);
assert_true(ssh_agent_is_running(session));
rc = ssh_pki_generate_key(SSH_KEYTYPE_RSA, NULL, &key);
assert_int_equal(rc, SSH_OK);
assert_non_null(key);
/* Key not in agent should fail */
rc = ssh_agent_remove_identity(session, key);
assert_int_equal(rc, SSH_ERROR);
ssh_key_free(key);
}
/* Session setup function that configures SSH agent */
static int session_setup(void **state)
{
@@ -300,7 +391,6 @@ static int session_setup(void **state)
/* Create a new session */
s->ssh.ssh.session = ssh_new();
assert_non_null(s->ssh.ssh.session);
rc = ssh_options_set(s->ssh.ssh.session,
SSH_OPTIONS_LOG_VERBOSITY,
&verbosity);
@@ -342,19 +432,32 @@ static int session_setup(void **state)
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_auth_agent_forwarding,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_agent_remove_identity,
agent_session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_agent_remove_identity_negative,
agent_session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_agent_remove_identity_nonexistent,
agent_session_setup,
session_teardown),
};
ssh_init();
/* Simplify the CMocka test filter handling */
#if defined HAVE_CMOCKA_SET_TEST_FILTER
cmocka_set_message_output(CM_OUTPUT_STDOUT);
#endif
/* Apply test filtering */
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests,
@@ -365,5 +468,4 @@ int torture_run_tests(void)
return rc;
}
#endif

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

@@ -167,7 +167,7 @@ static void torture_connect_addrfamily(void **state)
{SSH_ADDRESS_FAMILY_INET6, "afinet6", SSH_OK},
};
int aftest_count = sizeof(aftests) / sizeof(aftests[0]);
int aftest_count = ARRAY_SIZE(aftests);
for (int i = 0; i < aftest_count; ++i) {
struct aftest const *t = &aftests[i];

View File

@@ -94,8 +94,7 @@ static void torture_kex_basic_functionality(void **state)
assert_non_null(kex_algo);
is_valid_algo = false;
valid_algorithms_count =
sizeof(valid_algorithms) / sizeof(valid_algorithms[0]);
valid_algorithms_count = ARRAY_SIZE(valid_algorithms);
for (i = 0; i < valid_algorithms_count; i++) {
if (strcmp(kex_algo, valid_algorithms[i]) == 0) {
is_valid_algo = true;

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

@@ -0,0 +1,328 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2026 by Your Bulitha Kawushika De Zoysa
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "tests_config.h"
#define LIBSSH_STATIC
#include "libssh/libssh.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "torture.h"
#include <errno.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define TINYSSH_PIDFILE "tinyssh.pid"
#define TINYSSH_PORT 22
/* TINYSSH Server Setup and Teardown */
static int tinyssh_setup(void **state)
{
struct torture_state *s = NULL;
char cmd[4096];
char pid_path[1024];
int rc;
torture_setup_socket_dir(state);
s = *state;
snprintf(pid_path,
sizeof(pid_path),
"%s/%s",
s->socket_dir,
TINYSSH_PIDFILE);
free(s->srv_pidfile);
s->srv_pidfile = strdup(pid_path);
if (s->srv_pidfile == NULL) {
return -1;
}
snprintf(cmd,
sizeof(cmd),
"%s -l %s %d -k -c \"%s %s -v %s\" "
"> %s/tinyssh.log 2>&1 & echo $! > %s",
NCAT_EXECUTABLE,
TORTURE_SSH_SERVER,
TINYSSH_PORT,
TINYSSHD_EXECUTABLE,
"",
TINYSSH_KEYS_DIR,
s->socket_dir,
s->srv_pidfile);
SSH_LOG(SSH_LOG_DEBUG, "Executing: %s\n", cmd);
rc = system(cmd);
if (rc != 0) {
return -1;
}
rc = torture_wait_for_daemon(15);
if (rc != 0) {
return -1;
}
return 0;
}
static int tinyssh_teardown(void **state)
{
struct torture_state *s = *state;
torture_terminate_process(s->srv_pidfile);
torture_teardown_socket_dir(state);
return 0;
}
/* LIBSSH Client Setup and Teardown */
static int session_setup(void **state)
{
struct torture_state *s = *state;
int verbosity = torture_libssh_verbosity();
bool process_config = false;
int port = TINYSSH_PORT;
struct passwd *pwd = NULL;
int rc;
pwd = getpwnam("bob");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = ssh_new();
assert_non_null(s->ssh.session);
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
ssh_options_set(s->ssh.session, SSH_OPTIONS_PORT, &port);
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, "bob");
ssh_options_set(s->ssh.session,
SSH_OPTIONS_PROCESS_CONFIG,
&process_config);
return 0;
}
static int session_teardown(void **state)
{
struct torture_state *s = *state;
if (s->ssh.session) {
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
}
return 0;
}
/* Algorithms Helper */
static void test_specific_algorithm(ssh_session session,
const char *kex,
const char *cipher,
const char *hostkey,
int expected_rc)
{
int rc;
char data[256];
size_t len_to_test[] = {1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 15,
16, 20, 31, 32, 33, 63, 64, 65, 100, 127, 128};
unsigned int i;
/* Set Key Exchange */
if (kex != NULL) {
rc = ssh_options_set(session, SSH_OPTIONS_KEY_EXCHANGE, kex);
assert_ssh_return_code(session, rc);
}
/* Set Ciphers */
if (cipher != NULL) {
rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_C_S, cipher);
assert_ssh_return_code(session, rc);
rc = ssh_options_set(session, SSH_OPTIONS_CIPHERS_S_C, cipher);
assert_ssh_return_code(session, rc);
}
/* Set Hostkey */
if (hostkey != NULL) {
rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, hostkey);
assert_ssh_return_code(session, rc);
}
rc = ssh_connect(session);
if (expected_rc == SSH_OK) {
assert_ssh_return_code(session, rc);
if (cipher != NULL) {
const char *used_cipher = ssh_get_cipher_out(session);
assert_non_null(used_cipher);
assert_string_equal(used_cipher, cipher);
}
if (hostkey != NULL) {
ssh_key pubkey = NULL;
const char *type_str = NULL;
rc = ssh_get_server_publickey(session, &pubkey);
assert_int_equal(rc, SSH_OK);
assert_non_null(pubkey);
type_str = ssh_key_type_to_char(ssh_key_type(pubkey));
assert_non_null(type_str);
assert_string_equal(type_str, hostkey);
ssh_key_free(pubkey);
}
memset(data, 0, sizeof(data));
for (i = 0; i < (sizeof(len_to_test) / sizeof(size_t)); i++) {
memset(data, 'A', len_to_test[i]);
ssh_send_ignore(session, data);
ssh_handle_packets(session, 50);
}
rc = ssh_userauth_none(session, NULL);
if (rc != SSH_OK) {
rc = ssh_get_error_code(session);
assert_int_equal(rc, SSH_REQUEST_DENIED);
}
} else {
assert_int_not_equal(rc, SSH_OK);
}
}
/* Test Cases */
static void torture_tinyssh_curve25519(void **state)
{
struct torture_state *s = *state;
test_specific_algorithm(s->ssh.session,
"curve25519-sha256",
NULL,
NULL,
SSH_OK);
}
static void torture_tinyssh_curve25519_libssh(void **state)
{
struct torture_state *s = *state;
test_specific_algorithm(s->ssh.session,
"curve25519-sha256@libssh.org",
NULL,
NULL,
SSH_OK);
}
static void torture_tinyssh_sntrup761(void **state)
{
struct torture_state *s = *state;
test_specific_algorithm(s->ssh.session,
"sntrup761x25519-sha512@openssh.com",
NULL,
NULL,
SSH_OK);
}
static void torture_tinyssh_chacha20(void **state)
{
struct torture_state *s = *state;
test_specific_algorithm(s->ssh.session,
NULL,
"chacha20-poly1305@openssh.com",
NULL,
SSH_OK);
}
static void torture_tinyssh_neg_cipher(void **state)
{
struct torture_state *s = *state;
/* TinySSH does not support older ciphers like aes128-cbc.*/
test_specific_algorithm(s->ssh.session,
NULL,
"aes128-cbc",
NULL,
SSH_ERROR);
}
static void torture_tinyssh_hostkey_ed25519(void **state)
{
struct torture_state *s = *state;
test_specific_algorithm(s->ssh.session, NULL, NULL, "ssh-ed25519", SSH_OK);
}
static void torture_tinyssh_neg_kex(void **state)
{
struct torture_state *s = *state;
/* TinySSH does not support legacy Diffie-Hellman groups or NIST curves.*/
test_specific_algorithm(s->ssh.session,
"diffie-hellman-group1-sha1",
NULL,
NULL,
SSH_ERROR);
}
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_tinyssh_curve25519,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_tinyssh_curve25519_libssh,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_tinyssh_sntrup761,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_tinyssh_hostkey_ed25519,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_tinyssh_chacha20,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_tinyssh_neg_cipher,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_tinyssh_neg_kex,
session_setup,
session_teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, tinyssh_setup, tinyssh_teardown);
ssh_finalize();
return rc;
}

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:

Some files were not shown because too many files have changed in this diff Show More