Compare commits

..

34 Commits

Author SHA1 Message Date
Eshan Kelkar
dc39902006 connector: Fix sftp aio read/write with ProxyJump
Addresses issue #319

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

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

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

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

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

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

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

- ssh_connector_fd_out_cb() gets called to handle the POLLOUT.

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

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

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

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

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

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

- ssh_connector_fd_out_cb() gets called to handle the POLLOUT

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

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

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

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

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

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

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

- It hangs at different points in the test

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
ea3464532e test: Tighten testing to make sure right user and key is used with proxyjumps
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
7e235f8748 auth: Log the username used for authentication
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
052c8217b7 misc: Document ssh_list_append()
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Eshan Kelkar
26b9ba5f8c bugfix: test presence of before_connection before dereferencing
A proxyjump callback structure consists of three callbacks
as of this writing: before_connection, authenticate and
verify_knownhost. One or more of these callbacks can be
set as NULL by the user to indicate that libssh should use
the defaults.

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

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

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

Signed-off-by: Eshan Kelkar <eshankelkar@galorithm.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
1b3c061aae Reproducer for memory leak from parsing knonw hosts
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <norbertpocs0@gmail.com>
2026-02-03 18:01:36 +01:00
Jakub Jelen
1525ea3dda knownhosts: Avoid memory leaks on invalid entries
When `known_hosts` file contained matching valid entry followed by
invalid entry, the first record was already allocated in
`ssh_known_hosts_read_entries()`, but not freed on error.

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

Originally reported by Kang Yang.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Norbert Pocs <norbertpocs0@gmail.com>
2026-02-03 18:01:35 +01:00
Jakub Jelen
a189c2ef4d gssapi: Sanitize input parameters
Originally reported with this patch by Brian Carpenter from Deep Fork Cyber.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-03 12:09:17 +01:00
Jakub Jelen
b2abcf8534 cmake: Propagate WITH_FINAL to abimap conditionally
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-02 19:32:16 +01:00
Jakub Jelen
809f9b7729 Require abimap 0.4.0
The version 0.4.0 fixed the issues of multi-digit version numbers
which we hit with releaseing libssh ABI version 4_10 with last
release.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-02 19:32:16 +01:00
Jakub Jelen
d297621c33 tests: Workaround softhsm-2.7.0 bug in hashed ECDSA
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-02 19:32:16 +01:00
Jakub Jelen
d936b7e81d mlkem: Use fprintf instead of internal logging function
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-02-02 19:32:16 +01:00
Shreyas Mahajan
971d44107e ci: Test against latest LibreSSL
Signed-off-by: Shreyas Mahajan <shreyasmahajan05@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-02 18:37:54 +01:00
Shreyas Mahajan
a1e49728ba crypto: Add support for Poly1305 from LibreSSL
Signed-off-by: Shreyas Mahajan <shreyasmahajan05@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-02 18:37:54 +01:00
Shreyas Mahajan
6c5459e7fc reformat libcrypto.c
Signed-off-by: Shreyas Mahajan <shreyasmahajan05@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-02 18:37:53 +01:00
Shreyas Mahajan
f47d1c797a ci: add CLI helper to run GitLab CI jobs locally
Signed-off-by: Shreyas Mahajan <shreyasmahajan05@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-02 18:37:00 +01:00
Madhav Vasisth
da27d23125 docs: document sftp_session public API type
Signed-off-by: Madhav Vasisth <mv2363@srmist.edu.in>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-02-02 18:35:35 +01:00
40 changed files with 1725 additions and 594 deletions

View File

@@ -301,6 +301,37 @@ fedora/openssl_3.x/x86_64/minimal:
make test_memcheck make test_memcheck
- cat Testing/Temporary/MemoryChecker.*.log | wc -l | grep "^0$" - cat Testing/Temporary/MemoryChecker.*.log | wc -l | grep "^0$"
fedora/libressl/x86_64:
extends: .fedora
stage: test
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
variables:
LIBRESSL_VERSION: "4.2.1"
CMAKE_ADDITIONAL_OPTIONS: >
-DCMAKE_C_FLAGS="-I/opt/libressl/include"
-DOPENSSL_ROOT_DIR=/opt/libressl
-DOPENSSL_INCLUDE_DIR=/opt/libressl/include
-DOPENSSL_CRYPTO_LIBRARY=/opt/libressl/lib/libcrypto.so
-DOPENSSL_SSL_LIBRARY=/opt/libressl/lib/libssl.so
-DWITH_GSSAPI=OFF
-DWITH_FIDO2=OFF
before_script:
- *build
- dnf install -y perl-core autoconf automake libtool pkgconf-pkg-config
- curl -LO https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-${LIBRESSL_VERSION}.tar.gz
- tar xf libressl-${LIBRESSL_VERSION}.tar.gz
- cd libressl-${LIBRESSL_VERSION}
- ./configure --prefix=/opt/libressl
- make -j$(nproc)
- make install
- cd ..
script:
- export PKG_CONFIG_PATH=/opt/libressl/lib/pkgconfig
- export LD_LIBRARY_PATH=/opt/libressl/lib
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
ctest --output-on-failure
# The PKCS#11 support is turned off as it brings dozens of memory issues from # The PKCS#11 support is turned off as it brings dozens of memory issues from
# engine_pkcs11 or openssl itself # engine_pkcs11 or openssl itself
fedora/valgrind/openssl: fedora/valgrind/openssl:

116
.gitlab-ci/local-ci.sh Executable file
View File

@@ -0,0 +1,116 @@
#!/usr/bin/env bash
set -e
RED="\033[1;31m"
GREEN="\033[1;32m"
YELLOW="\033[1;33m"
BLUE="\033[1;34m"
RESET="\033[0m"
export GCL_IGNORE_PREDEFINED_VARS=CI_REGISTRY
BASE_SHA=$(git merge-base HEAD origin/master 2>/dev/null || git rev-parse HEAD~1)
COMMON_ARGS=(
--variable "CI_MERGE_REQUEST_DIFF_BASE_SHA=$BASE_SHA"
--variable "CI_REGISTRY=registry.gitlab.com"
--json-schema-validation=false
)
check_requirements() {
for cmd in docker git gitlab-ci-local; do
if ! command -v "$cmd" >/dev/null 2>&1; then
echo -e "${RED}Missing dependency: $cmd${RESET}"
exit 1
fi
echo -e "${GREEN}Found: $cmd${RESET}"
done
if ! docker info >/dev/null 2>&1; then
echo -e "${RED}Docker daemon is not running or permission denied${RESET}"
exit 1
fi
}
list_jobs() {
gitlab-ci-local --list --json-schema-validation=false | awk 'NR>1 {print $1}'
}
run_job() {
JOB="$1"
echo -e "${YELLOW}Running CI job: $JOB${RESET}"
gitlab-ci-local "$JOB" "${COMMON_ARGS[@]}"
}
cleanup_images() {
echo -e "${BLUE}Removing libssh CI images only...${RESET}"
docker images --format "{{.Repository}}:{{.Tag}} {{.ID}}" \
| grep "$CI_REGISTRY/$BUILD_IMAGES_PROJECT" \
| awk '{print $2}' \
| xargs -r docker rmi -f
}
usage() {
echo
echo -e "${BLUE}Usage:${RESET}"
echo " $0 --list"
echo " $0 --run <job-name>"
echo " $0 --all"
echo " $0 --run <job-name> --clean"
echo " $0 --all --clean"
echo
exit 1
}
check_requirements
CLEAN=0
MODE=""
JOB=""
while [[ $# -gt 0 ]]; do
case "$1" in
--list)
MODE="list"
shift
;;
--run)
MODE="run"
JOB="$2"
shift 2
;;
--all)
MODE="all"
shift
;;
--clean)
CLEAN=1
shift
;;
*)
usage
;;
esac
done
case "$MODE" in
list)
list_jobs
;;
run)
[[ -z "$JOB" ]] && usage
run_job "$JOB"
[[ "$CLEAN" -eq 1 ]] && cleanup_images
;;
all)
for job in $(list_jobs); do
run_job "$job"
[[ "$CLEAN" -eq 1 ]] && cleanup_images
done
;;
*)
usage
;;
esac
echo -e "${GREEN}Done.${RESET}"

View File

@@ -90,7 +90,7 @@ endif (WITH_FIDO2)
# Disable symbol versioning in non UNIX platforms # Disable symbol versioning in non UNIX platforms
if (UNIX) if (UNIX)
find_package(ABIMap 0.3.1) find_package(ABIMap 0.4.0)
else (UNIX) else (UNIX)
set(WITH_SYMBOL_VERSIONING OFF) set(WITH_SYMBOL_VERSIONING OFF)
endif (UNIX) endif (UNIX)
@@ -181,6 +181,10 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
set(ALLOW_ABI_BREAK "BREAK_ABI") set(ALLOW_ABI_BREAK "BREAK_ABI")
endif() endif()
if (WITH_FINAL)
set(FINAL "FINAL")
endif()
# Target we can depend on in 'make dist' # Target we can depend on in 'make dist'
set(_SYMBOL_TARGET "${PROJECT_NAME}.map") set(_SYMBOL_TARGET "${PROJECT_NAME}.map")
@@ -193,7 +197,7 @@ if (WITH_SYMBOL_VERSIONING AND ABIMAP_FOUND)
RELEASE_NAME_VERSION ${PROJECT_NAME}_${LIBRARY_VERSION} RELEASE_NAME_VERSION ${PROJECT_NAME}_${LIBRARY_VERSION}
CURRENT_MAP ${MAP_PATH} CURRENT_MAP ${MAP_PATH}
COPY_TO ${MAP_PATH} COPY_TO ${MAP_PATH}
FINAL ${FINAL}
${ALLOW_ABI_BREAK}) ${ALLOW_ABI_BREAK})
# Write the current version to the source # Write the current version to the source

