ssh2_dh_secret() in the Windows CNG backend uses the server's
Diffie-Hellman public value f without the range check the other four
backends apply through ssh2_dh_validate(), so a malicious server can
send f of 0, 1 or p-1 and push the shared secret into a trivial subgroup
that openssl, libgcrypt, mbedtls and os400qc3 all reject with
LIBSSH2_ERROR_PROTO. This adds a WinCNG ssh2_dh_validate() that enforces
1 < f < p-1 and the same minimum-set-bits check on the big-endian value,
and calls it at the top of ssh2_dh_secret() like the other backends do.
The shared test_ssh2_dh_validate() in tests/test_simple.c was stubbed
out on WinCNG only because the function did not exist, so it now runs
against the real implementation too.
Reported-by: yaotushaozhu on github
Ref: GHSA-m9rg-h9jv-9gvh
Follow-up to fcf830788d#2239Closes#2645
To help debugging issues.
Also:
- bump sshd LogLevel to DEBUG1, for essential details.
- bump command output buffer in `run_command_varg()`
- drop redundant condition in `run_command_varg()`
- replace `BUFSIZ` with literal sizes in `run_command_varg()`.
After this patch it's showing this in the CI log for the libgcrypt
fallout that previously needed a long time to diagnose:
```
61: ---- sshd log ----
61: No matching private key for certificate: /etc/ssh/ssh_host_mldsa44-ed25519_key-cert.pub
61: Server listening on 0.0.0.0 port 22.
61: Server listening on :: port 22.
61: debug1: sshd-session version OpenSSH_10.5, OpenSSL 3.6.3 9 Jun 2026
61: Connection from 172.17.0.1 port 51878 on 172.17.0.2 port 22 rdomain ""
61: debug1: sshd-auth version OpenSSH_10.5, OpenSSL 3.6.3 9 Jun 2026 [preauth]
61: debug1: list_hostkey_types: rsa-sha2-512,rsa-sha2-256,ssh-rsa,rsa-sha2-512-cert-v01@openssh.com,
rsa-sha2-256-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ecdsa-sha2-nistp256,
ecdsa-sha2-nistp256-cert-v01@openssh.com,ssh-ed25519,ssh-ed25519-cert-v01@openssh.com [preauth]
61: debug1: Local version string SSH-2.0-OpenSSH_10.5p1 Debian-1 [preauth]
61: debug1: Remote protocol version 2.0, remote software version libssh2_1.11.2_DEV [preauth]
61: debug1: compat_banner: no match: libssh2_1.11.2_DEV [preauth]
61: debug1: SSH2_MSG_KEXINIT sent [preauth]
61: debug1: SSH2_MSG_KEXINIT received [preauth]
61: debug1: kex: algorithm: (no match) [preauth]
61: Unable to negotiate with 172.17.0.1 port 51878: no matching key exchange method found.
Their offer: diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,
diffie-hellman-group18-sha512,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,
diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1,ext-info-c,
kex-strict-c-v00@openssh.com [preauth]
[...]
61: ------------------
```
Follow-up to 1d0b86ac84#2484
Follow-up to 8be5716437#2174
Follow-up to 51f6259d18#1720Closes#2621
In favor of direct execution or `docker login` and `docker logout`.
Also to limit the logged-in time to the steps the require it, and to
avoid passing a secret via the command-line.
Closes#2616
To overwrite default ones with the ones matching the copied private
keys.
Fixing in sshd log:
```
Public key for /etc/ssh/ssh_host_rsa_key does not match private key
Public key for /etc/ssh/ssh_host_ecdsa_key does not match private key
Public key for /etc/ssh/ssh_host_ed25519_key does not match private key
```
Closes#2614
To reflect the support for multiple container engines, and to make
terminology more consistent and implementation-agnostic.
Also:
- add FIXME for `docker-machine ip` command that has been dropped
upstream.
Follow-up to 9bd1f16dc2#2605Closes#2609
- add `FIXTURE_CONTAINER_CMD` to the container command. Supported:
`podman`, `container`, `docker` (default).
- deprecate `OPENSSH_NO_DOCKER` env. Replaced by setting
`FIXTURE_CONTAINER_CMD` to an empty string.
- GHA: replace Docker with `podman` in a some Linux jobs.
- openssh_fixture: fixup `inspect` commands to be compatible with both
docker and podman.
- openssh_fixture: add `inspect` fallback for podman, which does not
support `HostIp` before 6.1.0.
Ref: https://github.com/podman-container-tools/podman/issues/29164
- openssh_fixture: add Apple container alternatives for `pull` and
`tag`.
- openssh_fixture: fix `run` command to suppress progress output when
using Apple container.
- openssh_fixture: if `OPENSSH_SERVER_PORT` is set, omit querying
the port number via the container tool.
`podman` seems to be a tiny bit slower than Docker in CI.
Requirements for Apple container:
- Apple container 0.8.0+ (2026-01-02). Tested with 1.4.1.
- `jq` installed in PATH.
- setting the sshd port manually on the host via `OPENSSH_SERVER_PORT`
(`container` cannot automatically assign one).
- physical macOS machine.
Example to run conainerized tests on macOS:
```sh
brew install container jq
container system start --disable-kernel-install
container system kernel set --recommended
export FIXTURE_CONTAINER_CMD=container
export OPENSSH_SERVER_PORT=<free-port-number>
export OPENSSH_SERVER_IMAGE; OPENSSH_SERVER_IMAGE=ghcr.io/libssh2/ci_tests_openssh_server:$(git rev-parse --short=20 HEAD:tests/openssh_server)
ctest -VV
```
Also tried:
- using Apple container in CI. Does not work because nested
virtualization is not supported in GHA runners.
- installing WSL on Windows. Failed with cryptic error upon
`wsl --install --distribution Debian`.
Closes#2605
To enable testing on arm64 Linux and possibly also on macOS, and
possibly possibly on arm64 Windows.
Also:
- switch to debian:testing to trigger a container rebuild.
- Dockerfile: install `adduser` explicitly. Required for debian:forky.
- Dockerfile: stop installing suggested/recommended packages.
Ref: https://github.com/docker/build-push-actionCloses#2606
To use a key type that is:
- supported by all backends.
- can be generated with the most recent `ssh-keygen` version.
- here to stay for the foreseeable future.
(DSA is scheduled for removal from the libssh2 codebase.)
Cherry-picked from #2598Closes#2604
Should no longer be necessary after fixing the root cause.
(Though we do not run these tests in CI at the moment.)
Ref: #804
Follow-up to f216bbe3aa#2583
Follow-up to bc120a343b#1023Closes#2603
- normalize fixed-width WinCNG bignums before SSH `mpint` serialization
- apply the normalization to DH public values and DH/ECDH shared secrets
- zero the removed tail bytes and add common bignum conversion tests
WinCNG returns DH public values and raw DH/ECDH shared secrets in
fixed-width buffers. The WinCNG bignum retained that buffer width as its
numeric length. When a value started with `0x00`, libssh2 included the
redundant byte in the exchange hash even though RFC 4251 requires a
canonical `mpint` representation.
The peer hashes the normalized integer, so the exchange hashes differ
and a valid server host-key signature is rejected. This appeared
intermittently as `LIBSSH2_ERROR_KEY_EXCHANGE_FAILURE` because it
depends on the generated value.
The helper introduced here moves significant bytes to the start of the
existing allocation, explicitly clears the removed tail, and updates
only the logical length. It does not add an allocation or a new failure
path.
Reported-by: Dan Fandrich
Fixes#804
Ref: #2590 (tests)
Closes#2583
Omit direct backend-specific API use. Drop one test using negative
input, that's impossible with the internal crypto API. Using
backend-specific code in tests is highly undesired. It's also
discouraged to special-case any, in this case WinCNG had to be because
it does not implement the DH validation function.
Also:
- mbedtls: fix an internal API type difference with a cast.
- unfold a line.
Follow-up to fcf830788d#2239Closes#2595
In some cases this is binary, in others it's textual.
To better see what's in the keys, to debug them, and debug code dealing
with them. It's a manual script, not run automatically by this repo.
Closes#2574
Plain known-host entries are currently compared as text, so equivalent
expanded and compressed IPv6 literals do not match.
This change keeps the existing exact-string fast path and compares two
valid plain IPv6 literals by their 128-bit address. It also supports
bracketed IPv6 host-and-port entries while requiring the port suffix to
match exactly.
No DNS lookup is performed. Hostname matching and the CUSTOM and hashed
known-host paths remain unchanged.
Reported-by: Krisscut on github
Fixes#1398Closes#2568
Implement server hostkey verification for the six ECDSA/RSA OpenSSH
certificate hostkey variants:
- `ecdsa-sha2-nistp256-cert-v01@openssh.com`
- `ecdsa-sha2-nistp384-cert-v01@openssh.com`
- `ecdsa-sha2-nistp521-cert-v01@openssh.com`
- `ssh-rsa-cert-v01@openssh.com`
- `rsa-sha2-256-cert-v01@openssh.com`
- `rsa-sha2-512-cert-v01@openssh.com`
This fixes#2434, where these algorithms were advertised and accepted as
preferences but had `NULL` hostkey `init`/`sig_verify` callbacks. A
certificate-only hostkey offer therefore caused
`LIBSSH2_ERROR_KEX_FAILURE (-5)`.
## Implementation
- ECDSA certificate parsing uses one shared init path for all three
curves. It validates the certificate type, nonce, inner curve name, and
EC point before constructing the verification context.
- RSA certificate parsing uses one shared init path for all three RSA
signature variants.
- Per-algorithm RSA `sig_verify` callbacks remain distinct.
- Per `PROTOCOL.certkeys`, the RSA/SHA-2 certificate blob itself carries
the type string `ssh-rsa-cert-v01@openssh.com`; the negotiated SHA-2
algorithm is represented by KEXINIT and the exchange signature.
- Certificate CA trust validation remains out of scope, consistent with
existing plain-hostkey handling.
## Tests
- Host CA fixtures (`keys/ca_host_ecdsa`, `keys/ca_host_rsa`) and
CA-signed host certificates for the ECDSA P-256 and RSA host keys in the
Docker OpenSSH fixture.
- The certificate algorithms are exercised through `test_read_algos.txt`
and `FIXTURE_TEST_HOSTKEY`: `ecdsa-sha2-nistp256-cert-v01@openssh.com`,
`rsa-sha2-256-cert-v01@openssh.com`,
`rsa-sha2-512-cert-v01@openssh.com`, `ssh-rsa-cert-v01@openssh.com`,
plus the pre-existing `ssh-ed25519-cert-v01@openssh.com`.
- The ECDSA P-384/P-521 certificate variants share the same codepath and
are covered by it; only P-256 is tested directly to keep the fixture
matrix lean (per maintainer review feedback).
- Disabled-backend skip entries in `tests/session_fixture.c` for the new
certificate algorithms.
- Preserved the existing ed25519 certificate test path.
Fixes#2434
Follow-up to 6036e57720#2513
Follow-up to 65a276e8fe#2064Closes#2539
`libssh2_channel_get_exit_status()` returns 0 both for a real exit
status of 0 and for a channel that never received an `exit-status`
request. A server that force-closes a session, like OpenSSH under
`ChannelTimeout`, makes a still-running command look like a clean exit
0. Reproduction with the stock `example/ssh2_exec` is in #2515.
This adds `libssh2_channel_has_exit_status()`, returning 1 once the
request arrived, the name you suggested.
The new field sits in `_LIBSSH2_CHANNEL` in `libssh2_priv.h` and callers
only ever hold a pointer, so no ABI change.
`libssh2_channel_get_exit_signal()` already lets a caller see whether
that request arrived by leaving the pointer NULL, exit-status was the
odd one out.
Man page and a `test_exit_status` covering both states are included.
Follow-up to 77bd3c1215Fixes#2515Closes#2516
Free keyboard-interactive authentication state when a session is
destroyed.
This covers the authentication name, prompt and response arrays, and
their individual strings. Avoid calling memcpy() with null pointers for
zero-length test input.
Tested with the complete CMake/OpenSSL test suite and the targeted
ASan/UBSan test.
Also:
- test_auth_keyboard_info_request: tidy up to prevent zero-size
allocation and memcpy.
Closes#2517
Release the expected hostkey buffer allocated by ssh2_base64_decode() on
success and mismatch paths.
Tested with the complete CMake/OpenSSL test suite and the targeted
ASan/UBSan test.
Follow-up to cf80f2f4b5Closes#2518
- add host CA keys.
- add ed25519 signed hostkey for tests.
- make hostkey tests recognize it and skip hash checks.
- make existing tests select hostkey algos.
- add `ssh-ed25519-cert-v01@openssh.com` to the list of tested algos.
(exclude for unsupported backends.)
Ref: #2434
Inspired-by: #2435
Credits-to: Pier Carlo Cadoppi
Follow-up to 65a276e8fe#2064Closes#2513
Also: regen ed25519 key to have an empty comment like its siblings.
To be used for generating host certificate keys.
Ref: 65a276e8fe#2064
Ref: #2435Closes#2511
They are supported via the backends, except OS/400 which has its own
parser, leaving libgcrypt and WinCNG (untested) without support. With
mbedTLS PKCS8 encryption (aes-128-cbc) requires 3.6.0+.
Add RSA, ECDSA and ED25519 keys.
What I find interesting in PKCS8 private keys, is the easy way to
inspect the ASN.1 content (`openssl asn1parse -dump -in <fn>`). I could
not find similar tooling for PEM and OpenSSH files.
Also:
- tests/openssh_server/authorized_keys: sync up comments with private
keys and the generator script.
Ref: https://github.com/Mbed-TLS/mbedtls/releases/tag/v3.6.0Closes#2500
They are `ssh-rsa` and `ssh-rsa-cert-v01@openssh.com`, and insecure due
to their reliance on SHA1 hashes.
Also:
- hostkey: fix to accept all RSA hostkey methods in common
`hostkey_method_ssh_rsa_init()`.
- mbedtls: add missing RSA SHA1/SHA2 guards.
- mbedtls: cleanup on error in `mbed_pub_priv_key()`.
- os400qc3: add missing RSA SHA1/SHA2 guards. (untested)
- wincng: add missing RSA SHA1/SHA2 guards.
- crypto.h: add missing `#endif` comments.
- GHA: move stray `-DLIBSSH2_DSA_ENABLE` to its place.
Follow-up to b7ab0faa70#1435
- tests: fix to guard RSA tests with `LIBSSH2_RSA`, replacing
`LIBSSH2_RSA_SHA1` to keep test coverage. Keep using the latter for
testing SHA1-based signatures in `test_auth_pubkey_ok_rsa_signed`.
Follow-up to 3e14d67d26#2444
Follow-up to 99f935358d#2443Closes#2501
To improve coverage. This is the 'EC PRIVATE KEY` format, not tested
before. The encrypted flavor probably should not be used nowadays due to
its dependence on MD5, and thus also only tested in one job. Also use
this opportunity to introduce ECDSA 521 into the mix.
Also:
- test_auth_pubkey_ok_rsa_encrypted: require `LIBSSH2_AES_CBC`.
Closes#2499
Make it 256-bit to add variation.
Choose aes256-ctr for encryption to keep it compatible with mbedTLS,
which lacks AES-GCM support. (The other alternative, chacha-poly, has
decryption broken for keys.)
Closes#2486
via new env `FIXTURE_TRACE_NO_DEBUGDUMP`.
Also:
- CI: set `FIXTURE_TRACE_NO_DEBUGDUMP` to make detailed logs (much)
shorter and more readable for the common debug use-case.
- GHA: enable debug trace for clang-tidy OpenSSL jobs and simplify
configuration.
Cherry-picked from #2485Closes#2487
The PR implementing this did not add anything OpenSSL-specific, yet it
gated the feature OpenSSL only. Turns out the reason for this is that
the test RSA keys are in OpenSSH format, which is only supported by
OpenSSL (and wolfSSL) in libssh2.
Fix by converting the two test keys to PEM format, making them
compatible with all backends. (RSA OpenSSH key format is already tested
separately.)
Also: this also fixes outdated comment fields present in these keys.
Bug: https://github.com/libssh2/libssh2/pull/1314#discussion_r3542830756
Ref: #2252 (initial fix attempt)
Follow-up to 1410efad7a#2249
Follow-up to 3a6ab70dcf#1314Closes#2483