View File

@@ -137,6 +137,33 @@ The script exceeded the maximum execution time set for the job
Note, that the built dependencies are cached so after successful build in your Note, that the built dependencies are cached so after successful build in your
namespace, the rebuilds should be much faster. namespace, the rebuilds should be much faster.
## Running GitLab CI locally (optional helper)
For contributors working on CI, build system changes, or adding new CI jobs, it can be useful to run GitLab CI pipelines locally before pushing.
libssh provides a small helper script based on `gitlab-ci-local` that can:
- List all jobs defined in `.gitlab-ci.yml`
- Run a specific job or the full pipeline locally
- Automatically pick up new jobs when they are added to the CI configuration
- Optionally clean up CI Docker images after execution
### Requirements
- Docker (daemon running)
- git
- gitlab-ci-local
https://github.com/firecow/gitlab-ci-local
### Usage
```bash
./.gitlab-ci/local-ci.sh --list
./.gitlab-ci/local-ci.sh --run fedora/libressl/x86_64
./.gitlab-ci/local-ci.sh --all
./.gitlab-ci/local-ci.sh --run fedora/libressl/x86_64 --clean
```
# Coding conventions in the libssh tree # Coding conventions in the libssh tree
## Quick Start ## Quick Start

View File

@@ -189,7 +189,6 @@ if (DOXYGEN_FOUND)
sftp_message, sftp_message,
sftp_packet, sftp_packet,
sftp_request_queue, sftp_request_queue,
sftp_session,
sftp_status_message, sftp_status_message,
sftp_statvfs_t, sftp_statvfs_t,
poll_fn, poll_fn,

View File

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

View File

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

View File

@@ -43,8 +43,9 @@ extern "C" {
/* in misc.c */ /* in misc.c */
/* gets the user home dir. */ /* 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_username(void);
char *ssh_get_local_hostname(void);
int ssh_file_readaccess_ok(const char *file); int ssh_file_readaccess_ok(const char *file);
int ssh_dir_writeable(const char *path); int ssh_dir_writeable(const char *path);

View File

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

View File

@@ -74,6 +74,21 @@ typedef struct sftp_file_struct* sftp_file;
typedef struct sftp_message_struct* sftp_message; typedef struct sftp_message_struct* sftp_message;
typedef struct sftp_packet_struct* sftp_packet; typedef struct sftp_packet_struct* sftp_packet;
typedef struct sftp_request_queue_struct* sftp_request_queue; typedef struct sftp_request_queue_struct* sftp_request_queue;
/**
* @brief SFTP session handle.
*
* This type represents an active SFTP session associated with an SSH channel.
* It is created and destroyed via the libssh SFTP API and is internally
* managed by libssh. It is used by applications to perform SFTP operations
* such as file access and directory management.
*
* The internal structure of this type is opaque and must not be accessed
* directly by applications.
*
* @see sftp_new
* @see sftp_free
*/
typedef struct sftp_session_struct* sftp_session; typedef struct sftp_session_struct* sftp_session;
typedef struct sftp_status_message_struct* sftp_status_message; typedef struct sftp_status_message_struct* sftp_status_message;
typedef struct sftp_statvfs_struct* sftp_statvfs_t; typedef struct sftp_statvfs_struct* sftp_statvfs_t;

View File

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

View File

@@ -493,6 +493,10 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
bool parse_entry = do_parsing; bool parse_entry = do_parsing;
bool libssh_proxy_jump = ssh_libssh_proxy_jumps(); 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 */ /* Special value none disables the proxy */
cmp = strcasecmp(s, "none"); cmp = strcasecmp(s, "none");
if (cmp == 0) { if (cmp == 0) {
@@ -509,6 +513,17 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
return SSH_ERROR; return SSH_ERROR;
} }
if (do_parsing) {
/* Store the whole string in sesion */
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; cp = c;
do { do {
endp = strchr(cp, ','); endp = strchr(cp, ',');

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 occured.
*
* @warning This does not consider the case when the io indicated failure
*
* @warning This does not consider the case when the input indicated that
* EOF was encountered.
*/
static void ssh_connector_update_flags_after_io(ssh_connector connector)
{
/*
* With fds we can afford to mark:
* - in_available as 0 after an fd read (even if more pending data can be
* immediately read from the fd)
*
* - out_wontblock as 0 after an fd write (even if more data can
* be written to the fd without blocking)
*
* since poll events set on the fd will get raised to indicate
* possibility of read/write in case existing situation is apt
* (i.e can read/write occur right now) or if situation becomes
* apt in future (read data becomes available, write becomes
* possible)
*/
/*
* On the other hand, with channels we need to be more careful
* before claiming read/write not possible because channel callbacks
* are called in limited scenarios.
*
* (e.g. connector callback to indicate read data available on input
* channel is called only when new data is received on channel. It is
* not called when we have some pending data in channel's buffers but
* don't receive any new data on the channel)
*
* Hence, in case of channels, blindly setting flag associated with
* read/write input/output to 0 after a read/write may not be a good
* idea as the callback that sets it back to 1 again may not be ever
* called again.
*/
uint32_t window_size;
/* update in_available based on input source (fd or channel) */
if (connector->in_fd != SSH_INVALID_SOCKET) {
connector->in_available = 0;
} else if (connector->in_channel != NULL) {
if (ssh_channel_poll_timeout(connector->in_channel, 0, 0) > 0) {
connector->in_available = 1;
} else {
connector->in_available = 0;
}
} else {
/* connector input is invalid ! */
return;
}
/* update out_wontblock based on output source (fd or channel) */
if (connector->out_fd != SSH_INVALID_SOCKET) {
connector->out_wontblock = 0;
} else if (connector->out_channel != NULL) {
window_size = ssh_channel_window_size(connector->out_channel);
if (window_size > 0) {
connector->out_wontblock = 1;
} else {
connector->out_wontblock = 0;
}
} else {
/* connector output is invalid ! */
return;
}
}
/** /**
* @internal * @internal
* *
@@ -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"); ssh_set_error(connector->session, SSH_FATAL, "output socket or channel closed");
return; return;
} }
connector->out_wontblock = 0;
connector->in_available = 0; ssh_connector_update_flags_after_io(connector);
} else { } else {
connector->in_available = 1; connector->in_available = 1;
} }
@@ -444,8 +525,8 @@ ssh_connector_fd_out_cb(ssh_connector connector)
"Output socket or channel closed"); "Output socket or channel closed");
return; return;
} }
connector->in_available = 0;
connector->out_wontblock = 0; ssh_connector_update_flags_after_io(connector);
} else { } else {
connector->out_wontblock = 1; connector->out_wontblock = 1;
} }
@@ -542,16 +623,8 @@ static int ssh_connector_channel_data_cb(ssh_session session,
window_len = MIN(window, len); window_len = MIN(window, len);
/* Route the data to the right exception channel */ /* Route the data to the right exception channel */
if (is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR)) { if (connector->out_flags & SSH_CONNECTOR_STDOUT &&
w = ssh_channel_write_stderr(connector->out_channel, !(is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR))) {
data,
window_len);
} else if (!is_stderr &&
(connector->out_flags & SSH_CONNECTOR_STDOUT)) {
w = ssh_channel_write(connector->out_channel,
data,
window_len);
} else if (connector->out_flags & SSH_CONNECTOR_STDOUT) {
w = ssh_channel_write(connector->out_channel, w = ssh_channel_write(connector->out_channel,
data, data,
window_len); window_len);
@@ -574,11 +647,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
return SSH_ERROR; return SSH_ERROR;
} }
connector->out_wontblock = 0; ssh_connector_update_flags_after_io(connector);
connector->in_available = 0;
if ((unsigned int)w < len) {
connector->in_available = 1;
}
ssh_connector_reset_pollevents(connector); ssh_connector_reset_pollevents(connector);
return w; return w;
@@ -650,8 +719,8 @@ ssh_connector_channel_write_wontblock_cb(ssh_session session,
return 0; return 0;
} }
connector->in_available = 0;
connector->out_wontblock = 0; ssh_connector_update_flags_after_io(connector);
} else { } else {
connector->out_wontblock = 1; connector->out_wontblock = 1;
} }

View File

@@ -28,8 +28,9 @@
#include "config.h" #include "config.h"
#include <stdio.h>
#include "libssh/mlkem_native.h" #include "libssh/mlkem_native.h"
#include "libssh/priv.h"
#if !defined(__GNUC__) || (__GNUC__ < 2) #if !defined(__GNUC__) || (__GNUC__ < 2)
# define __attribute__(x) # define __attribute__(x)
@@ -38,7 +39,7 @@
#define KRML_NOINLINE __attribute__((noinline, unused)) #define KRML_NOINLINE __attribute__((noinline, unused))
#define KRML_HOST_EPRINTF(...) #define KRML_HOST_EPRINTF(...)
#define KRML_HOST_EXIT(x) do { \ #define KRML_HOST_EXIT(x) do { \
SSH_LOG(SSH_LOG_WARNING, "internal error"); \ fprintf(stderr, "mlkem internal error"); \
exit(x); \ exit(x); \
} while (0) } while (0)

View File

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

View File

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

View File

@@ -216,11 +216,22 @@ ssh_known_hosts_entries_compare(struct ssh_knownhosts_entry *k1,
return 0; return 0;
} }
/* This method reads the known_hosts file referenced by the path /**
* @internal
*
* @brief Read entries from filename to provided list
*
* This method reads the known_hosts file referenced by the path
* in filename argument, and entries matching the match argument * in filename argument, and entries matching the match argument
* will be added to the list in entries argument. * will be added to the list in entries argument.
* If the entries list is NULL, it will allocate a new list. Caller * If the entries list is NULL, it will allocate a new list. Caller
* is responsible to free it even if an error occurs. * is responsible to free it even if an error occurs.
*
* @param match[in] The host name (with port) to match against
* @param filename[in] The known hosts file to parse
* @param entries[in,out] The list of entries to append matching ones
* @return `SSH_OK` on missing file or success parsing,
* `SSH_ERROR` on error
*/ */
static int ssh_known_hosts_read_entries(const char *match, static int ssh_known_hosts_read_entries(const char *match,
const char *filename, const char *filename,
@@ -346,6 +357,33 @@ static char *ssh_session_get_host_port(ssh_session session)
/** /**
* @internal * @internal
*
* @brief Free known hosts entries list
*
* @param[in] entry_list The list of ssh_knownhosts_entry items
*/
static void ssh_knownhosts_entries_free(struct ssh_list *entry_list)
{
struct ssh_iterator *it = NULL;
if (entry_list == NULL) {
return;
}
for (it = ssh_list_get_iterator(entry_list);
it != NULL;
it = ssh_list_get_iterator(entry_list)) {
struct ssh_knownhosts_entry *entry = NULL;
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
ssh_knownhosts_entry_free(entry);
ssh_list_remove(entry_list, it);
}
ssh_list_free(entry_list);
}
/**
* @internal
*
* @brief Check which host keys should be preferred for the session. * @brief Check which host keys should be preferred for the session.
* *
* This checks the known_hosts file to find out which algorithms should be * This checks the known_hosts file to find out which algorithms should be
@@ -453,7 +491,7 @@ struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session)
return list; return list;
error: error:
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
ssh_list_free(list); ssh_list_free(list);
return NULL; return NULL;
} }
@@ -505,6 +543,7 @@ static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e typ
/** /**
* @internal * @internal
*
* @brief Get the host keys algorithms identifiers from the known_hosts files * @brief Get the host keys algorithms identifiers from the known_hosts files
* *
* This expands the signatures types that can be generated from the keys types * This expands the signatures types that can be generated from the keys types
@@ -549,7 +588,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return NULL; return NULL;
} }
@@ -558,7 +597,7 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session)
&entry_list); &entry_list);
SAFE_FREE(host_port); SAFE_FREE(host_port);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return NULL; return NULL;
} }
@@ -799,7 +838,6 @@ out:
enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
{ {
struct ssh_list *entry_list = NULL; struct ssh_list *entry_list = NULL;
struct ssh_iterator *it = NULL;
char *host_port = NULL; char *host_port = NULL;
bool global_known_hosts_found = false; bool global_known_hosts_found = false;
bool known_hosts_found = false; bool known_hosts_found = false;
@@ -860,7 +898,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_ERROR; return SSH_KNOWN_HOSTS_ERROR;
} }
} }
@@ -871,7 +909,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
SAFE_FREE(host_port); SAFE_FREE(host_port);
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_ERROR; return SSH_KNOWN_HOSTS_ERROR;
} }
} }
@@ -883,16 +921,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session)
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
for (it = ssh_list_get_iterator(entry_list); ssh_knownhosts_entries_free(entry_list);
it != NULL;
it = ssh_list_get_iterator(entry_list)) {
struct ssh_knownhosts_entry *entry = NULL;
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
ssh_knownhosts_entry_free(entry);
ssh_list_remove(entry_list, it);
}
ssh_list_free(entry_list);
return SSH_KNOWN_HOSTS_OK; return SSH_KNOWN_HOSTS_OK;
} }
@@ -1079,13 +1108,13 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
filename, filename,
&entry_list); &entry_list);
if (rc != 0) { if (rc != 0) {
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
it = ssh_list_get_iterator(entry_list); it = ssh_list_get_iterator(entry_list);
if (it == NULL) { if (it == NULL) {
ssh_list_free(entry_list); ssh_knownhosts_entries_free(entry_list);
return SSH_KNOWN_HOSTS_UNKNOWN; return SSH_KNOWN_HOSTS_UNKNOWN;
} }
@@ -1115,16 +1144,7 @@ ssh_known_hosts_check_server_key(const char *hosts_entry,
} }
} }
for (it = ssh_list_get_iterator(entry_list); ssh_knownhosts_entries_free(entry_list);
it != NULL;
it = ssh_list_get_iterator(entry_list)) {
struct ssh_knownhosts_entry *entry = NULL;
entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it);
ssh_knownhosts_entry_free(entry);
ssh_list_remove(entry_list, it);
}
ssh_list_free(entry_list);
return found; return found;
} }
@@ -1196,6 +1216,8 @@ ssh_session_get_known_hosts_entry(ssh_session session,
} }
/** /**
* @internal
*
* @brief Get the known_hosts entry for the current connected session * @brief Get the known_hosts entry for the current connected session
* from the given known_hosts file. * from the given known_hosts file.
* *

View File

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

View File

@@ -40,7 +40,9 @@
#endif #endif
#ifdef HAVE_LIBCRYPTO #ifdef HAVE_LIBCRYPTO
#ifdef LIBRESSL_VERSION_NUMBER
#include <openssl/poly1305.h>
#endif
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/md5.h> #include <openssl/md5.h>
#include <openssl/opensslv.h> #include <openssl/opensslv.h>
@@ -572,12 +574,11 @@ static void evp_cipher_cleanup(struct ssh_cipher_struct *cipher) {
} }
} }
static int static int evp_cipher_aead_get_length(struct ssh_cipher_struct *cipher,
evp_cipher_aead_get_length(struct ssh_cipher_struct *cipher, void *in,
void *in, uint8_t *out,
uint8_t *out, size_t len,
size_t len, uint64_t seq)
uint64_t seq)
{ {
(void)cipher; (void)cipher;
(void)seq; (void)seq;
@@ -588,13 +589,12 @@ evp_cipher_aead_get_length(struct ssh_cipher_struct *cipher,
return SSH_OK; return SSH_OK;
} }
static void static void evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *in, void *out,
void *out, size_t len,
size_t len, uint8_t *tag,
uint8_t *tag, uint64_t seq)
uint64_t seq)
{ {
size_t authlen, aadlen; size_t authlen, aadlen;
uint8_t lastiv[1]; uint8_t lastiv[1];
@@ -608,10 +608,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
authlen = cipher->tag_size; authlen = cipher->tag_size;
/* increment IV */ /* increment IV */
rc = EVP_CIPHER_CTX_ctrl(cipher->ctx, rc = EVP_CIPHER_CTX_ctrl(cipher->ctx, EVP_CTRL_GCM_IV_GEN, 1, lastiv);
EVP_CTRL_GCM_IV_GEN,
1,
lastiv);
if (rc == 0) { if (rc == 0) {
SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed");
return; return;
@@ -643,9 +640,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
} }
/* compute tag */ /* compute tag */
rc = EVP_EncryptFinal(cipher->ctx, rc = EVP_EncryptFinal(cipher->ctx, NULL, &tmplen);
NULL,
&tmplen);
if (rc < 0) { if (rc < 0) {
SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal failed: Failed to create a tag"); SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal failed: Failed to create a tag");
return; return;
@@ -661,12 +656,11 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
} }
} }
static int static int evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher, void *complete_packet,
void *complete_packet, uint8_t *out,
uint8_t *out, size_t encrypted_size,
size_t encrypted_size, uint64_t seq)
uint64_t seq)
{ {
size_t authlen, aadlen; size_t authlen, aadlen;
uint8_t lastiv[1]; uint8_t lastiv[1];
@@ -679,10 +673,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
authlen = cipher->tag_size; authlen = cipher->tag_size;
/* increment IV */ /* increment IV */
rc = EVP_CIPHER_CTX_ctrl(cipher->ctx, rc = EVP_CIPHER_CTX_ctrl(cipher->ctx, EVP_CTRL_GCM_IV_GEN, 1, lastiv);
EVP_CTRL_GCM_IV_GEN,
1,
lastiv);
if (rc == 0) { if (rc == 0) {
SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_IV_GEN failed");
return SSH_ERROR; return SSH_ERROR;
@@ -692,7 +683,8 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
rc = EVP_CIPHER_CTX_ctrl(cipher->ctx, rc = EVP_CIPHER_CTX_ctrl(cipher->ctx,
EVP_CTRL_GCM_SET_TAG, EVP_CTRL_GCM_SET_TAG,
(int)authlen, (int)authlen,
(unsigned char *)complete_packet + aadlen + encrypted_size); (unsigned char *)complete_packet + aadlen +
encrypted_size);
if (rc == 0) { if (rc == 0) {
SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_TAG failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_CTRL_GCM_SET_TAG failed");
return SSH_ERROR; return SSH_ERROR;
@@ -731,11 +723,10 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
} }
/* verify tag */ /* verify tag */
rc = EVP_DecryptFinal(cipher->ctx, rc = EVP_DecryptFinal(cipher->ctx, NULL, &outlen);
NULL,
&outlen);
if (rc < 0) { if (rc < 0) {
SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptFinal failed: Failed authentication"); SSH_LOG(SSH_LOG_TRACE,
"EVP_DecryptFinal failed: Failed authentication");
return SSH_ERROR; return SSH_ERROR;
} }
@@ -749,7 +740,10 @@ struct chacha20_poly1305_keysched {
EVP_CIPHER_CTX *main_evp; EVP_CIPHER_CTX *main_evp;
/* cipher handle used for encrypting the length field */ /* cipher handle used for encrypting the length field */
EVP_CIPHER_CTX *header_evp; EVP_CIPHER_CTX *header_evp;
#if OPENSSL_VERSION_NUMBER < 0x30000000L #if defined(LIBRESSL_VERSION_NUMBER)
/* LibreSSL Poly1305 context */
poly1305_context poly_ctx;
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
/* mac handle used for authenticating the packets */ /* mac handle used for authenticating the packets */
EVP_PKEY_CTX *pctx; EVP_PKEY_CTX *pctx;
/* Poly1305 key */ /* Poly1305 key */
@@ -762,8 +756,7 @@ struct chacha20_poly1305_keysched {
#endif /* OPENSSL_VERSION_NUMBER */ #endif /* OPENSSL_VERSION_NUMBER */
}; };
static void static void chacha20_poly1305_cleanup(struct ssh_cipher_struct *cipher)
chacha20_poly1305_cleanup(struct ssh_cipher_struct *cipher)
{ {
struct chacha20_poly1305_keysched *ctx = NULL; struct chacha20_poly1305_keysched *ctx = NULL;
@@ -774,10 +767,12 @@ chacha20_poly1305_cleanup(struct ssh_cipher_struct *cipher)
ctx = cipher->chacha20_schedule; ctx = cipher->chacha20_schedule;
EVP_CIPHER_CTX_free(ctx->main_evp); EVP_CIPHER_CTX_free(ctx->main_evp);
ctx->main_evp = NULL; ctx->main_evp = NULL;
EVP_CIPHER_CTX_free(ctx->header_evp); EVP_CIPHER_CTX_free(ctx->header_evp);
ctx->header_evp = NULL; ctx->header_evp = NULL;
#if OPENSSL_VERSION_NUMBER < 0x30000000L #if defined(LIBRESSL_VERSION_NUMBER)
/* nothing to free */
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
/* ctx->pctx is freed as part of MD context */ /* ctx->pctx is freed as part of MD context */
EVP_PKEY_free(ctx->key); EVP_PKEY_free(ctx->key);
ctx->key = NULL; ctx->key = NULL;
@@ -791,10 +786,9 @@ chacha20_poly1305_cleanup(struct ssh_cipher_struct *cipher)
SAFE_FREE(cipher->chacha20_schedule); SAFE_FREE(cipher->chacha20_schedule);
} }
static int static int chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher, void *key,
void *key, UNUSED_PARAM(void *IV))
UNUSED_PARAM(void *IV))
{ {
struct chacha20_poly1305_keysched *ctx = NULL; struct chacha20_poly1305_keysched *ctx = NULL;
uint8_t *u8key = key; uint8_t *u8key = key;
@@ -841,7 +835,9 @@ chacha20_poly1305_set_key(struct ssh_cipher_struct *cipher,
/* The Poly1305 key initialization is delayed to the time we know /* The Poly1305 key initialization is delayed to the time we know
* the actual key for packet so we do not need to create a bogus keys * the actual key for packet so we do not need to create a bogus keys
*/ */
#if OPENSSL_VERSION_NUMBER < 0x30000000L #if defined(LIBRESSL_VERSION_NUMBER)
/* nothing, poly1305_context is stack based */
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
ctx->mctx = EVP_MD_CTX_new(); ctx->mctx = EVP_MD_CTX_new();
if (ctx->mctx == NULL) { if (ctx->mctx == NULL) {
SSH_LOG(SSH_LOG_TRACE, "EVP_MD_CTX_new failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_MD_CTX_new failed");
@@ -873,10 +869,9 @@ out:
static const uint8_t zero_block[CHACHA20_BLOCKSIZE] = {0}; static const uint8_t zero_block[CHACHA20_BLOCKSIZE] = {0};
static int static int chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher, uint64_t seq,
uint64_t seq, int do_encrypt)
int do_encrypt)
{ {
struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule; struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule;
uint8_t seqbuf[16] = {0}; uint8_t seqbuf[16] = {0};
@@ -906,10 +901,9 @@ chacha20_poly1305_set_iv(struct ssh_cipher_struct *cipher,
return SSH_OK; return SSH_OK;
} }
static int static int chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher, uint64_t seq,
uint64_t seq, int do_encrypt)
int do_encrypt)
{ {
struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule; struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule;
uint8_t poly_key[CHACHA20_BLOCKSIZE]; uint8_t poly_key[CHACHA20_BLOCKSIZE];
@@ -935,12 +929,17 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
ssh_log_hexdump("poly_key", poly_key, POLY1305_KEYLEN); ssh_log_hexdump("poly_key", poly_key, POLY1305_KEYLEN);
#endif /* DEBUG_CRYPTO */ #endif /* DEBUG_CRYPTO */
/* Set the Poly1305 key */ /* LibreSSL path: use direct Poly1305 implementation */
#if OPENSSL_VERSION_NUMBER < 0x30000000L #if defined(LIBRESSL_VERSION_NUMBER)
CRYPTO_poly1305_init(&ctx->poly_ctx, poly_key);
/* Set the Poly1305 key */
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
if (ctx->key == NULL) { if (ctx->key == NULL) {
/* Poly1305 Initialization needs to know the actual key */ /* Poly1305 Initialization needs to know the actual key */
ctx->key = EVP_PKEY_new_mac_key(EVP_PKEY_POLY1305, NULL, ctx->key = EVP_PKEY_new_mac_key(EVP_PKEY_POLY1305,
poly_key, POLY1305_KEYLEN); NULL,
poly_key,
POLY1305_KEYLEN);
if (ctx->key == NULL) { if (ctx->key == NULL) {
SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_new_mac_key failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_new_mac_key failed");
goto out; goto out;
@@ -952,9 +951,12 @@ chacha20_poly1305_packet_setup(struct ssh_cipher_struct *cipher,
} }
} else { } else {
/* Updating the key is easier but less obvious */ /* Updating the key is easier but less obvious */
rv = EVP_PKEY_CTX_ctrl(ctx->pctx, -1, EVP_PKEY_OP_SIGNCTX, rv = EVP_PKEY_CTX_ctrl(ctx->pctx,
EVP_PKEY_CTRL_SET_MAC_KEY, -1,
POLY1305_KEYLEN, (void *)poly_key); EVP_PKEY_OP_SIGNCTX,
EVP_PKEY_CTRL_SET_MAC_KEY,
POLY1305_KEYLEN,
(void *)poly_key);
if (rv <= 0) { if (rv <= 0) {
SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_CTX_ctrl failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_PKEY_CTX_ctrl failed");
goto out; goto out;
@@ -1017,20 +1019,21 @@ chacha20_poly1305_aead_decrypt_length(struct ssh_cipher_struct *cipher,
return SSH_OK; return SSH_OK;
} }
static int static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher, void *complete_packet,
void *complete_packet, uint8_t *out,
uint8_t *out, size_t encrypted_size,
size_t encrypted_size, uint64_t seq)
uint64_t seq)
{ {
struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule; struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule;
uint8_t *mac = (uint8_t *)complete_packet + sizeof(uint32_t) + uint8_t *mac =
encrypted_size; (uint8_t *)complete_packet + sizeof(uint32_t) + encrypted_size;
uint8_t tag[POLY1305_TAGLEN] = {0}; uint8_t tag[POLY1305_TAGLEN] = {0};
int ret = SSH_ERROR; int ret = SSH_ERROR;
int rv, cmp, len = 0; int rv, cmp, len = 0;
#if !defined(LIBRESSL_VERSION_NUMBER)
size_t taglen = POLY1305_TAGLEN; size_t taglen = POLY1305_TAGLEN;
#endif
/* Prepare the Poly1305 key */ /* Prepare the Poly1305 key */
rv = chacha20_poly1305_packet_setup(cipher, seq, 0); rv = chacha20_poly1305_packet_setup(cipher, seq, 0);
@@ -1044,7 +1047,13 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
#endif /* DEBUG_CRYPTO */ #endif /* DEBUG_CRYPTO */
/* Calculate MAC of received data */ /* Calculate MAC of received data */
#if OPENSSL_VERSION_NUMBER < 0x30000000L #if defined(LIBRESSL_VERSION_NUMBER)
CRYPTO_poly1305_update(&ctx->poly_ctx,
complete_packet,
encrypted_size + sizeof(uint32_t));
CRYPTO_poly1305_finish(&ctx->poly_ctx, tag);
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
rv = EVP_DigestSignUpdate(ctx->mctx, complete_packet, rv = EVP_DigestSignUpdate(ctx->mctx, complete_packet,
encrypted_size + sizeof(uint32_t)); encrypted_size + sizeof(uint32_t));
if (rv != 1) { if (rv != 1) {
@@ -1058,7 +1067,8 @@ chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
goto out; goto out;
} }
#else #else
rv = EVP_MAC_update(ctx->mctx, complete_packet, rv = EVP_MAC_update(ctx->mctx,
complete_packet,
encrypted_size + sizeof(uint32_t)); encrypted_size + sizeof(uint32_t));
if (rv != 1) { if (rv != 1) {
SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed");
@@ -1106,17 +1116,18 @@ out:
return ret; return ret;
} }
static void static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher, void *in,
void *in, void *out,
void *out, size_t len,
size_t len, uint8_t *tag,
uint8_t *tag, uint64_t seq)
uint64_t seq)
{ {
struct ssh_packet_header *in_packet = in, *out_packet = out; struct ssh_packet_header *in_packet = in, *out_packet = out;
struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule; struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule;
#if !defined(LIBRESSL_VERSION_NUMBER)
size_t taglen = POLY1305_TAGLEN; size_t taglen = POLY1305_TAGLEN;
#endif
int ret, outlen = 0; int ret, outlen = 0;
/* Prepare the Poly1305 key */ /* Prepare the Poly1305 key */
@@ -1128,7 +1139,8 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
#ifdef DEBUG_CRYPTO #ifdef DEBUG_CRYPTO
ssh_log_hexdump("plaintext length", ssh_log_hexdump("plaintext length",
(unsigned char *)&in_packet->length, sizeof(uint32_t)); (unsigned char *)&in_packet->length,
sizeof(uint32_t));
#endif /* DEBUG_CRYPTO */ #endif /* DEBUG_CRYPTO */
/* step 2, encrypt length field */ /* step 2, encrypt length field */
ret = EVP_CipherUpdate(ctx->header_evp, ret = EVP_CipherUpdate(ctx->header_evp,
@@ -1142,7 +1154,8 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
} }
#ifdef DEBUG_CRYPTO #ifdef DEBUG_CRYPTO
ssh_log_hexdump("encrypted length", ssh_log_hexdump("encrypted length",
(unsigned char *)&out_packet->length, outlen); (unsigned char *)&out_packet->length,
outlen);
#endif /* DEBUG_CRYPTO */ #endif /* DEBUG_CRYPTO */
ret = EVP_CipherFinal_ex(ctx->header_evp, (uint8_t *)out + outlen, &outlen); ret = EVP_CipherFinal_ex(ctx->header_evp, (uint8_t *)out + outlen, &outlen);
if (ret != 1 || outlen != 0) { if (ret != 1 || outlen != 0) {
@@ -1163,7 +1176,13 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
} }
/* step 4, compute the MAC */ /* step 4, compute the MAC */
#if OPENSSL_VERSION_NUMBER < 0x30000000L #if defined(LIBRESSL_VERSION_NUMBER)
CRYPTO_poly1305_update(&ctx->poly_ctx,
(const unsigned char *)out_packet,
len);
CRYPTO_poly1305_finish(&ctx->poly_ctx, tag);
#elif OPENSSL_VERSION_NUMBER < 0x30000000L
ret = EVP_DigestSignUpdate(ctx->mctx, out_packet, len); ret = EVP_DigestSignUpdate(ctx->mctx, out_packet, len);
if (ret <= 0) { if (ret <= 0) {
SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignUpdate failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_DigestSignUpdate failed");
@@ -1175,7 +1194,7 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
return; return;
} }
#else #else
ret = EVP_MAC_update(ctx->mctx, (void*)out_packet, len); ret = EVP_MAC_update(ctx->mctx, (void *)out_packet, len);
if (ret != 1) { if (ret != 1) {
SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed"); SSH_LOG(SSH_LOG_TRACE, "EVP_MAC_update failed");
return; return;
@@ -1191,11 +1210,10 @@ chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
#endif /* HAVE_OPENSSL_EVP_CHACHA20 */ #endif /* HAVE_OPENSSL_EVP_CHACHA20 */
#ifdef WITH_INSECURE_NONE #ifdef WITH_INSECURE_NONE
static void static void none_crypt(UNUSED_PARAM(struct ssh_cipher_struct *cipher),
none_crypt(UNUSED_PARAM(struct ssh_cipher_struct *cipher), void *in,
void *in, void *out,
void *out, size_t len)
size_t len)
{ {
memcpy(out, in, len); memcpy(out, in, len);
} }
@@ -1206,163 +1224,163 @@ none_crypt(UNUSED_PARAM(struct ssh_cipher_struct *cipher),
*/ */
static struct ssh_cipher_struct ssh_ciphertab[] = { static struct ssh_cipher_struct ssh_ciphertab[] = {
#ifdef HAVE_BLOWFISH #ifdef HAVE_BLOWFISH
{ {
.name = "blowfish-cbc", .name = "blowfish-cbc",
.blocksize = 8, .blocksize = 8,
.ciphertype = SSH_BLOWFISH_CBC, .ciphertype = SSH_BLOWFISH_CBC,
.keysize = 128, .keysize = 128,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt, .encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt, .decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
#endif /* HAVE_BLOWFISH */ #endif /* HAVE_BLOWFISH */
#ifdef HAS_AES #ifdef HAS_AES
{ {
.name = "aes128-ctr", .name = "aes128-ctr",
.blocksize = AES_BLOCK_SIZE, .blocksize = AES_BLOCK_SIZE,
.ciphertype = SSH_AES128_CTR, .ciphertype = SSH_AES128_CTR,
.keysize = 128, .keysize = 128,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt, .encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt, .decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
{ {
.name = "aes192-ctr", .name = "aes192-ctr",
.blocksize = AES_BLOCK_SIZE, .blocksize = AES_BLOCK_SIZE,
.ciphertype = SSH_AES192_CTR, .ciphertype = SSH_AES192_CTR,
.keysize = 192, .keysize = 192,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt, .encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt, .decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
{ {
.name = "aes256-ctr", .name = "aes256-ctr",
.blocksize = AES_BLOCK_SIZE, .blocksize = AES_BLOCK_SIZE,
.ciphertype = SSH_AES256_CTR, .ciphertype = SSH_AES256_CTR,
.keysize = 256, .keysize = 256,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt, .encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt, .decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
{ {
.name = "aes128-cbc", .name = "aes128-cbc",
.blocksize = AES_BLOCK_SIZE, .blocksize = AES_BLOCK_SIZE,
.ciphertype = SSH_AES128_CBC, .ciphertype = SSH_AES128_CBC,
.keysize = 128, .keysize = 128,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt, .encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt, .decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
{ {
.name = "aes192-cbc", .name = "aes192-cbc",
.blocksize = AES_BLOCK_SIZE, .blocksize = AES_BLOCK_SIZE,
.ciphertype = SSH_AES192_CBC, .ciphertype = SSH_AES192_CBC,
.keysize = 192, .keysize = 192,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt, .encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt, .decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
{ {
.name = "aes256-cbc", .name = "aes256-cbc",
.blocksize = AES_BLOCK_SIZE, .blocksize = AES_BLOCK_SIZE,
.ciphertype = SSH_AES256_CBC, .ciphertype = SSH_AES256_CBC,
.keysize = 256, .keysize = 256,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt, .encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt, .decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
{ {
.name = "aes128-gcm@openssh.com", .name = "aes128-gcm@openssh.com",
.blocksize = AES_BLOCK_SIZE, .blocksize = AES_BLOCK_SIZE,
.lenfield_blocksize = 4, /* not encrypted, but authenticated */ .lenfield_blocksize = 4, /* not encrypted, but authenticated */
.ciphertype = SSH_AEAD_AES128_GCM, .ciphertype = SSH_AEAD_AES128_GCM,
.keysize = 128, .keysize = 128,
.tag_size = AES_GCM_TAGLEN, .tag_size = AES_GCM_TAGLEN,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.aead_encrypt = evp_cipher_aead_encrypt, .aead_encrypt = evp_cipher_aead_encrypt,
.aead_decrypt_length = evp_cipher_aead_get_length, .aead_decrypt_length = evp_cipher_aead_get_length,
.aead_decrypt = evp_cipher_aead_decrypt, .aead_decrypt = evp_cipher_aead_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
{ {
.name = "aes256-gcm@openssh.com", .name = "aes256-gcm@openssh.com",
.blocksize = AES_BLOCK_SIZE, .blocksize = AES_BLOCK_SIZE,
.lenfield_blocksize = 4, /* not encrypted, but authenticated */ .lenfield_blocksize = 4, /* not encrypted, but authenticated */
.ciphertype = SSH_AEAD_AES256_GCM, .ciphertype = SSH_AEAD_AES256_GCM,
.keysize = 256, .keysize = 256,
.tag_size = AES_GCM_TAGLEN, .tag_size = AES_GCM_TAGLEN,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.aead_encrypt = evp_cipher_aead_encrypt, .aead_encrypt = evp_cipher_aead_encrypt,
.aead_decrypt_length = evp_cipher_aead_get_length, .aead_decrypt_length = evp_cipher_aead_get_length,
.aead_decrypt = evp_cipher_aead_decrypt, .aead_decrypt = evp_cipher_aead_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
#endif /* HAS_AES */ #endif /* HAS_AES */
#ifdef HAS_DES #ifdef HAS_DES
{ {
.name = "3des-cbc", .name = "3des-cbc",
.blocksize = 8, .blocksize = 8,
.ciphertype = SSH_3DES_CBC, .ciphertype = SSH_3DES_CBC,
.keysize = 192, .keysize = 192,
.set_encrypt_key = evp_cipher_set_encrypt_key, .set_encrypt_key = evp_cipher_set_encrypt_key,
.set_decrypt_key = evp_cipher_set_decrypt_key, .set_decrypt_key = evp_cipher_set_decrypt_key,
.encrypt = evp_cipher_encrypt, .encrypt = evp_cipher_encrypt,
.decrypt = evp_cipher_decrypt, .decrypt = evp_cipher_decrypt,
.cleanup = evp_cipher_cleanup .cleanup = evp_cipher_cleanup,
}, },
#endif /* HAS_DES */ #endif /* HAS_DES */
{ {
#ifdef HAVE_OPENSSL_EVP_CHACHA20 #ifdef HAVE_OPENSSL_EVP_CHACHA20
.ciphertype = SSH_AEAD_CHACHA20_POLY1305, .ciphertype = SSH_AEAD_CHACHA20_POLY1305,
.name = "chacha20-poly1305@openssh.com", .name = "chacha20-poly1305@openssh.com",
.blocksize = CHACHA20_BLOCKSIZE/8, .blocksize = CHACHA20_BLOCKSIZE / 8,
.lenfield_blocksize = 4, .lenfield_blocksize = 4,
.keylen = sizeof(struct chacha20_poly1305_keysched), .keylen = sizeof(struct chacha20_poly1305_keysched),
.keysize = 2 * CHACHA20_KEYLEN * 8, .keysize = 2 * CHACHA20_KEYLEN * 8,
.tag_size = POLY1305_TAGLEN, .tag_size = POLY1305_TAGLEN,
.set_encrypt_key = chacha20_poly1305_set_key, .set_encrypt_key = chacha20_poly1305_set_key,
.set_decrypt_key = chacha20_poly1305_set_key, .set_decrypt_key = chacha20_poly1305_set_key,
.aead_encrypt = chacha20_poly1305_aead_encrypt, .aead_encrypt = chacha20_poly1305_aead_encrypt,
.aead_decrypt_length = chacha20_poly1305_aead_decrypt_length, .aead_decrypt_length = chacha20_poly1305_aead_decrypt_length,
.aead_decrypt = chacha20_poly1305_aead_decrypt, .aead_decrypt = chacha20_poly1305_aead_decrypt,
.cleanup = chacha20_poly1305_cleanup .cleanup = chacha20_poly1305_cleanup
#else #else
.name = "chacha20-poly1305@openssh.com" .name = "chacha20-poly1305@openssh.com"
#endif /* HAVE_OPENSSL_EVP_CHACHA20 */ #endif /* HAVE_OPENSSL_EVP_CHACHA20 */
}, },
#ifdef WITH_INSECURE_NONE #ifdef WITH_INSECURE_NONE
{ {
.name = "none", .name = "none",
.blocksize = 8, .blocksize = 8,
.keysize = 0, .keysize = 0,
.encrypt = none_crypt, .encrypt = none_crypt,
.decrypt = none_crypt, .decrypt = none_crypt,
}, },
#endif /* WITH_INSECURE_NONE */ #endif /* WITH_INSECURE_NONE */
{ {
.name = NULL .name = NULL,
} },
}; };
struct ssh_cipher_struct *ssh_get_ciphertab(void) struct ssh_cipher_struct *ssh_get_ciphertab(void)
{ {
return ssh_ciphertab; return ssh_ciphertab;
} }
/** /**
@@ -1378,19 +1396,19 @@ int ssh_crypto_init(void)
if (libcrypto_initialized) { if (libcrypto_initialized) {
return SSH_OK; return SSH_OK;
} }
if (OpenSSL_version_num() != OPENSSL_VERSION_NUMBER){ if (OpenSSL_version_num() != OPENSSL_VERSION_NUMBER) {
SSH_LOG(SSH_LOG_DEBUG, "libssh compiled with %s " SSH_LOG(SSH_LOG_DEBUG,
"headers, currently running with %s.", "libssh compiled with %s "
OPENSSL_VERSION_TEXT, "headers, currently running with %s.",
OpenSSL_version(OpenSSL_version_num()) OPENSSL_VERSION_TEXT,
); OpenSSL_version(OpenSSL_version_num()));
} }
#ifdef CAN_DISABLE_AESNI #ifdef CAN_DISABLE_AESNI
/* /*
* disable AES-NI when running within Valgrind, because they generate * disable AES-NI when running within Valgrind, because they generate
* too many "uninitialized memory access" false positives * too many "uninitialized memory access" false positives
*/ */
if (RUNNING_ON_VALGRIND){ if (RUNNING_ON_VALGRIND) {
SSH_LOG(SSH_LOG_INFO, "Running within Valgrind, disabling AES-NI"); SSH_LOG(SSH_LOG_INFO, "Running within Valgrind, disabling AES-NI");
/* Bit #57 denotes AES-NI instruction set extension */ /* Bit #57 denotes AES-NI instruction set extension */
OPENSSL_ia32cap &= ~(1LL << 57); OPENSSL_ia32cap &= ~(1LL << 57);
@@ -1453,7 +1471,8 @@ void ssh_crypto_finalize(void)
* @internal * @internal
* @brief Create EVP_PKEY from parameters * @brief Create EVP_PKEY from parameters
* *
* @param[in] name Algorithm to use. For more info see manpage of EVP_PKEY_CTX_new_from_name * @param[in] name Algorithm to use. For more info see manpage of
* EVP_PKEY_CTX_new_from_name
* *
* @param[in] param_bld Constructed param builder for the pkey * @param[in] param_bld Constructed param builder for the pkey
* *
@@ -1463,8 +1482,10 @@ void ssh_crypto_finalize(void)
* *
* @return 0 on success, -1 on error * @return 0 on success, -1 on error
*/ */
int evp_build_pkey(const char* name, OSSL_PARAM_BLD *param_bld, int evp_build_pkey(const char *name,
EVP_PKEY **pkey, int selection) OSSL_PARAM_BLD *param_bld,
EVP_PKEY **pkey,
int selection)
{ {
int rc; int rc;
EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL); EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, name, NULL);
@@ -1596,8 +1617,7 @@ int evp_dup_ed25519_pkey(const ssh_key key, ssh_key new_key, int demote)
#endif /* OPENSSL_VERSION_NUMBER */ #endif /* OPENSSL_VERSION_NUMBER */
ssh_string ssh_string pki_key_make_ecpoint_string(const EC_GROUP *g, const EC_POINT *p)
pki_key_make_ecpoint_string(const EC_GROUP *g, const EC_POINT *p)
{ {
ssh_string s = NULL; ssh_string s = NULL;
size_t len; size_t len;

View File

@@ -108,22 +108,22 @@
*/ */
#ifdef _WIN32 #ifdef _WIN32
char *ssh_get_user_home_dir(void) static char *ssh_get_user_home_dir_internal(void)
{ {
char tmp[PATH_MAX] = {0}; char tmp[PATH_MAX] = {0};
char *szPath = NULL; char *szPath = NULL;
if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) { if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
szPath = malloc(strlen(tmp) + 1); szPath = malloc(strlen(tmp) + 1);
if (szPath == NULL) { if (szPath == NULL) {
return NULL; return NULL;
}
strcpy(szPath, tmp);
return szPath;
} }
strcpy(szPath, tmp); return NULL;
return szPath;
}
return NULL;
} }
/* we have read access on file */ /* we have read access on file */
@@ -298,7 +298,7 @@ int ssh_is_ipaddr(const char *str)
#define NSS_BUFLEN_PASSWD 4096 #define NSS_BUFLEN_PASSWD 4096
#endif /* NSS_BUFLEN_PASSWD */ #endif /* NSS_BUFLEN_PASSWD */
char *ssh_get_user_home_dir(void) static char *ssh_get_user_home_dir_internal(void)
{ {
char *szPath = NULL; char *szPath = NULL;
struct passwd pwd; struct passwd pwd;
@@ -313,7 +313,6 @@ char *ssh_get_user_home_dir(void)
return NULL; return NULL;
} }
snprintf(buf, sizeof(buf), "%s", szPath); snprintf(buf, sizeof(buf), "%s", szPath);
return strdup(buf); return strdup(buf);
} }
@@ -428,6 +427,29 @@ int ssh_is_ipaddr(const char *str)
#endif /* _WIN32 */ #endif /* _WIN32 */
char *ssh_get_user_home_dir(ssh_session session)
{
char *szPath = NULL;
/* If used previously, reuse cached value */
if (session != NULL && session->opts.homedir != NULL) {
return strdup(session->opts.homedir);
}
szPath = ssh_get_user_home_dir_internal();
if (szPath == NULL) {
return NULL;
}
if (session != NULL) {
/* cache it:
* failure is not fatal -- at worst we will just not cache it */
session->opts.homedir = strdup(szPath);
}
return szPath;
}
char *ssh_lowercase(const char* str) char *ssh_lowercase(const char* str)
{ {
char *new = NULL, *p = NULL; char *new = NULL, *p = NULL;
@@ -468,6 +490,38 @@ char *ssh_hostport(const char *host, int port)
return dest; 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 (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. * @brief Convert a buffer into a colon separated hex string.
* The caller has to free the memory. * The caller has to free the memory.
@@ -483,28 +537,7 @@ char *ssh_hostport(const char *host, int port)
*/ */
char *ssh_get_hexa(const unsigned char *what, size_t len) char *ssh_get_hexa(const unsigned char *what, size_t len)
{ {
const char h[] = "0123456789abcdef"; return ssh_get_hexa_internal(what, len, true);
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;
} }
/** /**
@@ -814,7 +847,17 @@ static struct ssh_iterator *ssh_iterator_new(const void *data)
return iterator; return iterator;
} }
int ssh_list_append(struct ssh_list *list,const void *data) /**
* @internal
*
* @brief Appends an element to the end of the list.
*
* @param[in] list The list to append the element
* @param[in] data The element to append
*
* @return `SSH_OK` on success, `SSH_ERROR` on error
*/
int ssh_list_append(struct ssh_list *list, const void *data)
{ {
struct ssh_iterator *iterator = NULL; struct ssh_iterator *iterator = NULL;
@@ -1179,7 +1222,7 @@ char *ssh_path_expand_tilde(const char *d)
} else { } else {
ld = strlen(d); ld = strlen(d);
p = (char *) d; p = (char *) d;
h = ssh_get_user_home_dir(); h = ssh_get_user_home_dir(NULL);
} }
if (h == NULL) { if (h == NULL) {
return NULL; return NULL;
@@ -1201,15 +1244,105 @@ char *ssh_path_expand_tilde(const char *d)
return r; return r;
} }
char *ssh_get_local_hostname(void)
{
char host[NI_MAXHOST] = {0};
int rc;
rc = gethostname(host, sizeof(host));
if (rc != 0) {
return NULL;
}
return strdup(host);
}
static char *get_connection_hash(ssh_session session)
{
unsigned char conn_hash[SHA_DIGEST_LENGTH];
char *local_hostname = NULL;
SHACTX ctx = sha1_init();
char strport[10] = {0};
unsigned int port;
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 */
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 */
rc = sha1_update(ctx,
session->opts.username,
strlen(session->opts.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 /** @internal
* @brief expands a string in function of session options * @brief expands a string in function of session options
*
* @param[in] s Format string to expand. Known parameters: * @param[in] s Format string to expand. Known parameters:
* %d SSH configuration directory (~/.ssh) * - %d user home directory (~)
* %h target host name * - %h target host name
* %u local username * - %u local username
* %l local hostname * - %l local hostname
* %r remote username * - %r remote username
* %p remote port * - %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 * @returns Expanded string. The caller needs to free the memory using
* ssh_string_free_char(). * ssh_string_free_char().
* *
@@ -1217,7 +1350,6 @@ char *ssh_path_expand_tilde(const char *d)
*/ */
char *ssh_path_expand_escape(ssh_session session, const char *s) char *ssh_path_expand_escape(ssh_session session, const char *s)
{ {
char host[NI_MAXHOST] = {0};
char *buf = NULL; char *buf = NULL;
char *r = NULL; char *r = NULL;
char *x = NULL; char *x = NULL;
@@ -1266,65 +1398,67 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
} }
switch (*p) { switch (*p) {
case '%': case '%':
goto escape; goto escape;
case 'd': case 'd':
if (session->opts.sshdir) { x = ssh_get_user_home_dir(session);
x = strdup(session->opts.sshdir); if (x == NULL) {
} else { ssh_set_error(session, SSH_FATAL, "Cannot expand homedir");
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");
free(buf); free(buf);
free(r); free(r);
return NULL; return NULL;
}
break;
case 'u':
x = ssh_get_local_username();
break;
case 'l':
x = ssh_get_local_hostname();
break;
case 'h':
if (session->opts.host) {
x = strdup(session->opts.host);
} else {
ssh_set_error(session, SSH_FATAL, "Cannot expand host");
free(buf);
free(r);
return NULL;
}
break;
case 'r':
if (session->opts.username) {
x = strdup(session->opts.username);
} else {
ssh_set_error(session, SSH_FATAL, "Cannot expand username");
free(buf);
free(r);
return NULL;
}
break;
case 'p': {
char tmp[6];
unsigned int port;
ssh_options_get_port(session, &port);
snprintf(tmp, sizeof(tmp), "%u", port);
x = strdup(tmp);
break;
}
case 'j':
if (session->opts.proxy_jumps_str != NULL) {
x = strdup(session->opts.proxy_jumps_str);
} else {
x = strdup("");
}
break;
case 'C':
x = get_connection_hash(session);
break;
default:
ssh_set_error(session, SSH_FATAL, "Wrong escape sequence detected");
free(buf);
free(r);
return NULL;
} }
if (x == NULL) { if (x == NULL) {
@@ -1336,8 +1470,7 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
i += strlen(x); i += strlen(x);
if (i >= MAX_BUF_SIZE) { if (i >= MAX_BUF_SIZE) {
ssh_set_error(session, SSH_FATAL, ssh_set_error(session, SSH_FATAL, "String too long");
"String too long");
free(buf); free(buf);
free(x); free(x);
free(r); free(r);

View File

@@ -1197,7 +1197,6 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_invalid(session); ssh_set_error_invalid(session);
return -1; return -1;
} else { } else {
ssh_proxyjumps_free(session->opts.proxy_jumps);
rc = ssh_config_parse_proxy_jump(session, v, true); rc = ssh_config_parse_proxy_jump(session, v, true);
if (rc != SSH_OK) { if (rc != SSH_OK) {
return SSH_ERROR; return SSH_ERROR;
@@ -1562,7 +1561,16 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
* - SSH_OPTIONS_IDENTITY: * - SSH_OPTIONS_IDENTITY:
* Get the first identity file name (const char *).\n * Get the first identity file name (const char *).\n
* \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: * - SSH_OPTIONS_PROXYCOMMAND:
* Get the proxycommand necessary to log into the * Get the proxycommand necessary to log into the
@@ -1658,6 +1666,30 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
break; 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: case SSH_OPTIONS_PROXYCOMMAND:
src = session->opts.ProxyCommand; src = session->opts.ProxyCommand;
break; break;
@@ -1963,7 +1995,7 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
/* set default filename */ /* set default filename */
if (filename == NULL) { if (filename == NULL) {
expanded_filename = ssh_path_expand_escape(session, "%d/config"); expanded_filename = ssh_path_expand_escape(session, "%d/.ssh/config");
} else { } else {
expanded_filename = ssh_path_expand_escape(session, filename); expanded_filename = ssh_path_expand_escape(session, filename);
} }
@@ -2021,7 +2053,7 @@ int ssh_options_apply(ssh_session session)
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) { if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) {
if (session->opts.knownhosts == NULL) { 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 { } else {
tmp = ssh_path_expand_escape(session, session->opts.knownhosts); tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
} }

View File

@@ -2684,7 +2684,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
const char *filename) const char *filename)
{ {
char key_buf[MAX_LINE_SIZE]; char key_buf[MAX_LINE_SIZE];
char host[256]; char *host = NULL;
char *b64_key = NULL; char *b64_key = NULL;
char *user = NULL; char *user = NULL;
FILE *fp = NULL; FILE *fp = NULL;
@@ -2699,8 +2699,8 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
return SSH_ERROR; return SSH_ERROR;
} }
rc = gethostname(host, sizeof(host)); host = ssh_get_local_hostname();
if (rc < 0) { if (host == NULL) {
free(user); free(user);
return SSH_ERROR; return SSH_ERROR;
} }
@@ -2708,6 +2708,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
rc = ssh_pki_export_pubkey_base64(key, &b64_key); rc = ssh_pki_export_pubkey_base64(key, &b64_key);
if (rc < 0) { if (rc < 0) {
free(user); free(user);
free(host);
return SSH_ERROR; return SSH_ERROR;
} }
@@ -2718,6 +2719,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
user, user,
host); host);
free(user); free(user);
free(host);
free(b64_key); free(b64_key);
if (rc < 0) { if (rc < 0) {
return SSH_ERROR; return SSH_ERROR;

View File

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

View File

@@ -549,17 +549,14 @@ static sftp_attributes sftp_parse_attr_3(sftp_session sftp,
if (rc != SSH_OK){ if (rc != SSH_OK){
goto error; goto error;
} }
SSH_LOG(SSH_LOG_DEBUG, SSH_LOG(SSH_LOG_DEBUG, "Flags: %.8" PRIx32, attr->flags);
"Flags: %.8" PRIx32 "\n", attr->flags);
if (attr->flags & SSH_FILEXFER_ATTR_SIZE) { if (attr->flags & SSH_FILEXFER_ATTR_SIZE) {
rc = ssh_buffer_unpack(buf, "q", &attr->size); rc = ssh_buffer_unpack(buf, "q", &attr->size);
if(rc != SSH_OK) { if(rc != SSH_OK) {
goto error; goto error;
} }
SSH_LOG(SSH_LOG_DEBUG, SSH_LOG(SSH_LOG_DEBUG, "Size: %" PRIu64, (uint64_t)attr->size);
"Size: %" PRIu64 "\n",
(uint64_t) attr->size);
} }
if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) { if (attr->flags & SSH_FILEXFER_ATTR_UIDGID) {

View File

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

View File

@@ -394,6 +394,7 @@ if (CLIENT_TESTING OR SERVER_TESTING)
# Allow to auth with bob's public keys on alice and doe account # 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/alice/.ssh/authorized_keys @ONLY)
configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/authorized_keys @ONLY) configure_file(keys/id_rsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/authorized_keys @ONLY)
configure_file(keys/id_ecdsa.pub ${CMAKE_CURRENT_BINARY_DIR}/home/frank/.ssh/authorized_keys @ONLY)
# append ECDSA public key # append ECDSA public key
file(READ keys/id_ecdsa.pub CONTENTS) file(READ keys/id_ecdsa.pub CONTENTS)

View File

@@ -376,7 +376,7 @@ torture_auth_autopubkey_protected_auth_function (const char *prompt, char *buf,
assert_int_equal(echo, 0); assert_int_equal(echo, 0);
assert_int_equal(verify, 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); assert_true(expected_id != NULL);
rc = ssh_userauth_publickey_auto_get_current_identity(data->session, &id); rc = ssh_userauth_publickey_auto_get_current_identity(data->session, &id);
@@ -429,7 +429,7 @@ static void torture_auth_autopubkey_protected(void **state) {
/* Try id_rsa_protected first. /* 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); assert_int_equal(rc, SSH_OK);
rc = ssh_connect(session); rc = ssh_connect(session);

View File

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

View File

@@ -6,6 +6,7 @@
#include <errno.h> #include <errno.h>
#include "torture.h" #include "torture.h"
#include "libssh/session.h" #include "libssh/session.h"
#include "libssh/options.h"
#include "libssh/misc.h" #include "libssh/misc.h"
#define LIBSSH_SSH_CONFIG "libssh_config" #define LIBSSH_SSH_CONFIG "libssh_config"
@@ -59,6 +60,23 @@ static int setup_config_files(void **state)
return 0; 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) static int teardown(void **state)
{ {
struct torture_state *s = *state; struct torture_state *s = *state;
@@ -80,6 +98,16 @@ static int teardown(void **state)
return 0; 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 /* This tests makes sure that parsing both system-wide and per-user
* configuration files retains OpenSSH semantics (the per-user overrides * configuration files retains OpenSSH semantics (the per-user overrides
* the system-wide values). * the system-wide values).
@@ -210,10 +238,207 @@ static void torture_client_config_suppress(void **state)
assert_string_equal(s->ssh.session->opts.username, "bob"); assert_string_equal(s->ssh.session->opts.username, "bob");
} }
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 torture_run_tests(void) {
int rc; int rc;
struct CMUnitTest tests[] = { struct CMUnitTest tests[] = {
/* Keep this first -- following setup is changing user to bob, which we
* do not want */
cmocka_unit_test_setup_teardown(torture_client_config_expand,
setup_session,
teardown_session),
cmocka_unit_test_setup_teardown(torture_client_config_system, cmocka_unit_test_setup_teardown(torture_client_config_system,
setup_config_files, setup_config_files,
teardown), teardown),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -115,6 +115,8 @@ ssh_session torture_ssh_session(struct torture_state *s,
const char *user, const char *user,
const char *password); const char *password);
ssh_session torture_ssh_session_proxyjump(void);
ssh_bind torture_ssh_bind(const char *addr, ssh_bind torture_ssh_bind(const char *addr,
const unsigned int port, const unsigned int port,
enum ssh_keytypes_e key_type, enum ssh_keytypes_e key_type,

View File

@@ -25,7 +25,7 @@ extern LIBSSH_THREAD int ssh_log_level;
#define HOSTKEYALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp521,ssh-rsa" #define HOSTKEYALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp521,ssh-rsa"
#define PUBKEYACCEPTEDTYPES "rsa-sha2-512,ssh-rsa,ecdsa-sha2-nistp521" #define PUBKEYACCEPTEDTYPES "rsa-sha2-512,ssh-rsa,ecdsa-sha2-nistp521"
#define MACS "hmac-sha1,hmac-sha2-256,hmac-sha2-512,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com" #define MACS "hmac-sha1,hmac-sha2-256,hmac-sha2-512,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com"
#define USER_KNOWN_HOSTS "%d/my_known_hosts" #define USER_KNOWN_HOSTS "%d/.ssh/my_known_hosts"
#define GLOBAL_KNOWN_HOSTS "/etc/ssh/my_ssh_known_hosts" #define GLOBAL_KNOWN_HOSTS "/etc/ssh/my_ssh_known_hosts"
#define BIND_ADDRESS "::1" #define BIND_ADDRESS "::1"
@@ -159,6 +159,8 @@ extern LIBSSH_THREAD int ssh_log_level;
"\tProxyJump jumpbox:2222\n" \ "\tProxyJump jumpbox:2222\n" \
"Host two-step\n" \ "Host two-step\n" \
"\tProxyJump u1@first:222,u2@second:33\n" \ "\tProxyJump u1@first:222,u2@second:33\n" \
"Host three-step\n" \
"\tProxyJump u1@first:222,u2@second:33,u3@third:444\n" \
"Host none\n" \ "Host none\n" \
"\tProxyJump none\n" \ "\tProxyJump none\n" \
"Host only-command\n" \ "Host only-command\n" \
@@ -1172,6 +1174,23 @@ static void torture_config_proxyjump(void **state,
"u1", "u1",
"222"); "222");
/* Three step jump */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "three-step");
_parse_config(session, file, string, SSH_OK);
helper_proxy_jump_check(session->opts.proxy_jumps->root,
"third",
"u3",
"444");
helper_proxy_jump_check(session->opts.proxy_jumps->root->next,
"second",
"u2",
"33");
helper_proxy_jump_check(session->opts.proxy_jumps->root->next->next,
"first",
"u1",
"222");
/* none */ /* none */
torture_reset_config(session); torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "none"); ssh_options_set(session, SSH_OPTIONS_HOST, "none");
@@ -1237,6 +1256,13 @@ static void torture_config_proxyjump(void **state,
assert_string_equal(session->opts.ProxyCommand, assert_string_equal(session->opts.ProxyCommand,
"ssh -l u1 -p 222 -J u2@second:33 -W '[%h]:%p' first"); "ssh -l u1 -p 222 -J u2@second:33 -W '[%h]:%p' first");
/* Three step jump */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "three-step");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
"ssh -l u1 -p 222 -J u2@second:33,u3@third:444 -W '[%h]:%p' first");
/* none */ /* none */
torture_reset_config(session); torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "none"); ssh_options_set(session, SSH_OPTIONS_HOST, "none");

View File

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

View File

@@ -52,7 +52,7 @@ static void torture_get_user_home_dir(void **state) {
(void) state; (void) state;
user = ssh_get_user_home_dir(); user = ssh_get_user_home_dir(NULL);
assert_non_null(user); assert_non_null(user);
#ifndef _WIN32 #ifndef _WIN32
assert_string_equal(user, pwd->pw_dir); assert_string_equal(user, pwd->pw_dir);
@@ -288,7 +288,8 @@ static void torture_path_expand_escape(void **state) {
const char *s = "%d/%h/%p/by/%r"; const char *s = "%d/%h/%p/by/%r";
char *e; char *e;
session->opts.sshdir = strdup("guru"); /* Set the homedir here to prevent querying the NSS DB */
session->opts.homedir = strdup("guru");
session->opts.host = strdup("meditation"); session->opts.host = strdup("meditation");
session->opts.port = 0; session->opts.port = 0;
session->opts.username = strdup("root"); session->opts.username = strdup("root");
@@ -310,9 +311,10 @@ static void torture_path_expand_known_hosts(void **state) {
ssh_session session = *state; ssh_session session = *state;
char *tmp; char *tmp;
session->opts.sshdir = strdup("/home/guru/.ssh"); /* Set the homedir here to prevent querying the NSS DB */
session->opts.homedir = strdup("/home/guru");
tmp = ssh_path_expand_escape(session, "%d/known_hosts"); tmp = ssh_path_expand_escape(session, "%d/.ssh/known_hosts");
assert_non_null(tmp); assert_non_null(tmp);
assert_string_equal(tmp, "/home/guru/.ssh/known_hosts"); assert_string_equal(tmp, "/home/guru/.ssh/known_hosts");
free(tmp); free(tmp);
@@ -322,9 +324,10 @@ static void torture_path_expand_percent(void **state) {
ssh_session session = *state; ssh_session session = *state;
char *tmp; char *tmp;
session->opts.sshdir = strdup("/home/guru/.ssh"); /* Set the homedir here to prevent querying the NSS DB */
session->opts.homedir = strdup("/home/guru");
tmp = ssh_path_expand_escape(session, "%d/config%%1"); tmp = ssh_path_expand_escape(session, "%d/.ssh/config%%1");
assert_non_null(tmp); assert_non_null(tmp);
assert_string_equal(tmp, "/home/guru/.ssh/config%1"); assert_string_equal(tmp, "/home/guru/.ssh/config%1");
free(tmp); free(tmp);

View File

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