Compare commits

...

93 Commits

Author SHA1 Message Date
Jakub Jelen
34db488e4d Native ML-KEM768 implementation
for cryptographic backends that do not have support for ML-KEM (old
OpenSSL and Gcrypt; MbedTLS).

Based on the libcrux implementation used in OpenSSH, taken from this
revision:

https://github.com/openssh/openssh-portable/blob/6aba700/libcrux_mlkem768_sha3.h

But refactored to separate C and header file to support testing and
removed unused functions (to make compiler happy).

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-01-15 12:48:06 +01:00
Jakub Jelen
9780fa2f01 tests: Apply verbosity also for the ssh_bind
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-01-15 12:23:42 +01:00
Jakub Jelen
5a795ce47c Add missing check in ML-KEM implementation of gcrypt
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
2026-01-15 12:23:42 +01:00
Jakub Jelen
b33a90d20b tests: Provide minimal openssl configuration file
When we use empty configuration file, some stuff go south in c10s
and for example fips mode detection does not work anymore.

Providing minimal configuration file avoids the issues of loading
the provider too early, while keeping fips mode activation working
and tests happy.

It also configures the pkcs11-provider to assume the token provides
FIPS approved crypto so the tests can work.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
ef45b8ae8c options: Fix doc string
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
3c2b254206 config: Pass the right types to OPTIONS_RSA_MIN_SIZE
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
7dea005729 tests: Avoid needless skip in testcases
the whole unit is skipped in fips mode

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
ad8d0c1e03 ci: Use pkcs11-provider on c9s
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
cb0f7d963e tests: Remove trailing whitespace
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
c90b239230 tests: Skip agent forwarding test if we are too deep in filesystem
The maximal lenght of unix domain socket path is 108 characters. When
the build directory (and UID wrapper home directories) are too deep
in the filesystem, OpenSSH will fail to create the socket file,
which is failing this test.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
18ec01c980 tests: Authentication with Ed25519 pkcs11 key
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
a983142a07 tests: Set explicit kex algorithm
without explicitly setting the algorithms, they might be set by
some other configuration file, for example crypto policies pulled
from `/etc/libssh/libssh_server.config` during RPM build.

Log also the generated configuration file and change the other case
to use standard logging mechanism instead of fprintf.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
89d51ced0d tests: Log server messages to separate files
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
16771cc574 tests: Remove needless goto
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
247ebb4d7f tests: Remove unused variable
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
acd5dace66 tests: Print read bytes to debug failures
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
5545b8808b sntrup: Avoid linking issues in external_override tests
This linking worked only in CI and local builds, but not during
the build in RPM as it fails on missing symbols that were defined
only in the main library. This is solved as with the other digest
dependencies in external crypto by removing the intermediate
function. We are already linking the md_*.o objects.

The error was like this

sh: symbol lookup error: /path/libssh/libssh-0.12.0-build/libssh-0.12.0/redhat-linux-build/lib/libsntrup761_override.so: undefined symbol: crypto_hash_sha512

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Jakub Jelen
be5a900ed0 tests: Reformat torture_auth_pkcs11
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-15 12:22:10 +01:00
Andreas Schneider
40ba3c6c80 cmake: Download doxygen theme during build not configure run
Signed-off-by: Andreas Schneider <asn@cryptomilk.org>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-01-12 15:25:40 +01:00
Jakub Jelen
57225a7168 ci: Include internal docs in the docs coverage reports
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 16:10:19 +01:00
Jakub Jelen
02ae2ace35 cmake: Make the WITH_INTERNAL_DOC function do something
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 16:10:19 +01:00
Jakub Jelen
76c6ee9ccf Add ML-KEM implementation for gcrypt
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 15:46:33 +01:00
Jakub Jelen
9a3351934b gssapi: Fix typo
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 15:44:52 +01:00
Jakub Jelen
1f1309c915 pki: Improve documentation about pubkey import functions
Resolves: #253 and #254

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 15:44:52 +01:00
Jakub Jelen
a8ca282033 dh-gex: Initialize best_size to make the code mode straight-forward
Coverity thought that the best_nlines could underflow, but the best_size is
initialized to 0 before calling this function so its moot. Adjusting the code
to be better understandable to static analyzers by initializing the variable
inside of the function.

Thanks coverity!

CID 1548873

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 14:46:07 +01:00
Jakub Jelen
b61bb3f8ac connector: Avoid possible underflow ...
... if underlying functions read or write more than expected.

This should never happen, but static analysis tools are inventive.

Thanks coverity!

CID 1548868

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

Thanks coverity!

CID 1644001

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 14:46:07 +01:00
Jakub Jelen
48fdf4b80a gssapi: Avoid possible memory leak on error condition
Thanks coverity!

CID 1643999

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 14:46:07 +01:00
Jakub Jelen
f5eb3e532b gssapi: Check return value from ssh_gssapi_init()
Checking the session->gssapi is resulting in the very same results, but this
approach is more direct and makes static analysis tools more happy.

Thanks coverity!

CID 1644000

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-07 14:46:07 +01:00
anshul agrawal
3f0007895c Add Keyboard Interactive
Signed-off-by: anshul agrawal <anshulagrawal2902@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-01-06 22:56:44 +05:30
nikhil-nari
06186279a8 feat: Add interoperability tests for PuTTY
Signed-off-by: Nikhil V <nikhilgreyshines@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-01-05 17:28:15 +01:00
Jakub Jelen
c36bd2304a connect: Close possibly leaking socket
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 13:32:01 +01:00
Jakub Jelen
82db6a7ab3 tests: Test proxyjump configuration parsing
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 13:32:01 +01:00
Jakub Jelen
deffea5ad2 socket: Properly close the proxyjump FD when proxy connection fails
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 13:32:01 +01:00
Jakub Jelen
320844669a config: Allow setting username from configuration
... file, even if it was already set before. The options
level handles what was already set.

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

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 13:32:01 +01:00
Pavol Žáčik
d0d45c8915 gssapi: free session->gssapi->user before assigning
To prevent memory leaks with multiple authentication attempts.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
65abae059e ci: Add bug links as reasoning why some tests are not run
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
7c2574682c tests: test pubkey auth after gssapi-keyex with null host key
We want to make sure it suceeds because it could fail if
the client tries to send a hostbound public key authentication
request.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
d2bb1ba889 auth: do not prefer hostbound auth if there is no host key
If there is no host key (e.g., because we are doing
gssapi-keyex with "null" host key algorithm), it does not
make sense to use host bound authentication.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
9b4ee9c6d4 gssapi: enable gssapi-keyex in FIPS mode
All gssapi-keyex tests have to be disabled in Centos Stream 8
because the KEX is not allowed in FIPS. In Centos Stream 9,
only tests against OpenSSH have to be disabled because
OpenSSH only enables gssapi-keyex since Centos Stream 10.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
d3e80d9a19 tests: test fallback to regular key exchange from gssapi-keyex
If the parties cannot agree on a gssapi-keyex method.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
4d3da7819c bind: adjust hostkey error messages to be more precise
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
b79a681ebb auth: check for strdup allocation failure in ssh_userauth_gssapi_keyex
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
f7cad4245a tests: reenable wait in torture_gssapi_server_key_exchange_null
And setup a KDC server before pinging the server so we
can connect.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
11c4b29e20 packet_cb: adjust response to NEWKEYS w.r.t. GSSAPI
Do not try to verify mic if gssapi-keyex was not performed,
and fix a memory leak of the mic on error.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
e04d753ace gssapi: add null checks for session->gssapi before using it
These are not strictly necessary because we always check
that we performed GSSAPI KEX, but they won't hurt us.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
06eea93ded packet: complete GSSAPI packet filter
Reject all GSSAPI-related messages when compiled
without GSSAPI support.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
06edb2db5e options: replace SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS example
The ECDH-based GSS KEX methods are more modern.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
ced98d41cf doc: document support for gssapi-keyex and related KEX methods
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
88c2ea6752 gssapi: Add support for ECDH GSSAPI KEX
In particular, gss-nistp256-sha256-* and
gss-curve25519-sha256-*.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
5fed1bc8be torture_packet: use SSH2_MSG_IGNORE type of test packet
With packet filtering now implemented for type 65,
the current test packet would be rejected, resulting
in failed tests.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
a30ba0091f libgcrypt: make bignum_dup usable with const_bignum
Both gcry_mpi_copy and gcry_mpi_set take a pointer to
const gcry_mpi, which const_bignum is not.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
ad23fe8c27 curve25519: Make ssh_curve25519_build_k public
This is necessary to reuse the function
in gss-curve25519-sha256-* KEX.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
3710b31d24 session: Refactor ssh_get_publickey_hash
Make it use the one-shot API of hash functions,
and remove the FIPS restriction for OpenSSL 3.5+
where we can fetch the MD5 implementation from
a non-FIPS provider to use for non-crypto purposes.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Pavol Žáčik
2c5bb17211 md: Implement one-shot md5
Which can be used for non-cryptographic purposes
even in FIPS mode.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Jakub Jelen
83ae6b3f0a gssapi: reformat parts
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Jakub Jelen
06cefe1d67 packet: Implement packet filter for non-implemented GSSAPI messages
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Jakub Jelen
043b1fb133 Move GSSAPI KEX messages to be numerically sorted
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
f1490170f3 tests: add test for gssapi server key exchange with null hostkey and no tgt
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
4ba0746135 fix: some possible memory leaks
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
e94fd6ccd1 tests: add config tests for SSH_OPTIONS_GSSAPI_KEY_EXCHANGE
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
83114b636f fix: move ssh_gssapi_check_client_config() from ssh_options_set to ssh_options_apply
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
5a99cf9c7f refactor: remove extra else if branch for disable_hostkeys
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
213556ce01 reformat: some nits
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
5d06ee459b refactor: remove issue link from .gitlab-ci.yml
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
96807b9313 tests: add valgrind suppressions
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
6d81ecddbe fix: replace pthread_exit in gssapi tests
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
d0e5cf78d0 fix: use strcmp instead of strncmp to avoid prefix match
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
a0707afc3e reformat: gssapi key exchange
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
06b61f75fa feat: implement packet filter for SSH2_MSG_KEXGSS_COMPLETE
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
f9d7cadf4b fix: create fopen wrapper and block default hostkey paths
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
c1aab9903f feat: add null hostkey for server
fix: skip gssapi tests in fips mode

fix: skip gssapi_key_exchange_null test on ubuntu and tumbleweed

fix: return early when rc != 0 to show error

tests: replace int asserts by ssh return code asserts

fix: add fatal error when hostkeys are not found and gssapi kex is not enabled

ci: add comment linking gssapi null kex bug in ubuntu and tumbleweed

fix: don't specify hostkeys in config instead of deleting files

tests: assert kex method was null

refactor: remove redundant include

refactor: better error message

fix: check null before accessing in gssapi.c

fix: allow setting no hostkeys
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
fd1c3e8878 feat: test null hostkey on ci
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
d730b40b91 feat: add SSH2_MSG_KEXGSS_HOSTKEY support to client and server
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
9044fcdb52 feat: add "gssapi-keyex" for server
feat: add negative auth client tests, and more key exchange server tests

feat: add function for checkinf if GSSAPI key exchange was performed
Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:13 +01:00
Gauravsingh Sisodia
bc5211d055 feat: add gssapi key exchange
feat: add generic functions for importing name and initializing ctx

feat: add suffix to gsskex algs dynamically

feat: move gssapi key exchange to another file

feat: add gssapi key exchange for server

refactor: remove unnecessary fields in gssapi struct

refactor: add some documentation and improve logging

fix: remove gss_dh callbacks

feat: add a check to see if GSSAPI is configured correctly

fix: memory leaks

feat: add client side "gssapi-keyex" auth

feat: add gssapi_key_exchange_algs for server

fix: some memory issues

feat: add gssapi kex options to config

feat: add check to see if GSSAPI key exchange was performed

feat: add more tests for gssapi key exchange

fix: add valgrind supp

Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:24:00 +01:00
Jakub Jelen
701a2155a7 tests: Improve test coverage of comparing certificates
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:21:43 +01:00
Jakub Jelen
38f3d158f6 pki: Fix comparing public key of certificate
When the first key object is a certificate object, this match will
fall through to the generic key comparison that is unable to handle
the ed25519 keys and fails.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:21:43 +01:00
Jakub Jelen
0d5a2652b4 pki: Avoild false positive matches when comparing certificates in mbedtls and gcrypt
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:21:43 +01:00
Jakub Jelen
5c496acef7 pkd: Run openssh client with SK keys
Fixes: #331

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:20:28 +01:00
Jakub Jelen
3e074a3fba tests: Use standard way of setting cmake variables
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-05 12:20:28 +01:00
Samir Benmendil
98a844ceb2 tidy(unittests): zero-init config string pointers
Signed-off-by: Samir Benmendil <me@rmz.io>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-02 21:44:45 +00:00
Samir Benmendil
ce45ba8c61 tests: suppress leaks from NSS modules
Signed-off-by: Samir Benmendil <me@rmz.io>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-02 14:34:11 +00:00
Samir Benmendil
62c85a59a9 ssh_client: Return non-zero on config parsing failure
Signed-off-by: Samir Benmendil <me@rmz.io>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-02 14:34:11 +00:00
Samir Benmendil
c4f1a70a89 connect: Support AddressFamily option
* allow parsing of AddressFamily in config and cli
  * supports options "any", "inet" and "inet6"
* introduce SSH_OPTIONS_ADDRESS_FAMILY

Signed-off-by: Samir Benmendil <me@rmz.io>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-02 14:34:11 +00:00
Jakub Jelen
f52be27114 connect: Improve logging around the connection code
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-02 14:34:11 +00:00
Jakub Jelen
228208af5e Happy new year 2026!
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-01-02 14:36:19 +01:00
Jakub Jelen
163373c9d9 tests: Reproducer for missing value to LogLevel
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-12-19 22:08:15 +01:00
Jakub Jelen
e82677a923 config: Fix error paths of configuration parsing
Thanks coverity, oss-fuzz and Ram-Z reporting this independently.

CID 1643770

https://oss-fuzz.com/issue/4969113899565056
https://oss-fuzz.com/issue/6448013813022720

Fixes up 1833ce86f9.

Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2025-12-19 22:08:15 +01:00
Nikhil V
79966eb924 fix : modify ssh_connector_free to accept NULL values
Signed-off-by: Nikhil V <nikhilgreyshines@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-18 13:44:05 +01:00
Nikhil V
4feb0dd79d Improve doxygen documentation
Signed-off-by: Nikhil V <nikhilgreyshines@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-18 13:44:05 +01:00
nikhil-nari
f8d943afda Improve doxygen docs
Signed-off-by: Nikhil V <nikhilgreyshines@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-18 13:44:05 +01:00
Pavol Žáčik
4bad7cc08f hybrid_mlkem: Convert ECDH shared secret to a fixed-size string
The shared secret is derived as bignum, and draft-ietf-sshm-mlkem-hybrid-kex
mandates that it is converted to a fixed-size byte array. Not doing this
would lead to incompatibilities with other implementations when the derived
shared secret happens to start with zero bytes.

Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2025-12-17 14:51:29 +01:00
123 changed files with 16739 additions and 1216 deletions

1
.clang-format-ignore Normal file
View File

@@ -0,0 +1 @@
src/external/*

View File

@@ -83,6 +83,12 @@ workflow:
.tumbleweed:
extends: .tests
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$TUMBLEWEED_BUILD
script:
# torture_gssapi_key_exchange_null is excluded because of a bug
# https://bugzilla.opensuse.org/show_bug.cgi?id=1254680
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
ctest --output-on-failure -E "^torture_gssapi_key_exchange_null$"
.centos:
extends: .tests
@@ -178,7 +184,7 @@ centos10s/openssl_3.5.x/x86_64/fips:
centos9s/openssl_3.5.x/x86_64:
extends: .centos9
variables:
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON
CMAKE_ADDITIONAL_OPTIONS: -DWITH_PKCS11_URI=ON -DWITH_PKCS11_PROVIDER=ON
script:
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
@@ -196,9 +202,11 @@ centos9s/openssl_3.5.x/x86_64/fips:
variables:
OPENSSL_ENABLE_SHA1_SIGNATURES: 1
script:
# torture_gssapi_key_exchange_* tests are excluded because gssapi-keyex is disabled
# by OpenSSH in FIPS mode in RHEL 9
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure -E "^torture_gssapi_key_exchange.*"
centos8s/openssl_1.1.1/x86_64:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
@@ -214,9 +222,11 @@ centos8s/openssl_1.1.1/x86_64/fips:
extends: .fips
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$CENTOS8_BUILD
script:
# torture_gssapi_key_exchange_* and torture_gssapi_server_key_exchange_* tests are excluded
# because gssapi-keyex is not allowed in FIPS mode in RHEL 8
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure
OPENSSL_FORCE_FIPS_MODE=1 ctest --output-on-failure -E "^torture_gssapi.*key_exchange.*"
###############################################################################
# Fedora builds #
@@ -229,7 +239,7 @@ fedora/docs:
extends: .build
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$FEDORA_BUILD
script:
- cmake .. && make docs_coverage && make docs
- cmake -DWITH_INTERNAL_DOC=ON .. && make docs_coverage && make docs
coverage: '/^Documentation coverage is \d+.\d+%/'
fedora/ninja:
@@ -504,6 +514,12 @@ fedora/abidiff:
ubuntu/openssl_3.0.x/x86_64:
image: $CI_REGISTRY/$BUILD_IMAGES_PROJECT:$UBUNTU_BUILD
extends: .tests
script:
# torture_gssapi_key_exchange_null is excluded because of a bug
# https://bugs.launchpad.net/ubuntu/+source/openssh/+bug/2134527
- cmake $CMAKE_OPTIONS $CMAKE_ADDITIONAL_OPTIONS .. &&
make -j$(nproc) &&
ctest --output-on-failure -E "^torture_gssapi_key_exchange_null$"
###############################################################################

View File

@@ -104,9 +104,10 @@ if (OPENSSL_FOUND)
check_function_exists(RAND_priv_bytes HAVE_OPENSSL_RAND_PRIV_BYTES)
check_function_exists(EVP_chacha20 HAVE_OPENSSL_EVP_CHACHA20)
# Check for ML-KEM768 availability (OpenSSL 3.5+)
# Check for ML-KEM availability (OpenSSL 3.5+)
if (OPENSSL_VERSION VERSION_GREATER_EQUAL "3.5.0")
set(HAVE_MLKEM 1)
set(HAVE_OPENSSL_MLKEM 1)
set(HAVE_MLKEM1024 1)
endif ()
unset(CMAKE_REQUIRED_INCLUDES)
@@ -234,6 +235,10 @@ if (GCRYPT_FOUND)
set(HAVE_GCRYPT_CHACHA_POLY 1)
set(HAVE_GCRYPT_CURVE25519 1)
endif (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
if (GCRYPT_VERSION VERSION_GREATER_EQUAL "1.10.1")
set(HAVE_GCRYPT_MLKEM 1)
set(HAVE_MLKEM1024 1)
endif ()
endif (GCRYPT_FOUND)
if (MBEDTLS_FOUND)

View File

@@ -194,8 +194,14 @@
/* Define to 1 if we have support for blowfish */
#cmakedefine HAVE_BLOWFISH 1
/* Define to 1 if we have support for ML-KEM */
#cmakedefine HAVE_MLKEM 1
/* Define to 1 if we have support for ML-KEM in libgcrypt */
#cmakedefine HAVE_GCRYPT_MLKEM 1
/* Define to 1 if we have support for ML-KEM in OpenSSL */
#cmakedefine HAVE_OPENSSL_MLKEM 1
/* Define to 1 if we have support for ML-KEM1024 in either backend */
#cmakedefine HAVE_MLKEM1024 1
/*************************** LIBRARIES ***************************/

View File

@@ -1,35 +1,48 @@
#
# Build the documentation
#
# To build the documentation with a local doxygen-awesome-css tarball:
# To build the documentation with a local doxygen-awesome-css directory:
#
# cmake -S . -B obj \
# -DDOXYGEN_AWESOME_CSS_TARBALL=/path/to/doxygen-awesome-css.tar.gz cmake
# --build obj --target docs
# cmake -S . -B obj \
# -DDOXYGEN_AWESOME_CSS_DIR=/path/to/doxygen-awesome-css
# cmake --build obj --target docs
#
# The tarball can be downloaded from:
# https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.4.1.tar.gz
# https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.4.1.tar.gz
#
find_package(Doxygen)
if (DOXYGEN_FOUND)
# Allow specifying a local tarball for doxygen-awesome-css (useful for
set(DOXYGEN_AWESOME_CSS_PROJECT
"https://github.com/jothepro/doxygen-awesome-css")
set(DOXYGEN_AWESOME_CSS_VERSION "2.4.1")
set(DOXYGEN_AWESOME_CSS_URL
"${DOXYGEN_AWESOME_CSS_PROJECT}/archive/refs/tags/v${DOXYGEN_AWESOME_CSS_VERSION}.tar.gz"
)
# Allow specifying a local doxygen-awesome-css directory (useful for
# packaging)
if (NOT DEFINED DOXYGEN_AWESOME_CSS_TARBALL)
set(DOXYGEN_AWESOME_CSS_TARBALL
"https://github.com/jothepro/doxygen-awesome-css/archive/refs/tags/v2.4.1.tar.gz"
if (NOT DEFINED DOXYGEN_AWESOME_CSS_DIR)
# Custom target to download doxygen-awesome-css at build time
add_custom_target(
doxygen-awesome-css
COMMAND
${CMAKE_COMMAND} -DURL=${DOXYGEN_AWESOME_CSS_URL}
-DDEST_DIR=${CMAKE_CURRENT_BINARY_DIR}
-DVERSION=${DOXYGEN_AWESOME_CSS_VERSION} -P
${CMAKE_CURRENT_SOURCE_DIR}/fetch_doxygen_awesome.cmake
COMMENT "Fetching doxygen-awesome-css theme")
set(AWESOME_CSS_DIR
"${CMAKE_CURRENT_BINARY_DIR}/doxygen-awesome-css-${DOXYGEN_AWESOME_CSS_VERSION}"
)
else ()
message(
STATUS
"Using doxygen-awesome-css from ${DOXYGEN_AWESOME_CSS_DIR}")
set(AWESOME_CSS_DIR "${DOXYGEN_AWESOME_CSS_DIR}")
endif ()
include(FetchContent)
FetchContent_Declare(
doxygen-awesome-css URL ${DOXYGEN_AWESOME_CSS_TARBALL}
DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
FetchContent_MakeAvailable(doxygen-awesome-css)
# Get the path to doxygen-awesome.css
FetchContent_GetProperties(doxygen-awesome-css SOURCE_DIR AWESOME_CSS_DIR)
# Project title shown in documentation
set(DOXYGEN_PROJECT_NAME ${PROJECT_NAME})
# Project version number shown in documentation
@@ -49,8 +62,13 @@ if (DOXYGEN_FOUND)
set(DOXYGEN_WARN_IF_UNDOCUMENTED YES)
# Do not extract private class members
set(DOXYGEN_EXTRACT_PRIVATE NO)
# Do not include internal documentation
set(DOXYGEN_INTERNAL_DOCS NO)
if (WITH_INTERNAL_DOC)
# Include internal documentation
set(DOXYGEN_INTERNAL_DOCS YES)
else ()
# Do not include internal documentation
set(DOXYGEN_INTERNAL_DOCS NO)
endif( WITH_INTERNAL_DOC)
# Disable built-in clipboard (using doxygen-awesome extension instead)
set(DOXYGEN_HTML_COPY_CLIPBOARD NO)
# Disable page outline panel (using interactive TOC extension instead)
@@ -219,6 +237,11 @@ if (DOXYGEN_FOUND)
doxygen_add_docs(docs ${CMAKE_SOURCE_DIR}/include/libssh
${CMAKE_SOURCE_DIR}/src ${CMAKE_CURRENT_SOURCE_DIR})
# Make docs depend on doxygen-awesome-css download (if not using local dir)
if (TARGET doxygen-awesome-css)
add_dependencies(docs doxygen-awesome-css)
endif ()
add_custom_target(
docs_coverage COMMAND ${CMAKE_SOURCE_DIR}/doc/doc_coverage.sh
${CMAKE_BINARY_DIR})

View File

@@ -0,0 +1,41 @@
# Script to download doxygen-awesome-css at build time
#
# Usage:
# cmake -P fetch_doxygen_awesome.cmake \
# -DURL=<download_url> \
# -DDEST_DIR=<destination_directory> \
# -DVERSION=<version>
if(NOT DEFINED URL)
message(FATAL_ERROR "URL not specified")
endif()
if(NOT DEFINED DEST_DIR)
message(FATAL_ERROR "DEST_DIR not specified")
endif()
if(NOT DEFINED VERSION)
message(FATAL_ERROR "VERSION not specified")
endif()
set(EXTRACT_DIR "${DEST_DIR}/doxygen-awesome-css-${VERSION}")
if(NOT EXISTS "${EXTRACT_DIR}/doxygen-awesome.css")
message(STATUS "Downloading doxygen-awesome-css ${VERSION}...")
set(TARBALL "${DEST_DIR}/doxygen-awesome-css.tar.gz")
file(DOWNLOAD
"${URL}"
"${TARBALL}"
STATUS download_status
SHOW_PROGRESS
)
list(GET download_status 0 status_code)
if(NOT status_code EQUAL 0)
list(GET download_status 1 error_msg)
message(FATAL_ERROR "Download failed: ${error_msg}")
endif()
message(STATUS "Extracting doxygen-awesome-css...")
file(ARCHIVE_EXTRACT
INPUT "${TARBALL}"
DESTINATION "${DEST_DIR}"
)
file(REMOVE "${TARBALL}")
endif()

View File

@@ -20,11 +20,12 @@ the interesting functions as you go.
The libssh library provides:
- <strong>Key Exchange Methods</strong>: <i>sntrup761x25519-sha512, sntrup761x25519-sha512@openssh.com, mlkem768x25519-sha256, mlkem768nistp256-sha256, mlkem1024nistp384-sha384, curve25519-sha256, curve25519-sha256@libssh.org, ecdh-sha2-nistp256, ecdh-sha2-nistp384, ecdh-sha2-nistp521</i>, diffie-hellman-group1-sha1, diffie-hellman-group14-sha1
- <strong>GSSAPI Key Exchange Methods</strong>: gss-group14-sha256-*, gss-group16-sha512-*, gss-nistp256-sha256-*, gss-curve25519-sha256-*
- <strong>Public Key Algorithms</strong>: ssh-ed25519, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, ecdsa-sha2-nistp521, ssh-rsa, rsa-sha2-512, rsa-sha2-256
- <strong>Ciphers</strong>: <i>aes256-ctr, aes192-ctr, aes128-ctr</i>, aes256-cbc (rijndael-cbc@lysator.liu.se), aes192-cbc, aes128-cbc, 3des-cbc, blowfish-cbc
- <strong>Compression Schemes</strong>: zlib, <i>zlib@openssh.com</i>, none
- <strong>MAC hashes</strong>: hmac-sha1, hmac-sha2-256, hmac-sha2-512, hmac-md5
- <strong>Authentication</strong>: none, password, public-key, keyboard-interactive, <i>gssapi-with-mic</i>
- <strong>Authentication</strong>: none, password, public-key, keyboard-interactive, <i>gssapi-with-mic, gssapi-keyex</i>
- <strong>Channels</strong>: shell, exec (incl. SCP wrapper), direct-tcpip, subsystem, <i>auth-agent-req@openssh.com</i>
- <strong>Global Requests</strong>: tcpip-forward, forwarded-tcpip
- <strong>Channel Requests</strong>: x11, pty, <i>exit-status, signal, exit-signal, keepalive@openssh.com, auth-agent-req@openssh.com</i>

View File

@@ -355,10 +355,8 @@ static int client(ssh_session session)
}
/* Parse configuration file if specified: The command-line options will
* overwrite items loaded from configuration file */
if (config_file != NULL) {
ssh_options_parse_config(session, config_file);
} else {
ssh_options_parse_config(session, NULL);
if (ssh_options_parse_config(session, config_file) < 0) {
return -1;
}
if (ssh_connect(session)) {

View File

@@ -631,6 +631,58 @@ auth_publickey(ssh_session session,
return SSH_AUTH_DENIED;
}
static int kbdint_check_response(ssh_session session)
{
int count, cmp;
const char *answer = NULL;
count = ssh_userauth_kbdint_getnanswers(session);
if (count != 2) {
return 0;
}
answer = ssh_userauth_kbdint_getanswer(session, 0);
cmp = strcasecmp("omnitrix", answer);
if (cmp != 0) {
return 0;
}
answer = ssh_userauth_kbdint_getanswer(session, 1);
cmp = strcmp("000", answer);
if (cmp != 0) {
return 0;
}
return 1;
}
static int
auth_kbdint(ssh_message message, ssh_session session, void *userdata)
{
struct session_data_struct *sdata = (struct session_data_struct *)userdata;
const char *name = "\n\nKeyboard-Interactive Fancy Authentication\n";
const char *instruction = "Most powerful weapon in the galaxy";
const char *prompts[2] = {"Name of the weapon: ", "Destruct Code: "};
char echo[] = {1, 0};
if (!ssh_message_auth_kbdint_is_response(message)) {
printf("User %s wants to auth with kbdint\n",
ssh_message_auth_user(message));
ssh_message_auth_interactive_request(message,
name,
instruction,
2,
prompts,
echo);
return SSH_AUTH_INFO;
} else {
if (kbdint_check_response(session)) {
sdata->authenticated = 1;
return SSH_AUTH_SUCCESS;
}
return SSH_AUTH_DENIED;
}
}
static ssh_channel
channel_open(ssh_session session, void *userdata)
{
@@ -720,14 +772,15 @@ handle_session(ssh_event event, ssh_session session)
struct ssh_server_callbacks_struct server_cb = {
.userdata = &sdata,
.auth_password_function = auth_password,
.auth_kbdint_function = auth_kbdint,
.channel_open_request_session_function = channel_open,
};
if (authorizedkeys[0]) {
server_cb.auth_pubkey_function = auth_publickey;
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY);
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_INTERACTIVE);
} else
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD);
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_INTERACTIVE);
ssh_callbacks_init(&server_cb);
ssh_callbacks_init(&channel_cb);

View File

@@ -52,42 +52,45 @@ typedef struct ssh_kbdint_struct* ssh_kbdint;
ssh_kbdint ssh_kbdint_new(void);
void ssh_kbdint_clean(ssh_kbdint kbd);
void ssh_kbdint_free(ssh_kbdint kbd);
int ssh_userauth_gssapi_keyex(ssh_session session);
/** @internal
* States of authentication in the client-side. They describe
* what was the last response from the server
*/
enum ssh_auth_state_e {
/** No authentication asked */
SSH_AUTH_STATE_NONE=0,
/** Last authentication response was a partial success */
SSH_AUTH_STATE_PARTIAL,
/** Last authentication response was a success */
SSH_AUTH_STATE_SUCCESS,
/** Last authentication response was failed */
SSH_AUTH_STATE_FAILED,
/** Last authentication was erroneous */
SSH_AUTH_STATE_ERROR,
/** Last state was a keyboard-interactive ask for info */
SSH_AUTH_STATE_INFO,
/** Last state was a public key accepted for authentication */
SSH_AUTH_STATE_PK_OK,
/** We asked for a keyboard-interactive authentication */
SSH_AUTH_STATE_KBDINT_SENT,
/** We have sent an userauth request with gssapi-with-mic */
SSH_AUTH_STATE_GSSAPI_REQUEST_SENT,
/** We are exchanging tokens until authentication */
SSH_AUTH_STATE_GSSAPI_TOKEN,
/** We have sent the MIC and expecting to be authenticated */
SSH_AUTH_STATE_GSSAPI_MIC_SENT,
/** We have offered a pubkey to check if it is supported */
SSH_AUTH_STATE_PUBKEY_OFFER_SENT,
/** We have sent pubkey and signature expecting to be authenticated */
SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
/** We have sent a password expecting to be authenticated */
SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
/** We have sent a request without auth information (method 'none') */
SSH_AUTH_STATE_AUTH_NONE_SENT,
/** No authentication asked */
SSH_AUTH_STATE_NONE = 0,
/** Last authentication response was a partial success */
SSH_AUTH_STATE_PARTIAL,
/** Last authentication response was a success */
SSH_AUTH_STATE_SUCCESS,
/** Last authentication response was failed */
SSH_AUTH_STATE_FAILED,
/** Last authentication was erroneous */
SSH_AUTH_STATE_ERROR,
/** Last state was a keyboard-interactive ask for info */
SSH_AUTH_STATE_INFO,
/** Last state was a public key accepted for authentication */
SSH_AUTH_STATE_PK_OK,
/** We asked for a keyboard-interactive authentication */
SSH_AUTH_STATE_KBDINT_SENT,
/** We have sent an userauth request with gssapi-with-mic */
SSH_AUTH_STATE_GSSAPI_REQUEST_SENT,
/** We are exchanging tokens until authentication */
SSH_AUTH_STATE_GSSAPI_TOKEN,
/** We have sent the MIC and expecting to be authenticated */
SSH_AUTH_STATE_GSSAPI_MIC_SENT,
/** We have offered a pubkey to check if it is supported */
SSH_AUTH_STATE_PUBKEY_OFFER_SENT,
/** We have sent pubkey and signature expecting to be authenticated */
SSH_AUTH_STATE_PUBKEY_AUTH_SENT,
/** We have sent a password expecting to be authenticated */
SSH_AUTH_STATE_PASSWORD_AUTH_SENT,
/** We have sent a request without auth information (method 'none') */
SSH_AUTH_STATE_AUTH_NONE_SENT,
/** We have sent the MIC and expecting to be authenticated */
SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT,
};
/** @internal

View File

@@ -54,6 +54,8 @@ struct ssh_bind_struct {
char *pubkey_accepted_key_types;
char* moduli_file;
int rsa_min_size;
bool gssapi_key_exchange;
char *gssapi_key_exchange_algs;
};
struct ssh_poll_handle_struct *ssh_bind_get_poll(struct ssh_bind_struct

View File

@@ -220,36 +220,41 @@ typedef struct ssh_callbacks_struct *ssh_callbacks;
* @param user User that wants to authenticate
* @param password Password used for authentication
* @param userdata Userdata to be passed to the callback function.
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
* @returns SSH_AUTH_DENIED Authentication failed.
* @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
* @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
* are needed.
* @returns `SSH_AUTH_DENIED` Authentication failed.
*/
typedef int (*ssh_auth_password_callback) (ssh_session session, const char *user, const char *password,
void *userdata);
/**
* @brief SSH authentication callback. Tries to authenticates user with the "none" method
* which is anonymous or passwordless.
* @brief SSH authentication callback. Tries to authenticates user with the
* "none" method which is anonymous or passwordless.
* @param session Current session handler
* @param user User that wants to authenticate
* @param userdata Userdata to be passed to the callback function.
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
* @returns SSH_AUTH_DENIED Authentication failed.
* @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
* @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
* are needed.
* @returns `SSH_AUTH_DENIED` Authentication failed.
*/
typedef int (*ssh_auth_none_callback) (ssh_session session, const char *user, void *userdata);
/**
* @brief SSH authentication callback. Tries to authenticates user with the "gssapi-with-mic" method
* @brief SSH authentication callback. Tries to authenticates user with the
* "gssapi-with-mic" method
* @param session Current session handler
* @param user Username of the user (can be spoofed)
* @param principal Authenticated principal of the user, including realm.
* @param userdata Userdata to be passed to the callback function.
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
* @returns SSH_AUTH_DENIED Authentication failed.
* @warning Implementations should verify that parameter user matches in some way the principal.
* user and principal can be different. Only the latter is guaranteed to be safe.
* @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
* @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
* are needed.
* @returns `SSH_AUTH_DENIED` Authentication failed.
* @warning Implementations should verify that parameter user matches in some
* way the principal. user and principal can be different. Only the latter is
* guaranteed to be safe.
*/
typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *user, const char *principal,
void *userdata);
@@ -259,17 +264,29 @@ typedef int (*ssh_auth_gssapi_mic_callback) (ssh_session session, const char *us
* @param session Current session handler
* @param user User that wants to authenticate
* @param pubkey public key used for authentication
* @param signature_state SSH_PUBLICKEY_STATE_NONE if the key is not signed (simple public key probe),
* SSH_PUBLICKEY_STATE_VALID if the signature is valid. Others values should be
* replied with a SSH_AUTH_DENIED.
* @param signature_state `SSH_PUBLICKEY_STATE_NONE` if the key is not signed
* (simple public key probe), `SSH_PUBLICKEY_STATE_VALID` if the signature is
* valid. Others values should be replied with a `SSH_AUTH_DENIED`.
* @param userdata Userdata to be passed to the callback function.
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
* @returns SSH_AUTH_DENIED Authentication failed.
* @returns `SSH_AUTH_SUCCESS` Authentication is accepted.
* @returns `SSH_AUTH_PARTIAL` Partial authentication, more authentication means
* are needed.
* @returns `SSH_AUTH_DENIED` Authentication failed.
*/
typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user, struct ssh_key_struct *pubkey,
char signature_state, void *userdata);
/**
* @brief SSH authentication callback. Tries to authenticates user with the "keyboard-interactive" method
* @param message Current message
* @param session Current session handler
* @param userdata Userdata to be passed to the callback function.
* @returns SSH_AUTH_SUCCESS Authentication is accepted.
* @returns SSH_AUTH_INFO More info required for authentication.
* @returns SSH_AUTH_PARTIAL Partial authentication, more authentication means are needed.
* @returns SSH_AUTH_DENIED Authentication failed.
*/
typedef int (*ssh_auth_kbdint_callback) (ssh_message message, ssh_session session, void *userdata);
/**
* @brief Handles an SSH service request
@@ -279,7 +296,6 @@ typedef int (*ssh_auth_pubkey_callback) (ssh_session session, const char *user,
* @returns 0 if the request is to be allowed
* @returns -1 if the request should not be allowed
*/
typedef int (*ssh_service_request_callback) (ssh_session session, const char *service, void *userdata);
/**
@@ -292,7 +308,7 @@ typedef int (*ssh_service_request_callback) (ssh_session session, const char *se
*/
typedef ssh_channel (*ssh_channel_open_request_session_callback) (ssh_session session, void *userdata);
/*
/**
* @brief handle the beginning of a GSSAPI authentication, server side.
* Callback should select the oid and also acquire the server credential.
* @param session current session handler
@@ -307,29 +323,29 @@ typedef ssh_channel (*ssh_channel_open_request_session_callback) (ssh_session se
typedef ssh_string (*ssh_gssapi_select_oid_callback) (ssh_session session, const char *user,
int n_oid, ssh_string *oids, void *userdata);
/*
/**
* @brief handle the negotiation of a security context, server side.
* @param session current session handler
* @param[in] input_token input token provided by client
* @param[out] output_token output of the gssapi accept_sec_context method,
* NULL after completion.
* @returns SSH_OK if the token was generated correctly or accept_sec_context
* @returns `SSH_OK` if the token was generated correctly or accept_sec_context
* returned GSS_S_COMPLETE
* @returns SSH_ERROR in case of error
* @returns `SSH_ERROR` in case of error
* @warning It is not necessary to fill this callback in if libssh is linked
* with libgssapi.
*/
typedef int (*ssh_gssapi_accept_sec_ctx_callback) (ssh_session session,
ssh_string input_token, ssh_string *output_token, void *userdata);
/*
/**
* @brief Verify and authenticates a MIC, server side.
* @param session current session handler
* @param[in] mic input mic to be verified provided by client
* @param[in] mic_buffer buffer of data to be signed.
* @param[in] mic_buffer_size size of mic_buffer
* @returns SSH_OK if the MIC was authenticated correctly
* @returns SSH_ERROR in case of error
* @returns `SSH_OK` if the MIC was authenticated correctly
* @returns `SSH_ERROR` in case of error
* @warning It is not necessary to fill this callback in if libssh is linked
* with libgssapi.
*/
@@ -405,7 +421,7 @@ struct ssh_server_callbacks_struct {
/** This function will be called when a gssapi token comes in.
*/
ssh_gssapi_accept_sec_ctx_callback gssapi_accept_sec_ctx_function;
/* This function will be called when a MIC needs to be verified.
/** This function will be called when a MIC needs to be verified.
*/
ssh_gssapi_verify_mic_callback gssapi_verify_mic_function;
/**
@@ -414,6 +430,12 @@ struct ssh_server_callbacks_struct {
*/
ssh_channel_open_request_direct_tcpip_callback
channel_open_request_direct_tcpip_function;
/** This function gets called when a client tries to authenticate through
* keyboard interactive method.
*/
ssh_auth_kbdint_callback auth_kbdint_function;
};
typedef struct ssh_server_callbacks_struct *ssh_server_callbacks;
@@ -439,7 +461,7 @@ typedef struct ssh_server_callbacks_struct *ssh_server_callbacks;
*
* @param cb The callback structure itself.
*
* @return SSH_OK on success, SSH_ERROR on error.
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
LIBSSH_API int ssh_set_server_callbacks(ssh_session session, ssh_server_callbacks cb);
@@ -570,14 +592,17 @@ typedef struct ssh_socket_callbacks_struct *ssh_socket_callbacks;
} \
} while(0)
/** @brief Prototype for a packet callback, to be called when a new packet arrives
/** @brief Prototype for a packet callback, to be called when a new packet
* arrives
* @param session The current session of the packet
* @param type packet type (see ssh2.h)
* @param packet buffer containing the packet, excluding size, type and padding fields
* @param packet buffer containing the packet, excluding size, type and padding
* fields
* @param user user argument to the callback
* and are called each time a packet shows up
* @returns SSH_PACKET_USED Packet was parsed and used
* @returns SSH_PACKET_NOT_USED Packet was not used or understood, processing must continue
* @returns `SSH_PACKET_USED` Packet was parsed and used
* @returns `SSH_PACKET_NOT_USED` Packet was not used or understood, processing
* must continue
*/
typedef int (*ssh_packet_callback) (ssh_session session, uint8_t type, ssh_buffer packet, void *user);
@@ -636,7 +661,7 @@ typedef struct ssh_packet_callbacks_struct *ssh_packet_callbacks;
*
* @param cb The callback structure itself.
*
* @return SSH_OK on success, SSH_ERROR on error.
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
LIBSSH_API int ssh_set_callbacks(ssh_session session, ssh_callbacks cb);
@@ -988,7 +1013,7 @@ typedef struct ssh_channel_callbacks_struct *ssh_channel_callbacks;
*
* @param cb The callback structure itself.
*
* @return SSH_OK on success, SSH_ERROR on error.
* @return `SSH_OK` on success, `SSH_ERROR` on error.
* @warning this function will not replace existing callbacks but set the
* new one atop of them.
*/
@@ -1007,7 +1032,7 @@ LIBSSH_API int ssh_set_channel_callbacks(ssh_channel channel,
*
* @param cb The callback structure itself.
*
* @return SSH_OK on success, SSH_ERROR on error.
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*
* @see ssh_set_channel_callbacks
*/
@@ -1024,7 +1049,7 @@ LIBSSH_API int ssh_add_channel_callbacks(ssh_channel channel,
*
* @param cb The callback structure to remove
*
* @returns SSH_OK on success, SSH_ERROR on error.
* @returns `SSH_OK` on success, `SSH_ERROR` on error.
*/
LIBSSH_API int ssh_remove_channel_callbacks(ssh_channel channel,
ssh_channel_callbacks cb);
@@ -1057,7 +1082,7 @@ struct ssh_threads_callbacks_struct {
* @param[in] cb A pointer to a ssh_threads_callbacks_struct structure, which
* contains the different callbacks to be set.
*
* @returns Always returns SSH_OK.
* @returns Always returns `SSH_OK`.
*
* @see ssh_threads_callbacks_struct
* @see SSH_THREADS_PTHREAD

View File

@@ -68,6 +68,9 @@ enum ssh_config_opcode_e {
SOC_CONTROLPATH,
SOC_CERTIFICATE,
SOC_REQUIRED_RSA_SIZE,
SOC_ADDRESSFAMILY,
SOC_GSSAPIKEYEXCHANGE,
SOC_GSSAPIKEXALGORITHMS,
SOC_MAX /* Keep this one last in the list */
};

View File

@@ -87,14 +87,22 @@ enum ssh_key_exchange_e {
SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM,
/* sntrup761x25519-sha512 */
SSH_KEX_SNTRUP761X25519_SHA512,
#ifdef HAVE_MLKEM
/* mlkem768x25519-sha256 */
SSH_KEX_MLKEM768X25519_SHA256,
/* mlkem768nistp256-sha256 */
SSH_KEX_MLKEM768NISTP256_SHA256,
#ifdef HAVE_MLKEM1024
/* mlkem1024nistp384-sha384 */
SSH_KEX_MLKEM1024NISTP384_SHA384,
#endif /* HAVE_MLKEM */
#endif /* HAVE_MLKEM1024 */
/* gss-group14-sha256-* */
SSH_GSS_KEX_DH_GROUP14_SHA256,
/* gss-group16-sha512-* */
SSH_GSS_KEX_DH_GROUP16_SHA512,
/* gss-nistp256-sha256-* */
SSH_GSS_KEX_ECDH_NISTP256_SHA256,
/* gss-curve25519-sha256-* */
SSH_GSS_KEX_CURVE25519_SHA256,
};
enum ssh_cipher_e {
@@ -151,11 +159,14 @@ struct ssh_crypto_struct {
ssh_curve25519_pubkey curve25519_client_pubkey;
ssh_curve25519_pubkey curve25519_server_pubkey;
#endif
#ifdef HAVE_MLKEM
#ifdef HAVE_OPENSSL_MLKEM
EVP_PKEY *mlkem_privkey;
#else
unsigned char *mlkem_privkey;
size_t mlkem_privkey_len;
#endif
ssh_string mlkem_client_pubkey;
ssh_string mlkem_ciphertext;
#endif
#ifdef HAVE_SNTRUP761
ssh_sntrup761_privkey sntrup761_privkey;
ssh_sntrup761_pubkey sntrup761_client_pubkey;

View File

@@ -53,6 +53,7 @@ typedef unsigned char ssh_curve25519_privkey[CURVE25519_PRIVKEY_SIZE];
int ssh_curve25519_init(ssh_session session);
int curve25519_do_create_k(ssh_session session, ssh_curve25519_pubkey k);
int ssh_curve25519_create_k(ssh_session session, ssh_curve25519_pubkey k);
int ssh_curve25519_build_k(ssh_session session);
int ssh_client_curve25519_init(ssh_session session);
void ssh_client_curve25519_remove_callbacks(ssh_session session);

View File

@@ -29,12 +29,41 @@
* @{
*/
/** @internal
* @brief ED25519 public key.
* Ed25519 public key consist of 32 bytes.
*/
#define ED25519_PK_LEN 32
/** @internal
* @brief ED25519 secret key.
* Ed25519 secret key consist of 64 bytes.
*/
#define ED25519_SK_LEN 64
/** @internal
* @brief ED25519 signature.
* Ed25519 signatures consist of 64 bytes.
*/
#define ED25519_SIG_LEN 64
/** @internal
* @brief ED25519 public key.
* The public key consists of 32 bytes and can be used for signature
* verification.
*/
typedef uint8_t ed25519_pubkey[ED25519_PK_LEN];
/** @internal
* @brief ED25519 private key.
* The private key consists of 64 bytes and should be kept secret.
*/
typedef uint8_t ed25519_privkey[ED25519_SK_LEN];
/** @internal
* @brief ED25519 signature.
* Ed25519 signatures consists of 64 bytes.
*/
typedef uint8_t ed25519_signature[ED25519_SIG_LEN];
#ifdef __cplusplus
@@ -46,7 +75,7 @@ extern "C" {
* @param[out] pk generated public key
* @param[out] sk generated secret key
* @return 0 on success, -1 on error.
* */
*/
int crypto_sign_ed25519_keypair(ed25519_pubkey pk, ed25519_privkey sk);
/** @internal

View File

@@ -29,6 +29,11 @@
/* all OID begin with the tag identifier + length */
#define SSH_OID_TAG 06
#define GSSAPI_KEY_EXCHANGE_SUPPORTED "gss-group14-sha256-," \
"gss-group16-sha512-," \
"gss-nistp256-sha256-," \
"gss-curve25519-sha256-"
typedef struct ssh_gssapi_struct *ssh_gssapi;
#ifdef __cplusplus
@@ -44,14 +49,12 @@ enum ssh_gssapi_state_e {
struct ssh_gssapi_struct{
enum ssh_gssapi_state_e state; /* current state */
struct gss_OID_desc_struct mech; /* mechanism being elected for auth */
gss_cred_id_t server_creds; /* credentials of server */
gss_cred_id_t client_creds; /* creds delegated by the client */
gss_ctx_id_t ctx; /* the authentication context */
gss_name_t client_name; /* Identity of the client */
char *user; /* username of client */
char *canonic_user; /* canonic form of the client's username */
char *service; /* name of the service */
struct {
gss_name_t server_name; /* identity of server */
OM_uint32 flags; /* flags used for init context */
@@ -65,6 +68,7 @@ struct ssh_gssapi_struct{
int ssh_gssapi_handle_userauth(ssh_session session, const char *user, uint32_t n_oid, ssh_string *oids);
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server);
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic);
int ssh_gssapi_server_oids(gss_OID_set *selected);
#endif /* WITH_SERVER */
SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token);
@@ -76,7 +80,20 @@ int ssh_gssapi_init(ssh_session session);
void ssh_gssapi_log_error(int verb, const char *msg_a, int maj_stat, int min_stat);
int ssh_gssapi_auth_mic(ssh_session session);
void ssh_gssapi_free(ssh_session session);
int ssh_gssapi_client_identity(ssh_session session, gss_OID_set *valid_oids);
char *ssh_gssapi_name_to_char(gss_name_t name);
int ssh_gssapi_import_name(struct ssh_gssapi_struct *gssapi, const char *host);
OM_uint32 ssh_gssapi_init_ctx(struct ssh_gssapi_struct *gssapi,
gss_buffer_desc *input_token,
gss_buffer_desc *output_token,
OM_uint32 *ret_flags);
char *ssh_gssapi_oid_hash(ssh_string oid);
char *ssh_gssapi_kex_mechs(ssh_session session);
int ssh_gssapi_check_client_config(ssh_session session);
ssh_buffer ssh_gssapi_build_mic(ssh_session session, const char *context);
int ssh_gssapi_auth_keyex_mic(ssh_session session,
gss_buffer_desc *mic_token_buf);
#ifdef __cplusplus
}

View File

@@ -34,6 +34,9 @@
extern "C" {
#endif
#define NISTP256_SHARED_SECRET_SIZE 32
#define NISTP384_SHARED_SECRET_SIZE 48
int ssh_client_hybrid_mlkem_init(ssh_session session);
void ssh_client_hybrid_mlkem_remove_callbacks(ssh_session session);

36
include/libssh/kex-gss.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* kex-gss.h - GSSAPI key exchange
*
* This file is part of the SSH Library
*
* Copyright (c) 2024 by Gauravsingh Sisodia <xaerru@gmail.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#ifndef KEX_GSS_H_
#define KEX_GSS_H_
#include "config.h"
#ifdef WITH_GSSAPI
int ssh_client_gss_kex_init(ssh_session session);
void ssh_server_gss_kex_init(ssh_session session);
int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet);
void ssh_client_gss_kex_remove_callbacks(ssh_session session);
void ssh_client_gss_kex_remove_callback_hostkey(ssh_session session);
#endif /* WITH_GSSAPI */
#endif /* KEX_GSS_H_ */

View File

@@ -31,6 +31,9 @@ struct ssh_kex_struct {
char *methods[SSH_KEX_METHODS];
};
/* crypto.h needs ssh_kex_struct so it is included below the struct definition */
#include "libssh/crypto.h"
#ifdef __cplusplus
extern "C" {
#endif
@@ -64,6 +67,7 @@ int ssh_make_sessionid(ssh_session session);
int ssh_hashbufin_add_cookie(ssh_session session, unsigned char *cookie);
int ssh_hashbufout_add_cookie(ssh_session session);
int ssh_generate_session_keys(ssh_session session);
bool ssh_kex_is_gss(struct ssh_crypto_struct *crypto);
#ifdef __cplusplus
}

View File

@@ -98,9 +98,9 @@ int ssh_gcry_rand_range(bignum rnd, bignum max);
#define bignum_rand_range(rnd, max) ssh_gcry_rand_range(rnd, max);
#define bignum_dup(orig, dest) do { \
if (*(dest) == NULL) { \
*(dest) = gcry_mpi_copy(orig); \
*(dest) = gcry_mpi_copy((const gcry_mpi_t)orig); \
} else { \
gcry_mpi_set(*(dest), orig); \
gcry_mpi_set(*(dest), (const gcry_mpi_t)orig); \
} \
} while(0)
/* Helper functions for data conversions. */

View File

@@ -1,7 +1,7 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2003-2025 by Aris Adamantiadis and the libssh team
* Copyright (c) 2003-2026 by Aris Adamantiadis and the libssh team
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -152,13 +152,14 @@ enum ssh_auth_e {
};
/* auth flags */
#define SSH_AUTH_METHOD_UNKNOWN 0x0000u
#define SSH_AUTH_METHOD_NONE 0x0001u
#define SSH_AUTH_METHOD_PASSWORD 0x0002u
#define SSH_AUTH_METHOD_PUBLICKEY 0x0004u
#define SSH_AUTH_METHOD_HOSTBASED 0x0008u
#define SSH_AUTH_METHOD_INTERACTIVE 0x0010u
#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u
#define SSH_AUTH_METHOD_UNKNOWN 0x0000u
#define SSH_AUTH_METHOD_NONE 0x0001u
#define SSH_AUTH_METHOD_PASSWORD 0x0002u
#define SSH_AUTH_METHOD_PUBLICKEY 0x0004u
#define SSH_AUTH_METHOD_HOSTBASED 0x0008u
#define SSH_AUTH_METHOD_INTERACTIVE 0x0010u
#define SSH_AUTH_METHOD_GSSAPI_MIC 0x0020u
#define SSH_AUTH_METHOD_GSSAPI_KEYEX 0x0040u
/* messages */
enum ssh_requests_e {
@@ -371,6 +372,12 @@ enum ssh_control_master_options_e {
SSH_CONTROL_MASTER_AUTOASK
};
enum ssh_address_family_options_e {
SSH_ADDRESS_FAMILY_ANY,
SSH_ADDRESS_FAMILY_INET,
SSH_ADDRESS_FAMILY_INET6
};
enum ssh_options_e {
SSH_OPTIONS_HOST,
SSH_OPTIONS_PORT,
@@ -422,6 +429,9 @@ enum ssh_options_e {
SSH_OPTIONS_PROXYJUMP,
SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND,
SSH_OPTIONS_PKI_CONTEXT,
SSH_OPTIONS_ADDRESS_FAMILY,
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE,
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
};
enum {

View File

@@ -36,11 +36,20 @@ extern "C" {
struct mlkem_type_info {
size_t pubkey_size;
size_t ciphertext_size;
#ifdef HAVE_GCRYPT_MLKEM
size_t privkey_size;
enum gcry_kem_algos alg;
#elif defined(HAVE_OPENSSL_MLKEM)
const char *name;
#else
size_t privkey_size;
#endif
};
extern const struct mlkem_type_info MLKEM768_INFO;
#ifdef HAVE_MLKEM1024
extern const struct mlkem_type_info MLKEM1024_INFO;
#endif
#define MLKEM_SHARED_SECRET_SIZE 32

View File

@@ -0,0 +1,127 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2025 by Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1 of the License.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#ifndef MLKEM_NATIVE_H_
#define MLKEM_NATIVE_H_
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
A monomorphic instance of libcrux_ml_kem.types.MlKemPrivateKey
with const generics
- $2400size_t
*/
typedef struct libcrux_ml_kem_types_MlKemPrivateKey_d9_s {
uint8_t value[2400U];
} libcrux_ml_kem_types_MlKemPrivateKey_d9;
/**
A monomorphic instance of libcrux_ml_kem.types.MlKemPublicKey
with const generics
- $1184size_t
*/
typedef struct libcrux_ml_kem_types_MlKemPublicKey_30_s {
uint8_t value[1184U];
} libcrux_ml_kem_types_MlKemPublicKey_30;
typedef struct libcrux_ml_kem_mlkem768_MlKem768KeyPair_s {
libcrux_ml_kem_types_MlKemPrivateKey_d9 sk;
libcrux_ml_kem_types_MlKemPublicKey_30 pk;
} libcrux_ml_kem_mlkem768_MlKem768KeyPair;
typedef struct libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s {
uint8_t value[1088U];
} libcrux_ml_kem_mlkem768_MlKem768Ciphertext;
/**
A monomorphic instance of K.
with types libcrux_ml_kem_types_MlKemCiphertext[[$1088size_t]],
uint8_t[32size_t]
*/
typedef struct tuple_c2_s {
libcrux_ml_kem_mlkem768_MlKem768Ciphertext fst;
uint8_t snd[32U];
} tuple_c2;
/**
Generate ML-KEM 768 Key Pair
*/
libcrux_ml_kem_mlkem768_MlKem768KeyPair
libcrux_ml_kem_mlkem768_portable_generate_key_pair(uint8_t randomness[64U]);
/**
Validate a public key.
Returns `true` if valid, and `false` otherwise.
*/
bool libcrux_ml_kem_mlkem768_portable_validate_public_key(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key);
/**
Encapsulate ML-KEM 768
Generates an ([`MlKem768Ciphertext`], [`MlKemSharedSecret`]) tuple.
The input is a reference to an [`MlKem768PublicKey`] and [`SHARED_SECRET_SIZE`]
bytes of `randomness`.
*/
tuple_c2 libcrux_ml_kem_mlkem768_portable_encapsulate(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
uint8_t randomness[32U]);
/**
Decapsulate ML-KEM 768
Generates an [`MlKemSharedSecret`].
The input is a reference to an [`MlKem768PrivateKey`] and an
[`MlKem768Ciphertext`].
*/
void libcrux_ml_kem_mlkem768_portable_decapsulate(
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
uint8_t ret[32U]);
/* rename some types to be a bit more ergonomic */
#define libcrux_mlkem768_keypair libcrux_ml_kem_mlkem768_MlKem768KeyPair_s
#define libcrux_mlkem768_pk libcrux_ml_kem_types_MlKemPublicKey_30_s
#define libcrux_mlkem768_sk libcrux_ml_kem_types_MlKemPrivateKey_d9_s
#define libcrux_mlkem768_ciphertext libcrux_ml_kem_mlkem768_MlKem768Ciphertext_s
#define libcrux_mlkem768_enc_result tuple_c2_s
/* defines for PRNG inputs */
#define LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN 64U
#define LIBCRUX_ML_KEM_ENC_PRNG_LEN 32
#ifdef __cplusplus
}
#endif
#endif /* MLKEM_NATIVE_H_ */

View File

@@ -59,6 +59,8 @@ enum ssh_bind_options_e {
SSH_BIND_OPTIONS_MODULI,
SSH_BIND_OPTIONS_RSA_MIN_SIZE,
SSH_BIND_OPTIONS_IMPORT_KEY_STR,
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE,
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
};
typedef struct ssh_bind_struct* ssh_bind;
@@ -118,7 +120,7 @@ LIBSSH_API int ssh_bind_listen(ssh_bind ssh_bind_o);
*
* @param[in] userdata A pointer to private data to pass to the callbacks.
*
* @return SSH_OK on success, SSH_ERROR if an error occurred.
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*
* @code
* struct ssh_callbacks_struct cb = {
@@ -172,7 +174,7 @@ LIBSSH_API void ssh_bind_fd_toaccept(ssh_bind ssh_bind_o);
* @param ssh_bind_o The ssh server bind to accept a connection.
* @param session A preallocated ssh session
* @see ssh_new
* @return SSH_OK when a connection is established
* @return `SSH_OK` when a connection is established
*/
LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session);
@@ -186,7 +188,7 @@ LIBSSH_API int ssh_bind_accept(ssh_bind ssh_bind_o, ssh_session session);
* inbound connection
* @see ssh_new
* @see ssh_bind_accept
* @return SSH_OK when a connection is established
* @return `SSH_OK` when a connection is established
*/
LIBSSH_API int ssh_bind_accept_fd(ssh_bind ssh_bind_o, ssh_session session,
socket_t fd);
@@ -198,7 +200,7 @@ LIBSSH_API ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session);
*
* @param session A connected ssh session
* @see ssh_bind_accept
* @return SSH_OK if the key exchange was successful
* @return `SSH_OK` if the key exchange was successful
*/
LIBSSH_API int ssh_handle_key_exchange(ssh_session session);
@@ -215,9 +217,8 @@ LIBSSH_API int ssh_handle_key_exchange(ssh_session session);
* @see ssh_handle_key_exchange
* @see ssh_options_set
*
* @return SSH_OK if initialization succeeds.
* @return `SSH_OK` if initialization succeeds.
*/
LIBSSH_API int ssh_server_init_kex(ssh_session session);
/**
@@ -246,6 +247,7 @@ LIBSSH_API void ssh_bind_free(ssh_bind ssh_bind_o);
* SSH_AUTH_METHOD_HOSTBASED
* SSH_AUTH_METHOD_INTERACTIVE
* SSH_AUTH_METHOD_GSSAPI_MIC
* SSH_AUTH_METHOD_GSSAPI_KEYEX
*/
LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods);
@@ -257,7 +259,7 @@ LIBSSH_API void ssh_set_auth_methods(ssh_session session, int auth_methods);
*
* @param[in] banner The server's banner.
*
* @return SSH_OK on success, SSH_ERROR on error.
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
LIBSSH_API int ssh_send_issue_banner(ssh_session session, const ssh_string banner);

View File

@@ -58,16 +58,17 @@ enum ssh_dh_state_e {
};
enum ssh_pending_call_e {
SSH_PENDING_CALL_NONE = 0,
SSH_PENDING_CALL_CONNECT,
SSH_PENDING_CALL_AUTH_NONE,
SSH_PENDING_CALL_AUTH_PASSWORD,
SSH_PENDING_CALL_AUTH_OFFER_PUBKEY,
SSH_PENDING_CALL_AUTH_PUBKEY,
SSH_PENDING_CALL_AUTH_AGENT,
SSH_PENDING_CALL_AUTH_KBDINT_INIT,
SSH_PENDING_CALL_AUTH_KBDINT_SEND,
SSH_PENDING_CALL_AUTH_GSSAPI_MIC
SSH_PENDING_CALL_NONE = 0,
SSH_PENDING_CALL_CONNECT,
SSH_PENDING_CALL_AUTH_NONE,
SSH_PENDING_CALL_AUTH_PASSWORD,
SSH_PENDING_CALL_AUTH_OFFER_PUBKEY,
SSH_PENDING_CALL_AUTH_PUBKEY,
SSH_PENDING_CALL_AUTH_AGENT,
SSH_PENDING_CALL_AUTH_KBDINT_INIT,
SSH_PENDING_CALL_AUTH_KBDINT_SEND,
SSH_PENDING_CALL_AUTH_GSSAPI_MIC,
SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX,
};
/* libssh calls may block an undefined amount of time */
@@ -201,6 +202,8 @@ struct ssh_session_struct {
*/
bool first_kex_follows_guess_wrong;
ssh_string gssapi_key_exchange_mic;
ssh_buffer in_hashbuf;
ssh_buffer out_hashbuf;
struct ssh_crypto_struct *current_crypto;
@@ -265,6 +268,8 @@ struct ssh_session_struct {
char compressionlevel;
char *gss_server_identity;
char *gss_client_identity;
bool gssapi_key_exchange;
char *gssapi_key_exchange_algs;
int gss_delegate_creds;
int flags;
int exp_flags;
@@ -277,6 +282,7 @@ struct ssh_session_struct {
bool identities_only;
int control_master;
char *control_path;
int address_family;
} opts;
/* server options */

View File

@@ -35,10 +35,6 @@ extern "C" {
#define HAVE_SNTRUP761 1
#endif
extern void crypto_hash_sha512(unsigned char *out,
const unsigned char *in,
unsigned long long inlen);
/*
* Derived from public domain source, written by (in alphabetical order):
* - Daniel J. Bernstein

View File

@@ -24,6 +24,15 @@
#define SSH2_MSG_KEX_DH_GEX_INIT 32
#define SSH2_MSG_KEX_DH_GEX_REPLY 33
#define SSH2_MSG_KEX_DH_GEX_REQUEST 34
#define SSH2_MSG_KEXGSS_INIT 30
#define SSH2_MSG_KEXGSS_CONTINUE 31
#define SSH2_MSG_KEXGSS_COMPLETE 32
#define SSH2_MSG_KEXGSS_HOSTKEY 33
#define SSH2_MSG_KEXGSS_ERROR 34
#define SSH2_MSG_KEXGSS_GROUPREQ 40
#define SSH2_MSG_KEXGSS_GROUP 41
#define SSH2_MSG_USERAUTH_REQUEST 50
#define SSH2_MSG_USERAUTH_FAILURE 51
#define SSH2_MSG_USERAUTH_SUCCESS 52

View File

@@ -75,6 +75,7 @@ MD5CTX md5_init(void);
void md5_ctx_free(MD5CTX);
int md5_update(MD5CTX c, const void *data, size_t len);
int md5_final(unsigned char *md, MD5CTX c);
int md5(const unsigned char *digest, size_t len, unsigned char *hash);
SHACTX sha1_init(void);
void sha1_ctx_free(SHACTX);

View File

@@ -105,6 +105,7 @@ set(libssh_SRCS
error.c
getpass.c
gzip.c
hybrid_mlkem.c
init.c
kdf.c
kex.c
@@ -115,6 +116,7 @@ set(libssh_SRCS
match.c
messages.c
misc.c
mlkem.c
options.c
packet.c
packet_cb.c
@@ -195,6 +197,13 @@ if (WITH_GCRYPT)
curve25519_gcrypt.c
)
endif(HAVE_GCRYPT_CURVE25519)
if (HAVE_GCRYPT_MLKEM)
set(libssh_SRCS
${libssh_SRCS}
mlkem_gcrypt.c
)
endif (HAVE_GCRYPT_MLKEM)
elseif (WITH_MBEDTLS)
set(libssh_SRCS
${libssh_SRCS}
@@ -248,6 +257,12 @@ else (WITH_GCRYPT)
chachapoly.c
)
endif (NOT HAVE_OPENSSL_EVP_CHACHA20)
if (HAVE_OPENSSL_MLKEM)
set(libssh_SRCS
${libssh_SRCS}
mlkem_crypto.c
)
endif (HAVE_OPENSSL_MLKEM)
endif (WITH_GCRYPT)
if (WITH_SFTP)
@@ -286,6 +301,7 @@ if (WITH_GSSAPI AND GSSAPI_FOUND)
set(libssh_SRCS
${libssh_SRCS}
gssapi.c
kex-gss.c
)
endif (WITH_GSSAPI AND GSSAPI_FOUND)
@@ -299,14 +315,18 @@ if (NOT WITH_NACL)
endif()
endif (NOT WITH_NACL)
if (HAVE_MLKEM)
set(libssh_SRCS
${libssh_SRCS}
hybrid_mlkem.c
mlkem_crypto.c
mlkem.c
)
endif (HAVE_MLKEM)
if (NOT HAVE_MLKEM1024)
set(libssh_SRCS
${libssh_SRCS}
mlkem_native.c
external/libcrux_mlkem768_sha3.c
)
if (WITH_WERROR_DECLARATION_AFTER_STATEMENT_FLAG)
set_source_files_properties(external/libcrux_mlkem768_sha3.c
PROPERTIES
COMPILE_FLAGS -Wno-error=declaration-after-statement)
endif()
endif()
if (WITH_FIDO2)
set(libssh_SRCS

View File

@@ -32,19 +32,19 @@
#include <arpa/inet.h>
#endif
#include "libssh/priv.h"
#include "libssh/crypto.h"
#include "libssh/ssh2.h"
#include "libssh/buffer.h"
#include "libssh/agent.h"
#include "libssh/auth.h"
#include "libssh/buffer.h"
#include "libssh/crypto.h"
#include "libssh/gssapi.h"
#include "libssh/keys.h"
#include "libssh/legacy.h"
#include "libssh/misc.h"
#include "libssh/packet.h"
#include "libssh/session.h"
#include "libssh/keys.h"
#include "libssh/auth.h"
#include "libssh/pki.h"
#include "libssh/gssapi.h"
#include "libssh/legacy.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/ssh2.h"
/**
* @defgroup libssh_auth The SSH authentication functions
@@ -88,6 +88,7 @@ static int ssh_auth_response_termination(void *user)
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
case SSH_AUTH_STATE_GSSAPI_TOKEN:
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
case SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
@@ -118,9 +119,14 @@ static const char *ssh_auth_get_current_method(ssh_session session)
case SSH_AUTH_METHOD_INTERACTIVE:
method = "keyboard interactive";
break;
#ifdef WITH_GSSAPI
case SSH_AUTH_METHOD_GSSAPI_MIC:
method = "gssapi";
break;
case SSH_AUTH_METHOD_GSSAPI_KEYEX:
method = "gssapi-keyex";
break;
#endif
default:
break;
}
@@ -175,6 +181,7 @@ static int ssh_userauth_get_response(ssh_session session)
case SSH_AUTH_STATE_GSSAPI_REQUEST_SENT:
case SSH_AUTH_STATE_GSSAPI_TOKEN:
case SSH_AUTH_STATE_GSSAPI_MIC_SENT:
case SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT:
case SSH_AUTH_STATE_PUBKEY_OFFER_SENT:
case SSH_AUTH_STATE_PUBKEY_AUTH_SENT:
case SSH_AUTH_STATE_PASSWORD_AUTH_SENT:
@@ -269,9 +276,14 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_failure) {
if (strstr(auth_methods, "hostbased") != NULL) {
session->auth.supported_methods |= SSH_AUTH_METHOD_HOSTBASED;
}
#ifdef WITH_GSSAPI
if (strstr(auth_methods, "gssapi-with-mic") != NULL) {
session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_MIC;
}
if (strstr(auth_methods, "gssapi-keyex") != NULL) {
session->auth.supported_methods |= SSH_AUTH_METHOD_GSSAPI_KEYEX;
}
#endif
end:
session->auth.current_method = SSH_AUTH_METHOD_UNKNOWN;
@@ -536,7 +548,8 @@ static int build_pubkey_auth_request(ssh_session session,
int rc;
const char *auth_method = "publickey";
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND) {
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND &&
session->current_crypto->server_pubkey != NULL) {
auth_method = "publickey-hostbound-v00@openssh.com";
}
@@ -555,7 +568,8 @@ static int build_pubkey_auth_request(ssh_session session,
return SSH_ERROR;
}
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND) {
if (session->extensions & SSH_EXT_PUBLICKEY_HOSTBOUND &&
session->current_crypto->server_pubkey != NULL) {
rc = add_hostbound_pubkey(session);
if (rc < 0) {
return SSH_ERROR;
@@ -2446,4 +2460,111 @@ pending:
return rc;
}
/**
* @brief Try to authenticate through the "gssapi-keyex" method.
*
* @param[in] session The ssh session to use.
*
* @returns
* - `SSH_AUTH_ERROR`: A serious error happened.
* - `SSH_AUTH_DENIED`: Authentication failed : use another method.
* - `SSH_AUTH_PARTIAL`: You've been partially authenticated, you still
* have to use another method.
* - `SSH_AUTH_SUCCESS`: Authentication success.
* - `SSH_AUTH_AGAIN`: In nonblocking mode, you've got to call this again
* later.
*/
int ssh_userauth_gssapi_keyex(ssh_session session)
{
int rc = SSH_AUTH_DENIED;
#ifdef WITH_GSSAPI
OM_uint32 min_stat;
gss_buffer_desc mic_token_buf = GSS_C_EMPTY_BUFFER;
switch (session->pending_call_state) {
case SSH_PENDING_CALL_NONE:
break;
case SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX:
goto pending;
default:
ssh_set_error(session,
SSH_FATAL,
"Wrong state (%d) during pending SSH call",
session->pending_call_state);
return SSH_ERROR;
}
/* Check if GSSAPI Key exchange was performed */
if (!ssh_kex_is_gss(session->current_crypto)) {
ssh_set_error(session,
SSH_FATAL,
"Attempt to authenticate with gssapi-keyex without "
"doing GSSAPI Key exchange.");
return SSH_ERROR;
}
if (session->gssapi == NULL) {
ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
return SSH_ERROR;
}
rc = ssh_userauth_request_service(session);
if (rc == SSH_AGAIN) {
return SSH_AUTH_AGAIN;
} else if (rc == SSH_ERROR) {
return SSH_AUTH_ERROR;
}
SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi-keyex");
session->auth.current_method = SSH_AUTH_METHOD_GSSAPI_KEYEX;
session->auth.state = SSH_AUTH_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_AUTH_GSSAPI_KEYEX;
SAFE_FREE(session->gssapi->user);
session->gssapi->user = strdup(session->opts.username);
if (session->gssapi->user == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
rc = ssh_gssapi_auth_keyex_mic(session, &mic_token_buf);
if (rc != SSH_OK) {
session->auth.state = SSH_AUTH_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_NONE;
return rc;
}
rc = ssh_buffer_pack(session->out_buffer,
"bsssdP",
SSH2_MSG_USERAUTH_REQUEST,
session->opts.username,
"ssh-connection",
"gssapi-keyex",
mic_token_buf.length,
(size_t)mic_token_buf.length,
mic_token_buf.value);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
session->auth.state = SSH_AUTH_STATE_NONE;
session->pending_call_state = SSH_PENDING_CALL_NONE;
gss_release_buffer(&min_stat, &mic_token_buf);
return rc;
}
gss_release_buffer(&min_stat, &mic_token_buf);
session->auth.state = SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT;
ssh_packet_send(session);
pending:
rc = ssh_userauth_get_response(session);
if (rc != SSH_AUTH_AGAIN) {
session->pending_call_state = SSH_PENDING_CALL_NONE;
}
#else
(void)session; /* unused */
#endif
return rc;
}
/** @} */

View File

@@ -245,8 +245,13 @@ int ssh_bind_listen(ssh_bind sshbind)
sshbind->ecdsa == NULL &&
sshbind->ed25519 == NULL) {
rc = ssh_bind_import_keys(sshbind);
if (rc != SSH_OK) {
return SSH_ERROR;
if (rc == SSH_ERROR) {
if (!sshbind->gssapi_key_exchange) {
ssh_set_error(sshbind, SSH_FATAL, "No usable hostkeys found");
return SSH_ERROR;
}
SSH_LOG(SSH_LOG_DEBUG,
"No usable hostkeys found: Using \"null\" hostkey algorithm");
}
}
@@ -386,6 +391,7 @@ void ssh_bind_free(ssh_bind sshbind){
SAFE_FREE(sshbind->rsakey);
SAFE_FREE(sshbind->ecdsakey);
SAFE_FREE(sshbind->ed25519key);
SAFE_FREE(sshbind->gssapi_key_exchange_algs);
ssh_key_free(sshbind->rsa);
sshbind->rsa = NULL;
@@ -463,6 +469,17 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
}
session->common.log_verbosity = sshbind->common.log_verbosity;
session->opts.gssapi_key_exchange = sshbind->gssapi_key_exchange;
if (sshbind->gssapi_key_exchange_algs != NULL) {
SAFE_FREE(session->opts.gssapi_key_exchange_algs);
session->opts.gssapi_key_exchange_algs =
strdup(sshbind->gssapi_key_exchange_algs);
if (session->opts.gssapi_key_exchange_algs == NULL) {
ssh_set_error_oom(sshbind);
return SSH_ERROR;
}
}
if (sshbind->banner != NULL) {
session->server_opts.custombanner = strdup(sshbind->banner);
@@ -509,8 +526,13 @@ int ssh_bind_accept_fd(ssh_bind sshbind, ssh_session session, socket_t fd)
sshbind->ecdsa == NULL &&
sshbind->ed25519 == NULL) {
rc = ssh_bind_import_keys(sshbind);
if (rc != SSH_OK) {
return SSH_ERROR;
if (rc == SSH_ERROR) {
if (!sshbind->gssapi_key_exchange) {
ssh_set_error(sshbind, SSH_FATAL, "No usable hostkeys found");
return SSH_ERROR;
}
SSH_LOG(SSH_LOG_DEBUG,
"No usable hostkeys found: Using \"null\" hostkey algorithm");
}
}

View File

@@ -602,8 +602,9 @@ ssh_bind_config_parse_line(ssh_bind bind,
break;
case BIND_CFG_REQUIRED_RSA_SIZE:
l = ssh_config_get_long(&s, -1);
if (l >= 0 && (*parser_flags & PARSING)) {
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSA_MIN_SIZE, &l);
if (l >= 0 && l <= INT_MAX && (*parser_flags & PARSING)) {
int i = (int)l;
rc = ssh_bind_options_set(bind, SSH_BIND_OPTIONS_RSA_MIN_SIZE, &i);
if (rc != 0) {
SSH_LOG(SSH_LOG_TRACE,
"line %d: Failed to set RequiredRSASize value '%ld'",

View File

@@ -321,7 +321,7 @@ static int ssh_channel_open_termination(void *c)
*
* @param[in] payload The buffer containing additional payload for the query.
*
* @return SSH_OK if successful; SSH_ERROR otherwise.
* @return `SSH_OK` if successful; `SSH_ERROR` otherwise.
*/
static int
channel_open(ssh_channel channel,
@@ -433,7 +433,7 @@ ssh_channel ssh_channel_from_local(ssh_session session, uint32_t id)
* @brief grows the local window and sends a packet to the other party
* @param session SSH session
* @param channel SSH channel
* @return SSH_OK if successful; SSH_ERROR otherwise.
* @return `SSH_OK` if successful; `SSH_ERROR` otherwise.
*/
static int grow_window(ssh_session session,
ssh_channel channel)
@@ -1059,9 +1059,9 @@ int channel_default_bufferize(ssh_channel channel,
*
* @param[in] channel An allocated channel.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @see ssh_channel_open_forward()
@@ -1084,15 +1084,15 @@ int ssh_channel_open_session(ssh_channel channel)
/**
* @brief Open an agent authentication forwarding channel. This type of channel
* can be opened by a server towards a client in order to provide SSH-Agent services
* to the server-side process. This channel can only be opened if the client
* claimed support by sending a channel request beforehand.
* can be opened by a server towards a client in order to provide SSH-Agent
* services to the server-side process. This channel can only be opened if the
* client claimed support by sending a channel request beforehand.
*
* @param[in] channel An allocated channel.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @see ssh_channel_open_forward()
@@ -1110,7 +1110,6 @@ int ssh_channel_open_auth_agent(ssh_channel channel)
NULL);
}
/**
* @brief Open a TCP/IP forwarding channel.
*
@@ -1127,14 +1126,14 @@ int ssh_channel_open_auth_agent(ssh_channel channel)
* @param[in] localport The port on the host from where the connection
* originated. This is mostly for logging purposes.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
* use ssh_channel_read and ssh_channel_write for this.
* @warning This function does not bind the local port and does not
* automatically forward the content of a socket to the channel. You still have
* to use ssh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_forward(ssh_channel channel, const char *remotehost,
int remoteport, const char *sourcehost, int localport)
@@ -1199,16 +1198,16 @@ error:
* @param[in] localport The port on the host from where the connection
* originated. This is mostly for logging purposes.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK on` success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @warning This function does not bind the local port and does not
* automatically forward the content of a socket to the channel.
* You still have to use ssh_channel_read and ssh_channel_write for this.
* automatically forward the content of a socket to the channel.
* You still have to use ssh_channel_read and ssh_channel_write for this.
* @warning Requires support of OpenSSH for UNIX domain socket forwarding.
*/
*/
int ssh_channel_open_forward_unix(ssh_channel channel,
const char *remotepath,
const char *sourcehost,
@@ -1359,7 +1358,7 @@ void ssh_channel_do_free(ssh_channel channel)
*
* @param[in] channel The channel to send the eof to.
*
* @return SSH_OK on success, SSH_ERROR if an error occurred.
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*
* Example:
@code
@@ -1436,7 +1435,7 @@ error:
*
* @param[in] channel The channel to close.
*
* @return SSH_OK on success, SSH_ERROR if an error occurred.
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*
* @see ssh_channel_free()
* @see ssh_channel_is_eof()
@@ -1524,11 +1523,11 @@ static int ssh_waitsession_unblocked(void *s)
/**
* @internal
* @brief Flushes a channel (and its session) until the output buffer
* is empty, or timeout elapsed.
* is empty, or timeout elapsed.
* @param channel SSH channel
* @return SSH_OK On success,
* SSH_ERROR On error.
* SSH_AGAIN Timeout elapsed (or in nonblocking mode).
* @return `SSH_OK` On success,
* `SSH_ERROR` On error.
* `SSH_AGAIN` Timeout elapsed (or in nonblocking mode).
*/
int ssh_channel_flush(ssh_channel channel)
{
@@ -1719,7 +1718,7 @@ uint32_t ssh_channel_window_size(ssh_channel channel)
*
* @param[in] len The length of the buffer to write to.
*
* @return The number of bytes written, SSH_ERROR on error.
* @return The number of bytes written, `SSH_ERROR` on error.
*
* @see ssh_channel_read()
*/
@@ -1997,9 +1996,9 @@ error:
*
* @param[in] modes_len Number of bytes in 'modes'
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_channel_request_pty_size_modes(ssh_channel channel, const char *terminal,
@@ -2055,6 +2054,19 @@ error:
return rc;
}
/**
* @brief Request a PTY with a specific size using current TTY modes.
*
* Encodes @p terminal modes from the current TTY and sends a PTY request
* for the given channel, terminal type, and size in columns/rows.
*
* @param[in] channel The channel to send the request on.
* @param[in] terminal The terminal type (e.g. "xterm").
* @param[in] col Number of columns.
* @param[in] row Number of rows.
*
* @return `SSH_OK` on success; `SSH_ERROR` on failure.
*/
int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
int col, int row)
{
@@ -2077,9 +2089,9 @@ int ssh_channel_request_pty_size(ssh_channel channel, const char *terminal,
*
* @param[in] channel The channel to send the request.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @see ssh_channel_request_pty_size()
@@ -2098,7 +2110,7 @@ int ssh_channel_request_pty(ssh_channel channel)
*
* @param[in] rows The new number of rows.
*
* @return SSH_OK on success, SSH_ERROR if an error occurred.
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*
* @warning Do not call it from a signal handler if you are not sure any other
* libssh function using the same channel/session is running at the
@@ -2139,9 +2151,9 @@ error:
*
* @param[in] channel The channel to send the request.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_channel_request_shell(ssh_channel channel)
@@ -2160,9 +2172,9 @@ int ssh_channel_request_shell(ssh_channel channel)
*
* @param[in] subsys The subsystem to request (for example "sftp").
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @warning You normally don't have to call it for sftp, see sftp_new().
@@ -2210,9 +2222,9 @@ error:
*
* @param[in] channel The channel to request the sftp subsystem.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @note You should use sftp_new() which does this for you.
@@ -2266,9 +2278,9 @@ static char *generate_cookie(void)
*
* @param[in] screen_number The screen number.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_channel_request_x11(ssh_channel channel, int single_connection, const char *protocol,
@@ -2401,15 +2413,16 @@ ssh_channel ssh_channel_accept_x11(ssh_channel channel, int timeout_ms)
}
/**
* @brief Send an "auth-agent-req" channel request over an existing session channel.
* @brief Send an "auth-agent-req" channel request over an existing session
* channel.
*
* This client-side request will enable forwarding the agent over an secure tunnel.
* When the server is ready to open one authentication agent channel, an
* This client-side request will enable forwarding the agent over an secure
* tunnel. When the server is ready to open one authentication agent channel, an
* ssh_channel_open_request_auth_agent_callback event will be generated.
*
* @param[in] channel The channel to send signal.
*
* @return SSH_OK on success, SSH_ERROR if an error occurred
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
*/
int ssh_channel_request_auth_agent(ssh_channel channel) {
if (channel == NULL) {
@@ -2490,9 +2503,9 @@ static int ssh_global_request_termination(void *s)
*
* @param[in] reply Set if you expect a reply from server.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_global_request(ssh_session session,
@@ -2584,7 +2597,7 @@ error:
/**
* @brief Sends the "tcpip-forward" global request to ask the server to begin
* listening for inbound connections.
* listening for inbound connections.
*
* @param[in] session The ssh session to send the request.
*
@@ -2599,9 +2612,9 @@ error:
* @param[in] bound_port The pointer to get actual bound port. Pass NULL to
* ignore.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
**/
int ssh_channel_listen_forward(ssh_session session,
@@ -2708,9 +2721,9 @@ ssh_channel ssh_channel_open_forward_port(ssh_session session, int timeout_ms, i
*
* @param[in] port The bound port on the server.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*/
int ssh_channel_cancel_forward(ssh_session session,
@@ -2759,9 +2772,9 @@ int ssh_forward_cancel(ssh_session session, const char *address, int port)
*
* @param[in] value The value to set.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
* @warning Some environment variables may be refused by security reasons.
*/
@@ -2815,9 +2828,9 @@ error:
* @param[in] cmd The command to execute
* (e.g. "ls ~/ -al | grep -i reports").
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* Example:
@@ -2880,9 +2893,9 @@ error:
return rc;
}
/**
* @brief Send a signal to remote process (as described in RFC 4254, section 6.9).
* @brief Send a signal to remote process (as described in RFC 4254,
* section 6.9).
*
* Sends a signal 'sig' to the remote process.
* Note, that remote system may not support signals concept.
@@ -2906,7 +2919,7 @@ error:
* SIGUSR1 -> USR1 \n
* SIGUSR2 -> USR2 \n
*
* @return SSH_OK on success, SSH_ERROR if an error occurred.
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*/
int ssh_channel_request_send_signal(ssh_channel channel, const char *sig)
{
@@ -2939,7 +2952,6 @@ error:
return rc;
}
/**
* @brief Send a break signal to the server (as described in RFC 4335).
*
@@ -2951,7 +2963,7 @@ error:
*
* @param[in] length The break-length in milliseconds to send.
*
* @return SSH_OK on success, SSH_ERROR if an error occurred
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
*/
int ssh_channel_request_send_break(ssh_channel channel, uint32_t length)
{
@@ -2989,13 +3001,14 @@ error:
* @param[out] buffer The buffer which will get the data.
*
* @param[in] count The count of bytes to be read. If it is bigger than 0,
* the exact size will be read, else (bytes=0) it will
* return once anything is available.
* the exact size will be read, else (bytes=0) it will return once anything is
* available.
*
* @param is_stderr A boolean value to mark reading from the stderr stream.
*
* @return The number of bytes read, 0 on end of file, SSH_AGAIN on
* timeout and SSH_ERROR on error.
* @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
* on timeout and `SSH_ERROR` on error.
*
* @deprecated Please use ssh_channel_read instead
* @warning This function doesn't work in nonblocking/timeout mode
* @see ssh_channel_read
@@ -3098,11 +3111,11 @@ static int ssh_channel_read_termination(void *s)
*
* @param[in] is_stderr A boolean value to mark reading from the stderr flow.
*
* @return The number of bytes read, 0 on end of file, SSH_AGAIN on
* timeout and SSH_ERROR on error.
* @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
* on timeout and `SSH_ERROR` on error.
*
* @warning This function may return less than count bytes of data, and won't
* block until count bytes have been read.
* block until count bytes have been read.
*/
int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_stderr)
{
@@ -3127,8 +3140,8 @@ int ssh_channel_read(ssh_channel channel, void *dest, uint32_t count, int is_std
* @param[in] timeout_ms A timeout in milliseconds. A value of -1 means
* infinite timeout.
*
* @return The number of bytes read, 0 on end of file, SSH_AGAIN on
* timeout, SSH_ERROR on error.
* @return The number of bytes read, 0 on end of file, `SSH_AGAIN`
* on timeout, `SSH_ERROR` on error.
*
* @warning This function may return less than count bytes of data, and won't
* block until count bytes have been read.
@@ -3238,8 +3251,8 @@ int ssh_channel_read_timeout(ssh_channel channel,
*
* @param[in] is_stderr A boolean to select the stderr stream.
*
* @return The number of bytes read, SSH_AGAIN if nothing is
* available, SSH_ERROR on error, and SSH_EOF if the channel is EOF.
* @return The number of bytes read, `SSH_AGAIN` if nothing is
* available, `SSH_ERROR` on error, and `SSH_EOF` if the channel is EOF.
*
* @see ssh_channel_is_eof()
*/
@@ -3296,11 +3309,11 @@ int ssh_channel_read_nonblocking(ssh_channel channel,
* @param[in] is_stderr A boolean to select the stderr stream.
*
* @return The number of bytes available for reading, 0 if nothing
* is available or SSH_ERROR on error.
* is available or `SSH_ERROR` on error.
* When a channel is freed the function returns
* SSH_ERROR immediately.
* `SSH_ERROR` immediately.
*
* @warning When the channel is in EOF state, the function returns SSH_EOF.
* @warning When the channel is in EOF state, the function returns `SSH_EOF`.
*
* @see ssh_channel_is_eof()
*/
@@ -3343,18 +3356,19 @@ int ssh_channel_poll(ssh_channel channel, int is_stderr)
*
* @param[in] channel The channel to poll.
* @param[in] timeout Set an upper limit on the time for which this function
* will block, in milliseconds. Specifying a negative value
* means an infinite timeout. This parameter is passed to
* the poll() function.
* will block, in milliseconds. Specifying a negative
* value means an infinite timeout. This parameter is
* passed to the poll() function.
* @param[in] is_stderr A boolean to select the stderr stream.
*
* @return The number of bytes available for reading,
* 0 if nothing is available (timeout elapsed),
* SSH_EOF on end of file,
* SSH_ERROR on error.
* `SSH_EOF` on end of file,
* `SSH_ERROR` on error.
*
* @warning When the channel is in EOF state, the function returns SSH_EOF.
* When a channel is freed the function returns SSH_ERROR immediately.
* @warning When the channel is in EOF state, the function returns `SSH_EOF`.
* When a channel is freed the function returns `SSH_ERROR`
* immediately.
*
* @see ssh_channel_is_eof()
*/
@@ -3455,12 +3469,12 @@ static int ssh_channel_exit_status_termination(void *c)
* @param[out] pcore_dumped A pointer to store a boolean value if it dumped a
* core.
*
* @return SSH_OK on success, SSH_AGAIN if we don't have a status
* or an SSH error.
* @return `SSH_OK` on success, `SSH_AGAIN` if we don't have a
* status or an SSH error.
* @warning This function may block until a timeout (or never)
* if the other side is not willing to close the channel.
* When a channel is freed the function returns
* SSH_ERROR immediately.
* `SSH_ERROR` immediately.
*
* If you're looking for an async handling of this register a callback for the
* exit status!
@@ -3524,11 +3538,11 @@ int ssh_channel_get_exit_state(ssh_channel channel,
* @param[in] channel The channel to get the status from.
*
* @return The exit status, -1 if no exit status has been returned
* (yet), or SSH_ERROR on error.
* (yet), or `SSH_ERROR` on error.
* @warning This function may block until a timeout (or never)
* if the other side is not willing to close the channel.
* When a channel is freed the function returns
* SSH_ERROR immediately.
* `SSH_ERROR` immediately.
*
* If you're looking for an async handling of this register a callback for the
* exit status.
@@ -3641,9 +3655,9 @@ static size_t count_ptrs(ssh_channel *ptrs)
*
* @param[in] timeout Timeout as defined by select(2).
*
* @return SSH_OK on a successful operation, SSH_EINTR if the
* @return `SSH_OK` on a successful operation, `SSH_EINTR` if the
* select(2) syscall was interrupted, then relaunch the
* function, or SSH_ERROR on error.
* function, or `SSH_ERROR` on error.
*/
int ssh_channel_select(ssh_channel *readchans, ssh_channel *writechans,
ssh_channel *exceptchans, struct timeval * timeout)
@@ -3840,14 +3854,14 @@ int ssh_channel_write_stderr(ssh_channel channel, const void *data, uint32_t len
* @param[in] localport The source port (your local computer). It's optional
* and for logging purpose.
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
*
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
* use ssh_channel_read and ssh_channel_write for this.
* @warning This function does not bind the local port and does not
* automatically forward the content of a socket to the channel. You
* still have to use ssh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_reverse_forward(ssh_channel channel, const char *remotehost,
int remoteport, const char *sourcehost, int localport)
@@ -3905,13 +3919,13 @@ error:
*
* @param[in] orig_port The source port (the local server).
*
* @return SSH_OK on success,
* SSH_ERROR if an error occurred,
* SSH_AGAIN if in nonblocking mode and call has
* @return `SSH_OK` on success,
* `SSH_ERROR` if an error occurred,
* `SSH_AGAIN` if in nonblocking mode and call has
* to be done again.
* @warning This function does not bind the local port and does not automatically
* forward the content of a socket to the channel. You still have to
* use shh_channel_read and ssh_channel_write for this.
* @warning This function does not bind the local port and does not
* automatically forward the content of a socket to the channel. You
* still have to use shh_channel_read and ssh_channel_write for this.
*/
int ssh_channel_open_x11(ssh_channel channel,
const char *orig_addr, int orig_port)
@@ -3966,7 +3980,7 @@ error:
*
* @param[in] exit_status The exit status to send
*
* @return SSH_OK on success, SSH_ERROR if an error occurred.
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred.
*/
int ssh_channel_request_send_exit_status(ssh_channel channel, int exit_status)
{
@@ -4010,7 +4024,7 @@ error:
* @param[in] errmsg A CRLF explanation text about the error condition
* @param[in] lang The language used in the message (format: RFC 3066)
*
* @return SSH_OK on success, SSH_ERROR if an error occurred
* @return `SSH_OK` on success, `SSH_ERROR` if an error occurred
*/
int ssh_channel_request_send_exit_signal(ssh_channel channel, const char *sig,
int core, const char *errmsg, const char *lang)

View File

@@ -30,14 +30,15 @@
#include <arpa/inet.h>
#endif
#include "libssh/priv.h"
#include "libssh/ssh2.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/options.h"
#include "libssh/socket.h"
#include "libssh/session.h"
#include "libssh/kex-gss.h"
#include "libssh/dh.h"
#include "libssh/options.h"
#include "libssh/packet.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/socket.h"
#include "libssh/ssh2.h"
#ifdef WITH_GEX
#include "libssh/dh-gex.h"
#endif /* WITH_GEX */
@@ -46,9 +47,7 @@
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/kex.h"
#ifdef HAVE_MLKEM
#include "libssh/hybrid_mlkem.h"
#endif
#ifndef _WIN32
#ifdef HAVE_PTHREAD
@@ -267,6 +266,14 @@ int dh_handshake(ssh_session session)
switch (session->dh_handshake_state) {
case DH_STATE_INIT:
switch (session->next_crypto->kex_type) {
#ifdef WITH_GSSAPI
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
case SSH_GSS_KEX_CURVE25519_SHA256:
rc = ssh_client_gss_kex_init(session);
break;
#endif
case SSH_KEX_DH_GROUP1_SHA1:
case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256:
@@ -299,13 +306,13 @@ int dh_handshake(ssh_session session)
rc = ssh_client_sntrup761x25519_init(session);
break;
#endif
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_client_hybrid_mlkem_init(session);
break;
#endif
default:
rc = SSH_ERROR;
}
@@ -915,7 +922,7 @@ error:
*/
const char *ssh_copyright(void)
{
return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2025 "
return SSH_STRINGIFY(LIBSSH_VERSION) " (c) 2003-2026 "
"Aris Adamantiadis, Andreas Schneider "
"and libssh contributors. "
"Distributed under the LGPL, please refer to COPYING "

View File

@@ -91,7 +91,7 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{"passwordauthentication", SOC_PASSWORDAUTHENTICATION, true},
{"pubkeyauthentication", SOC_PUBKEYAUTHENTICATION, true},
{"addkeystoagent", SOC_UNSUPPORTED, true},
{"addressfamily", SOC_UNSUPPORTED, true},
{"addressfamily", SOC_ADDRESSFAMILY, true},
{"batchmode", SOC_UNSUPPORTED, true},
{"canonicaldomains", SOC_UNSUPPORTED, true},
{"canonicalizefallbacklocal", SOC_UNSUPPORTED, true},
@@ -155,6 +155,8 @@ static struct ssh_config_keyword_table_s ssh_config_keyword_table[] = {
{"xauthlocation", SOC_NA, true},
{"pubkeyacceptedkeytypes", SOC_PUBKEYACCEPTEDKEYTYPES, true},
{"requiredrsasize", SOC_REQUIRED_RSA_SIZE, true},
{"gssapikeyexchange", SOC_GSSAPIKEYEXCHANGE, true},
{"gssapikexalgorithms", SOC_GSSAPIKEXALGORITHMS, true},
{NULL, SOC_UNKNOWN, false},
};
@@ -806,28 +808,26 @@ ssh_config_get_auth_option(enum ssh_config_opcode_e opcode)
return -1;
}
#define CHECK_COND_OR_FAIL(cond, error_message) \
do { \
if ((cond)) { \
SSH_LOG(SSH_LOG_DEBUG, \
"line %d: %s: %s", \
count, \
error_message, \
keyword); \
if (fail_on_unknown) { \
ssh_set_error(session, \
SSH_FATAL, \
is_cli ? "%s '%s' value on CLI" \
: "%s '%s' value at line %d", \
error_message, \
keyword, \
is_cli ? 0 : count); \
SAFE_FREE(x); \
return SSH_ERROR; \
} \
break; \
} \
} while (0)
#define CHECK_COND_OR_FAIL(cond, error_message) \
if ((cond)) { \
SSH_LOG(SSH_LOG_DEBUG, \
"line %d: %s: %s", \
count, \
error_message, \
keyword); \
if (fail_on_unknown) { \
ssh_set_error(session, \
SSH_FATAL, \
is_cli ? "%s '%s' value on CLI" \
: "%s '%s' value at line %d", \
error_message, \
keyword, \
is_cli ? 0 : count); \
SAFE_FREE(x); \
return SSH_ERROR; \
} \
break; \
}
static int ssh_config_parse_line_internal(ssh_session session,
const char *line,
@@ -1177,14 +1177,12 @@ static int ssh_config_parse_line_internal(ssh_session session,
}
break;
case SOC_USERNAME:
if (session->opts.username == NULL) {
p = ssh_config_get_str_tok(&s, NULL);
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_USER, p);
}
}
break;
p = ssh_config_get_str_tok(&s, NULL);
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_USER, p);
}
break;
case SOC_IDENTITY:
p = ssh_config_get_str_tok(&s, NULL);
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
@@ -1559,11 +1557,57 @@ static int ssh_config_parse_line_internal(ssh_session session,
ssh_options_set(session, SSH_OPTIONS_CERTIFICATE, p);
}
break;
case SOC_GSSAPIKEYEXCHANGE: {
i = ssh_config_get_yesno(&s, -1);
CHECK_COND_OR_FAIL(i < 0, "Invalid argument");
if (*parsing) {
bool b = (i == 1) ? true : false;
ssh_options_set(session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &b);
}
break;
}
case SOC_GSSAPIKEXALGORITHMS:
p = ssh_config_get_str_tok(&s, NULL);
CHECK_COND_OR_FAIL(p == NULL, "Missing argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS, p);
}
break;
case SOC_REQUIRED_RSA_SIZE:
l = ssh_config_get_long(&s, -1);
CHECK_COND_OR_FAIL(l < 0, "Invalid argument");
CHECK_COND_OR_FAIL(l < 0 || l > INT_MAX, "Invalid argument");
if (*parsing) {
ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &l);
i = (int)l;
ssh_options_set(session, SSH_OPTIONS_RSA_MIN_SIZE, &i);
}
break;
case SOC_ADDRESSFAMILY:
p = ssh_config_get_str_tok(&s, NULL);
if (p == NULL) {
SSH_LOG(SSH_LOG_WARNING,
"line %d: no argument after keyword \"addressfamily\"",
count);
SAFE_FREE(x);
return SSH_ERROR;
}
if (*parsing) {
int value = -1;
if (strcasecmp(p, "any") == 0) {
value = SSH_ADDRESS_FAMILY_ANY;
} else if (strcasecmp(p, "inet") == 0) {
value = SSH_ADDRESS_FAMILY_INET;
} else if (strcasecmp(p, "inet6") == 0) {
value = SSH_ADDRESS_FAMILY_INET6;
} else {
SSH_LOG(SSH_LOG_WARNING,
"line %d: invalid argument \"%s\"",
count,
p);
SAFE_FREE(x);
return SSH_ERROR;
}
ssh_options_set(session, SSH_OPTIONS_ADDRESS_FAMILY, &value);
}
break;
default:
@@ -1578,7 +1622,7 @@ static int ssh_config_parse_line_internal(ssh_session session,
return 0;
}
#undef SSH_PARSE_OR_FAIL
#undef CHECK_COND_OR_FAIL
int ssh_config_parse_line(ssh_session session,
const char *line,

View File

@@ -109,7 +109,8 @@ static int ssh_connect_socket_close(socket_t s)
#endif
}
static int getai(const char *host, int port, struct addrinfo **ai)
static int
getai(const char *host, int port, int ai_family, struct addrinfo **ai)
{
const char *service = NULL;
struct addrinfo hints;
@@ -118,7 +119,7 @@ static int getai(const char *host, int port, struct addrinfo **ai)
ZERO_STRUCT(hints);
hints.ai_protocol = IPPROTO_TCP;
hints.ai_family = PF_UNSPEC;
hints.ai_family = ai_family;
hints.ai_socktype = SOCK_STREAM;
if (port == 0) {
@@ -165,14 +166,39 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
{
socket_t s = -1, first = -1;
int rc;
int ai_family;
static const char *ai_family_str = NULL;
struct addrinfo *ai = NULL;
struct addrinfo *itr = NULL;
char addrname[NI_MAXHOST], portname[NI_MAXSERV];
rc = getai(host, port, &ai);
switch (session->opts.address_family) {
case SSH_ADDRESS_FAMILY_INET:
ai_family = PF_INET;
ai_family_str = "inet";
break;
case SSH_ADDRESS_FAMILY_INET6:
ai_family = PF_INET6;
ai_family_str = "inet6";
break;
case SSH_ADDRESS_FAMILY_ANY:
default:
ai_family = PF_UNSPEC;
ai_family_str = "any";
}
SSH_LOG(SSH_LOG_PACKET,
"Resolve target hostname %s port %d (%s)",
host,
port,
ai_family_str);
rc = getai(host, port, ai_family, &ai);
if (rc != 0) {
ssh_set_error(session, SSH_FATAL,
"Failed to resolve hostname %s (%s)",
host, gai_strerror(rc));
ssh_set_error(session,
SSH_FATAL,
"Failed to resolve hostname %s (%s): %s",
host,
ai_family_str,
gai_strerror(rc));
return -1;
}
@@ -192,13 +218,18 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
struct addrinfo *bind_ai = NULL;
struct addrinfo *bind_itr = NULL;
SSH_LOG(SSH_LOG_PACKET, "Resolving %s", bind_addr);
SSH_LOG(SSH_LOG_PACKET,
"Resolving bind address %s (%s)",
bind_addr,
ai_family_str);
rc = getai(bind_addr, 0, &bind_ai);
rc = getai(bind_addr, 0, ai_family, &bind_ai);
if (rc != 0) {
ssh_set_error(session, SSH_FATAL,
"Failed to resolve bind address %s (%s)",
ssh_set_error(session,
SSH_FATAL,
"Failed to resolve bind address %s (%s): %s",
bind_addr,
ai_family_str,
gai_strerror(rc));
ssh_connect_socket_close(s);
s = -1;
@@ -252,7 +283,28 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
}
}
rc = getnameinfo(itr->ai_addr,
itr->ai_addrlen,
addrname,
sizeof(addrname),
portname,
sizeof(portname),
NI_NUMERICHOST | NI_NUMERICSERV);
if (rc != 0) {
ssh_set_error(session, SSH_FATAL,
"getnameinfo failed: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
ssh_connect_socket_close(s);
s = -1;
continue;
}
errno = 0;
SSH_LOG(SSH_LOG_PACKET,
"Connecting to host %s [%s] port %s",
host,
addrname,
portname);
rc = connect(s, itr->ai_addr, itr->ai_addrlen);
if (rc == -1) {
if ((errno != 0) && (errno != EINPROGRESS)) {
@@ -263,6 +315,7 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
s = -1;
} else {
if (first == -1) {
SSH_LOG(SSH_LOG_PACKET, "EINPROGRESS => Store for later.");
first = s;
} else { /* errno == EINPROGRESS */
/* save only the first "working" socket */
@@ -282,6 +335,9 @@ socket_t ssh_connect_host_nonblocking(ssh_session session, const char *host,
* connection, otherwise return the first address without error or error */
if (s == -1) {
s = first;
} else if (s != first && first != -1) {
/* Clean up the saved socket if any */
ssh_connect_socket_close(first);
}
return s;

View File

@@ -83,6 +83,20 @@ static ssize_t ssh_connector_fd_write(ssh_connector connector,
uint32_t len);
static bool ssh_connector_fd_is_socket(socket_t socket);
/**
* @brief Create a new SSH connector.
*
* Allocates and initializes a new connector object for moving data between
* an SSH session and file descriptors. The connector is created with invalid
* file descriptors and callback structures initialized, but not yet attached
* to any channels or sockets.
*
* @param[in] session The SSH session to associate with the connector.
*
* @return A newly allocated connector on success, or NULL if an
* error occurred. On error, an out-of-memory error is
* set on the session.
*/
ssh_connector ssh_connector_new(ssh_session session)
{
ssh_connector connector;
@@ -112,8 +126,20 @@ ssh_connector ssh_connector_new(ssh_session session)
return connector;
}
/**
* @brief Free an SSH connector.
*
* Cleans up and deallocates a connector created by ssh_connector_new().
* Any channel callbacks and poll objects associated with the @p connector
* are removed and freed before the connector structure itself is released.
*
* @param[in] connector The connector to free.
*/
void ssh_connector_free (ssh_connector connector)
{
if (connector == NULL) {
return;
}
if (connector->in_channel != NULL) {
ssh_remove_channel_callbacks(connector->in_channel,
&connector->in_channel_cb);
@@ -140,6 +166,24 @@ void ssh_connector_free (ssh_connector connector)
free(connector);
}
/**
* @brief Set the input channel for a connector.
*
* Associates an SSH channel with the @p connector as its input source and
* installs the internal channel callbacks used for reading data. Any
* configured input file descriptor is disabled and the connector will
* receive data from the given channel only.
*
* If neither `SSH_CONNECTOR_STDOUT` nor `SSH_CONNECTOR_STDERR` is specified
* in @p flags, `SSH_CONNECTOR_STDOUT` is used as the default.
*
* @param[in] connector The connector to configure.
* @param[in] channel The SSH channel to use as input.
* @param[in] flags A combination of ssh_connector_flags_e values
* selecting which channel streams to read from.
*
* @return `SSH_OK` on success, `SSH_ERROR` on failure.
*/
int ssh_connector_set_in_channel(ssh_connector connector,
ssh_channel channel,
enum ssh_connector_flags_e flags)
@@ -156,6 +200,24 @@ int ssh_connector_set_in_channel(ssh_connector connector,
return ssh_add_channel_callbacks(channel, &connector->in_channel_cb);
}
/**
* @brief Set the output channel for a connector.
*
* Associates an SSH channel with the @p connector as its output target and
* installs the internal channel callbacks used for writing data. Any
* configured output file descriptor is disabled and the connector will
* send data to the given channel only.
*
* If neither `SSH_CONNECTOR_STDOUT` nor `SSH_CONNECTOR_STDERR` is specified
* in @p flags, `SSH_CONNECTOR_STDOUT` is used as the default.
*
* @param[in] connector The connector to configure.
* @param[in] channel The SSH channel to use as output.
* @param[in] flags A combination of ssh_connector_flags_e values
* selecting which channel streams to write to.
*
* @return `SSH_OK` on success, `SSH_ERROR` on failure.
*/
int ssh_connector_set_out_channel(ssh_connector connector,
ssh_channel channel,
enum ssh_connector_flags_e flags)
@@ -172,6 +234,15 @@ int ssh_connector_set_out_channel(ssh_connector connector,
return ssh_add_channel_callbacks(channel, &connector->out_channel_cb);
}
/**
* @brief Set the connector's input file descriptor.
*
* Sets the @p fd (file descriptor) to be used as the input source for the
* @p connector , replacing any previously configured input channel.
*
* @param[in] connector The connector to configure.
* @param[in] fd The file descriptor (socket or regular).
*/
void ssh_connector_set_in_fd(ssh_connector connector, socket_t fd)
{
connector->in_fd = fd;
@@ -179,6 +250,15 @@ void ssh_connector_set_in_fd(ssh_connector connector, socket_t fd)
connector->in_channel = NULL;
}
/**
* @brief Set the connector's output file descriptor.
*
* Sets the @p fd (file descriptor) to be used as the output target for the
* @p connector , replacing any previously configured output channel.
*
* @param[in] connector The connector to configure.
* @param[in] fd The file descriptor (socket or regular).
*/
void ssh_connector_set_out_fd(ssh_connector connector, socket_t fd)
{
connector->out_fd = fd;
@@ -250,7 +330,9 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
}
r = ssh_connector_fd_read(connector, buffer, toread);
if (r < 0) {
/* Sanity: Make sure we do not get too large return value to make static
* analysis tools happy */
if (r < 0 || r > (ssize_t)toread) {
ssh_connector_except(connector, connector->in_fd);
return;
}
@@ -295,7 +377,9 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
w = ssh_connector_fd_write(connector,
buffer + total,
(uint32_t)(r - total));
if (w < 0) {
/* Sanity: Make sure we do not get too large return value
* to make static analysis tools happy */
if (w < 0 || w > (r - total)) {
ssh_connector_except(connector, connector->out_fd);
return;
}
@@ -372,7 +456,7 @@ ssh_connector_fd_out_cb(ssh_connector connector)
*
* @brief Callback called when a poll event is received on a file descriptor.
*
* This is for (input or output.
* This is for input or output.
*
* @param[in] fd file descriptor receiving the event
*

View File

@@ -101,7 +101,7 @@ void ssh_client_curve25519_remove_callbacks(ssh_session session)
ssh_packet_remove_callbacks(session, &ssh_curve25519_client_callbacks);
}
static int ssh_curve25519_build_k(ssh_session session)
int ssh_curve25519_build_k(ssh_session session)
{
ssh_curve25519_pubkey k;
int rc;

View File

@@ -409,6 +409,7 @@ static int ssh_retrieve_dhgroup_file(FILE *moduli,
size_t line = 0;
size_t best_nlines = 0;
*best_size = 0;
for(;;) {
line++;
firstbyte = getc(moduli);

View File

@@ -26,6 +26,10 @@
#include "config.h"
#include <stdio.h>
#ifdef WITH_GSSAPI
#include "libssh/gssapi.h"
#include <gssapi/gssapi.h>
#endif
#include "libssh/priv.h"
#include "libssh/crypto.h"
@@ -36,6 +40,7 @@
#include "libssh/ssh2.h"
#include "libssh/pki.h"
#include "libssh/bignum.h"
#include "libssh/string.h"
static unsigned char p_group1_value[] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xC9, 0x0F, 0xDA, 0xA2,

View File

@@ -424,9 +424,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
break;
case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP14_SHA256:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
break;
case SSH_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
break;
case SSH_KEX_DH_GROUP18_SHA512:

View File

@@ -253,9 +253,11 @@ int ssh_dh_init_common(struct ssh_crypto_struct *crypto)
break;
case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP14_SHA256:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group14, ssh_dh_generator);
break;
case SSH_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
rc = ssh_dh_set_parameters(ctx, ssh_dh_group16, ssh_dh_generator);
break;
case SSH_KEX_DH_GROUP18_SHA512:

View File

@@ -53,12 +53,11 @@ static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
#endif /* OPENSSL_VERSION_NUMBER */
switch (kex_type) {
case SSH_KEX_ECDH_SHA2_NISTP256:
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768NISTP256_SHA256:
#endif
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
return NISTP256;
case SSH_KEX_ECDH_SHA2_NISTP384:
#ifdef HAVE_MLKEM
#if HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
return NISTP384;

View File

@@ -36,15 +36,23 @@
/** @internal
* @brief Map the given key exchange enum value to its curve name.
*/
static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
static const char *ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type)
{
switch (kex_type) {
case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
return "NIST P-256";
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
case SSH_KEX_ECDH_SHA2_NISTP384:
#if HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
return "NIST P-384";
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
case SSH_KEX_ECDH_SHA2_NISTP521:
return "NIST P-521";
default:
return NULL;
}
return NULL;
}
/** @internal

View File

@@ -38,15 +38,21 @@
#ifdef HAVE_ECDH
static mbedtls_ecp_group_id ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type) {
if (kex_type == SSH_KEX_ECDH_SHA2_NISTP256) {
static mbedtls_ecp_group_id
ecdh_kex_type_to_curve(enum ssh_key_exchange_e kex_type)
{
switch (kex_type) {
case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
return MBEDTLS_ECP_DP_SECP256R1;
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP384) {
case SSH_KEX_ECDH_SHA2_NISTP384:
return MBEDTLS_ECP_DP_SECP384R1;
} else if (kex_type == SSH_KEX_ECDH_SHA2_NISTP521) {
case SSH_KEX_ECDH_SHA2_NISTP521:
return MBEDTLS_ECP_DP_SECP521R1;
default:
return MBEDTLS_ECP_DP_NONE;
}
return MBEDTLS_ECP_DP_NONE;
}

8896
src/external/libcrux_mlkem768_sha3.c vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -27,9 +27,7 @@ sntrup761_enc (uint8_t *c, uint8_t *k, const uint8_t *pk,
void
sntrup761_dec (uint8_t *k, const uint8_t *c, const uint8_t *sk);
extern void crypto_hash_sha512 (unsigned char *out,
const unsigned char *in,
unsigned long long inlen);
extern int sha512(const unsigned char *digest, size_t len, unsigned char *hash);
#define MAX_LEN 761
@@ -701,7 +699,7 @@ Hash_prefix (unsigned char *out, int b, const unsigned char *in, int inlen)
x[0] = b;
for (i = 0; i < inlen; ++i)
x[i + 1] = in[i];
crypto_hash_sha512 (h, x, inlen + 1);
sha512 (x, inlen + 1, h);
for (i = 0; i < 32; ++i)
out[i] = h[i];
}

View File

@@ -21,6 +21,7 @@
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
@@ -29,14 +30,17 @@
#include <gssapi/gssapi.h>
#include <libssh/buffer.h>
#include <libssh/callbacks.h>
#include <libssh/crypto.h>
#include <libssh/gssapi.h>
#include <libssh/libssh.h>
#include <libssh/ssh2.h>
#include <libssh/buffer.h>
#include <libssh/crypto.h>
#include <libssh/callbacks.h>
#include <libssh/string.h>
#include <libssh/server.h>
#include <libssh/ssh2.h>
#include <libssh/string.h>
#include <libssh/token.h>
static gss_OID_desc spnego_oid = {6, (void *)"\x2B\x06\x01\x05\x05\x02"};
/** @internal
* @initializes a gssapi context for authentication
@@ -110,7 +114,6 @@ ssh_gssapi_free(ssh_session session)
gss_release_oid(&min, &session->gssapi->client.oid);
gss_delete_sec_context(&min, &session->gssapi->ctx, GSS_C_NO_BUFFER);
SAFE_FREE(session->gssapi->mech.elements);
SAFE_FREE(session->gssapi->canonic_user);
SAFE_FREE(session->gssapi);
}
@@ -147,6 +150,47 @@ static int ssh_gssapi_send_response(ssh_session session, ssh_string oid)
#ifdef WITH_SERVER
/** @internal
* @brief get all the oids server supports
* @param[out] selected OID set of supported oids
* @returns SSH_OK if successful, SSH_ERROR otherwise
*/
int ssh_gssapi_server_oids(gss_OID_set *selected)
{
OM_uint32 maj_stat, min_stat;
size_t i;
char *ptr = NULL;
gss_OID_set supported; /* oids supported by server */
maj_stat = gss_indicate_mechs(&min_stat, &supported);
if (maj_stat != GSS_S_COMPLETE) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"indicate mechs",
maj_stat,
min_stat);
return SSH_ERROR;
}
for (i = 0; i < supported->count; ++i) {
ptr = ssh_get_hexa(supported->elements[i].elements,
supported->elements[i].length);
/* According to RFC 4462 we MUST NOT use SPNEGO */
if (supported->elements[i].length == spnego_oid.length &&
memcmp(supported->elements[i].elements,
spnego_oid.elements,
supported->elements[i].length) == 0) {
SAFE_FREE(ptr);
continue;
}
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
SAFE_FREE(ptr);
}
*selected = supported;
return SSH_OK;
}
/** @internal
* @brief handles an user authentication using GSSAPI
*/
@@ -154,12 +198,9 @@ int
ssh_gssapi_handle_userauth(ssh_session session, const char *user,
uint32_t n_oid, ssh_string *oids)
{
char service_name[] = "host";
gss_buffer_desc name_buf;
gss_name_t server_name; /* local server fqdn */
char hostname[NI_MAXHOST] = {0};
OM_uint32 maj_stat, min_stat;
size_t i;
char *ptr = NULL;
gss_OID_set supported; /* oids supported by server */
gss_OID_set both_supported; /* oids supported by both client and server */
gss_OID_set selected; /* oid selected for authentication */
@@ -167,12 +208,22 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
size_t oid_count=0;
struct gss_OID_desc_struct oid;
int rc;
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
rc = gethostname(hostname, 64);
if (rc != 0) {
SSH_LOG(SSH_LOG_TRACE,
"Error getting hostname: %s",
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
return SSH_ERROR;
}
/* Destroy earlier GSSAPI context if any */
ssh_gssapi_free(session);
rc = ssh_gssapi_init(session);
if (rc == SSH_ERROR)
if (rc == SSH_ERROR) {
return rc;
}
/* Callback should select oid and acquire credential */
if (ssh_callbacks_exists(session->server_callbacks,
@@ -197,23 +248,13 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
/* Default implementation for selecting oid and acquiring credential */
gss_create_empty_oid_set(&min_stat, &both_supported);
maj_stat = gss_indicate_mechs(&min_stat, &supported);
if (maj_stat != GSS_S_COMPLETE) {
SSH_LOG(SSH_LOG_DEBUG, "indicate mechs %d, %d", maj_stat, min_stat);
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"indicate mechs",
maj_stat,
min_stat);
gss_release_oid_set(&min_stat, &both_supported);
/* Get the server supported oids */
rc = ssh_gssapi_server_oids(&supported);
if (rc != SSH_OK) {
return SSH_ERROR;
}
for (i=0; i < supported->count; ++i){
ptr = ssh_get_hexa(supported->elements[i].elements, supported->elements[i].length);
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
free(ptr);
}
/* Loop through client supported oids */
for (i=0 ; i< n_oid ; ++i){
unsigned char *oid_s = (unsigned char *) ssh_string_data(oids[i]);
size_t len = ssh_string_len(oids[i]);
@@ -225,8 +266,10 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
SSH_LOG(SSH_LOG_TRACE,"GSSAPI: received invalid OID");
continue;
}
/* Convert oid from string to gssapi format */
oid.elements = &oid_s[2];
oid.length = len - 2;
/* Check if this client oid is supported by server */
gss_test_oid_set_member(&min_stat,&oid,supported,&present);
if(present){
gss_add_oid_set_member(&min_stat,&oid,&both_supported);
@@ -241,28 +284,23 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
return SSH_OK;
}
name_buf.value = service_name;
name_buf.length = strlen(name_buf.value) + 1;
maj_stat = gss_import_name(&min_stat, &name_buf,
(gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &server_name);
if (maj_stat != GSS_S_COMPLETE) {
SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"importing name",
maj_stat,
min_stat);
rc = ssh_gssapi_import_name(session->gssapi, hostname);
if (rc != SSH_OK) {
ssh_auth_reply_default(session, 0);
gss_release_oid_set(&min_stat, &both_supported);
return -1;
return SSH_ERROR;
}
maj_stat = gss_acquire_cred(&min_stat, server_name, 0,
both_supported, GSS_C_ACCEPT,
&session->gssapi->server_creds, &selected, NULL);
gss_release_name(&min_stat, &server_name);
maj_stat = gss_acquire_cred(&min_stat,
session->gssapi->client.server_name,
0,
both_supported,
GSS_C_ACCEPT,
&session->gssapi->server_creds,
&selected,
NULL);
gss_release_oid_set(&min_stat, &both_supported);
if (maj_stat != GSS_S_COMPLETE) {
SSH_LOG(SSH_LOG_TRACE, "error acquiring credentials %d, %d", maj_stat, min_stat);
ssh_gssapi_log_error(SSH_LOG_TRACE,
"acquiring creds",
maj_stat,
@@ -270,8 +308,7 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
ssh_auth_reply_default(session,0);
return SSH_ERROR;
}
SSH_LOG(SSH_LOG_DEBUG, "acquiring credentials %d, %d", maj_stat, min_stat);
SSH_LOG(SSH_LOG_DEBUG, "acquired credentials");
/* finding which OID from client we selected */
for (i=0 ; i< n_oid ; ++i){
@@ -293,17 +330,8 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
break;
}
}
session->gssapi->mech.length = oid.length;
session->gssapi->mech.elements = malloc(oid.length);
if (session->gssapi->mech.elements == NULL){
ssh_set_error_oom(session);
gss_release_oid_set(&min_stat, &selected);
return SSH_ERROR;
}
memcpy(session->gssapi->mech.elements, oid.elements, oid.length);
gss_release_oid_set(&min_stat, &selected);
session->gssapi->user = strdup(user);
session->gssapi->service = service_name;
session->gssapi->state = SSH_GSSAPI_STATE_RCV_TOKEN;
return ssh_gssapi_send_response(session, oids[i]);
}
@@ -381,7 +409,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
return SSH_PACKET_USED;
}
hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token));
SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s", hexa);
SAFE_FREE(hexa);
input_token.length = ssh_string_len(token);
input_token.value = ssh_string_data(token);
@@ -400,7 +428,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
}
if (GSS_ERROR(maj_stat)){
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Gssapi error",
"accepting token failed",
maj_stat,
min_stat);
gss_release_buffer(&min_stat, &output_token);
@@ -428,7 +456,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
gss_release_buffer(&min_stat, &output_token);
gss_release_name(&min_stat, &client_name);
if(maj_stat == GSS_S_COMPLETE){
if (maj_stat == GSS_S_COMPLETE) {
session->gssapi->state = SSH_GSSAPI_STATE_RCV_MIC;
}
return SSH_PACKET_USED;
@@ -436,7 +464,7 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_server)
#endif /* WITH_SERVER */
static ssh_buffer ssh_gssapi_build_mic(ssh_session session)
ssh_buffer ssh_gssapi_build_mic(ssh_session session, const char *context)
{
struct ssh_crypto_struct *crypto = NULL;
ssh_buffer mic_buffer = NULL;
@@ -456,11 +484,12 @@ static ssh_buffer ssh_gssapi_build_mic(ssh_session session)
rc = ssh_buffer_pack(mic_buffer,
"dPbsss",
crypto->session_id_len,
crypto->session_id_len, crypto->session_id,
crypto->session_id_len,
crypto->session_id,
SSH2_MSG_USERAUTH_REQUEST,
session->gssapi->user,
"ssh-connection",
"gssapi-with-mic");
context);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
SSH_BUFFER_FREE(mic_buffer);
@@ -483,24 +512,27 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic)
(void)user;
(void)type;
SSH_LOG(SSH_LOG_PACKET,"Received SSH_MSG_USERAUTH_GSSAPI_MIC");
SSH_LOG(SSH_LOG_PACKET, "Received SSH_MSG_USERAUTH_GSSAPI_MIC");
mic_token = ssh_buffer_get_ssh_string(packet);
if (mic_token == NULL) {
ssh_set_error(session, SSH_FATAL, "Missing MIC in packet");
goto error;
}
if (session->gssapi == NULL
|| session->gssapi->state != SSH_GSSAPI_STATE_RCV_MIC) {
ssh_set_error(session, SSH_FATAL, "Received SSH_MSG_USERAUTH_GSSAPI_MIC in invalid state");
if (session->gssapi == NULL ||
session->gssapi->state != SSH_GSSAPI_STATE_RCV_MIC) {
ssh_set_error(session,
SSH_FATAL,
"Received SSH_MSG_USERAUTH_GSSAPI_MIC in invalid state");
goto error;
}
mic_buffer = ssh_gssapi_build_mic(session);
mic_buffer = ssh_gssapi_build_mic(session, "gssapi-with-mic");
if (mic_buffer == NULL) {
ssh_set_error_oom(session);
goto error;
}
if (ssh_callbacks_exists(session->server_callbacks, gssapi_verify_mic_function)){
if (ssh_callbacks_exists(session->server_callbacks,
gssapi_verify_mic_function)) {
int rc = session->server_callbacks->gssapi_verify_mic_function(session, mic_token,
ssh_buffer_get(mic_buffer), ssh_buffer_get_len(mic_buffer),
session->server_callbacks->userdata);
@@ -513,7 +545,11 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_mic)
mic_token_buf.length = ssh_string_len(mic_token);
mic_token_buf.value = ssh_string_data(mic_token);
maj_stat = gss_verify_mic(&min_stat, session->gssapi->ctx, &mic_buf, &mic_token_buf, NULL);
maj_stat = gss_verify_mic(&min_stat,
session->gssapi->ctx,
&mic_buf,
&mic_token_buf,
NULL);
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"verifying MIC",
maj_stat,
@@ -580,12 +616,14 @@ ssh_gssapi_creds ssh_gssapi_get_creds(ssh_session session)
*/
void ssh_gssapi_set_creds(ssh_session session, const ssh_gssapi_creds creds)
{
int rc;
if (session == NULL) {
return;
}
if (session->gssapi == NULL) {
ssh_gssapi_init(session);
if (session->gssapi == NULL) {
rc = ssh_gssapi_init(session);
if (rc == SSH_ERROR) {
return;
}
}
@@ -626,9 +664,182 @@ fail:
return SSH_ERROR;
}
/** @brief returns the OIDs of the mechs that have usable credentials
/** @internal
* @brief Get the base64 encoding of md5 of the oid to add as suffix to GSSAPI
* key exchange algorithms.
*
* @param[in] oid The OID as a ssh_string
*
* @returns the hash or NULL on error
*/
static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
char *ssh_gssapi_oid_hash(ssh_string oid)
{
unsigned char *h = NULL;
int rc;
char *base64 = NULL;
h = calloc(MD5_DIGEST_LEN, sizeof(unsigned char));
if (h == NULL) {
return NULL;
}
rc = md5(ssh_string_data(oid), ssh_string_len(oid), h);
if (rc != SSH_OK) {
SAFE_FREE(h);
return NULL;
}
base64 = (char *)bin_to_base64(h, 16);
SAFE_FREE(h);
return base64;
}
/** @internal
* @brief Check if client has GSSAPI mechanisms configured
*
* @param[in] session The SSH session
*
* @returns SSH_OK if any one of the mechanisms is configured or NULL
*/
int ssh_gssapi_check_client_config(ssh_session session)
{
OM_uint32 maj_stat, min_stat;
size_t i;
char *ptr = NULL;
gss_OID_set supported = GSS_C_NO_OID_SET;
gss_name_t client_id = GSS_C_NO_NAME;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc namebuf = GSS_C_EMPTY_BUFFER;
OM_uint32 oflags;
struct ssh_gssapi_struct *gssapi = NULL;
int ret = SSH_ERROR;
gss_OID_set one_oidset = GSS_C_NO_OID_SET;
maj_stat = gss_indicate_mechs(&min_stat, &supported);
if (maj_stat != GSS_S_COMPLETE) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"indicate mechs",
maj_stat,
min_stat);
return SSH_ERROR;
}
for (i = 0; i < supported->count; ++i) {
gssapi = calloc(1, sizeof(struct ssh_gssapi_struct));
if (gssapi == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
gssapi->server_creds = GSS_C_NO_CREDENTIAL;
gssapi->client_creds = GSS_C_NO_CREDENTIAL;
gssapi->ctx = GSS_C_NO_CONTEXT;
gssapi->state = SSH_GSSAPI_STATE_NONE;
/* According to RFC 4462 we MUST NOT use SPNEGO */
if (supported->elements[i].length == spnego_oid.length &&
memcmp(supported->elements[i].elements,
spnego_oid.elements,
supported->elements[i].length) == 0) {
ret = SSH_ERROR;
goto end;
}
gss_create_empty_oid_set(&min_stat, &one_oidset);
gss_add_oid_set_member(&min_stat, &supported->elements[i], &one_oidset);
if (session->opts.gss_client_identity != NULL) {
namebuf.value = (void *)session->opts.gss_client_identity;
namebuf.length = strlen(session->opts.gss_client_identity);
maj_stat = gss_import_name(&min_stat,
&namebuf,
GSS_C_NT_USER_NAME,
&client_id);
if (GSS_ERROR(maj_stat)) {
ret = SSH_ERROR;
goto end;
}
}
maj_stat = gss_acquire_cred(&min_stat,
client_id,
GSS_C_INDEFINITE,
one_oidset,
GSS_C_INITIATE,
&gssapi->client.creds,
NULL,
NULL);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_WARN,
"acquiring credential",
maj_stat,
min_stat);
ret = SSH_ERROR;
goto end;
}
ret = ssh_gssapi_import_name(gssapi, session->opts.host);
if (ret != SSH_OK) {
goto end;
}
maj_stat =
ssh_gssapi_init_ctx(gssapi, &input_token, &output_token, &oflags);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_WARN,
"initializing context",
maj_stat,
min_stat);
ret = SSH_ERROR;
goto end;
}
ptr = ssh_get_hexa(supported->elements[i].elements,
supported->elements[i].length);
SSH_LOG(SSH_LOG_DEBUG, "Supported mech %zu: %s", i, ptr);
free(ptr);
/* If at least one mechanism is configured then return successfully */
ret = SSH_OK;
end:
if (ret == SSH_ERROR) {
SSH_LOG(SSH_LOG_WARN, "GSSAPI not configured correctly");
}
SAFE_FREE(gssapi->user);
gss_release_oid_set(&min_stat, &one_oidset);
gss_release_name(&min_stat, &gssapi->client.server_name);
gss_release_cred(&min_stat, &gssapi->server_creds);
gss_release_cred(&min_stat, &gssapi->client.creds);
gss_release_oid(&min_stat, &gssapi->client.oid);
gss_release_buffer(&min_stat, &output_token);
gss_delete_sec_context(&min_stat, &gssapi->ctx, GSS_C_NO_BUFFER);
SAFE_FREE(gssapi->canonic_user);
SAFE_FREE(gssapi);
if (ret == SSH_OK) {
break;
}
}
gss_release_oid_set(&min_stat, &supported);
return ret;
}
/** @internal
* @brief acquires a credential and returns a set of mechanisms for which it is
* valid
*
* @param[in] session The SSH session
* @param[out] valid_oids The set of OIDs for which the credential is valid
*
* @returns SSH_OK if successful, SSH_ERROR otherwise
*/
int ssh_gssapi_client_identity(ssh_session session, gss_OID_set *valid_oids)
{
OM_uint32 maj_stat, min_stat, lifetime;
gss_OID_set actual_mechs = GSS_C_NO_OID_SET;
@@ -675,6 +886,7 @@ static int ssh_gssapi_match(ssh_session session, gss_OID_set *valid_oids)
goto end;
}
}
SSH_LOG(SSH_LOG_DEBUG, "acquired credentials");
gss_create_empty_oid_set(&min_stat, valid_oids);
@@ -702,6 +914,189 @@ end:
return ret;
}
/** @internal
* @brief Add suffixes of oid hash to each GSSAPI key exchange algorithm
* @param[in] session current session handler
* @returns string suffixed kex algorithms or NULL on error
*/
char *ssh_gssapi_kex_mechs(ssh_session session)
{
size_t i, j;
/* oid selected for authentication */
gss_OID_set selected = GSS_C_NO_OID_SET;
ssh_string *oids = NULL;
int rc;
size_t n_oids = 0;
struct ssh_tokens_st *algs = NULL;
char *oid_hash = NULL;
const char *gss_algs = session->opts.gssapi_key_exchange_algs;
char *new_gss_algs = NULL;
char gss_kex_algs[8000] = {0};
OM_uint32 min_stat;
size_t offset = 0;
/* Get supported oids */
if (session->server) {
#ifdef WITH_SERVER
rc = ssh_gssapi_server_oids(&selected);
if (rc == SSH_ERROR) {
return NULL;
}
#endif
} else {
rc = ssh_gssapi_client_identity(session, &selected);
if (rc == SSH_ERROR) {
return NULL;
}
}
ssh_gssapi_free(session);
n_oids = selected->count;
SSH_LOG(SSH_LOG_DEBUG, "Sending %zu oids", n_oids);
oids = calloc(n_oids, sizeof(ssh_string));
if (oids == NULL) {
ssh_set_error_oom(session);
return NULL;
}
/* Check if algorithms are valid */
new_gss_algs =
ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, gss_algs);
if (gss_algs == NULL) {
ssh_set_error(
session,
SSH_FATAL,
"GSSAPI key exchange algorithms not supported or invalid");
rc = SSH_ERROR;
goto out;
}
algs = ssh_tokenize(new_gss_algs, ',');
if (algs == NULL) {
ssh_set_error(session,
SSH_FATAL,
"Couldn't tokenize GSSAPI key exchange algs");
rc = SSH_ERROR;
goto out;
}
for (i = 0; i < n_oids; ++i) {
oids[i] = ssh_string_new(selected->elements[i].length + 2);
if (oids[i] == NULL) {
ssh_set_error_oom(session);
rc = SSH_ERROR;
goto out;
}
((unsigned char *)oids[i]->data)[0] = SSH_OID_TAG;
((unsigned char *)oids[i]->data)[1] = selected->elements[i].length;
memcpy((unsigned char *)oids[i]->data + 2,
selected->elements[i].elements,
selected->elements[i].length);
/* Get the algorithm suffix */
oid_hash = ssh_gssapi_oid_hash(oids[i]);
if (oid_hash == NULL) {
ssh_set_error_oom(session);
rc = SSH_ERROR;
goto out;
}
/* For each oid loop through the algorithms, append the oid and append
* the algorithms to a string */
for (j = 0; algs->tokens[j]; j++) {
if (sizeof(gss_kex_algs) < offset) {
ssh_set_error(session, SSH_FATAL, "snprintf failed");
rc = SSH_ERROR;
goto out;
}
rc = snprintf(&gss_kex_algs[offset],
sizeof(gss_kex_algs) - offset,
"%s%s,",
algs->tokens[j],
oid_hash);
if (rc < 0 || rc >= (ssize_t)sizeof(gss_kex_algs)) {
ssh_set_error(session, SSH_FATAL, "snprintf failed");
rc = SSH_ERROR;
goto out;
}
/* + 1 for ',' */
offset += strlen(algs->tokens[j]) + strlen(oid_hash) + 1;
}
SAFE_FREE(oid_hash);
SSH_STRING_FREE(oids[i]);
}
rc = SSH_OK;
out:
SAFE_FREE(oid_hash);
SAFE_FREE(oids);
SAFE_FREE(new_gss_algs);
gss_release_oid_set(&min_stat, &selected);
ssh_tokens_free(algs);
if (rc != SSH_OK) {
return NULL;
}
return strdup(gss_kex_algs);
}
int ssh_gssapi_import_name(struct ssh_gssapi_struct *gssapi, const char *host)
{
gss_buffer_desc hostname;
char name_buf[256] = {0};
OM_uint32 maj_stat, min_stat;
/* import target host name */
snprintf(name_buf, sizeof(name_buf), "host@%s", host);
hostname.value = name_buf;
hostname.length = strlen(name_buf) + 1;
maj_stat = gss_import_name(&min_stat,
&hostname,
(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
&gssapi->client.server_name);
SSH_LOG(SSH_LOG_DEBUG, "importing name: %s", name_buf);
if (maj_stat != GSS_S_COMPLETE) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"error importing name",
maj_stat,
min_stat);
}
return maj_stat;
}
OM_uint32 ssh_gssapi_init_ctx(struct ssh_gssapi_struct *gssapi,
gss_buffer_desc *input_token,
gss_buffer_desc *output_token,
OM_uint32 *ret_flags)
{
OM_uint32 maj_stat, min_stat;
maj_stat = gss_init_sec_context(&min_stat,
gssapi->client.creds,
&gssapi->ctx,
gssapi->client.server_name,
gssapi->client.oid,
gssapi->client.flags,
0,
NULL,
input_token,
NULL,
output_token,
ret_flags,
NULL);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"initializing gssapi context",
maj_stat,
min_stat);
}
return maj_stat;
}
/**
* @brief launches a gssapi-with-mic auth request
* @returns SSH_AUTH_ERROR: A serious error happened\n
@@ -716,9 +1111,7 @@ int ssh_gssapi_auth_mic(ssh_session session)
ssh_string *oids = NULL;
int rc;
size_t n_oids = 0;
OM_uint32 maj_stat, min_stat;
char name_buf[256] = {0};
gss_buffer_desc hostname;
OM_uint32 min_stat;
const char *gss_host = session->opts.host;
/* Destroy earlier GSSAPI context if any */
@@ -731,20 +1124,9 @@ int ssh_gssapi_auth_mic(ssh_session session)
if (session->opts.gss_server_identity != NULL) {
gss_host = session->opts.gss_server_identity;
}
/* import target host name */
snprintf(name_buf, sizeof(name_buf), "host@%s", gss_host);
hostname.value = name_buf;
hostname.length = strlen(name_buf) + 1;
maj_stat = gss_import_name(&min_stat, &hostname,
(gss_OID)GSS_C_NT_HOSTBASED_SERVICE,
&session->gssapi->client.server_name);
if (maj_stat != GSS_S_COMPLETE) {
SSH_LOG(SSH_LOG_DEBUG, "importing name %d, %d", maj_stat, min_stat);
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"importing name",
maj_stat,
min_stat);
rc = ssh_gssapi_import_name(session->gssapi, gss_host);
if (rc != SSH_OK) {
return SSH_AUTH_DENIED;
}
@@ -757,7 +1139,7 @@ int ssh_gssapi_auth_mic(ssh_session session)
SSH_LOG(SSH_LOG_DEBUG, "Authenticating with gssapi to host %s with user %s",
session->opts.host, session->gssapi->user);
rc = ssh_gssapi_match(session, &selected);
rc = ssh_gssapi_client_identity(session, &selected);
if (rc == SSH_ERROR) {
return SSH_AUTH_DENIED;
}
@@ -800,6 +1182,50 @@ out:
return SSH_AUTH_ERROR;
}
/**
* @brief Get the MIC for "gssapi-keyex" authentication.
* @returns SSH_ERROR: A serious error happened\n
* SSH_OK: MIC token is stored in mic_token_buf
*/
int ssh_gssapi_auth_keyex_mic(ssh_session session,
gss_buffer_desc *mic_token_buf)
{
ssh_buffer buf = NULL;
gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER;
OM_uint32 maj_stat, min_stat;
if (session->gssapi == NULL || session->gssapi->ctx == NULL) {
ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
return SSH_ERROR;
}
buf = ssh_gssapi_build_mic(session, "gssapi-keyex");
if (buf == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
mic_buf.length = ssh_buffer_get_len(buf);
mic_buf.value = ssh_buffer_get(buf);
maj_stat = gss_get_mic(&min_stat,
session->gssapi->ctx,
GSS_C_QOP_DEFAULT,
&mic_buf,
mic_token_buf);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"generating MIC",
maj_stat,
min_stat);
SSH_BUFFER_FREE(buf);
return SSH_ERROR;
}
SSH_BUFFER_FREE(buf);
return SSH_OK;
}
static gss_OID ssh_gssapi_oid_from_string(ssh_string oid_s)
{
gss_OID ret = NULL;
@@ -869,22 +1295,12 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_response){
session->gssapi->client.flags |= GSS_C_DELEG_FLAG;
}
/* prepare the first TOKEN response */
maj_stat = gss_init_sec_context(&min_stat,
session->gssapi->client.creds,
&session->gssapi->ctx,
session->gssapi->client.server_name,
session->gssapi->client.oid,
session->gssapi->client.flags,
0, NULL, &input_token, NULL,
&output_token, NULL, NULL);
if(GSS_ERROR(maj_stat)){
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Initializing gssapi context",
maj_stat,
min_stat);
maj_stat =
ssh_gssapi_init_ctx(session->gssapi, &input_token, &output_token, NULL);
if (GSS_ERROR(maj_stat)) {
goto error;
}
if (output_token.length != 0){
hexa = ssh_get_hexa(output_token.value, output_token.length);
SSH_LOG(SSH_LOG_PACKET, "GSSAPI: sending token %s", hexa);
@@ -920,7 +1336,7 @@ static int ssh_gssapi_send_mic(ssh_session session)
SSH_LOG(SSH_LOG_PACKET,"Sending SSH_MSG_USERAUTH_GSSAPI_MIC");
mic_buffer = ssh_gssapi_build_mic(session);
mic_buffer = ssh_gssapi_build_mic(session, "gssapi-with-mic");
if (mic_buffer == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
@@ -984,27 +1400,13 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_gssapi_token_client)
hexa = ssh_get_hexa(ssh_string_data(token),ssh_string_len(token));
SSH_LOG(SSH_LOG_PACKET, "GSSAPI Token : %s",hexa);
SAFE_FREE(hexa);
input_token.length = ssh_string_len(token);
input_token.value = ssh_string_data(token);
maj_stat = gss_init_sec_context(&min_stat,
session->gssapi->client.creds,
&session->gssapi->ctx,
session->gssapi->client.server_name,
session->gssapi->client.oid,
session->gssapi->client.flags,
0, NULL, &input_token, NULL,
&output_token, NULL, NULL);
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"accepting token",
maj_stat,
min_stat);
maj_stat =
ssh_gssapi_init_ctx(session->gssapi, &input_token, &output_token, NULL);
SSH_STRING_FREE(token);
if (GSS_ERROR(maj_stat)){
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"Gssapi error",
maj_stat,
min_stat);
if (GSS_ERROR(maj_stat)) {
goto error;
}

View File

@@ -24,12 +24,14 @@
#include "config.h"
#include "libssh/bignum.h"
#include "libssh/buffer.h"
#include "libssh/hybrid_mlkem.h"
#include "libssh/pki.h"
#include "libssh/ssh2.h"
/* sorry, this needs to come last to avoid header dependency issues */
#include "libssh/bignum.h"
static SSH_PACKET_CALLBACK(ssh_packet_client_hybrid_mlkem_reply);
static ssh_packet_callback dh_client_callbacks[] = {
@@ -43,44 +45,68 @@ static struct ssh_packet_callbacks_struct ssh_hybrid_mlkem_client_callbacks = {
.user = NULL,
};
static ssh_string derive_ecdh_secret(ssh_session session)
static ssh_string derive_curve25519_secret(ssh_session session)
{
ssh_string secret = NULL;
int rc;
secret = ssh_string_new(CURVE25519_PUBKEY_SIZE);
if (secret == NULL) {
ssh_set_error_oom(session);
return NULL;
}
rc = ssh_curve25519_create_k(session, ssh_string_data(secret));
if (rc != SSH_OK) {
ssh_set_error(session,
SSH_FATAL,
"Curve25519 secret derivation failed");
ssh_string_free(secret);
return NULL;
}
return secret;
}
static ssh_string derive_nist_curve_secret(ssh_session session,
size_t secret_size)
{
struct ssh_crypto_struct *crypto = session->next_crypto;
ssh_string secret = NULL;
size_t secret_size;
int rc;
switch (crypto->kex_type) {
rc = ecdh_build_k(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "ECDH secret derivation failed");
return NULL;
}
secret = ssh_make_padded_bignum_string(crypto->shared_secret, secret_size);
if (secret == NULL) {
ssh_set_error(session, SSH_FATAL, "Failed to encode the shared secret");
}
bignum_safe_free(crypto->shared_secret);
return secret;
}
static ssh_string derive_ecdh_secret(ssh_session session)
{
ssh_string secret = NULL;
switch (session->next_crypto->kex_type) {
case SSH_KEX_MLKEM768X25519_SHA256:
secret = ssh_string_new(CURVE25519_PUBKEY_SIZE);
if (secret == NULL) {
ssh_set_error_oom(session);
return NULL;
}
rc = ssh_curve25519_create_k(session, ssh_string_data(secret));
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Curve25519 secret derivation failed");
ssh_string_free(secret);
return NULL;
}
secret = derive_curve25519_secret(session);
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
case SSH_KEX_MLKEM1024NISTP384_SHA384:
rc = ecdh_build_k(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "ECDH secret derivation failed");
return NULL;
}
secret_size = bignum_num_bytes(crypto->shared_secret);
secret = ssh_string_new(secret_size);
if (secret == NULL) {
ssh_set_error_oom(session);
bignum_safe_free(crypto->shared_secret);
return NULL;
}
bignum_bn2bin(crypto->shared_secret, secret_size, ssh_string_data(secret));
bignum_safe_free(crypto->shared_secret);
secret = derive_nist_curve_secret(session, NISTP256_SHARED_SECRET_SIZE);
break;
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
secret = derive_nist_curve_secret(session, NISTP384_SHARED_SECRET_SIZE);
break;
#endif
default:
ssh_set_error(session, SSH_FATAL, "Unsupported KEX type");
return NULL;
@@ -105,10 +131,12 @@ static int derive_hybrid_secret(ssh_session session,
digest = sha256;
digest_len = SHA256_DIGEST_LEN;
break;
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
digest = sha384;
digest_len = SHA384_DIGEST_LEN;
break;
#endif
default:
ssh_set_error(session, SSH_FATAL, "Unsupported KEX type");
goto cleanup;
@@ -220,7 +248,9 @@ int ssh_client_hybrid_mlkem_init(ssh_session session)
crypto->curve25519_client_pubkey);
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_ecdh_init(session);
if (rc != SSH_OK) {
ssh_set_error(session,
@@ -411,7 +441,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_client_hybrid_mlkem_reply)
#endif
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
ecdh_server_pubkey_size = ssh_buffer_get_len(server_reply_buffer);
ssh_string_free(crypto->ecdh_server_pubkey);
crypto->ecdh_server_pubkey = ssh_string_new(ecdh_server_pubkey_size);
@@ -561,7 +593,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
#endif
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_ecdh_init(session);
if (rc != SSH_OK) {
ssh_set_error(session,
@@ -658,7 +692,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
#endif
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
ecdh_client_pubkey_size = ssh_buffer_get_len(client_init_buffer);
ssh_string_free(crypto->ecdh_client_pubkey);
crypto->ecdh_client_pubkey = ssh_string_new(ecdh_client_pubkey_size);
@@ -736,7 +772,9 @@ static SSH_PACKET_CALLBACK(ssh_packet_server_hybrid_mlkem_init)
crypto->curve25519_server_pubkey);
break;
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_buffer_pack(server_reply_buffer,
"PP",
ssh_string_len(crypto->mlkem_ciphertext),

670
src/kex-gss.c Normal file
View File

@@ -0,0 +1,670 @@
/*
* kex-gss.c - GSSAPI key exchange
*
* This file is part of the SSH Library
*
* Copyright (c) 2024 by Gauravsingh Sisodia <xaerru@gmail.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or (at your
* option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/gssapi.h"
#include <errno.h>
#include <gssapi/gssapi.h>
#include <stdio.h>
#include "libssh/buffer.h"
#include "libssh/crypto.h"
#include "libssh/kex-gss.h"
#include "libssh/bignum.h"
#include "libssh/curve25519.h"
#include "libssh/ecdh.h"
#include "libssh/dh.h"
#include "libssh/priv.h"
#include "libssh/session.h"
#include "libssh/ssh2.h"
static SSH_PACKET_CALLBACK(ssh_packet_client_gss_kex_reply);
static ssh_packet_callback gss_kex_client_callbacks[] = {
ssh_packet_client_gss_kex_reply,
};
static struct ssh_packet_callbacks_struct ssh_gss_kex_client_callbacks = {
.start = SSH2_MSG_KEXGSS_COMPLETE,
.n_callbacks = 1,
.callbacks = gss_kex_client_callbacks,
.user = NULL,
};
static SSH_PACKET_CALLBACK(ssh_packet_client_gss_kex_hostkey);
static ssh_packet_callback gss_kex_client_callback_hostkey[] = {
ssh_packet_client_gss_kex_hostkey,
};
static struct ssh_packet_callbacks_struct ssh_gss_kex_client_callback_hostkey = {
.start = SSH2_MSG_KEXGSS_HOSTKEY,
.n_callbacks = 1,
.callbacks = gss_kex_client_callback_hostkey,
.user = NULL,
};
static ssh_string dh_init(ssh_session session)
{
int rc, keypair;
#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
const_bignum const_pubkey;
#endif
bignum pubkey = NULL;
ssh_string pubkey_string = NULL;
struct ssh_crypto_struct *crypto = session->next_crypto;
if (session->server) {
keypair = DH_SERVER_KEYPAIR;
} else {
keypair = DH_CLIENT_KEYPAIR;
}
rc = ssh_dh_init_common(crypto);
if (rc != SSH_OK) {
goto end;
}
rc = ssh_dh_keypair_gen_keys(crypto->dh_ctx, keypair);
if (rc != SSH_OK) {
goto end;
}
#if !defined(HAVE_LIBCRYPTO) || OPENSSL_VERSION_NUMBER < 0x30000000L
rc = ssh_dh_keypair_get_keys(crypto->dh_ctx, keypair, NULL, &const_pubkey);
bignum_dup(const_pubkey, &pubkey);
#else
rc = ssh_dh_keypair_get_keys(crypto->dh_ctx, keypair, NULL, &pubkey);
#endif
if (rc != SSH_OK) {
goto end;
}
pubkey_string = ssh_make_bignum_string(pubkey);
end:
bignum_safe_free(pubkey);
return pubkey_string;
}
static int dh_import_peer_key(ssh_session session, ssh_string peer_key)
{
int rc, keypair;
bignum peer_key_bn;
struct ssh_crypto_struct *crypto = session->next_crypto;
if (session->server) {
keypair = DH_CLIENT_KEYPAIR;
} else {
keypair = DH_SERVER_KEYPAIR;
}
peer_key_bn = ssh_make_string_bn(peer_key);
rc = ssh_dh_keypair_set_keys(crypto->dh_ctx, keypair, NULL, peer_key_bn);
if (rc != SSH_OK) {
bignum_safe_free(peer_key_bn);
}
return rc;
}
/** @internal
* @brief Starts gssapi key exchange
*/
int ssh_client_gss_kex_init(ssh_session session)
{
struct ssh_crypto_struct *crypto = session->next_crypto;
int rc, ret = SSH_ERROR;
/* oid selected for authentication */
gss_OID_set selected = GSS_C_NO_OID_SET;
OM_uint32 maj_stat, min_stat;
const char *gss_host = session->opts.host;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
OM_uint32 oflags;
ssh_string pubkey = NULL;
switch (crypto->kex_type) {
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
pubkey = dh_init(session);
if (pubkey == NULL) {
ssh_set_error(session, SSH_FATAL, "Failed to generate DH keypair");
goto out;
}
break;
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
rc = ssh_ecdh_init(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Failed to generate ECDH keypair");
goto out;
}
pubkey = ssh_string_copy(crypto->ecdh_client_pubkey);
break;
case SSH_GSS_KEX_CURVE25519_SHA256:
rc = ssh_curve25519_init(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Failed to generate Curve25519 keypair");
goto out;
}
pubkey = ssh_string_new(CURVE25519_PUBKEY_SIZE);
if (pubkey == NULL) {
ssh_set_error_oom(session);
goto out;
}
rc = ssh_string_fill(pubkey,
crypto->curve25519_client_pubkey,
CURVE25519_PUBKEY_SIZE);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Failed to copy Curve25519 pubkey");
goto out;
}
break;
default:
ssh_set_error(session, SSH_FATAL, "Unsupported GSSAPI KEX method");
goto out;
}
rc = ssh_gssapi_init(session);
if (rc != SSH_OK) {
goto out;
}
if (session->opts.gss_server_identity != NULL) {
gss_host = session->opts.gss_server_identity;
}
rc = ssh_gssapi_import_name(session->gssapi, gss_host);
if (rc != SSH_OK) {
goto out;
}
rc = ssh_gssapi_client_identity(session, &selected);
if (rc != SSH_OK) {
goto out;
}
session->gssapi->client.flags = GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG;
maj_stat = ssh_gssapi_init_ctx(session->gssapi,
&input_token,
&output_token,
&oflags);
gss_release_oid_set(&min_stat, &selected);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_WARN,
"Initializing gssapi context",
maj_stat,
min_stat);
goto out;
}
if (!(oflags & GSS_C_INTEG_FLAG) || !(oflags & GSS_C_MUTUAL_FLAG)) {
SSH_LOG(SSH_LOG_WARN,
"GSSAPI(init) integrity and mutual flags were not set");
goto out;
}
rc = ssh_buffer_pack(session->out_buffer,
"bdPS",
SSH2_MSG_KEXGSS_INIT,
output_token.length,
(size_t)output_token.length,
output_token.value,
pubkey);
if (rc != SSH_OK) {
goto out;
}
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_gss_kex_client_callbacks);
ssh_packet_set_callbacks(session, &ssh_gss_kex_client_callback_hostkey);
session->dh_handshake_state = DH_STATE_INIT_SENT;
rc = ssh_packet_send(session);
if (rc != SSH_OK) {
goto out;
}
ret = SSH_OK;
out:
gss_release_buffer(&min_stat, &output_token);
ssh_string_free(pubkey);
return ret;
}
void ssh_client_gss_kex_remove_callbacks(ssh_session session)
{
ssh_packet_remove_callbacks(session, &ssh_gss_kex_client_callbacks);
}
void ssh_client_gss_kex_remove_callback_hostkey(ssh_session session)
{
ssh_packet_remove_callbacks(session, &ssh_gss_kex_client_callback_hostkey);
}
SSH_PACKET_CALLBACK(ssh_packet_client_gss_kex_reply)
{
struct ssh_crypto_struct *crypto = session->next_crypto;
ssh_string mic = NULL, otoken = NULL, server_pubkey = NULL;
uint8_t b;
int rc;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
OM_uint32 oflags;
OM_uint32 maj_stat;
(void)type;
(void)user;
ssh_client_gss_kex_remove_callbacks(session);
rc = ssh_buffer_unpack(packet, "SSbS", &server_pubkey, &mic, &b, &otoken);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "No public key in server reply");
goto error;
}
SSH_STRING_FREE(session->gssapi_key_exchange_mic);
session->gssapi_key_exchange_mic = mic;
input_token.length = ssh_string_len(otoken);
input_token.value = ssh_string_data(otoken);
maj_stat = ssh_gssapi_init_ctx(session->gssapi,
&input_token,
&output_token,
&oflags);
if (maj_stat != GSS_S_COMPLETE) {
goto error;
}
SSH_STRING_FREE(otoken);
switch (crypto->kex_type) {
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
rc = dh_import_peer_key(session, server_pubkey);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not import server pubkey");
goto error;
}
rc = ssh_dh_compute_shared_secret(crypto->dh_ctx,
DH_CLIENT_KEYPAIR,
DH_SERVER_KEYPAIR,
&crypto->shared_secret);
ssh_dh_debug_crypto(crypto);
break;
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
crypto->ecdh_server_pubkey = ssh_string_copy(server_pubkey);
rc = ecdh_build_k(session);
break;
case SSH_GSS_KEX_CURVE25519_SHA256:
memcpy(crypto->curve25519_server_pubkey,
ssh_string_data(server_pubkey),
CURVE25519_PUBKEY_SIZE);
rc = ssh_curve25519_build_k(session);
break;
default:
ssh_set_error(session, SSH_FATAL, "Unsupported GSSAPI KEX method");
goto error;
}
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not derive shared secret");
goto error;
}
/* Send the MSG_NEWKEYS */
rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
ssh_string_free(server_pubkey);
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
return SSH_PACKET_USED;
error:
ssh_string_free(server_pubkey);
session->session_state = SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
}
SSH_PACKET_CALLBACK(ssh_packet_client_gss_kex_hostkey)
{
ssh_string pubkey_blob = NULL;
int rc;
(void)type;
(void)user;
ssh_client_gss_kex_remove_callback_hostkey(session);
rc = ssh_buffer_unpack(packet, "S", &pubkey_blob);
if (rc == SSH_ERROR) {
ssh_set_error(session,
SSH_FATAL,
"Invalid SSH2_MSG_KEXGSS_HOSTKEY packet");
goto error;
}
rc = ssh_dh_import_next_pubkey_blob(session, pubkey_blob);
SSH_STRING_FREE(pubkey_blob);
if (rc != 0) {
goto error;
}
return SSH_PACKET_USED;
error:
session->session_state = SSH_SESSION_STATE_ERROR;
return SSH_PACKET_USED;
}
#ifdef WITH_SERVER
static SSH_PACKET_CALLBACK(ssh_packet_server_gss_kex_init);
static ssh_packet_callback gss_kex_server_callbacks[] = {
ssh_packet_server_gss_kex_init,
};
static struct ssh_packet_callbacks_struct ssh_gss_kex_server_callbacks = {
.start = SSH2_MSG_KEXGSS_INIT,
.n_callbacks = 1,
.callbacks = gss_kex_server_callbacks,
.user = NULL,
};
/** @internal
* @brief sets up the gssapi kex callbacks
*/
void ssh_server_gss_kex_init(ssh_session session)
{
/* register the packet callbacks */
ssh_packet_set_callbacks(session, &ssh_gss_kex_server_callbacks);
}
/** @internal
* @brief processes a SSH_MSG_KEXGSS_INIT and sends
* the appropriate SSH_MSG_KEXGSS_COMPLETE
*/
int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
{
struct ssh_crypto_struct *crypto = session->next_crypto;
ssh_key privkey = NULL;
enum ssh_digest_e digest = SSH_DIGEST_AUTO;
ssh_string client_pubkey = NULL;
ssh_string server_pubkey = NULL;
int rc;
gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
ssh_string otoken = NULL;
ssh_string server_pubkey_blob = NULL;
OM_uint32 maj_stat, min_stat;
gss_name_t client_name = GSS_C_NO_NAME;
OM_uint32 ret_flags = 0;
gss_buffer_desc mic = GSS_C_EMPTY_BUFFER, msg = GSS_C_EMPTY_BUFFER;
char hostname[NI_MAXHOST] = {0};
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
rc = ssh_buffer_unpack(packet, "S", &otoken);
if (rc == SSH_ERROR) {
ssh_set_error(session, SSH_FATAL, "No token in client request");
goto error;
}
input_token.length = ssh_string_len(otoken);
input_token.value = ssh_string_data(otoken);
rc = ssh_buffer_unpack(packet, "S", &client_pubkey);
if (rc == SSH_ERROR) {
ssh_set_error(session, SSH_FATAL, "No public key in client request");
goto error;
}
switch (crypto->kex_type) {
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
server_pubkey = dh_init(session);
if (server_pubkey == NULL) {
ssh_set_error(session, SSH_FATAL, "Could not generate a DH keypair");
goto error;
}
rc = dh_import_peer_key(session, client_pubkey);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not import client pubkey");
goto error;
}
rc = ssh_dh_compute_shared_secret(crypto->dh_ctx,
DH_SERVER_KEYPAIR,
DH_CLIENT_KEYPAIR,
&crypto->shared_secret);
ssh_dh_debug_crypto(crypto);
break;
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
rc = ssh_ecdh_init(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not generate an ECDH keypair");
goto error;
}
crypto->ecdh_client_pubkey = ssh_string_copy(client_pubkey);
server_pubkey = ssh_string_copy(crypto->ecdh_server_pubkey);
rc = ecdh_build_k(session);
break;
case SSH_GSS_KEX_CURVE25519_SHA256:
rc = ssh_curve25519_init(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not generate a Curve25519 keypair");
goto error;
}
server_pubkey = ssh_string_new(CURVE25519_PUBKEY_SIZE);
if (server_pubkey == NULL) {
ssh_set_error_oom(session);
goto error;
}
rc = ssh_string_fill(server_pubkey,
crypto->curve25519_server_pubkey,
CURVE25519_PUBKEY_SIZE);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Failed to copy Curve25519 pubkey");
goto error;
}
memcpy(crypto->curve25519_client_pubkey,
ssh_string_data(client_pubkey),
CURVE25519_PUBKEY_SIZE);
rc = ssh_curve25519_build_k(session);
break;
default:
ssh_set_error(session, SSH_FATAL, "Unsupported GSSAPI KEX method");
goto error;
}
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not derive shared secret");
goto error;
}
/* Also imports next_crypto->server_pubkey
* Can give error when using null hostkey */
ssh_get_key_params(session, &privkey, &digest);
rc = ssh_make_sessionid(session);
if (rc != SSH_OK) {
ssh_set_error(session, SSH_FATAL, "Could not create a session id");
goto error;
}
if (strcmp(crypto->kex_methods[SSH_HOSTKEYS], "null") != 0) {
rc =
ssh_dh_get_next_server_publickey_blob(session, &server_pubkey_blob);
if (rc != SSH_OK) {
goto error;
}
rc = ssh_buffer_pack(session->out_buffer,
"bS",
SSH2_MSG_KEXGSS_HOSTKEY,
server_pubkey_blob);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
ssh_buffer_reinit(session->out_buffer);
goto error;
}
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
goto error;
}
SSH_LOG(SSH_LOG_DEBUG, "Sent SSH2_MSG_KEXGSS_HOSTKEY");
SSH_STRING_FREE(server_pubkey_blob);
}
rc = ssh_gssapi_init(session);
if (rc == SSH_ERROR) {
goto error;
}
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));
goto error;
}
rc = ssh_gssapi_import_name(session->gssapi, hostname);
if (rc != SSH_OK) {
goto error;
}
maj_stat = gss_acquire_cred(&min_stat,
session->gssapi->client.server_name,
0,
GSS_C_NO_OID_SET,
GSS_C_ACCEPT,
&session->gssapi->server_creds,
NULL,
NULL);
if (maj_stat != GSS_S_COMPLETE) {
ssh_gssapi_log_error(SSH_LOG_TRACE,
"acquiring credentials",
maj_stat,
min_stat);
goto error;
}
maj_stat = gss_accept_sec_context(&min_stat,
&session->gssapi->ctx,
session->gssapi->server_creds,
&input_token,
GSS_C_NO_CHANNEL_BINDINGS,
&client_name,
NULL /*mech_oid*/,
&output_token,
&ret_flags,
NULL /*time*/,
&session->gssapi->client_creds);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"accepting token failed",
maj_stat,
min_stat);
goto error;
}
SSH_STRING_FREE(otoken);
gss_release_name(&min_stat, &client_name);
if (!(ret_flags & GSS_C_INTEG_FLAG) || !(ret_flags & GSS_C_MUTUAL_FLAG)) {
SSH_LOG(SSH_LOG_WARN,
"GSSAPI(accept) integrity and mutual flags were not set");
goto error;
}
SSH_LOG(SSH_LOG_DEBUG, "token accepted");
msg.length = session->next_crypto->digest_len;
msg.value = session->next_crypto->secret_hash;
maj_stat = gss_get_mic(&min_stat,
session->gssapi->ctx,
GSS_C_QOP_DEFAULT,
&msg,
&mic);
if (GSS_ERROR(maj_stat)) {
ssh_gssapi_log_error(SSH_LOG_DEBUG,
"creating mic failed",
maj_stat,
min_stat);
goto error;
}
rc = ssh_buffer_pack(session->out_buffer,
"bSdPbdP",
SSH2_MSG_KEXGSS_COMPLETE,
server_pubkey,
mic.length,
(size_t)mic.length,
mic.value,
1,
output_token.length,
(size_t)output_token.length,
output_token.value);
if (rc != SSH_OK) {
ssh_set_error_oom(session);
ssh_buffer_reinit(session->out_buffer);
goto error;
}
gss_release_buffer(&min_stat, &output_token);
gss_release_buffer(&min_stat, &mic);
rc = ssh_packet_send(session);
if (rc == SSH_ERROR) {
goto error;
}
SSH_LOG(SSH_LOG_DEBUG, "Sent SSH2_MSG_KEXGSS_COMPLETE");
session->dh_handshake_state = DH_STATE_NEWKEYS_SENT;
/* Send the MSG_NEWKEYS */
rc = ssh_packet_send_newkeys(session);
if (rc == SSH_ERROR) {
goto error;
}
ssh_string_free(server_pubkey);
ssh_string_free(client_pubkey);
return SSH_OK;
error:
SSH_STRING_FREE(server_pubkey_blob);
ssh_string_free(server_pubkey);
ssh_string_free(client_pubkey);
session->session_state = SSH_SESSION_STATE_ERROR;
return SSH_ERROR;
}
/** @internal
* @brief parse an incoming SSH_MSG_KEXGSS_INIT packet and complete
* Diffie-Hellman key exchange
**/
static SSH_PACKET_CALLBACK(ssh_packet_server_gss_kex_init)
{
(void)type;
(void)user;
SSH_LOG(SSH_LOG_DEBUG, "Received SSH_MSG_KEXGSS_INIT");
ssh_packet_remove_callbacks(session, &ssh_gss_kex_server_callbacks);
ssh_server_gss_kex_process_init(session, packet);
return SSH_PACKET_USED;
}
#endif /* WITH_SERVER */

136
src/kex.c
View File

@@ -41,14 +41,14 @@
#include "libssh/string.h"
#include "libssh/curve25519.h"
#include "libssh/sntrup761.h"
#ifdef HAVE_MLKEM
#include "libssh/hybrid_mlkem.h"
#endif
#include "libssh/kex-gss.h"
#include "libssh/knownhosts.h"
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/bignum.h"
#include "libssh/token.h"
#include "libssh/gssapi.h"
#ifdef HAVE_BLOWFISH
# define BLOWFISH ",blowfish-cbc"
@@ -105,13 +105,14 @@
#define SNTRUP761X25519 ""
#endif /* HAVE_SNTRUP761 */
#ifdef HAVE_MLKEM
#ifdef HAVE_MLKEM1024
#define HYBRID_MLKEM "mlkem768x25519-sha256," \
"mlkem768nistp256-sha256," \
"mlkem1024nistp384-sha384,"
#else
#define HYBRID_MLKEM ""
#endif /* HAVE_MLKEM */
#define HYBRID_MLKEM "mlkem768x25519-sha256," \
"mlkem768nistp256-sha256,"
#endif /* HAVE_MLKEM1024 */
#ifdef HAVE_ECC
#define ECDH "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,"
@@ -794,6 +795,8 @@ int ssh_set_client_kex(ssh_session session)
const char *wanted = NULL;
int ok;
int i;
bool gssapi_null_alg = false;
char *hostkeys = NULL;
/* Skip if already set, for example for the rekey or when we do the guessing
* it could have been already used to make some protocol decisions. */
@@ -806,6 +809,42 @@ int ssh_set_client_kex(ssh_session session)
ssh_set_error(session, SSH_FATAL, "PRNG error");
return SSH_ERROR;
}
#ifdef WITH_GSSAPI
if (session->opts.gssapi_key_exchange) {
char *gssapi_algs = NULL;
ok = ssh_gssapi_init(session);
if (ok != SSH_OK) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
ok = ssh_gssapi_import_name(session->gssapi, session->opts.host);
if (ok != SSH_OK) {
return SSH_ERROR;
}
gssapi_algs = ssh_gssapi_kex_mechs(session);
if (gssapi_algs == NULL) {
return SSH_ERROR;
}
/* Prefix the default algorithms with gsskex algs */
if (ssh_fips_mode()) {
session->opts.wanted_methods[SSH_KEX] =
ssh_prefix_without_duplicates(fips_methods[SSH_KEX],
gssapi_algs);
} else {
session->opts.wanted_methods[SSH_KEX] =
ssh_prefix_without_duplicates(default_methods[SSH_KEX],
gssapi_algs);
}
gssapi_null_alg = true;
SAFE_FREE(gssapi_algs);
}
#endif
/* Set the list of allowed algorithms in order of preference, if it hadn't
* been set yet. */
@@ -819,6 +858,16 @@ int ssh_set_client_kex(ssh_session session)
ssh_set_error_oom(session);
return SSH_ERROR;
}
if (gssapi_null_alg) {
hostkeys =
ssh_append_without_duplicates(client->methods[i], "null");
if (hostkeys == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
SAFE_FREE(client->methods[i]);
client->methods[i] = hostkeys;
}
continue;
}
@@ -910,6 +959,14 @@ kex_select_kex_type(const char *kex)
{
if (strcmp(kex, "diffie-hellman-group1-sha1") == 0) {
return SSH_KEX_DH_GROUP1_SHA1;
} else if (strncmp(kex, "gss-group14-sha256-", 19) == 0) {
return SSH_GSS_KEX_DH_GROUP14_SHA256;
} else if (strncmp(kex, "gss-group16-sha512-", 19) == 0) {
return SSH_GSS_KEX_DH_GROUP16_SHA512;
} else if (strncmp(kex, "gss-nistp256-sha256-", 20) == 0) {
return SSH_GSS_KEX_ECDH_NISTP256_SHA256;
} else if (strncmp(kex, "gss-curve25519-sha256-", 22) == 0) {
return SSH_GSS_KEX_CURVE25519_SHA256;
} else if (strcmp(kex, "diffie-hellman-group14-sha1") == 0) {
return SSH_KEX_DH_GROUP14_SHA1;
} else if (strcmp(kex, "diffie-hellman-group14-sha256") == 0) {
@@ -938,11 +995,11 @@ kex_select_kex_type(const char *kex)
return SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM;
} else if (strcmp(kex, "sntrup761x25519-sha512") == 0) {
return SSH_KEX_SNTRUP761X25519_SHA512;
#ifdef HAVE_MLKEM
} else if (strcmp(kex, "mlkem768x25519-sha256") == 0) {
return SSH_KEX_MLKEM768X25519_SHA256;
} else if (strcmp(kex, "mlkem768nistp256-sha256") == 0) {
return SSH_KEX_MLKEM768NISTP256_SHA256;
#ifdef HAVE_MLKEM1024
} else if (strcmp(kex, "mlkem1024nistp384-sha384") == 0) {
return SSH_KEX_MLKEM1024NISTP384_SHA384;
#endif
@@ -967,6 +1024,14 @@ static void revert_kex_callbacks(ssh_session session)
case SSH_KEX_DH_GROUP18_SHA512:
ssh_client_dh_remove_callbacks(session);
break;
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
case SSH_GSS_KEX_CURVE25519_SHA256:
#ifdef WITH_GSSAPI
ssh_client_gss_kex_remove_callbacks(session);
#endif /* WITH_GSSAPI */
break;
#ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1:
case SSH_KEX_DH_GEX_SHA256:
@@ -992,13 +1057,13 @@ static void revert_kex_callbacks(ssh_session session)
ssh_client_sntrup761x25519_remove_callbacks(session);
break;
#endif
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
ssh_client_hybrid_mlkem_remove_callbacks(session);
break;
#endif
}
}
@@ -1436,6 +1501,18 @@ int ssh_make_sessionid(ssh_session session)
goto error;
}
if (server_pubkey_blob == NULL) {
if ((session->server && ssh_kex_is_gss(session->next_crypto)) ||
session->opts.gssapi_key_exchange) {
server_pubkey_blob = ssh_string_new(0);
if (server_pubkey_blob == NULL) {
ssh_set_error_oom(session);
rc = SSH_ERROR;
goto error;
}
}
}
rc = ssh_buffer_pack(buf,
"dPdPS",
ssh_buffer_get_len(client_hash),
@@ -1457,7 +1534,9 @@ int ssh_make_sessionid(ssh_session session)
case SSH_KEX_DH_GROUP1_SHA1:
case SSH_KEX_DH_GROUP14_SHA1:
case SSH_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
case SSH_KEX_DH_GROUP18_SHA512:
rc = ssh_dh_keypair_get_keys(session->next_crypto->dh_ctx,
DH_CLIENT_KEYPAIR, NULL, &client_pubkey);
@@ -1523,6 +1602,7 @@ int ssh_make_sessionid(ssh_session session)
case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_KEX_ECDH_SHA2_NISTP384:
case SSH_KEX_ECDH_SHA2_NISTP521:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
if (session->next_crypto->ecdh_client_pubkey == NULL ||
session->next_crypto->ecdh_server_pubkey == NULL) {
SSH_LOG(SSH_LOG_TRACE, "ECDH parameter missing");
@@ -1541,6 +1621,7 @@ int ssh_make_sessionid(ssh_session session)
#ifdef HAVE_CURVE25519
case SSH_KEX_CURVE25519_SHA256:
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
case SSH_GSS_KEX_CURVE25519_SHA256:
rc = ssh_buffer_pack(buf,
"dPdP",
CURVE25519_PUBKEY_SIZE,
@@ -1582,10 +1663,11 @@ int ssh_make_sessionid(ssh_session session)
}
break;
#endif /* HAVE_SNTRUP761 */
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_buffer_pack(buf,
"SS",
session->next_crypto->hybrid_client_init,
@@ -1597,7 +1679,6 @@ int ssh_make_sessionid(ssh_session session)
goto error;
}
break;
#endif /* HAVE_MLKEM */
default:
/* Handle unsupported kex types - this should not happen in normal operation */
rc = SSH_ERROR;
@@ -1612,13 +1693,13 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->shared_secret,
SHA512_DIGEST_LEN);
break;
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
rc = ssh_buffer_pack(buf, "S", session->next_crypto->hybrid_shared_secret);
break;
#endif /* HAVE_MLKEM */
default:
rc = ssh_buffer_pack(buf, "B", session->next_crypto->shared_secret);
break;
@@ -1651,13 +1732,14 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->secret_hash);
break;
case SSH_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_KEX_ECDH_SHA2_NISTP256:
case SSH_KEX_CURVE25519_SHA256:
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#endif
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
case SSH_GSS_KEX_CURVE25519_SHA256:
#ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA256:
#endif /* WITH_GEX */
@@ -1672,7 +1754,7 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->secret_hash);
break;
case SSH_KEX_ECDH_SHA2_NISTP384:
#ifdef HAVE_MLKEM
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
session->next_crypto->digest_len = SHA384_DIGEST_LENGTH;
@@ -1686,6 +1768,7 @@ int ssh_make_sessionid(ssh_session session)
session->next_crypto->secret_hash);
break;
case SSH_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
case SSH_KEX_DH_GROUP18_SHA512:
case SSH_KEX_ECDH_SHA2_NISTP521:
case SSH_KEX_SNTRUP761X25519_SHA512:
@@ -1839,13 +1922,13 @@ int ssh_generate_session_keys(ssh_session session)
k_string = ssh_make_padded_bignum_string(crypto->shared_secret,
crypto->digest_len);
break;
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
k_string = ssh_string_copy(crypto->hybrid_shared_secret);
break;
#endif /* HAVE_MLKEM */
default:
k_string = ssh_make_bignum_string(crypto->shared_secret);
break;
@@ -1961,3 +2044,22 @@ error:
return rc;
}
/** @internal
* @brief Check if a given crypto context has a GSSAPI KEX set
*
* @param[in] crypto The SSH crypto context
* @return true if the KEX of the context is a GSSAPI KEX, false otherwise
*/
bool ssh_kex_is_gss(struct ssh_crypto_struct *crypto)
{
switch (crypto->kex_type) {
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
case SSH_GSS_KEX_CURVE25519_SHA256:
return true;
default:
return false;
}
}

View File

@@ -322,3 +322,52 @@ md5_final(unsigned char *md, MD5CTX c)
}
return SSH_OK;
}
/**
* @ brief One-shot MD5. Not intended for use in security-relevant contexts.
*/
int
md5(const unsigned char *digest, size_t len, unsigned char *hash)
{
int rc, ret = SSH_ERROR;
unsigned int mdlen = 0;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_MD *md5 = NULL;
#endif
MD5CTX c = EVP_MD_CTX_new();
if (c == NULL) {
goto out;
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
md5 = EVP_MD_fetch(NULL, "MD5", "provider=default,-fips");
if (md5 == NULL) {
goto out;
}
rc = EVP_DigestInit(c, md5);
#else
rc = EVP_DigestInit_ex(c, EVP_md5(), NULL);
#endif
if (rc == 0) {
goto out;
}
rc = EVP_DigestUpdate(c, digest, len);
if (rc != 1) {
goto out;
}
rc = EVP_DigestFinal(c, hash, &mdlen);
if (rc != 1) {
goto out;
}
ret = SSH_OK;
out:
EVP_MD_CTX_free(c);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_MD_free(md5);
#endif
return ret;
}

View File

@@ -244,3 +244,9 @@ md5_final(unsigned char *md, MD5CTX c)
gcry_md_close(c);
return SSH_OK;
}
int md5(const unsigned char *digest, size_t len, unsigned char *hash)
{
gcry_md_hash_buffer(GCRY_MD_MD5, hash, digest, len);
return SSH_OK;
}

View File

@@ -405,3 +405,51 @@ md5_final(unsigned char *md, MD5CTX c)
}
return SSH_OK;
}
int md5(const unsigned char *digest, size_t len, unsigned char *hash)
{
MD5CTX ctx = NULL;
int rc;
const mbedtls_md_info_t *md_info =
mbedtls_md_info_from_type(MBEDTLS_MD_MD5);
if (md_info == NULL) {
return SSH_ERROR;
}
ctx = malloc(sizeof(mbedtls_md_context_t));
if (ctx == NULL) {
return SSH_ERROR;
}
mbedtls_md_init(ctx);
rc = mbedtls_md_setup(ctx, md_info, 0);
if (rc != 0) {
mbedtls_md_free(ctx);
SAFE_FREE(ctx);
return SSH_ERROR;
}
rc = mbedtls_md_starts(ctx);
if (rc != 0) {
mbedtls_md_free(ctx);
SAFE_FREE(ctx);
return SSH_ERROR;
}
rc = mbedtls_md_update(ctx, digest, len);
if (rc != 0) {
mbedtls_md_free(ctx);
SAFE_FREE(ctx);
return SSH_ERROR;
}
rc = mbedtls_md_finish(ctx, hash);
mbedtls_md_free(ctx);
SAFE_FREE(ctx);
if (rc != 0) {
return SSH_ERROR;
}
return SSH_OK;
}

View File

@@ -184,6 +184,19 @@ static int ssh_execute_server_request(ssh_session session, ssh_message msg)
ssh_message_reply_default(msg);
}
return SSH_OK;
} else if (msg->auth_request.method == SSH_AUTH_METHOD_INTERACTIVE &&
ssh_callbacks_exists(session->server_callbacks, auth_kbdint_function)) {
rc = session->server_callbacks->auth_kbdint_function(msg,
session,
session->server_callbacks->userdata);
if (rc == SSH_AUTH_SUCCESS || rc == SSH_AUTH_PARTIAL) {
ssh_message_auth_reply_success(msg, rc == SSH_AUTH_PARTIAL);
} else if (rc == SSH_AUTH_INFO) {
return SSH_OK;
} else {
ssh_message_reply_default(msg);
}
return SSH_OK;
}
break;
@@ -1145,6 +1158,75 @@ SSH_PACKET_CALLBACK(ssh_packet_userauth_request)
SAFE_FREE(method);
SSH_MESSAGE_FREE(msg);
return SSH_PACKET_USED;
}
if (strcmp(method, "gssapi-keyex") == 0) {
gss_buffer_desc received_mic = GSS_C_EMPTY_BUFFER;
gss_buffer_desc mic_buf = GSS_C_EMPTY_BUFFER;
ssh_string mic_token_string = NULL;
OM_uint32 maj_stat, min_stat;
ssh_buffer buf = NULL;
if (!ssh_kex_is_gss(session->current_crypto)) {
ssh_set_error(session,
SSH_FATAL,
"Attempt to authenticate with gssapi-keyex without "
"doing GSSAPI Key Exchange.");
ssh_auth_reply_default(session, 0);
goto error;
}
if (session->gssapi == NULL || session->gssapi->ctx == NULL) {
ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
ssh_auth_reply_default(session, 0);
goto error;
}
rc = ssh_buffer_unpack(packet, "S", &mic_token_string);
if (rc != SSH_OK) {
ssh_auth_reply_default(session, 0);
goto error;
}
received_mic.length = ssh_string_len(mic_token_string);
received_mic.value = ssh_string_data(mic_token_string);
SAFE_FREE(session->gssapi->user);
session->gssapi->user = strdup(msg->auth_request.username);
buf = ssh_gssapi_build_mic(session, "gssapi-keyex");
if (buf == NULL) {
ssh_set_error_oom(session);
SSH_STRING_FREE(mic_token_string);
ssh_auth_reply_default(session, 0);
goto error;
}
mic_buf.length = ssh_buffer_get_len(buf);
mic_buf.value = ssh_buffer_get(buf);
maj_stat = gss_verify_mic(&min_stat,
session->gssapi->ctx,
&mic_buf,
&received_mic,
NULL);
if (maj_stat != GSS_S_COMPLETE) {
ssh_set_error(session,
SSH_FATAL,
"Failed to verify MIC for gssapi-keyex auth");
SSH_BUFFER_FREE(buf);
SSH_STRING_FREE(mic_token_string);
ssh_auth_reply_default(session, 0);
goto error;
}
ssh_auth_reply_success(session, 0);
/* bypass the message queue thing */
SAFE_FREE(service);
SAFE_FREE(method);
SSH_BUFFER_FREE(buf);
SSH_MESSAGE_FREE(msg);
SSH_STRING_FREE(mic_token_string);
return SSH_PACKET_USED;
}
#endif

View File

@@ -30,8 +30,10 @@ const struct mlkem_type_info *kex_type_to_mlkem_info(enum ssh_key_exchange_e kex
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
return &MLKEM768_INFO;
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
return &MLKEM1024_INFO;
#endif
default:
return NULL;
}

207
src/mlkem_gcrypt.c Normal file
View File

@@ -0,0 +1,207 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2025 by Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1 of the License.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/crypto.h"
#include "libssh/mlkem.h"
#include "libssh/session.h"
#include <gcrypt.h>
const struct mlkem_type_info MLKEM768_INFO = {
.pubkey_size = GCRY_KEM_MLKEM768_PUBKEY_LEN,
.privkey_size = GCRY_KEM_MLKEM768_SECKEY_LEN,
.ciphertext_size = GCRY_KEM_MLKEM768_CIPHER_LEN,
.alg = GCRY_KEM_MLKEM768,
};
const struct mlkem_type_info MLKEM1024_INFO = {
.pubkey_size = GCRY_KEM_MLKEM1024_PUBKEY_LEN,
.privkey_size = GCRY_KEM_MLKEM1024_SECKEY_LEN,
.ciphertext_size = GCRY_KEM_MLKEM1024_CIPHER_LEN,
.alg = GCRY_KEM_MLKEM1024,
};
int ssh_mlkem_init(ssh_session session)
{
int ret = SSH_ERROR;
struct ssh_crypto_struct *crypto = session->next_crypto;
const struct mlkem_type_info *mlkem_info = NULL;
ssh_string pubkey = NULL;
unsigned char *privkey = NULL, *pubkey_data = NULL;
gcry_error_t err;
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
goto cleanup;
}
privkey = malloc(mlkem_info->privkey_size);
if (privkey == NULL) {
ssh_set_error_oom(session);
goto cleanup;
}
pubkey = ssh_string_new(mlkem_info->pubkey_size);
if (pubkey == NULL) {
ssh_set_error_oom(session);
goto cleanup;
}
pubkey_data = ssh_string_data(pubkey);
err = gcry_kem_keypair(mlkem_info->alg,
pubkey_data,
mlkem_info->pubkey_size,
privkey,
mlkem_info->privkey_size);
if (err) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to generate ML-KEM key: %s",
gpg_strerror(err));
goto cleanup;
}
ssh_string_free(crypto->mlkem_client_pubkey);
crypto->mlkem_client_pubkey = pubkey;
pubkey = NULL;
free(crypto->mlkem_privkey);
crypto->mlkem_privkey = privkey;
crypto->mlkem_privkey_len = mlkem_info->privkey_size;
privkey = NULL;
ret = SSH_OK;
cleanup:
ssh_string_free(pubkey);
if (privkey != NULL) {
ssh_burn(privkey, mlkem_info->privkey_size);
free(privkey);
}
return ret;
}
int ssh_mlkem_encapsulate(ssh_session session,
ssh_mlkem_shared_secret shared_secret)
{
int ret = SSH_ERROR;
const struct mlkem_type_info *mlkem_info = NULL;
struct ssh_crypto_struct *crypto = session->next_crypto;
const unsigned char *pubkey_data = NULL;
unsigned char *ciphertext_data = NULL;
ssh_string ciphertext = NULL;
ssh_string pubkey = crypto->mlkem_client_pubkey;
gcry_error_t err;
if (pubkey == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Missing pubkey in session");
return SSH_ERROR;
}
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
return SSH_ERROR;
}
ciphertext = ssh_string_new(mlkem_info->ciphertext_size);
if (ciphertext == NULL) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
pubkey_data = ssh_string_data(pubkey);
ciphertext_data = ssh_string_data(ciphertext);
err = gcry_kem_encap(mlkem_info->alg,
pubkey_data,
mlkem_info->pubkey_size,
ciphertext_data,
mlkem_info->ciphertext_size,
shared_secret,
MLKEM_SHARED_SECRET_SIZE,
NULL,
0);
if (err) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to encapsulate ML-KEM shared secret: %s",
gpg_strerror(err));
goto cleanup;
}
ssh_string_free(crypto->mlkem_ciphertext);
crypto->mlkem_ciphertext = ciphertext;
ciphertext = NULL;
ret = SSH_OK;
cleanup:
ssh_string_free(ciphertext);
return ret;
}
int ssh_mlkem_decapsulate(const ssh_session session,
ssh_mlkem_shared_secret shared_secret)
{
const struct mlkem_type_info *mlkem_info = NULL;
struct ssh_crypto_struct *crypto = session->next_crypto;
ssh_string ciphertext = NULL;
unsigned char *ciphertext_data = NULL;
gcry_error_t err;
ciphertext = crypto->mlkem_ciphertext;
if (ciphertext == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Missing ciphertext in session");
return SSH_ERROR;
}
if (crypto->mlkem_privkey == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Missing ML-KEM private key in session");
return SSH_ERROR;
}
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
return SSH_ERROR;
}
ciphertext_data = ssh_string_data(ciphertext);
err = gcry_kem_decap(mlkem_info->alg,
crypto->mlkem_privkey,
mlkem_info->privkey_size,
ciphertext_data,
mlkem_info->ciphertext_size,
shared_secret,
MLKEM_SHARED_SECRET_SIZE,
NULL,
0);
if (err) {
SSH_LOG(SSH_LOG_TRACE,
"Failed to decapsulate ML-KEM shared secret: %s",
gpg_strerror(err));
return SSH_ERROR;
}
return SSH_OK;
}

202
src/mlkem_native.c Normal file
View File

@@ -0,0 +1,202 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2025 by Red Hat, Inc.
*
* Author: Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 2.1 of the License.
*
* The SSH Library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the SSH Library; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
* MA 02111-1307, USA.
*/
#include "config.h"
#include "libssh/crypto.h"
#include "libssh/mlkem.h"
#include "libssh/mlkem_native.h"
#include "libssh/session.h"
#define crypto_kem_mlkem768_PUBLICKEYBYTES 1184
#define crypto_kem_mlkem768_SECRETKEYBYTES 2400
#define crypto_kem_mlkem768_CIPHERTEXTBYTES 1088
const struct mlkem_type_info MLKEM768_INFO = {
.pubkey_size = crypto_kem_mlkem768_PUBLICKEYBYTES,
.privkey_size = crypto_kem_mlkem768_SECRETKEYBYTES,
.ciphertext_size = crypto_kem_mlkem768_CIPHERTEXTBYTES,
};
int ssh_mlkem_init(ssh_session session)
{
int ret = SSH_ERROR;
struct ssh_crypto_struct *crypto = session->next_crypto;
const struct mlkem_type_info *mlkem_info = NULL;
unsigned char rnd[LIBCRUX_ML_KEM_KEY_PAIR_PRNG_LEN];
struct libcrux_mlkem768_keypair keypair;
int err;
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
goto cleanup;
}
err = ssh_get_random(rnd, sizeof(rnd), 0);
if (err != 1) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to generate random data for ML-KEM keygen");
goto cleanup;
}
keypair = libcrux_ml_kem_mlkem768_portable_generate_key_pair(rnd);
if (ssh_string_len(crypto->mlkem_client_pubkey) < mlkem_info->pubkey_size) {
SSH_STRING_FREE(crypto->mlkem_client_pubkey);
}
if (crypto->mlkem_client_pubkey == NULL) {
crypto->mlkem_client_pubkey = ssh_string_new(mlkem_info->pubkey_size);
if (crypto->mlkem_client_pubkey == NULL) {
ssh_set_error_oom(session);
goto cleanup;
}
}
err = ssh_string_fill(crypto->mlkem_client_pubkey,
keypair.pk.value,
mlkem_info->pubkey_size);
if (err) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to fill the string with client pubkey");
goto cleanup;
}
if (crypto->mlkem_privkey == NULL) {
crypto->mlkem_privkey = malloc(mlkem_info->privkey_size);
if (crypto->mlkem_privkey == NULL) {
ssh_set_error_oom(session);
goto cleanup;
}
}
memcpy(crypto->mlkem_privkey, keypair.sk.value, mlkem_info->privkey_size);
crypto->mlkem_privkey_len = mlkem_info->privkey_size;
ret = SSH_OK;
cleanup:
ssh_burn(&keypair, sizeof(keypair));
ssh_burn(rnd, sizeof(rnd));
return ret;
}
int ssh_mlkem_encapsulate(ssh_session session,
ssh_mlkem_shared_secret shared_secret)
{
int ret = SSH_ERROR;
const struct mlkem_type_info *mlkem_info = NULL;
struct ssh_crypto_struct *crypto = session->next_crypto;
const unsigned char *pubkey_data = NULL;
ssh_string pubkey = crypto->mlkem_client_pubkey;
struct libcrux_mlkem768_enc_result enc;
struct libcrux_mlkem768_pk mlkem_pub = {0};
unsigned char rnd[LIBCRUX_ML_KEM_ENC_PRNG_LEN];
int err;
if (pubkey == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Missing pubkey in session");
return SSH_ERROR;
}
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
return SSH_ERROR;
}
pubkey_data = ssh_string_data(pubkey);
memcpy(mlkem_pub.value, pubkey_data, mlkem_info->pubkey_size);
err = libcrux_ml_kem_mlkem768_portable_validate_public_key(&mlkem_pub);
if (err == 0) {
SSH_LOG(SSH_LOG_WARNING, "Invalid public key");
return SSH_ERROR;
}
err = ssh_get_random(rnd, sizeof(rnd), 0);
if (err != 1) {
SSH_LOG(SSH_LOG_WARNING,
"Failed to generate random data for ML-KEM keygen");
goto cleanup;
}
enc = libcrux_ml_kem_mlkem768_portable_encapsulate(&mlkem_pub, rnd);
if (ssh_string_len(crypto->mlkem_ciphertext) < mlkem_info->ciphertext_size) {
SSH_STRING_FREE(crypto->mlkem_ciphertext);
}
if (crypto->mlkem_ciphertext == NULL) {
crypto->mlkem_ciphertext = ssh_string_new(mlkem_info->ciphertext_size);
if (crypto->mlkem_ciphertext == NULL) {
ssh_set_error_oom(session);
goto cleanup;
}
}
err = ssh_string_fill(crypto->mlkem_ciphertext,
enc.fst.value,
sizeof(enc.fst.value));
if (err != SSH_OK) {
SSH_LOG(SSH_LOG_WARNING, "Failed to fill the string with ciphertext");
goto cleanup;
}
memcpy(shared_secret, enc.snd, sizeof(enc.snd));
ret = SSH_OK;
cleanup:
ssh_burn(rnd, sizeof(rnd));
ssh_burn(&enc, sizeof(enc));
return ret;
}
int ssh_mlkem_decapsulate(const ssh_session session,
ssh_mlkem_shared_secret shared_secret)
{
const struct mlkem_type_info *mlkem_info = NULL;
struct ssh_crypto_struct *crypto = session->next_crypto;
ssh_string ciphertext = NULL;
unsigned char *ciphertext_data = NULL;
struct libcrux_mlkem768_sk mlkem_priv = {0};
struct libcrux_mlkem768_ciphertext mlkem_ciphertext = {0};
mlkem_info = kex_type_to_mlkem_info(crypto->kex_type);
if (mlkem_info == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Unknown ML-KEM type");
return SSH_ERROR;
}
ciphertext = crypto->mlkem_ciphertext;
if (ciphertext == NULL) {
SSH_LOG(SSH_LOG_WARNING, "Missing ciphertext in session");
return SSH_ERROR;
}
ciphertext_data = ssh_string_data(ciphertext);
memcpy(mlkem_ciphertext.value,
ciphertext_data,
sizeof(mlkem_ciphertext.value));
memcpy(mlkem_priv.value, crypto->mlkem_privkey, crypto->mlkem_privkey_len);
libcrux_ml_kem_mlkem768_portable_decapsulate(&mlkem_priv,
&mlkem_ciphertext,
shared_secret);
return SSH_OK;
}

View File

@@ -41,6 +41,11 @@
#include "libssh/priv.h"
#include "libssh/session.h"
#include <sys/types.h>
#include "libssh/misc.h"
#include "libssh/options.h"
#include "libssh/config_parser.h"
#include "libssh/gssapi.h"
#include "libssh/token.h"
#ifdef WITH_SERVER
#include "libssh/server.h"
#include "libssh/bind.h"
@@ -256,6 +261,7 @@ int ssh_options_copy(ssh_session src, ssh_session *dest)
new->opts.nodelay = src->opts.nodelay;
new->opts.config_processed = src->opts.config_processed;
new->opts.control_master = src->opts.control_master;
new->opts.address_family = src->opts.address_family;
new->common.log_verbosity = src->common.log_verbosity;
new->common.callbacks = src->common.callbacks;
@@ -559,6 +565,16 @@ int ssh_options_set_algo(ssh_session session,
* Set it to specify that GSSAPI should delegate credentials
* to the server (int, 0 = false).
*
* - SSH_OPTIONS_GSSAPI_KEY_EXCHANGE
* Set to true to allow GSSAPI key exchange (bool).
*
* - SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS
* Set the GSSAPI key exchange method to be used (const char *,
* comma-separated list). ex:
* "gss-curve25519-sha256-,gss-nistp256-sha256-"
* These will prefix the default algorithms if
* SSH_OPTIONS_GSSAPI_KEY_EXCHANGE is true.
*
* - SSH_OPTIONS_PASSWORD_AUTH
* Set it if password authentication should be used
* in ssh_userauth_auto_pubkey(). (int, 0=false).
@@ -611,12 +627,12 @@ int ssh_options_set_algo(ssh_session session,
* Setting 0 will revert the value to defaults.
* Default is 3072 bits or 2048 bits in FIPS mode.
* (int)
*
* - SSH_OPTIONS_IDENTITY_AGENT
* Set the path to the SSH agent socket. If unset, the
* SSH_AUTH_SOCK environment is consulted.
* (const char *)
*
* - SSH_OPTIONS_IDENTITIES_ONLY
* Use only keys specified in the SSH config, even if agent
* offers more.
@@ -650,6 +666,13 @@ int ssh_options_set_algo(ssh_session session,
* context and can free it after this call.
* (ssh_pki_ctx)
*
* - SSH_OPTIONS_ADDRESS_FAMILY
* Specify which address family to use when connecting.
*
* Possible options:
* - SSH_ADDRESS_FAMILY_ANY: use any address family
* - SSH_ADDRESS_FAMILY_INET: IPv4 only
* - SSH_ADDRESS_FAMILY_INET6: IPv6 only
*
* @param value The value to set. This is a generic pointer and the
* datatype which is used should be set according to the
@@ -1232,6 +1255,37 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
session->opts.gss_delegate_creds = (x & 0xff);
}
break;
#ifdef WITH_GSSAPI
case SSH_OPTIONS_GSSAPI_KEY_EXCHANGE:
if (value == NULL) {
ssh_set_error_invalid(session);
return -1;
} else {
bool *x = (bool *)value;
session->opts.gssapi_key_exchange = *x;
}
break;
case SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS:
v = value;
if (v == NULL || v[0] == '\0') {
ssh_set_error_invalid(session);
return -1;
} else {
/* Check if algorithms are supported */
char *ret =
ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, v);
if (ret == NULL) {
ssh_set_error(session,
SSH_FATAL,
"GSSAPI key exchange algorithms not "
"supported or invalid");
return -1;
}
SAFE_FREE(session->opts.gssapi_key_exchange_algs);
session->opts.gssapi_key_exchange_algs = ret;
}
break;
#endif
case SSH_OPTIONS_PASSWORD_AUTH:
case SSH_OPTIONS_PUBKEY_AUTH:
case SSH_OPTIONS_KBDINT_AUTH:
@@ -1393,6 +1447,20 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
return -1;
}
break;
case SSH_OPTIONS_ADDRESS_FAMILY:
if (value == NULL) {
ssh_set_error_invalid(session);
return -1;
} else {
int *x = (int *)value;
if (*x < SSH_ADDRESS_FAMILY_ANY ||
*x > SSH_ADDRESS_FAMILY_INET6) {
ssh_set_error_invalid(session);
return -1;
}
session->opts.address_family = *x;
}
break;
default:
ssh_set_error(session, SSH_REQUEST_DENIED, "Unknown ssh option %d", type);
return -1;
@@ -2073,6 +2141,16 @@ int ssh_options_apply(ssh_session session)
}
}
#ifdef WITH_GSSAPI
if (session->opts.gssapi_key_exchange) {
rc = ssh_gssapi_check_client_config(session);
if (rc != SSH_OK) {
SSH_LOG(SSH_LOG_WARN, "Disabled GSSAPI key exchange");
session->opts.gssapi_key_exchange = false;
}
}
#endif
return 0;
}
@@ -2255,6 +2333,14 @@ static int ssh_bind_set_algo(ssh_bind sshbind,
* Default is 3072 bits or 2048 bits in FIPS mode.
* (int)
*
* - SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE
* Set true to enable GSSAPI key exchange,
* false to disable GSSAPI key exchange. (bool)
*
* - SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS
* Set the GSSAPI key exchange method to be used
* (const char *, comma-separated list).
* ex: "gss-group14-sha256-,gss-group16-sha512-"
*
* @param value The value to set. This is a generic pointer and the
* datatype which should be used is described at the
@@ -2652,6 +2738,35 @@ ssh_bind_options_set(ssh_bind sshbind,
sshbind->rsa_min_size = *x;
}
break;
#ifdef WITH_GSSAPI
case SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
} else {
bool *x = (bool *)value;
sshbind->gssapi_key_exchange = *x;
}
break;
case SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS:
if (value == NULL) {
ssh_set_error_invalid(sshbind);
return -1;
} else {
char *ret = NULL;
SAFE_FREE(sshbind->gssapi_key_exchange_algs);
ret = ssh_find_all_matching(GSSAPI_KEY_EXCHANGE_SUPPORTED, value);
if (ret == NULL) {
ssh_set_error(
sshbind,
SSH_REQUEST_DENIED,
"GSSAPI key exchange algorithms not supported or invalid");
return -1;
}
sshbind->gssapi_key_exchange_algs = ret;
}
break;
#endif /* WITH_GSSAPI */
default:
ssh_set_error(sshbind,
SSH_REQUEST_DENIED,

View File

@@ -422,35 +422,67 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
rc = SSH_PACKET_ALLOWED;
break;
case SSH2_MSG_KEX_DH_GEX_INIT: // 32
/* Server only */
// SSH2_MSG_KEXGSS_COMPLETE: // 32
if (ssh_kex_is_gss(session->next_crypto)) {
/* SSH2_MSG_KEXGSS_COMPLETE */
/* Client only */
/*
* States required:
* - session_state == SSH_SESSION_STATE_DH
* - dh_handshake_state == DH_STATE_GROUP_SENT
*
* Transitions:
* - session->dh_handshake_state = DH_STATE_GROUP_SENT
* then calls ssh_packet_server_dhgex_init which triggers:
* - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
* */
/*
* States required:
* - session_state == SSH_SESSION_STATE_DH
* - dh_handshake_state == DH_STATE_INIT_SENT
*
* Transitions:
* - session->dh_handshake_state = DH_STATE_INIT_SENT
* then calls ssh_packet_client_gss_kex_reply which triggers:
* - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
* */
if (session->client) {
rc = SSH_PACKET_DENIED;
break;
if (!session->client) {
rc = SSH_PACKET_DENIED;
break;
}
if (session->session_state != SSH_SESSION_STATE_DH) {
rc = SSH_PACKET_DENIED;
break;
}
if (session->dh_handshake_state != DH_STATE_INIT_SENT) {
rc = SSH_PACKET_DENIED;
break;
}
} else {
/* SSH2_MSG_KEX_DH_GEX_INIT */
/* Server only */
/*
* States required:
* - session_state == SSH_SESSION_STATE_DH
* - dh_handshake_state == DH_STATE_GROUP_SENT
*
* Transitions:
* - session->dh_handshake_state = DH_STATE_GROUP_SENT
* then calls ssh_packet_server_dhgex_init which triggers:
* - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT
* */
if (session->client) {
rc = SSH_PACKET_DENIED;
break;
}
if (session->session_state != SSH_SESSION_STATE_DH) {
rc = SSH_PACKET_DENIED;
break;
}
/* Only allowed if dh_handshake_state is in initial state */
if (session->dh_handshake_state != DH_STATE_GROUP_SENT) {
rc = SSH_PACKET_DENIED;
break;
}
}
if (session->session_state != SSH_SESSION_STATE_DH) {
rc = SSH_PACKET_DENIED;
break;
}
/* Only allowed if dh_handshake_state is in initial state */
if (session->dh_handshake_state != DH_STATE_GROUP_SENT) {
rc = SSH_PACKET_DENIED;
break;
}
rc = SSH_PACKET_ALLOWED;
break;
case SSH2_MSG_KEX_DH_GEX_REPLY: // 33
@@ -594,6 +626,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* or session->auth.state == SSH_AUTH_STATE_PUBKEY_AUTH_SENT
* or session->auth.state == SSH_AUTH_STATE_PASSWORD_AUTH_SENT
* or session->auth.state == SSH_AUTH_STATE_GSSAPI_MIC_SENT
* or session->auth.state == SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT
* or session->auth.state == SSH_AUTH_STATE_AUTH_NONE_SENT
*
* Transitions:
@@ -623,8 +656,8 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
(session->auth.state != SSH_AUTH_STATE_PUBKEY_AUTH_SENT) &&
(session->auth.state != SSH_AUTH_STATE_PASSWORD_AUTH_SENT) &&
(session->auth.state != SSH_AUTH_STATE_GSSAPI_MIC_SENT) &&
(session->auth.state != SSH_AUTH_STATE_AUTH_NONE_SENT))
{
(session->auth.state != SSH_AUTH_STATE_GSSAPI_KEYEX_MIC_SENT) &&
(session->auth.state != SSH_AUTH_STATE_AUTH_NONE_SENT)) {
rc = SSH_PACKET_DENIED;
break;
}
@@ -716,17 +749,83 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
rc = SSH_PACKET_ALLOWED;
break;
case SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE: // 63
/* TODO Not filtered */
/* Server only */
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
* - session->gssapi->state == SSH_GSSAPI_STATE_RCV_MIC
*
* Transitions:
* - None
*/
#ifdef WITH_GSSAPI
if (session->client) {
rc = SSH_PACKET_DENIED;
break;
}
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
rc = SSH_PACKET_DENIED;
break;
}
if (session->gssapi == NULL) {
rc = SSH_PACKET_DENIED;
break;
}
if (session->gssapi->state != SSH_GSSAPI_STATE_RCV_MIC) {
rc = SSH_PACKET_DENIED;
break;
}
rc = SSH_PACKET_ALLOWED;
break;
#else
rc = SSH_PACKET_DENIED;
break;
#endif /* WITH_GSSAPI */
case SSH2_MSG_USERAUTH_GSSAPI_ERROR: // 64
/* TODO Not filtered */
/* Client only */
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
*
* Transitions:
* - None
*/
#ifdef WITH_GSSAPI
if (session->server) {
rc = SSH_PACKET_DENIED;
break;
}
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
rc = SSH_PACKET_DENIED;
break;
}
rc = SSH_PACKET_ALLOWED;
break;
#else
rc = SSH_PACKET_DENIED;
break;
#endif /* WITH_GSSAPI */
case SSH2_MSG_USERAUTH_GSSAPI_ERRTOK: // 65
/* TODO Not filtered */
/*
* States required:
* - session_state == SSH_SESSION_STATE_AUTHENTICATING
*
* Transitions:
* - None
*/
#ifdef WITH_GSSAPI
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATING) {
rc = SSH_PACKET_DENIED;
break;
}
rc = SSH_PACKET_ALLOWED;
break;
#else
rc = SSH_PACKET_DENIED;
break;
#endif /* WITH_GSSAPI */
case SSH2_MSG_USERAUTH_GSSAPI_MIC: // 66
/* Server only */
@@ -746,7 +845,7 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
* - any other case:
* - None
* */
#ifdef WITH_GSSAPI
/* If this is a client, reject the message */
if (session->client) {
rc = SSH_PACKET_DENIED;
@@ -765,6 +864,10 @@ static enum ssh_packet_filter_result_e ssh_packet_incoming_filter(ssh_session se
rc = SSH_PACKET_ALLOWED;
break;
#else
rc = SSH_PACKET_DENIED;
break;
#endif /* WITH_GSSAPI */
case SSH2_MSG_GLOBAL_REQUEST: // 80
/*
* States required:

View File

@@ -27,6 +27,10 @@
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#ifdef WITH_GSSAPI
#include "libssh/gssapi.h"
#include <gssapi/gssapi.h>
#endif
#include "libssh/priv.h"
#include "libssh/buffer.h"
@@ -173,53 +177,93 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys)
/* server things are done in server.c */
session->dh_handshake_state=DH_STATE_FINISHED;
} else {
ssh_key server_key = NULL;
#ifdef WITH_GSSAPI
if (ssh_kex_is_gss(session->next_crypto)) {
OM_uint32 maj_stat, min_stat;
gss_buffer_desc mic = GSS_C_EMPTY_BUFFER, msg = GSS_C_EMPTY_BUFFER;
/* client */
/* Verify the host's signature. FIXME do it sooner */
sig_blob = session->next_crypto->dh_server_signature;
session->next_crypto->dh_server_signature = NULL;
/* get the server public key */
server_key = ssh_dh_get_next_server_publickey(session);
if (server_key == NULL) {
goto error;
}
rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
ssh_string_burn(sig_blob);
SSH_STRING_FREE(sig_blob);
if (rc != SSH_OK) {
goto error;
}
/* Check if signature from server matches user preferences */
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
rc = match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
sig->type_c);
if (rc == 0) {
ssh_set_error(session,
SSH_FATAL,
"Public key from server (%s) doesn't match user "
"preference (%s)",
sig->type_c,
session->opts.wanted_methods[SSH_HOSTKEYS]);
if (session->gssapi == NULL || session->gssapi->ctx == NULL) {
ssh_set_error(session, SSH_FATAL, "GSSAPI context not initialized");
goto error;
}
}
rc = ssh_pki_signature_verify(session,
sig,
server_key,
session->next_crypto->secret_hash,
session->next_crypto->digest_len);
SSH_SIGNATURE_FREE(sig);
if (rc == SSH_ERROR) {
ssh_set_error(session,
SSH_FATAL,
"Failed to verify server hostkey signature");
goto error;
if (session->gssapi_key_exchange_mic == NULL) {
ssh_set_error(session,
SSH_FATAL,
"GSSAPI mic not set");
goto error;
}
mic.length = ssh_string_len(session->gssapi_key_exchange_mic);
mic.value = ssh_string_data(session->gssapi_key_exchange_mic);
msg.length = session->next_crypto->digest_len;
msg.value = session->next_crypto->secret_hash;
maj_stat = gss_verify_mic(&min_stat,
session->gssapi->ctx,
&msg,
&mic,
NULL);
if (maj_stat != GSS_S_COMPLETE) {
ssh_set_error(session,
SSH_FATAL,
"Failed to verify mic after GSSAPI Key Exchange");
goto error;
}
SSH_STRING_FREE(session->gssapi_key_exchange_mic);
} else
#endif
{
ssh_key server_key = NULL;
/* client */
/* Verify the host's signature. FIXME do it sooner */
sig_blob = session->next_crypto->dh_server_signature;
session->next_crypto->dh_server_signature = NULL;
/* get the server public key */
server_key = ssh_dh_get_next_server_publickey(session);
if (server_key == NULL) {
goto error;
}
rc = ssh_pki_import_signature_blob(sig_blob, server_key, &sig);
ssh_string_burn(sig_blob);
SSH_STRING_FREE(sig_blob);
if (rc != SSH_OK) {
goto error;
}
/* Check if signature from server matches user preferences */
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
rc = match_group(session->opts.wanted_methods[SSH_HOSTKEYS],
sig->type_c);
if (rc == 0) {
ssh_set_error(
session,
SSH_FATAL,
"Public key from server (%s) doesn't match user "
"preference (%s)",
sig->type_c,
session->opts.wanted_methods[SSH_HOSTKEYS]);
goto error;
}
}
rc = ssh_pki_signature_verify(session,
sig,
server_key,
session->next_crypto->secret_hash,
session->next_crypto->digest_len);
SSH_SIGNATURE_FREE(sig);
if (rc == SSH_ERROR) {
ssh_set_error(session,
SSH_FATAL,
"Failed to verify server hostkey signature");
goto error;
}
}
SSH_LOG(SSH_LOG_DEBUG, "Signature verified and valid");
@@ -234,6 +278,9 @@ SSH_PACKET_CALLBACK(ssh_packet_newkeys)
return SSH_PACKET_USED;
error:
#ifdef WITH_GSSAPI
SSH_STRING_FREE(session->gssapi_key_exchange_mic);
#endif
SSH_SIGNATURE_FREE(sig);
ssh_string_burn(sig_blob);
SSH_STRING_FREE(sig_blob);

View File

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

View File

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

View File

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

View File

@@ -44,21 +44,22 @@
# include <netinet/in.h>
#endif
#include "libssh/priv.h"
#include "libssh/libssh.h"
#include "libssh/server.h"
#include "libssh/ssh2.h"
#include "libssh/buffer.h"
#include "libssh/packet.h"
#include "libssh/socket.h"
#include "libssh/session.h"
#include "libssh/kex.h"
#include "libssh/misc.h"
#include "libssh/pki.h"
#include "libssh/dh.h"
#include "libssh/messages.h"
#include "libssh/options.h"
#include "libssh/curve25519.h"
#include "libssh/dh.h"
#include "libssh/gssapi.h"
#include "libssh/kex.h"
#include "libssh/libssh.h"
#include "libssh/messages.h"
#include "libssh/misc.h"
#include "libssh/options.h"
#include "libssh/packet.h"
#include "libssh/pki.h"
#include "libssh/priv.h"
#include "libssh/server.h"
#include "libssh/session.h"
#include "libssh/socket.h"
#include "libssh/ssh2.h"
#include "libssh/token.h"
#define set_status(session, status) do {\
@@ -72,15 +73,22 @@
* @{
*/
/** @internal
/**
* @internal
* @brief Sets the server's key exchange, encryption, MAC, and compression
* algorithms.
*
* @brief initialize the set of key exchange, hostkey, ciphers, MACs, and
* compression algorithms for the given ssh_session
* Prepares the server key exchange (KEX) proposals by prioritizing the
* available host keys (Ed25519, ECDSA, RSA) based on their strength and fills
* in the KEX method lists based on session options or defaults. This is
* essential for negotiating secure communication parameters in the SSH
* handshake.
*
* The selection of algorithms and keys used are determined by the
* options that are currently set in the given ssh_session structure.
* @param[in] session The SSH session to set up.
*
* @return `SSH_OK` on success, `SSH_ERROR` on failure (e.g., no host keys
* available, random number generation error, or memory allocation failure).
*/
int server_set_kex(ssh_session session)
{
struct ssh_kex_struct *server = &session->next_crypto->server_kex;
@@ -91,6 +99,9 @@ int server_set_kex(ssh_session session)
enum ssh_keytypes_e keytype;
size_t len;
int ok;
#ifdef WITH_GSSAPI
char *gssapi_algs = NULL;
#endif /* WITH_GSSAPI */
/* Skip if already set, for example for the rekey or when we do the guessing
* it could have been already used to make some protocol decisions. */
@@ -130,10 +141,6 @@ int server_set_kex(ssh_session session)
",%s", ssh_key_type_to_char(keytype));
}
if (strlen(hostkeys) == 0) {
return -1;
}
if (session->opts.wanted_methods[SSH_HOSTKEYS]) {
allowed = session->opts.wanted_methods[SSH_HOSTKEYS];
} else {
@@ -144,23 +151,52 @@ int server_set_kex(ssh_session session)
}
}
/* It is expected for the list of allowed hostkeys to be ordered by
* preference */
kept = ssh_find_all_matching(hostkeys[0] == ',' ? hostkeys + 1 : hostkeys,
allowed);
if (kept == NULL) {
/* Nothing was allowed */
return -1;
}
if (strlen(hostkeys) != 0) {
/* It is expected for the list of allowed hostkeys to be ordered by
* preference */
kept =
ssh_find_all_matching(hostkeys[0] == ',' ? hostkeys + 1 : hostkeys,
allowed);
if (kept == NULL) {
/* Nothing was allowed */
return -1;
}
rc = ssh_options_set_algo(session,
SSH_HOSTKEYS,
kept,
&session->opts.wanted_methods[SSH_HOSTKEYS]);
SAFE_FREE(kept);
if (rc < 0) {
return -1;
rc = ssh_options_set_algo(session,
SSH_HOSTKEYS,
kept,
&session->opts.wanted_methods[SSH_HOSTKEYS]);
SAFE_FREE(kept);
if (rc < 0) {
return -1;
}
}
#ifdef WITH_GSSAPI
if (session->opts.gssapi_key_exchange) {
ok = ssh_gssapi_init(session);
if (ok != SSH_OK) {
ssh_set_error_oom(session);
return SSH_ERROR;
}
gssapi_algs = ssh_gssapi_kex_mechs(session);
if (gssapi_algs == NULL) {
return SSH_ERROR;
}
ssh_gssapi_free(session);
/* Prefix the default algorithms with gsskex algs */
session->opts.wanted_methods[SSH_KEX] =
ssh_prefix_without_duplicates(ssh_kex_get_default_methods(SSH_KEX),
gssapi_algs);
if (strlen(hostkeys) == 0) {
session->opts.wanted_methods[SSH_HOSTKEYS] = strdup("null");
}
SAFE_FREE(gssapi_algs);
}
#endif /* WITH_GSSAPI */
for (i = 0; i < SSH_KEX_METHODS; i++) {
wanted = session->opts.wanted_methods[i];
@@ -211,6 +247,23 @@ int ssh_server_init_kex(ssh_session session) {
return server_set_kex(session);
}
/**
* @internal
*
* @brief Sends SSH extension information from the server to client.
*
* A server may send this message (`SSH_MSG_EXT_INFO`) after its first
* `SSH_MSG_NEWKEYS` message or just before sending `SSH_MSG_USERAUTH_SUCCESS`
* to provide additional extensions support that are not meant for an
* unauthenticated client.
*
* If any error occurs during the packing or sending of the packet, the function
* aborts to avoid partial or corrupted sends.
*
* @param[in] session The SSH session.
*
* @return `SSH_OK` on success, `SSH_ERROR` on failure.
*/
static int ssh_server_send_extensions(ssh_session session)
{
int rc;
@@ -276,6 +329,22 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init){
return SSH_PACKET_NOT_USED;
}
/**
* @brief Prepares server host key parameters for the key exchange process.
*
* Selects the appropriate private host key (RSA, ECDSA, or Ed25519) based on
* the session's configured host key type, sets the corresponding @p digest
* algorithm, and imports the public key blob into the Diffie-Hellman key
* exchange state.
*
* @param[in] session The SSH session to which we are preparing host key
* parameters.
* @param[out] privkey Pointer to receive the selected private host key.
* @param[out] digest Pointer to receive the host key digest algorithm.
*
* @return `SSH_OK` on success; `SSH_ERROR` on failure (invalid key type, export
* failure, or DH import error).
*/
int
ssh_get_key_params(ssh_session session,
ssh_key *privkey,
@@ -633,6 +702,14 @@ int ssh_auth_reply_default(ssh_session session,int partial) {
strncat(methods_c,"gssapi-with-mic,",
sizeof(methods_c) - strlen(methods_c) - 1);
}
/* Check if GSSAPI Key exchange was performed */
if (session->auth.supported_methods & SSH_AUTH_METHOD_GSSAPI_KEYEX) {
if (ssh_kex_is_gss(session->current_crypto)) {
strncat(methods_c,
"gssapi-keyex,",
sizeof(methods_c) - strlen(methods_c) - 1);
}
}
if (session->auth.supported_methods & SSH_AUTH_METHOD_INTERACTIVE) {
strncat(methods_c, "keyboard-interactive,",
sizeof(methods_c) - strlen(methods_c) - 1);
@@ -669,6 +746,20 @@ int ssh_auth_reply_default(ssh_session session,int partial) {
return rc;
}
/**
* @internal
*
* @brief Sends default refusal for a channel open request.
*
* Default handler that rejects incoming SSH channel open requests by sending
* a `SSH2_MSG_CHANNEL_OPEN_FAILURE` packet with "administratively prohibited"
* reason code. Used when no custom channel open handler is registered.
*
* @param[in] msg The SSH message containing the channel open request details.
*
* @return `SSH_OK` on successful packet send; `SSH_ERROR` on buffer allocation
* or packet send failure.
*/
static int ssh_message_channel_request_open_reply_default(ssh_message msg) {
int rc;
@@ -690,6 +781,21 @@ static int ssh_message_channel_request_open_reply_default(ssh_message msg) {
return rc;
}
/**
* @internal
*
* @brief Sends default refusal for a channel request.
*
* Default handler that rejects incoming SSH channel requests. If the client
* requested a reply (`want_reply`), sends `SSH2_MSG_CHANNEL_FAILURE` to the
* specific channel. If no reply requested, logs the refusal and returns
* success.
*
* @param[in] msg The SSH message containing the channel request details.
*
* @return `SSH_OK` on success; `SSH_ERROR` if buffer allocation or packet send
* fails.
*/
static int ssh_message_channel_request_reply_default(ssh_message msg) {
uint32_t channel;
int rc;
@@ -723,11 +829,11 @@ static int ssh_message_service_request_reply_default(ssh_message msg) {
}
/**
* @brief Sends SERVICE_ACCEPT to the client
* @brief Sends `SSH2_MSG_SERVICE_ACCEPT` to the client
*
* @param msg The message to reply to
*
* @returns SSH_OK when success otherwise SSH_ERROR
* @returns `SSH_OK` when success otherwise `SSH_ERROR`
*/
int ssh_message_service_reply_success(ssh_message msg)
{
@@ -761,7 +867,7 @@ int ssh_message_service_reply_success(ssh_message msg)
*
* @param bound_port The remote bind port
*
* @returns SSH_OK on success, otherwise SSH_ERROR
* @returns `SSH_OK` on success, otherwise `SSH_ERROR`
*/
int ssh_message_global_request_reply_success(ssh_message msg, uint16_t bound_port) {
int rc;
@@ -797,6 +903,20 @@ error:
return SSH_ERROR;
}
/**
* @internal
*
* @brief Sends default refusal for a global request.
*
* Default handler that rejects incoming SSH global requests. If the client
* requested a reply (`want_reply`), sends `SSH2_MSG_REQUEST_FAILURE`. If no
* reply requested, logs the refusal and returns success immediately.
*
* @param[in] msg The SSH message containing the global request details.
*
* @return `SSH_OK` on success; `SSH_ERROR` if buffer allocation or packet send
* fails.
*/
static int ssh_message_global_request_reply_default(ssh_message msg) {
SSH_LOG(SSH_LOG_FUNCTIONS, "Refusing a global request");
@@ -935,6 +1055,28 @@ int ssh_message_auth_set_methods(ssh_message msg, int methods) {
return 0;
}
/**
* @brief Sends an interactive authentication request message.
*
* Builds and sends an `SSH2_MSG_USERAUTH_INFO_REQUEST` packet containing the
* given name and @p instruction, followed by a number of @p prompts with
* associated @p echo flags to control whether user input is echoed.
* It initializes the keyboard-interactive state in the session.
*
* @param[in] msg The SSH message representing the client
* authentication request.
* @param[in] name The name of the authentication request.
* @param[in] instruction Instruction string with information for the user.
* @param[in] num_prompts Number of prompts to send. The arrays prompts and
* echo must both have num_prompts elements.
* @param[in] prompts Array of @p num_prompts prompt strings to display.
* @param[in] echo Array of num_prompts boolean values (0 or 1). A
* non-zero value means the user input for that prompt
* is echoed (visible); 0 means the input is hidden
* (typically for passwords).
*
* @return `SSH_OK` on successful send; `SSH_ERROR` on failure.
*/
int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
const char *instruction, unsigned int num_prompts,
const char **prompts, char *echo) {
@@ -1041,15 +1183,15 @@ int ssh_message_auth_interactive_request(ssh_message msg, const char *name,
}
/**
* @brief Sends SSH2_MSG_USERAUTH_SUCCESS or SSH2_MSG_USERAUTH_FAILURE message
* depending on the success of the authentication method
* @brief Sends `SSH2_MSG_USERAUTH_SUCCESS` or `SSH2_MSG_USERAUTH_FAILURE`
* message depending on the success of the authentication method
*
* @param session The session to reply to
*
* @param partial Denotes if the authentication process was partially completed
* (unsuccessful)
*
* @returns SSH_OK on success, otherwise SSH_ERROR
* @returns `SSH_OK` on success, otherwise `SSH_ERROR`
*/
int ssh_auth_reply_success(ssh_session session, int partial)
{
@@ -1073,9 +1215,9 @@ int ssh_auth_reply_success(ssh_session session, int partial)
/*
* Consider the session as having been authenticated only after sending
* the USERAUTH_SUCCESS message. Setting these flags after ssh_packet_send
* ensures that a rekey is not triggered prematurely, causing the message
* to be queued.
* the `USERAUTH_SUCCESS` message. Setting these flags after
* ssh_packet_send ensures that a rekey is not triggered prematurely,
* causing the message to be queued.
*/
session->session_state = SSH_SESSION_STATE_AUTHENTICATED;
session->flags |= SSH_SESSION_FLAG_AUTHENTICATED;
@@ -1094,6 +1236,18 @@ int ssh_auth_reply_success(ssh_session session, int partial)
return r;
}
/**
* @brief Replies to an authentication request with success.
*
* Sends an authentication success message (`SSH2_MSG_USERAUTH_SUCCESS`) to the
* client, or a partial success if further authentication steps are required.
*
* @param[in] msg The SSH authentication message being handled.
* @param[in] partial Set to nonzero if partial success (more auth needed), zero
* for full success.
*
* @return `SSH_OK` on success, `SSH_ERROR` if msg is NULL or on send failure.
*/
int ssh_message_auth_reply_success(ssh_message msg, int partial) {
if(msg == NULL)
return SSH_ERROR;
@@ -1101,7 +1255,7 @@ int ssh_message_auth_reply_success(ssh_message msg, int partial) {
}
/**
* @brief Answer SSH2_MSG_USERAUTH_PK_OK to a pubkey authentication request
* @brief Answer `SSH2_MSG_USERAUTH_PK_OK` to a pubkey authentication request
*
* @param msg The message
*
@@ -1109,7 +1263,7 @@ int ssh_message_auth_reply_success(ssh_message msg, int partial) {
*
* @param pubkey The accepted public key
*
* @returns SSH_OK on success, otherwise SSH_ERROR
* @returns `SSH_OK` on success, otherwise `SSH_ERROR`
*/
int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pubkey) {
int rc;
@@ -1132,11 +1286,11 @@ int ssh_message_auth_reply_pk_ok(ssh_message msg, ssh_string algo, ssh_string pu
}
/**
* @brief Answer SSH2_MSG_USERAUTH_PK_OK to a pubkey authentication request
* @brief Answer `SSH2_MSG_USERAUTH_PK_OK` to a pubkey authentication request
*
* @param msg The message
*
* @returns SSH_OK on success, otherwise SSH_ERROR
* @returns `SSH_OK` on success, otherwise `SSH_ERROR`
*/
int ssh_message_auth_reply_pk_ok_simple(ssh_message msg)
{
@@ -1163,83 +1317,271 @@ int ssh_message_auth_reply_pk_ok_simple(ssh_message msg)
return ret;
}
/**
* @brief Get the originator address from the channel open message.
*
* @param[in] msg The message.
*
* @return The originator address, or NULL.
*/
const char *ssh_message_channel_request_open_originator(ssh_message msg){
return msg->channel_request_open.originator;
}
/**
* @brief Get the originator port from the channel open message.
*
* @param[in] msg The message.
*
* @return The originator port.
*/
int ssh_message_channel_request_open_originator_port(ssh_message msg){
return msg->channel_request_open.originator_port;
}
/**
* @brief Get the destination address from the channel open message.
*
* @param[in] msg The message.
*
* @return The destination address, or NULL.
*/
const char *ssh_message_channel_request_open_destination(ssh_message msg){
return msg->channel_request_open.destination;
}
/**
* @brief Get the destination port from the channel open message.
*
* @param[in] msg The message.
*
* @return The destination port.
*/
int ssh_message_channel_request_open_destination_port(ssh_message msg){
return msg->channel_request_open.destination_port;
}
/**
* @brief Get the channel associated with the message.
*
* @param[in] msg The message.
*
* @return The channel associated with the message.
*/
ssh_channel ssh_message_channel_request_channel(ssh_message msg){
return msg->channel_request.channel;
}
/**
* @brief Get the terminal type from the message.
*
* @param[in] msg The message.
*
* @return The terminal type (e.g. "xterm"), or NULL.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function channel_pty_request_function.
*
* @see channel_pty_request_function.
*/
const char *ssh_message_channel_request_pty_term(ssh_message msg){
return msg->channel_request.TERM;
}
/**
* @brief Get the terminal width from the message.
*
* @param[in] msg The message.
*
* @return The terminal width in characters.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function channel_pty_request_function.
*
* @see channel_pty_request_function.
*/
int ssh_message_channel_request_pty_width(ssh_message msg){
return msg->channel_request.width;
}
/**
* @brief Get the terminal height from the message.
*
* @param[in] msg The message.
*
* @return The terminal height in characters.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function channel_pty_request_function.
*
* @see channel_pty_request_function.
*/
int ssh_message_channel_request_pty_height(ssh_message msg){
return msg->channel_request.height;
}
/**
* @brief Get the terminal pixel width from the message.
*
* @param[in] msg The message.
*
* @return The pixel width.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function channel_pty_request_function.
*
* @see channel_pty_request_function.
*/
int ssh_message_channel_request_pty_pxwidth(ssh_message msg){
return msg->channel_request.pxwidth;
}
/**
* @brief Get the terminal pixel height from the message.
*
* @param[in] msg The message.
*
* @return The pixel height.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function channel_pty_request_function.
*
* @see channel_pty_request_function.
*/
int ssh_message_channel_request_pty_pxheight(ssh_message msg){
return msg->channel_request.pxheight;
}
/**
* @brief Get the name of the environment variable from the message.
*
* @param[in] msg The message.
*
* @return The variable name, or NULL.
*/
const char *ssh_message_channel_request_env_name(ssh_message msg){
return msg->channel_request.var_name;
}
/**
* @brief Get the value of the environment variable from the message.
*
* @param[in] msg The message.
*
* @return The variable value, or NULL.
*/
const char *ssh_message_channel_request_env_value(ssh_message msg){
return msg->channel_request.var_value;
}
/**
* @brief Get the command from a channel request message.
*
* @param[in] msg The message.
*
* @return The command, or NULL.
*/
const char *ssh_message_channel_request_command(ssh_message msg){
return msg->channel_request.command;
}
/**
* @brief Get the subsystem from a channel request message.
*
* @param[in] msg The message.
*
* @return The subsystem, or NULL.
*/
const char *ssh_message_channel_request_subsystem(ssh_message msg){
return msg->channel_request.subsystem;
}
/**
* @brief Check if the X11 request is for a single connection.
*
* @param[in] msg The message.
*
* @return 1 if single connection, 0 otherwise.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function
* channel_open_request_x11_function.
*
* @see channel_open_request_x11_function.
*/
int ssh_message_channel_request_x11_single_connection(ssh_message msg){
return msg->channel_request.x11_single_connection ? 1 : 0;
}
/**
* @brief Get the X11 authentication protocol from the message.
*
* @param[in] msg The message.
*
* @return The authentication protocol, or NULL.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function
* channel_open_request_x11_function.
*
* @see channel_open_request_x11_function.
*/
const char *ssh_message_channel_request_x11_auth_protocol(ssh_message msg){
return msg->channel_request.x11_auth_protocol;
}
/**
* @brief Get the X11 authentication cookie from the message.
*
* @param[in] msg The message.
*
* @return The authentication cookie, or NULL.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function
* channel_open_request_x11_function.
*
* @see channel_open_request_x11_function.
*/
const char *ssh_message_channel_request_x11_auth_cookie(ssh_message msg){
return msg->channel_request.x11_auth_cookie;
}
/**
* @brief Get the X11 screen number from the message.
*
* @param[in] msg The message.
*
* @return The screen number.
*
* @deprecated This function should not be used anymore as there is a
* callback based server implementation function
* channel_open_request_x11_function.
*
* @see channel_open_request_x11_function.
*/
int ssh_message_channel_request_x11_screen_number(ssh_message msg){
return msg->channel_request.x11_screen_number;
}
/**
* @brief Get the bind address from the global request message.
*
* @param[in] msg The message.
*
* @return The bind address, or NULL.
*/
const char *ssh_message_global_request_address(ssh_message msg){
return msg->global_request.bind_address;
}
/**
* @brief Get the bind port from the global request message.
*
* @param[in] msg The message.
*
* @return The bind port.
*/
int ssh_message_global_request_port(ssh_message msg){
return msg->global_request.bind_port;
}
@@ -1259,6 +1601,13 @@ void ssh_set_message_callback(ssh_session session,
session->ssh_message_callback_data = data;
}
/**
* @brief Execute callbacks for the messages in the queue.
*
* @param[in] session The session.
*
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*/
int ssh_execute_message_callbacks(ssh_session session){
ssh_message msg=NULL;
int ret;
@@ -1294,7 +1643,7 @@ int ssh_execute_message_callbacks(ssh_session session){
*
* @param session The session to send the message to
*
* @returns SSH_OK
* @returns `SSH_OK`
*/
int ssh_send_keepalive(ssh_session session)
{

View File

@@ -160,6 +160,14 @@ ssh_session ssh_new(void)
goto err;
}
#ifdef WITH_GSSAPI
session->opts.gssapi_key_exchange_algs =
strdup(GSSAPI_KEY_EXCHANGE_SUPPORTED);
if (session->opts.gssapi_key_exchange_algs == NULL) {
goto err;
}
#endif /* WITH_GSSAPI */
id = strdup("%d/id_ed25519");
if (id == NULL) {
goto err;
@@ -325,6 +333,7 @@ void ssh_free(ssh_session session)
#ifdef WITH_GSSAPI
ssh_gssapi_free(session);
SAFE_FREE(session->opts.gssapi_key_exchange_algs);
#endif
/* options */
@@ -470,12 +479,18 @@ const char* ssh_get_kex_algo(ssh_session session) {
return "diffie-hellman-group14-sha1";
case SSH_KEX_DH_GROUP14_SHA256:
return "diffie-hellman-group14-sha256";
case SSH_GSS_KEX_DH_GROUP14_SHA256:
return "gss-group14-sha256-";
case SSH_KEX_DH_GROUP16_SHA512:
return "diffie-hellman-group16-sha512";
case SSH_GSS_KEX_DH_GROUP16_SHA512:
return "gss-group16-sha512-";
case SSH_KEX_DH_GROUP18_SHA512:
return "diffie-hellman-group18-sha512";
case SSH_KEX_ECDH_SHA2_NISTP256:
return "ecdh-sha2-nistp256";
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
return "gss-nistp256-sha256-";
case SSH_KEX_ECDH_SHA2_NISTP384:
return "ecdh-sha2-nistp384";
case SSH_KEX_ECDH_SHA2_NISTP521:
@@ -484,18 +499,20 @@ const char* ssh_get_kex_algo(ssh_session session) {
return "curve25519-sha256";
case SSH_KEX_CURVE25519_SHA256_LIBSSH_ORG:
return "curve25519-sha256@libssh.org";
case SSH_GSS_KEX_CURVE25519_SHA256:
return "gss-curve25519-sha256-";
case SSH_KEX_SNTRUP761X25519_SHA512_OPENSSH_COM:
return "sntrup761x25519-sha512@openssh.com";
case SSH_KEX_SNTRUP761X25519_SHA512:
return "sntrup761x25519-sha512";
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
return "mlkem768x25519-sha256";
case SSH_KEX_MLKEM768NISTP256_SHA256:
return "mlkem768nistp256-sha256";
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
return "mlkem1024nistp384-sha384";
#endif /* HAVE_MLKEM */
#endif /* HAVE_MLKEM1024 */
#ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1:
return "diffie-hellman-group-exchange-sha1";
@@ -646,10 +663,9 @@ static int ssh_flush_termination(void *c){
* will block, in milliseconds. Specifying -1
* means an infinite timeout. This parameter is passed to
* the poll() function.
* @returns SSH_OK on success, SSH_AGAIN if timeout occurred,
* SSH_ERROR otherwise.
* @returns `SSH_OK` on success, `SSH_AGAIN` if timeout occurred,
* `SSH_ERROR` otherwise.
*/
int ssh_blocking_flush(ssh_session session, int timeout){
int rc;
if (session == NULL) {
@@ -731,7 +747,7 @@ void ssh_set_fd_towrite(ssh_session session) {
/**
* @brief Tell the session it has an exception to catch on the file descriptor.
*
* \param[in] session The ssh session to use.
* @param[in] session The ssh session to use.
*/
void ssh_set_fd_except(ssh_session session) {
if (session == NULL) {
@@ -745,20 +761,22 @@ void ssh_set_fd_except(ssh_session session) {
* @internal
*
* @brief Poll the current session for an event and call the appropriate
* callbacks. This function will not loop until the timeout is expired.
* callbacks. This function will not loop until the @p timeout is expired.
*
* This will block until one event happens.
*
* @param[in] session The session handle to use.
*
* @param[in] timeout Set an upper limit on the time for which this function
* will block, in milliseconds. Specifying SSH_TIMEOUT_INFINITE
* will block, in milliseconds. Specifying
* `SSH_TIMEOUT_INFINITE`
* (-1) means an infinite timeout.
* Specifying SSH_TIMEOUT_USER means to use the timeout
* specified in options. 0 means poll will return immediately.
* Specifying `SSH_TIMEOUT_USER` means to use the timeout
* specified in options. 0 means poll will return
* immediately.
* This parameter is passed to the poll() function.
*
* @return SSH_OK on success, SSH_ERROR otherwise.
* @return `SSH_OK` on success, `SSH_ERROR` otherwise.
*/
int ssh_handle_packets(ssh_session session, int timeout)
{
@@ -813,13 +831,14 @@ int ssh_handle_packets(ssh_session session, int timeout)
* @brief Poll the current session for an event and call the appropriate
* callbacks.
*
* This will block until termination function returns true, or timeout expired.
* This will block until termination function returns true, or @p timeout
* expired.
*
* @param[in] session The session handle to use.
*
* @param[in] timeout Set an upper limit on the time for which this function
* will block, in milliseconds. Specifying
* SSH_TIMEOUT_INFINITE (-1) means an infinite timeout.
* `SSH_TIMEOUT_INFINITE` (-1) means an infinite timeout.
* Specifying SSH_TIMEOUT_USER means using the timeout
* specified in options. 0 means poll will return
* immediately.
@@ -830,8 +849,8 @@ int ssh_handle_packets(ssh_session session, int timeout)
* @param[in] fct Termination function to be used to determine if it is
* possible to stop polling.
* @param[in] user User parameter to be passed to fct termination function.
* @returns SSH_OK on success, SSH_AGAIN if timeout occurred,
* SSH_ERROR otherwise.
* @returns `SSH_OK` on success, `SSH_AGAIN` if timeout occurred,
* `SSH_ERROR` otherwise.
*/
int ssh_handle_packets_termination(ssh_session session,
int timeout,
@@ -888,9 +907,10 @@ int ssh_handle_packets_termination(ssh_session session,
*
* @param session The ssh session to use.
*
* @returns A bitmask including SSH_CLOSED, SSH_READ_PENDING, SSH_WRITE_PENDING
* or SSH_CLOSED_ERROR which respectively means the session is closed,
* has data to read on the connection socket and session was closed
* @returns A bitmask including `SSH_CLOSED`, `SSH_READ_PENDING`,
* `SSH_WRITE_PENDING` or `SSH_CLOSED_ERROR` which
* respectively means the session is closed, has data to
* read on the connection socket and session was closed
* due to an error.
*/
int ssh_get_status(ssh_session session) {
@@ -926,9 +946,9 @@ int ssh_get_status(ssh_session session) {
*
* @param session The ssh session to use.
*
* @returns A bitmask including SSH_READ_PENDING or SSH_WRITE_PENDING.
* For SSH_READ_PENDING, your invocation of poll() should include
* POLLIN. For SSH_WRITE_PENDING, your invocation of poll() should
* @returns A bitmask including `SSH_READ_PENDING` or `SSH_WRITE_PENDING`.
* For `SSH_READ_PENDING`, your invocation of poll() should include
* POLLIN. For `SSH_WRITE_PENDING`, your invocation of poll() should
* include POLLOUT.
*/
int ssh_get_poll_flags(ssh_session session)
@@ -1036,7 +1056,7 @@ void ssh_socket_exception_callback(int code, int errno_code, void *user){
* @param[in] session The SSH session
* @param[in] data Data to be sent
*
* @return SSH_OK on success, SSH_ERROR otherwise.
* @return `SSH_OK` on success, `SSH_ERROR` otherwise.
*/
int ssh_send_ignore (ssh_session session, const char *data) {
const int type = SSH2_MSG_IGNORE;
@@ -1071,7 +1091,7 @@ error:
* SHOULD NOT be displayed unless debugging
* information has been explicitly requested.
*
* @return SSH_OK on success, SSH_ERROR otherwise.
* @return `SSH_OK` on success, `SSH_ERROR` otherwise.
*/
int ssh_send_debug (ssh_session session, const char *message, int always_display) {
int rc;
@@ -1237,7 +1257,7 @@ void ssh_clean_pubkey_hash(unsigned char **hash)
* @param[out] key A pointer to store the allocated key. You need to free
* the key using ssh_key_free().
*
* @return SSH_OK on success, SSH_ERROR on error.
* @return `SSH_OK` on success, `SSH_ERROR` on error.
*
* @see ssh_key_free()
*/
@@ -1271,7 +1291,7 @@ int ssh_get_publickey(ssh_session session, ssh_key *key)
/**
* @brief Allocates a buffer with the hash of the public key.
*
* This function allows you to get a hash of the public key. You can then
* This function allows you to get a @p hash of the public @p key. You can then
* print this hash in a human-readable form to the user so that he is able to
* verify it. Use ssh_get_hexa() or ssh_print_hash() to display it.
*
@@ -1284,7 +1304,7 @@ int ssh_get_publickey(ssh_session session, ssh_key *key)
*
* @param[in] hlen The length of the hash.
*
* @return 0 on success, -1 if an error occurred.
* @return SSH_OK on success, SSH_ERROR if an error occurred.
*
* @warning It is very important that you verify at some moment that the hash
* matches a known server. If you don't do it, cryptography won't help
@@ -1303,126 +1323,57 @@ int ssh_get_publickey_hash(const ssh_key key,
{
ssh_string blob = NULL;
unsigned char *h = NULL;
int rc;
int (*digest)(const unsigned char *, size_t, unsigned char *) = NULL;
int rc, ret = SSH_ERROR;
rc = ssh_pki_export_pubkey_blob(key, &blob);
if (rc < 0) {
return rc;
goto out;
}
switch (type) {
case SSH_PUBLICKEY_HASH_SHA1: {
SHACTX ctx = NULL;
h = calloc(1, SHA_DIGEST_LEN);
if (h == NULL) {
rc = -1;
goto out;
}
ctx = sha1_init();
if (ctx == NULL) {
free(h);
rc = -1;
goto out;
}
rc = sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
if (rc != SSH_OK) {
free(h);
sha1_ctx_free(ctx);
goto out;
}
rc = sha1_final(h, ctx);
if (rc != SSH_OK) {
free(h);
goto out;
}
case SSH_PUBLICKEY_HASH_SHA1:
digest = sha1;
*hlen = SHA_DIGEST_LEN;
break;
}
case SSH_PUBLICKEY_HASH_SHA256: {
SHA256CTX ctx = NULL;
h = calloc(1, SHA256_DIGEST_LEN);
if (h == NULL) {
rc = -1;
goto out;
}
ctx = sha256_init();
if (ctx == NULL) {
free(h);
rc = -1;
goto out;
}
rc = sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
if (rc != SSH_OK) {
free(h);
sha256_ctx_free(ctx);
goto out;
}
rc = sha256_final(h, ctx);
if (rc != SSH_OK) {
free(h);
goto out;
}
case SSH_PUBLICKEY_HASH_SHA256:
digest = sha256;
*hlen = SHA256_DIGEST_LEN;
break;
}
case SSH_PUBLICKEY_HASH_MD5: {
MD5CTX ctx = NULL;
/* In FIPS mode, we cannot use MD5 */
case SSH_PUBLICKEY_HASH_MD5:
#if defined(HAVE_LIBCRYPTO) && OPENSSL_VERSION_NUMBER < 0x30000000L
/* In FIPS mode without OpenSSL providers, we cannot use MD5 */
if (ssh_fips_mode()) {
SSH_LOG(SSH_LOG_TRACE,
"In FIPS mode MD5 is not allowed."
"Try using SSH_PUBLICKEY_HASH_SHA256");
rc = SSH_ERROR;
goto out;
}
h = calloc(1, MD5_DIGEST_LEN);
if (h == NULL) {
rc = -1;
goto out;
}
ctx = md5_init();
if (ctx == NULL) {
free(h);
rc = -1;
goto out;
}
rc = md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob));
if (rc != SSH_OK) {
free(h);
md5_ctx_free(ctx);
goto out;
}
rc = md5_final(h, ctx);
if (rc != SSH_OK) {
free(h);
goto out;
}
#endif
digest = md5;
*hlen = MD5_DIGEST_LEN;
break;
}
default:
rc = -1;
goto out;
}
h = calloc(1, *hlen);
if (h == NULL) {
goto out;
}
rc = digest(ssh_string_data(blob), ssh_string_len(blob), h);
if (rc != SSH_OK) {
free(h);
goto out;
}
*hash = h;
rc = 0;
ret = SSH_OK;
out:
SSH_STRING_FREE(blob);
return rc;
return ret;
}
/** @} */

View File

@@ -60,6 +60,27 @@
#define MAX_ENTRIES_NUM_IN_PACKET 50
#define MAX_LONG_NAME_LEN 350
/**
* @internal
*
* @brief Creates an SFTP client message from a received packet.
*
* Allocates and initializes a sftp_client_message structure from a raw
* SFTP packet. Copies the complete @p packet payload and parses common
* fields such as message type, request id, and message-specific data
* (handle, filename, attributes, offsets, etc.) depending on the SFTP
* message type.
*
* On success, the returned message owns its internal buffers and must
* be freed with sftp_client_message_free().
*
* @param[in] sftp The SFTP session associated with the packet.
* @param[in] packet The received SFTP packet to decode.
*
* @return A newly allocated sftp_client_message on success, or NULL on
* error (memory allocation failure, malformed packet, or
* unsupported message type).
*/
static sftp_client_message
sftp_make_client_message(sftp_session sftp, sftp_packet packet)
{
@@ -256,6 +277,17 @@ error:
return NULL;
}
/**
* @brief Reads the next SFTP client message from the session.
*
* Reads a single SFTP packet from the given SFTP session and converts it
* into a parsed sftp_client_message structure.
*
* @param[in] sftp The SFTP session to read from.
*
* @return A newly allocated sftp_client_message on success; NULL if no packet
* is available or an error occurs.
*/
sftp_client_message sftp_get_client_message(sftp_session sftp)
{
sftp_packet packet = NULL;
@@ -286,22 +318,62 @@ sftp_get_client_message_from_packet(sftp_session sftp)
return sftp_make_client_message(sftp, packet);
}
/* Send an sftp client message. Can be used in case of proxying */
/**
* @brief Send an SFTP client message.
*
* Writes the given client message as a packet using the stored message
* type and complete_message buffer. Can be used in case of proxying.
*
* @param[in] sftp The SFTP session.
* @param[in] msg The client message to send.
*
* @return 0 on success; -1 on error from sftp_packet_write().
*/
int sftp_send_client_message(sftp_session sftp, sftp_client_message msg)
{
return sftp_packet_write(sftp, msg->type, msg->complete_message);
}
/**
* @brief Get the SFTP client message type.
*
* Returns the SFTP packet type associated with the given client message
* (for example `SSH_FXP_READ`, `SSH_FXP_WRITE`, `SSH_FXP_OPEN`, ...).
*
* @param[in] msg The SFTP client message.
*
* @return The SFTP message type as an unsigned 8-bit value.
*/
uint8_t sftp_client_message_get_type(sftp_client_message msg)
{
return msg->type;
}
/**
* @brief Get the filename associated with an SFTP client message.
*
* Returns the filename carried by the given SFTP client message, if the
* message type includes a filename field (for example OPEN, REMOVE, RENAME).
*
* @param[in] msg The SFTP client message.
*
* @return Filename string, or NULL if no filename
* is associated with the message.
*/
const char *sftp_client_message_get_filename(sftp_client_message msg)
{
return msg->filename;
}
/**
* @brief Set the filename associated with an SFTP client message.
*
* Replaces the current filename stored in the client message with a copy
* of the given @p newname string.
*
* @param[in] msg The SFTP client message to modify.
* @param[in] newname The new filename to store in the message.
*/
void
sftp_client_message_set_filename(sftp_client_message msg, const char *newname)
{
@@ -309,6 +381,17 @@ sftp_client_message_set_filename(sftp_client_message msg, const char *newname)
msg->filename = strdup(newname);
}
/**
* @brief Get the data field of an SFTP client message as a string.
*
* Converts the internal ssh_string data field to a string
* on first use and caches the result in the message. Subsequent calls
* return the cached pointer.
*
* @param[in] msg The SFTP client message.
*
* @return The data as string, or NULL on error.
*/
const char *sftp_client_message_get_data(sftp_client_message msg)
{
if (msg->str_data == NULL)
@@ -316,16 +399,48 @@ const char *sftp_client_message_get_data(sftp_client_message msg)
return msg->str_data;
}
/**
* @brief Get the flags associated with an SFTP client message.
*
* Returns the flags field stored in the given SFTP client message. The exact
* meaning of the flags depends on the SFTP message type (for example, open
* or stat flags).
*
* @param[in] msg The SFTP client message.
*
* @return The flags value as an unsigned 32-bit integer.
*/
uint32_t sftp_client_message_get_flags(sftp_client_message msg)
{
return msg->flags;
}
/**
* @brief Get the submessage name associated with an SFTP client message.
*
* Returns the submessage string stored in the given SFTP client message.
* This is typically used for vendor-specific SFTP operations.
*
* @param[in] msg The SFTP client message.
*
* @return The submessage name as a string, or NULL if no
* submessage is associated with the message.
*/
const char *sftp_client_message_get_submessage(sftp_client_message msg)
{
return msg->submessage;
}
/**
* @brief Free an SFTP client message and its associated resources.
*
* Releases all dynamically allocated fields in the SFTP client message
* (such as filename, submessage, data, handle, attributes, and cached
* buffers) and then frees the message structure itself. The function
* does nothing if msg is NULL.
*
* @param[in] msg The SFTP client message to free, or NULL.
*/
void sftp_client_message_free(sftp_client_message msg)
{
if (msg == NULL) {
@@ -343,6 +458,20 @@ void sftp_client_message_free(sftp_client_message msg)
SAFE_FREE(msg);
}
/**
* @brief Send an SFTP NAME reply for a client message.
*
* Builds and sends an `SSH_FXP_NAME` packet in response to the given
* SFTP client message, containing a single filename and its attributes.
* The function encodes the message id, the count of returned names
* (always 1), the filename fields and the provided attributes.
*
* @param[in] msg The SFTP client message being answered.
* @param[in] name The filename to return to the client.
* @param[in] attr The file attributes associated with the filename.
*
* @return 0 on success; -1 on memory allocation failure or packet send error.
*/
int
sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr)
{
@@ -378,6 +507,19 @@ sftp_reply_name(sftp_client_message msg, const char *name, sftp_attributes attr)
return 0;
}
/**
* @brief Send an SFTP HANDLE reply for a client message.
*
* Builds and sends an `SSH_FXP_HANDLE` packet in response to the given
* SFTP client message, containing the provided file @p handle. The message
* id is taken from the client message and the handle is encoded as an
* SSH string.
*
* @param[in] msg The SFTP client message being answered.
* @param[in] handle The file handle to return to the client.
*
* @return 0 on success; -1 on memory allocation failure or packet send error.
*/
int sftp_reply_handle(sftp_client_message msg, ssh_string handle)
{
ssh_buffer out;
@@ -402,6 +544,18 @@ int sftp_reply_handle(sftp_client_message msg, ssh_string handle)
return 0;
}
/**
* @brief Send an SFTP ATTRS reply for a client message.
*
* Builds and sends an `SSH_FXP_ATTRS` packet in response to the given
* SFTP client message, encoding the message id and the provided file
* attributes.
*
* @param[in] msg The SFTP client message being answered.
* @param[in] attr The file attributes to return to the client.
*
* @return 0 on success; -1 on memory allocation failure or packet send error.
*/
int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr)
{
ssh_buffer out;
@@ -424,6 +578,20 @@ int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr)
return 0;
}
/**
* @brief Add one name entry to a multi-name SFTP reply.
*
* Appends a @p file name, @p longname and attributes to the buffered NAME reply
* stored in the client message. Can be called multiple times before the
* reply is sent.
*
* @param[in] msg The SFTP client message being prepared.
* @param[in] file The filename to add.
* @param[in] longname The long name to add.
* @param[in] attr The file attributes for this entry.
*
* @return 0 on success; -1 on memory allocation or buffer write error.
*/
int
sftp_reply_names_add(sftp_client_message msg, const char *file,
const char *longname, sftp_attributes attr)
@@ -464,6 +632,17 @@ sftp_reply_names_add(sftp_client_message msg, const char *file,
return 0;
}
/**
* @brief Send a multi-name SFTP reply.
*
* Sends an `SSH_FXP_NAME` packet for the given client message using the
* accumulated name entries stored in msg->attrbuf and msg->attr_num.
* After sending, the buffer and counter are reset.
*
* @param[in] msg The SFTP client message to reply to.
*
* @return 0 on success; -1 on memory allocation or packet send error.
*/
int sftp_reply_names(sftp_client_message msg)
{
ssh_buffer out;
@@ -495,6 +674,20 @@ int sftp_reply_names(sftp_client_message msg)
return 0;
}
/**
* @brief Send an SFTP STATUS reply.
*
* Sends an `SSH_FXP_STATUS` packet for the given client message, including
* the @p status code and an optional human readable @p message. The language
* tag is sent as an empty string.
*
* @param[in] msg The SFTP client message to reply to.
* @param[in] status The SFTP status code to send (e.g. `SSH_FX_OK`,
* `SSH_FX_FAILURE`).
* @param[in] message Optional text message describing the status, or NULL.
*
* @return 0 on success; -1 on memory allocation or packet send error.
*/
int
sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message)
{
@@ -531,6 +724,18 @@ sftp_reply_status(sftp_client_message msg, uint32_t status, const char *message)
return 0;
}
/**
* @brief Send an SFTP DATA reply.
*
* Sends an `SSH_FXP_DATA` packet for the given client message, containing
* the provided data buffer and its length.
*
* @param[in] msg The SFTP client message to reply to.
* @param[in] data The data buffer to send.
* @param[in] len Number of bytes from data to send.
*
* @return 0 on success; -1 on memory allocation or packet send error.
*/
int sftp_reply_data(sftp_client_message msg, const void *data, int len)
{
ssh_buffer out;
@@ -646,12 +851,18 @@ int sftp_reply_version(sftp_client_message client_msg)
return SSH_OK;
}
/*
* This function will return you a new handle to give the client.
* the function accepts an info that can be retrieved later with
* the handle. Care is given that a corrupted handle won't give a
* valid info (or worse).
/**
* @brief Allocate a new SFTP handle slot.
*
* Finds a free handle slot in the SFTP session, stores the given @p info
* there and returns a 4-byte ssh_string that encodes the handle
* index.
*
* @param[in] sftp The SFTP session.
* @param[in] info Info to be stored in the handle slot.
*
* @return A new handle as ssh_string on success; NULL if no slot is
* available or on memory allocation failure.
*/
ssh_string sftp_handle_alloc(sftp_session sftp, void *info)
{
@@ -688,6 +899,19 @@ ssh_string sftp_handle_alloc(sftp_session sftp, void *info)
return ret;
}
/**
* @brief Resolve an SFTP handle to its stored info.
*
* Decodes the 4-byte @p handle value, checks bounds and returns the pointer
* stored in the corresponding handle slot in the SFTP session.
*
* @param[in] sftp The SFTP session.
* @param[in] handle The handle value as ssh_string.
*
* @return The stored pointer on success, or NULL if the handle table is
* not initialized, the handle size is invalid, or the index is
* out of range.
*/
void *sftp_handle(sftp_session sftp, ssh_string handle)
{
uint32_t val;
@@ -1787,8 +2011,8 @@ process_client_message(sftp_client_message client_msg)
* @param[out] userdata The pointer to sftp_session which will get the
* resulting SFTP session
*
* @return SSH_OK when the SFTP server was successfully initialized, SSH_ERROR
* otherwise.
* @return `SSH_OK` when the SFTP server was successfully initialized,
* `SSH_ERROR` otherwise.
*/
int
sftp_channel_default_subsystem_request(ssh_session session,

View File

@@ -37,13 +37,6 @@
#include "libssh/session.h"
#include "libssh/ssh2.h"
void crypto_hash_sha512(unsigned char *out,
const unsigned char *in,
unsigned long long inlen)
{
sha512(in, inlen, out);
}
#ifndef HAVE_LIBGCRYPT
static void crypto_random(void *ctx, size_t length, uint8_t *dst)
{

View File

@@ -112,8 +112,14 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
uint32_t len);
/**
* \internal
* \brief inits the socket system (windows specific)
* @internal
*
* @brief Initialize socket support for libssh.
*
* Initializes the socket subsystem, calling WSAStartup() on Windows and
* ssh_poll_init() on all platforms. Can be called multiple times.
*
* @return 0 on success; -1 on Windows socket initialization failure.
*/
int ssh_socket_init(void)
{
@@ -135,7 +141,12 @@ int ssh_socket_init(void)
}
/**
* @brief Cleanup the socket system.
* @internal
*
* @brief Cleanup socket support for libssh.
*
* Cleans up the socket subsystem, calling ssh_poll_cleanup() on all platforms
* and WSACleanup() on Windows. Can be called multiple times.
*/
void ssh_socket_cleanup(void)
{
@@ -148,10 +159,17 @@ void ssh_socket_cleanup(void)
}
}
/**
* \internal
* \brief creates a new Socket object
* @internal
*
* @brief Allocate and initialize a new SSH socket structure.
*
* Creates a new ssh_socket structure associated with the given session,
* initializes input/output buffers and sets default socket state.
*
* @param[in] session The SSH session to associate with the socket.
*
* @return A new ssh_socket on success; NULL on memory allocation failure.
*/
ssh_socket ssh_socket_new(ssh_session session)
{
@@ -189,8 +207,13 @@ ssh_socket ssh_socket_new(ssh_session session)
/**
* @internal
* @brief Reset the state of a socket so it looks brand-new
* @param[in] s socket to rest
*
* @brief Reset the state of a socket, so it looks brand new.
*
* Clears the file descriptor, reinitializes input/output buffers, frees
* the poll handle if present, and resets all socket state flags.
*
* @param[in] s The SSH socket to reset.
*/
void ssh_socket_reset(ssh_socket s)
{
@@ -219,24 +242,36 @@ void ssh_socket_reset(ssh_socket s)
* @param s socket to set callbacks on.
* @param callbacks a ssh_socket_callback object reference.
*/
void ssh_socket_set_callbacks(ssh_socket s, ssh_socket_callbacks callbacks)
{
s->callbacks = callbacks;
}
/**
* @internal
*
* @brief Mark an SSH socket as connected.
*
* Sets the socket state to connected and configures the poll handle
* to wait for `POLLIN` and `POLLOUT` events (needed for non-blocking connect).
*
* @param[in] s The SSH socket.
* @param[in] p The poll handle to configure, or NULL.
*/
void ssh_socket_set_connected(ssh_socket s, struct ssh_poll_handle_struct *p)
{
s->state = SSH_SOCKET_CONNECTED;
/* POLLOUT is the event to wait for in a nonblocking connect */
/* `POLLOUT` is the event to wait for in a non-blocking connect */
if (p != NULL) {
ssh_poll_set_events(p, POLLIN | POLLOUT);
}
}
/**
* @brief SSH poll callback. This callback will be used when an event
* caught on the socket.
* @internal
*
* @brief SSH poll callback. This callback will be used when an
* event caught on the socket.
*
* @param p Poll object this callback belongs to.
* @param fd The raw socket.
@@ -416,8 +451,15 @@ ssh_poll_handle ssh_socket_get_poll_handle(ssh_socket s)
return s->poll_handle;
}
/** \internal
* \brief Deletes a socket object
/**
* @internal
*
* @brief Deletes a socket object.
*
* Closes the socket connection, frees input/output buffers and
* releases the socket structure memory.
*
* @param[in] s The SSH socket to free, or NULL.
*/
void ssh_socket_free(ssh_socket s)
{
@@ -430,6 +472,20 @@ void ssh_socket_free(ssh_socket s)
SAFE_FREE(s);
}
/**
* @internal
*
* @brief Connect an SSH socket to a Unix domain socket.
*
* Creates a Unix domain socket connection to the given @p path and associates
* it with the SSH socket.
*
* @param[in] s The SSH socket to connect.
* @param[in] path Path to the Unix domain socket.
*
* @return `SSH_OK` on success; `SSH_ERROR` on socket creation, connect, or fd
* setup failure.
*/
int ssh_socket_unix(ssh_socket s, const char *path)
{
struct sockaddr_un sunaddr;
@@ -466,8 +522,17 @@ int ssh_socket_unix(ssh_socket s, const char *path)
return ssh_socket_set_fd(s, fd);
}
/** \internal
* \brief closes a socket
/**
* @internal
*
* @brief Close an SSH socket.
*
* Closes the socket file descriptor if open, saves the last error code,
* frees the poll handle if unlocked, and marks the socket state as closed.
* On Unix, attempts to terminate and wait for any running proxy command
* process.
*
* @param[in] s The SSH socket to close.
*/
void ssh_socket_close(ssh_socket s)
{
@@ -545,24 +610,48 @@ int ssh_socket_set_fd(ssh_socket s, socket_t fd)
return SSH_OK;
}
/** \internal
* \brief returns the input file descriptor of the socket
/**
* @internal
*
* @brief Returns the input file descriptor of a socket.
*
* @param[in] s The SSH socket.
*
* @return The socket file descriptor (socket_t).
*/
socket_t ssh_socket_get_fd(ssh_socket s)
{
return s->fd;
}
/** \internal
* \brief returns nonzero if the socket is open
/**
* @internal
*
* @brief Check if an SSH socket is open.
*
* @param[in] s The SSH socket.
*
* @return Non-zero if socket is open, 0 if closed or invalid.
*/
int ssh_socket_is_open(ssh_socket s)
{
return s->fd != SSH_INVALID_SOCKET;
}
/** \internal
* \brief read len bytes from socket into buffer
/**
* @internal
*
* @brief Perform an unbuffered read from an SSH socket.
*
* Reads @p len bytes from the socket file descriptor directly into @p buffer,
* using `recv()` if the descriptor is a socket, or `read()` otherwise.
* Updates internal error and state flags based on the result.
*
* @param[in] s The SSH socket.
* @param[out] buffer Buffer to read data into.
* @param[in] len Maximum number of bytes to read.
*
* @return Number of bytes read on success, or -1 on error.
*/
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
void *buffer,
@@ -594,8 +683,21 @@ static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
return rc;
}
/** \internal
* \brief writes len bytes from buffer to socket
/**
* @internal
*
* @brief Perform an unbuffered write to an SSH socket.
*
* Writes @p len bytes from @p buffer to the socket file descriptor,
* using `send()` if the descriptor is a socket or `write()` otherwise.
* Updates internal error and state flags, and re-enables POLLOUT
* polling if a poll handle exists.
*
* @param[in] s The SSH socket.
* @param[in] buffer Buffer containing data to write.
* @param[in] len Number of bytes to write.
*
* @return Number of bytes written on success, or -1 on error.
*/
static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
const void *buffer,
@@ -636,8 +738,18 @@ static ssize_t ssh_socket_unbuffered_write(ssh_socket s,
return w;
}
/** \internal
* \brief returns nonzero if the current socket is in the fd_set
/**
* @internal
*
* @brief Check if SSH socket file descriptor is set in an fd_set.
*
* Tests if the socket's file descriptor is present in the
* given @p set (fd_set) . Returns 0 if the socket has no valid file descriptor.
*
* @param[in] s The SSH socket.
* @param[in] set The fd_set to test against.
*
* @return Non-zero if the socket fd is set in the fd_set, 0 otherwise.
*/
int ssh_socket_fd_isset(ssh_socket s, fd_set *set)
{
@@ -647,10 +759,17 @@ int ssh_socket_fd_isset(ssh_socket s, fd_set *set)
return FD_ISSET(s->fd,set);
}
/** \internal
* \brief sets the current fd in a fd_set and updates the max_fd
/**
* @internal
*
* @brief Add SSH socket file descriptor to an fd_set.
*
* Adds the socket's file descriptor to the given @p set (fd_set)
* and updates @p max_fd if this socket has the highest file descriptor number.
* @param[in] s The SSH socket.
* @param[in,out] set The fd_set to add the socket to.
* @param[in,out] max_fd the maximum fd value.
*/
void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
{
if (s->fd == SSH_INVALID_SOCKET) {
@@ -666,10 +785,21 @@ void ssh_socket_fd_set(ssh_socket s, fd_set *set, socket_t *max_fd)
}
}
/** \internal
* \brief buffered write of data
* \returns SSH_OK, or SSH_ERROR
* \warning has no effect on socket before a flush
/**
* @internal
*
* @brief Write data to an SSH socket output buffer.
*
* Adds the data to the socket's output @p buffer and calls a nonblocking
* flush attempt to send buffered data.
*
* @param[in] s The SSH socket.
* @param[in] buffer Data to write.
* @param[in] len Number of bytes to write.
*
* @return `SSH_OK` on success; `SSH_ERROR` on buffer allocation failure.
*
* @warning It has no effect on socket before a flush.
*/
int ssh_socket_write(ssh_socket s, const void *buffer, uint32_t len)
{
@@ -684,10 +814,22 @@ int ssh_socket_write(ssh_socket s, const void *buffer, uint32_t len)
return SSH_OK;
}
/** \internal
* \brief starts a nonblocking flush of the output buffer
/**
* @internal
*
* @brief Starts a nonblocking flush of the output buffer.
*
* Sends all buffered data from the socket's output buffer.
* If the socket is not open, marks the session as dead and calls an
* exception callback or sets a fatal error. If the socket cannot currently
* accept data, polls for writable events and returns `SSH_AGAIN`.
* On write errors, closes the socket and signals the error. Updates
* byte counters on successful writes.
*
* @param[in] s The SSH socket.
*
* @return `SSH_OK` if all data was sent; `SSH_AGAIN` if the operation should
* be retried later; `SSH_ERROR` on fatal socket error.
*/
int ssh_socket_nonblocking_flush(ssh_socket s)
{
@@ -767,26 +909,79 @@ int ssh_socket_nonblocking_flush(ssh_socket s)
return SSH_OK;
}
/**
* @internal
*
* @brief Set the SSH socket write_wontblock flag.
*
* Marks the socket as ready for nonblocking writes (`write_wontblock = 1`).
* Used by the poll system when POLLOUT becomes available.
*
* @param[in] s The SSH socket.
*/
void ssh_socket_set_write_wontblock(ssh_socket s)
{
s->write_wontblock = 1;
}
/**
* @internal
*
* @brief Set the SSH socket read_wontblock flag.
*
* Marks the socket as ready for nonblocking reads (`read_wontblock = 1`).
* Used by the poll system when POLLIN becomes available.
*
* @param[in] s The SSH socket.
*/
void ssh_socket_set_read_wontblock(ssh_socket s)
{
s->read_wontblock = 1;
}
/**
* @internal
*
* @brief Set the SSH socket exception flag.
*
* Marks the socket as having an exception condition (`data_except = 1`).
*
* @param[in] s The SSH socket.
*/
void ssh_socket_set_except(ssh_socket s)
{
s->data_except = 1;
}
/**
* @internal
*
* @brief Check if SSH socket data is available for reading.
*
* Returns true if the socket is ready for nonblocking reads
* (`read_wontblock` flag is set).
*
* @param[in] s The SSH socket.
*
* @return 1 if data is available, 0 otherwise.
*/
int ssh_socket_data_available(ssh_socket s)
{
return s->read_wontblock;
}
/**
* @internal
*
* @brief Check if SSH socket is writable.
*
* Returns true if the socket is ready for nonblocking writes
* (`write_wontblock` flag is set).
*
* @param[in] s The SSH socket.
*
* @return 1 if socket is writable, 0 otherwise.
*/
int ssh_socket_data_writable(ssh_socket s)
{
return s->write_wontblock;
@@ -806,7 +1001,19 @@ int ssh_socket_buffered_write_bytes(ssh_socket s)
return ssh_buffer_get_len(s->out_buffer);
}
/**
* @internal
*
* @brief Get the current status of an SSH socket.
*
* Checks the input/output buffers and exception flag to determine socket
* status: `SSH_READ_PENDING` if input data available, `SSH_WRITE_PENDING`
* if output data pending, `SSH_CLOSED_ERROR` if exception occurred.
*
* @param[in] s The SSH socket.
*
* @return Socket status flags.
*/
int ssh_socket_get_status(ssh_socket s)
{
int r = 0;
@@ -826,6 +1033,18 @@ int ssh_socket_get_status(ssh_socket s)
return r;
}
/**
* @internal
*
* @brief Get SSH socket poll flags from the poll handle.
*
* Checks the poll handle events and returns `SSH_READ_PENDING` if POLLIN
* is set, `SSH_WRITE_PENDING` if POLLOUT is set.
*
* @param[in] s The SSH socket.
*
* @return Socket status flags based on poll events.
*/
int ssh_socket_get_poll_flags(ssh_socket s)
{
int r = 0;
@@ -872,8 +1091,8 @@ int ssh_socket_set_blocking(socket_t fd)
* @param host hostname or ip address to connect to.
* @param port port number to connect to.
* @param bind_addr address to bind to, or NULL for default.
* @returns SSH_OK socket is being connected.
* @returns SSH_ERROR error while connecting to remote host.
* @returns `SSH_OK` socket is being connected.
* @returns `SSH_ERROR` error while connecting to remote host.
*/
int ssh_socket_connect(ssh_socket s,
const char *host,
@@ -958,8 +1177,8 @@ ssh_execute_command(const char *command, socket_t in, socket_t out)
* This call will always be nonblocking.
* @param s socket to connect.
* @param command Command to execute.
* @returns SSH_OK socket is being connected.
* @returns SSH_ERROR error while executing the command.
* @returns `SSH_OK` socket is being connected.
* @returns `SSH_ERROR` error while executing the command.
*/
int
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
@@ -1215,6 +1434,8 @@ exit:
ssh_event_free(event);
ssh_free(jump_session);
shutdown(jump_thread_data->fd, SHUT_RDWR);
close(jump_thread_data->fd);
SAFE_FREE(jump_thread_data);
pthread_exit(NULL);

View File

@@ -49,11 +49,10 @@
#include "libssh/dh-gex.h"
#endif /* WITH_GEX */
#include "libssh/curve25519.h"
#include "libssh/kex-gss.h"
#include "libssh/ecdh.h"
#include "libssh/sntrup761.h"
#ifdef HAVE_MLKEM
#include "libssh/hybrid_mlkem.h"
#endif
#include "libssh/sntrup761.h"
static struct ssh_hmac_struct ssh_hmac_tab[] = {
{ "hmac-sha1", SSH_HMAC_SHA1, false },
@@ -229,15 +228,21 @@ void crypto_free(struct ssh_crypto_struct *crypto)
SAFE_FREE(crypto->kex_methods[i]);
}
#ifdef HAVE_MLKEM
#ifdef HAVE_OPENSSL_MLKEM
EVP_PKEY_free(crypto->mlkem_privkey);
#else
if (crypto->mlkem_privkey != NULL) {
ssh_burn(crypto->mlkem_privkey, crypto->mlkem_privkey_len);
SAFE_FREE(crypto->mlkem_privkey);
crypto->mlkem_privkey_len = 0;
}
#endif
ssh_string_burn(crypto->hybrid_shared_secret);
ssh_string_free(crypto->mlkem_client_pubkey);
ssh_string_free(crypto->mlkem_ciphertext);
ssh_string_free(crypto->hybrid_client_init);
ssh_string_free(crypto->hybrid_server_reply);
ssh_string_free(crypto->hybrid_shared_secret);
#endif
ssh_burn(crypto, sizeof(struct ssh_crypto_struct));
@@ -587,6 +592,14 @@ int crypt_set_algorithms_server(ssh_session session){
case SSH_KEX_DH_GROUP18_SHA512:
ssh_server_dh_init(session);
break;
#ifdef WITH_GSSAPI
case SSH_GSS_KEX_DH_GROUP14_SHA256:
case SSH_GSS_KEX_DH_GROUP16_SHA512:
case SSH_GSS_KEX_ECDH_NISTP256_SHA256:
case SSH_GSS_KEX_CURVE25519_SHA256:
ssh_server_gss_kex_init(session);
break;
#endif /* WITH_GSSAPI */
#ifdef WITH_GEX
case SSH_KEX_DH_GEX_SHA1:
case SSH_KEX_DH_GEX_SHA256:
@@ -612,13 +625,13 @@ int crypt_set_algorithms_server(ssh_session session){
ssh_server_sntrup761x25519_init(session);
break;
#endif
#ifdef HAVE_MLKEM
case SSH_KEX_MLKEM768X25519_SHA256:
case SSH_KEX_MLKEM768NISTP256_SHA256:
#ifdef HAVE_MLKEM1024
case SSH_KEX_MLKEM1024NISTP384_SHA384:
#endif
ssh_server_hybrid_mlkem_init(session);
break;
#endif
default:
ssh_set_error(session,
SSH_FATAL,

View File

@@ -58,8 +58,8 @@ if (SK_DUMMY_LIBRARY)
add_library(sk-dummy SHARED IMPORTED)
set_target_properties(sk-dummy PROPERTIES IMPORTED_LOCATION "${SK_DUMMY_LIBRARY}")
target_link_libraries(${TORTURE_LIBRARY} PRIVATE sk-dummy)
target_compile_definitions(${TORTURE_LIBRARY} PUBLIC HAVE_SK_DUMMY)
target_compile_definitions(${TORTURE_LIBRARY} PUBLIC SK_DUMMY_LIBRARY_PATH="${SK_DUMMY_LIBRARY}")
set(HAVE_SK_DUMMY 1)
set(SK_DUMMY_LIBRARY_PATH ${SK_DUMMY_LIBRARY})
endif()
if (WITH_COVERAGE)
@@ -90,6 +90,11 @@ if (CLIENT_TESTING)
"-Wl,--wrap=sntrup761_keypair"
"-Wl,--wrap=sntrup761_enc"
"-Wl,--wrap=sntrup761_dec")
list(APPEND WRAP_SYMBOLS
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_generate_key_pair"
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_validate_public_key"
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_encapsulate"
"-Wl,--wrap=libcrux_ml_kem_mlkem768_portable_decapsulate")
add_library(${TORTURE_SHARED_LIBRARY}
SHARED
@@ -109,8 +114,6 @@ if (CLIENT_TESTING)
# Link sk-dummy to torture_shared library if available
if (SK_DUMMY_LIBRARY)
target_link_libraries(${TORTURE_SHARED_LIBRARY} PRIVATE sk-dummy)
target_compile_definitions(${TORTURE_SHARED_LIBRARY} PUBLIC HAVE_SK_DUMMY)
target_compile_definitions(${TORTURE_SHARED_LIBRARY} PUBLIC SK_DUMMY_LIBRARY_PATH="${SK_DUMMY_LIBRARY}")
endif (SK_DUMMY_LIBRARY)
target_compile_options(${TORTURE_SHARED_LIBRARY} PRIVATE
@@ -231,6 +234,31 @@ else()
set(DROPBEAR_EXECUTABLE "/bin/false")
endif()
find_program(PUTTY_EXECUTABLE
NAMES
plink
plink.exe
putty # Fallback for systems where plink isn't separate
DOC "Path to PuTTY plink executable for automated tests")
if (PUTTY_EXECUTABLE)
message(STATUS "Found PuTTY client: ${PUTTY_EXECUTABLE}")
else()
set(PUTTY_EXECUTABLE "/bin/putty-not-found")
endif()
find_program(PUTTYGEN_EXECUTABLE
NAMES
puttygen
puttygen.exe
DOC "Path to PuTTYgen executable for key conversion")
if (PUTTYGEN_EXECUTABLE)
message(STATUS "Found PuTTY keygen: ${PUTTYGEN_EXECUTABLE}")
else()
set(PUTTYGEN_EXECUTABLE "/bin/puttygen-not-found")
endif()
find_program(SSHD_EXECUTABLE
NAME
sshd
@@ -334,7 +362,7 @@ if (CLIENT_TESTING OR SERVER_TESTING)
set(TORTURE_ENVIRONMENT
"LD_PRELOAD=${SOCKET_WRAPPER_LIBRARY}:${NSS_WRAPPER_LIBRARY}:${UID_WRAPPER_LIBRARY}:${PAM_WRAPPER_LIBRARY}:${CHROOT_WRAPPER}:${FS_WRAPPER}")
"LD_PRELOAD=${FS_WRAPPER}:${SOCKET_WRAPPER_LIBRARY}:${NSS_WRAPPER_LIBRARY}:${UID_WRAPPER_LIBRARY}:${PAM_WRAPPER_LIBRARY}:${CHROOT_WRAPPER}")
if (priv_wrapper_FOUND)
list(APPEND TORTURE_ENVIRONMENT PRIV_WRAPPER=1 PRIV_WRAPPER_CHROOT_DISABLE=1)
list(APPEND TORTURE_ENVIRONMENT PRIV_WRAPPER_PRCTL_DISABLE="ALL" PRIV_WRAPPER_SETRLIMIT_DISABLE="ALL")
@@ -396,6 +424,9 @@ if (CLIENT_TESTING OR SERVER_TESTING)
file(READ keys/pkcs11/id_pkcs11_ecdsa_521_openssh.pub CONTENTS)
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
file(READ keys/pkcs11/id_pkcs11_ed25519_openssh.pub CONTENTS)
file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/home/charlie/.ssh/authorized_keys "${CONTENTS}")
# Copy the signed key to an doe's homedir.
file(COPY keys/certauth/id_rsa DESTINATION
${CMAKE_CURRENT_BINARY_DIR}/home/doe/.ssh/ FILE_PERMISSIONS OWNER_READ OWNER_WRITE)

View File

@@ -45,7 +45,9 @@ endif()
if (WITH_GSSAPI AND GSSAPI_FOUND AND GSSAPI_TESTING)
set(LIBSSH_CLIENT_TESTS
${LIBSSH_CLIENT_TESTS}
torture_gssapi_auth)
torture_gssapi_auth
torture_gssapi_key_exchange
torture_gssapi_key_exchange_null)
endif()
if (DEFAULT_C_NO_DEPRECATION_FLAGS)

View File

@@ -752,7 +752,7 @@ torture_algorithms_ecdh_sntrup761x25519_sha512(void **state)
}
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
#if defined(OPENSSH_MLKEM768X25519_SHA256)
static void torture_algorithms_ecdh_mlkem768x25519_sha256(void **state)
{
struct torture_state *s = *state;
@@ -766,9 +766,9 @@ static void torture_algorithms_ecdh_mlkem768x25519_sha256(void **state)
NULL /*cipher*/,
NULL /*hmac*/);
}
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768X25519_SHA256) */
#endif /* defined(OPENSSH_MLKEM768X25519_SHA256) */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
static void torture_algorithms_ecdh_mlkem768nistp256_sha256(void **state)
{
struct torture_state *s = *state;
@@ -782,9 +782,9 @@ static void torture_algorithms_ecdh_mlkem768nistp256_sha256(void **state)
NULL /*cipher*/,
NULL /*hmac*/);
}
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768NISTP256_SHA256) */
#endif /* defined(OPENSSH_MLKEM768NISTP256_SHA256) */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
static void torture_algorithms_ecdh_mlkem1024nistp384_sha384(void **state)
{
struct torture_state *s = *state;
@@ -798,7 +798,7 @@ static void torture_algorithms_ecdh_mlkem1024nistp384_sha384(void **state)
NULL /*cipher*/,
NULL /*hmac*/);
}
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
#endif /* HAVE_MLKEM1024 && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
static void torture_algorithms_dh_group1(void **state) {
struct torture_state *s = *state;
@@ -1077,21 +1077,21 @@ int torture_run_tests(void) {
session_setup,
session_teardown),
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
#if defined(OPENSSH_MLKEM768X25519_SHA256)
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem768x25519_sha256,
session_setup,
session_teardown),
#endif /* HAVE_MLKEM && defined(OPENSSH_MLKEM768X25519_SHA256) */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
#endif /* defined(OPENSSH_MLKEM768X25519_SHA256) */
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem768nistp256_sha256,
session_setup,
session_teardown),
#endif /* defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256) */
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
#endif /* defined(OPENSSH_MLKEM768NISTP256_SHA256) */
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_mlkem1024nistp384_sha384,
session_setup,
session_teardown),
#endif /* defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
#endif /* defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384) */
#if defined(HAVE_ECC)
cmocka_unit_test_setup_teardown(torture_algorithms_ecdh_sha2_nistp256,
session_setup,

View File

@@ -18,6 +18,10 @@
#include <sys/types.h>
#include <unistd.h> /* usleep */
#ifndef UNIX_PATH_MAX
#define UNIX_PATH_MAX 108
#endif
/* struct to store the state of the test */
struct agent_callback_state {
int called;
@@ -137,6 +141,19 @@ static void torture_auth_agent_forwarding(void **state)
int read_count = 0;
bool agent_available = false;
bool agent_not_available_found = false;
size_t exp_socket_len;
/* The forwarded agent socket is created under the home directory, which
* might easily extend the maximum unix domain socket path length.
* If we see this, just skip the test as it will not work */
exp_socket_len = strlen(BINARYDIR) +
strlen("/home/bob/.ssh/agent.1234567890.sshd.XXXXXXXXXX");
if (exp_socket_len > UNIX_PATH_MAX) {
SSH_LOG(SSH_LOG_WARNING,
"The working directory is too long for agent forwarding to work"
": Skipping the test");
skip();
}
assert_non_null(s);
session = s->ssh.ssh.session;
@@ -205,6 +222,7 @@ static void torture_auth_agent_forwarding(void **state)
if (nbytes > 0) {
buffer[nbytes] = 0;
ssh_log_hexdump("Read bytes:", (unsigned char *)buffer, nbytes);
/* Process the command output to check for three key conditions:
* 1. If SSH_AUTH_SOCK is properly set (meaning agent forwarding

View File

@@ -35,10 +35,11 @@
/* agent_is_running */
#include "agent.c"
#define LIBSSH_RSA_TESTKEY "id_pkcs11_rsa"
#define LIBSSH_RSA_TESTKEY "id_pkcs11_rsa"
#define LIBSSH_ECDSA_256_TESTKEY "id_pkcs11_ecdsa_256"
#define LIBSSH_ECDSA_384_TESTKEY "id_pkcs11_ecdsa_384"
#define LIBSSH_ECDSA_521_TESTKEY "id_pkcs11_ecdsa_521"
#define LIBSSH_ED25519_TESTKEY "id_pkcs11_ed25519"
const char template[] = "/tmp/temp_dir_XXXXXX";
@@ -58,17 +59,22 @@ static int setup_tokens(void **state, const char *type, const char *obj_name)
cwd = test_state->temp_dir;
assert_non_null(cwd);
snprintf(priv_filename, sizeof(priv_filename), "%s%s", test_state->keys_dir, type);
snprintf(priv_filename,
sizeof(priv_filename),
"%s%s",
test_state->keys_dir,
type);
torture_setup_tokens(cwd, priv_filename, obj_name, "1");
return 0;
}
static int session_setup(void **state)
{
int verbosity = torture_libssh_verbosity();
struct torture_state *s = *state;
struct passwd *pwd;
struct passwd *pwd = NULL;
bool b = false;
int rc;
@@ -110,7 +116,7 @@ static int setup_pkcs11(void **state)
struct pki_st *test_state = NULL;
int rc;
char keys_dir[1024] = {0};
char *temp_dir;
char *temp_dir = NULL;
test_state = malloc(sizeof(struct pki_st));
assert_non_null(test_state);
@@ -137,6 +143,9 @@ static int setup_pkcs11(void **state)
setup_tokens(state, LIBSSH_ECDSA_256_TESTKEY, "ecdsa256");
setup_tokens(state, LIBSSH_ECDSA_384_TESTKEY, "ecdsa384");
setup_tokens(state, LIBSSH_ECDSA_521_TESTKEY, "ecdsa521");
if (!ssh_fips_mode()) {
setup_tokens(state, LIBSSH_ED25519_TESTKEY, "ed25519");
}
return 0;
}
@@ -150,8 +159,8 @@ static int sshd_setup(void **state)
return 0;
}
static int sshd_teardown(void **state) {
static int sshd_teardown(void **state)
{
struct torture_state *s = *state;
struct pki_st *test_state = s->private_data;
int rc;
@@ -176,7 +185,9 @@ static int sshd_teardown(void **state) {
return 0;
}
static void torture_auth_autopubkey(void **state, const char *obj_name, const char *pin) {
static void
torture_auth_autopubkey(void **state, const char *obj_name, const char *pin)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
@@ -186,8 +197,12 @@ static void torture_auth_autopubkey(void **state, const char *obj_name, const ch
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_CHARLIE);
assert_int_equal(rc, SSH_OK);
snprintf(priv_uri, sizeof(priv_uri), "pkcs11:token=%s;object=%s;type=private?pin-value=%s",
obj_name, obj_name, pin);
snprintf(priv_uri,
sizeof(priv_uri),
"pkcs11:token=%s;object=%s;type=private?pin-value=%s",
obj_name,
obj_name,
pin);
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, priv_uri);
assert_int_equal(rc, SSH_OK);
@@ -207,23 +222,40 @@ static void torture_auth_autopubkey(void **state, const char *obj_name, const ch
assert_int_equal(rc, SSH_AUTH_SUCCESS);
}
static void torture_auth_autopubkey_rsa(void **state) {
static void torture_auth_autopubkey_rsa(void **state)
{
torture_auth_autopubkey(state, "rsa", "1234");
}
static void torture_auth_autopubkey_ecdsa_key_256(void **state) {
static void torture_auth_autopubkey_ecdsa_key_256(void **state)
{
torture_auth_autopubkey(state, "ecdsa256", "1234");
}
static void torture_auth_autopubkey_ecdsa_key_384(void **state) {
static void torture_auth_autopubkey_ecdsa_key_384(void **state)
{
torture_auth_autopubkey(state, "ecdsa384", "1234");
}
static void torture_auth_autopubkey_ecdsa_key_521(void **state) {
static void torture_auth_autopubkey_ecdsa_key_521(void **state)
{
torture_auth_autopubkey(state, "ecdsa521", "1234");
}
int torture_run_tests(void) {
#ifdef WITH_PKCS11_PROVIDER
static void torture_auth_autopubkey_ed25519(void **state)
{
/* The Ed25519 keys are not supported in FIPS mode */
if (ssh_fips_mode()) {
skip();
}
torture_auth_autopubkey(state, "ed25519", "1234");
}
#endif /* WITH_PKCS11_PROVIDER */
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_auth_autopubkey_rsa,
@@ -238,6 +270,11 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_auth_autopubkey_ecdsa_key_521,
session_setup,
session_teardown),
#ifdef WITH_PKCS11_PROVIDER
cmocka_unit_test_setup_teardown(torture_auth_autopubkey_ed25519,
session_setup,
session_teardown),
#endif /* WITH_PKCS11_PROVIDER */
};
/* Do not use system openssl.cnf for the pkcs11 uri tests.
@@ -246,7 +283,7 @@ int torture_run_tests(void) {
* tokens, causing unexpected failures.
* Make sure this comes before ssh_init(), which initializes OpenSSL!
*/
setenv("OPENSSL_CONF", "/dev/null", 1);
setenv("OPENSSL_CONF", SOURCEDIR "/tests/etc/openssl.cnf", 1);
ssh_init();
torture_filter_tests(tests);

View File

@@ -20,6 +20,7 @@
*/
#include "config.h"
#include "torture_cmocka.h"
#define LIBSSH_STATIC
@@ -143,6 +144,48 @@ static void torture_connect_ipv6(void **state) {
assert_ssh_return_code(session, rc);
}
static void torture_connect_addrfamily(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
struct aftest {
enum ssh_address_family_options_e family;
char const *host;
int return_code;
};
static struct aftest aftests[] = {
{SSH_ADDRESS_FAMILY_ANY, "afboth", SSH_OK},
{SSH_ADDRESS_FAMILY_INET, "afboth", SSH_OK},
{SSH_ADDRESS_FAMILY_INET6, "afboth", SSH_OK},
{SSH_ADDRESS_FAMILY_ANY, "afinet", SSH_OK},
{SSH_ADDRESS_FAMILY_INET, "afinet", SSH_OK},
{SSH_ADDRESS_FAMILY_INET6, "afinet", SSH_ERROR},
{SSH_ADDRESS_FAMILY_ANY, "afinet6", SSH_OK},
{SSH_ADDRESS_FAMILY_INET, "afinet6", SSH_ERROR},
{SSH_ADDRESS_FAMILY_INET6, "afinet6", SSH_OK},
};
int aftest_count = sizeof(aftests) / sizeof(aftests[0]);
for (int i = 0; i < aftest_count; ++i) {
struct aftest const *t = &aftests[i];
rc = ssh_options_set(session, SSH_OPTIONS_ADDRESS_FAMILY, &t->family);
assert_ssh_return_code(session, rc);
rc = ssh_options_set(session, SSH_OPTIONS_HOST, t->host);
assert_ssh_return_code(session, rc);
do {
rc = ssh_connect(session);
} while (rc == SSH_AGAIN);
assert_ssh_return_code_equal(session, rc, t->return_code);
ssh_disconnect(session);
}
}
#if 0 /* This does not work with socket_wrapper */
static void torture_connect_timeout(void **state) {
struct torture_state *s = *state;
@@ -329,6 +372,9 @@ int torture_run_tests(void) {
cmocka_unit_test_setup_teardown(torture_connect_ipv6,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_connect_addrfamily,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_connect_double,
session_setup,
session_teardown),

View File

@@ -0,0 +1,295 @@
#include "config.h"
#define LIBSSH_STATIC
#include "libssh/crypto.h"
#include "torture.h"
#include <libssh/libssh.h>
#include <errno.h>
#include <fcntl.h>
#include <gssapi.h>
#include <pwd.h>
static int sshd_setup(void **state)
{
torture_setup_sshd_server(state, false);
torture_update_sshd_config(state,
"GSSAPIAuthentication yes\n"
"GSSAPIKeyExchange yes\n");
return 0;
}
static int sshd_teardown(void **state)
{
assert_non_null(state);
torture_teardown_sshd_server(state);
return 0;
}
static int session_setup(void **state)
{
struct torture_state *s = *state;
int verbosity = torture_libssh_verbosity();
struct passwd *pwd = NULL;
int rc;
bool b = false;
pwd = getpwnam("bob");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = ssh_new();
assert_non_null(s->ssh.session);
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
/* Make sure no other configuration options from system will get used */
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
assert_ssh_return_code(s->ssh.session, rc);
return 0;
}
static int session_teardown(void **state)
{
struct torture_state *s = *state;
assert_non_null(s);
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
return 0;
}
static void torture_gssapi_key_exchange(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
torture_teardown_kdc_server(state);
}
static void torture_gssapi_key_exchange_no_tgt(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Don't run kinit */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
/* No TGT */
"");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
assert_false(ssh_kex_is_gss(session->current_crypto));
torture_teardown_kdc_server(state);
}
static void torture_gssapi_key_exchange_alg(void **state,
const char *kex_string,
enum ssh_key_exchange_e kex_type)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_options_set(s->ssh.session,
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
kex_string);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
assert_int_equal(session->current_crypto->kex_type, kex_type);
torture_teardown_kdc_server(state);
}
static void torture_gssapi_key_exchange_gss_group14_sha256(void **state)
{
torture_gssapi_key_exchange_alg(state,
"gss-group14-sha256-",
SSH_GSS_KEX_DH_GROUP14_SHA256);
}
static void torture_gssapi_key_exchange_gss_group16_sha512(void **state)
{
torture_gssapi_key_exchange_alg(state,
"gss-group16-sha512-",
SSH_GSS_KEX_DH_GROUP16_SHA512);
}
static void torture_gssapi_key_exchange_gss_nistp256_sha256(void **state)
{
torture_gssapi_key_exchange_alg(state,
"gss-nistp256-sha256-",
SSH_GSS_KEX_ECDH_NISTP256_SHA256);
}
static void torture_gssapi_key_exchange_gss_curve25519_sha256(void **state)
{
if (ssh_fips_mode()) {
skip();
}
torture_gssapi_key_exchange_alg(state,
"gss-curve25519-sha256-",
SSH_GSS_KEX_CURVE25519_SHA256);
}
static void torture_gssapi_key_exchange_auth(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
rc = ssh_userauth_gssapi_keyex(session);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
torture_teardown_kdc_server(state);
}
static void torture_gssapi_key_exchange_no_auth(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool f = false;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
/* Don't do GSSAPI Key Exchange */
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &f);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(session, rc);
/* Still try to do "gssapi-keyex" auth */
rc = ssh_userauth_gssapi_keyex(session);
assert_int_equal(rc, SSH_AUTH_ERROR);
torture_teardown_kdc_server(state);
}
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_no_tgt,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(
torture_gssapi_key_exchange_gss_group14_sha256,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(
torture_gssapi_key_exchange_gss_group16_sha512,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(
torture_gssapi_key_exchange_gss_nistp256_sha256,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(
torture_gssapi_key_exchange_gss_curve25519_sha256,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_auth,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_no_auth,
session_setup,
session_teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
ssh_finalize();
return rc;
}

View File

@@ -0,0 +1,169 @@
#include "config.h"
#define LIBSSH_STATIC
#include "torture.h"
#include <libssh/libssh.h>
#include <errno.h>
#include <fcntl.h>
#include <gssapi.h>
#include <pwd.h>
static int sshd_setup(void **state)
{
struct torture_state *s = NULL;
torture_setup_sshd_server(state, false);
s = *state;
s->disable_hostkeys = true;
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
torture_update_sshd_config(state,
"GSSAPIAuthentication yes\n"
"GSSAPIKeyExchange yes\n");
torture_teardown_kdc_server(state);
return 0;
}
static int sshd_teardown(void **state)
{
assert_non_null(state);
torture_teardown_sshd_server(state);
return 0;
}
static int session_setup(void **state)
{
struct torture_state *s = *state;
int verbosity = torture_libssh_verbosity();
struct passwd *pwd = NULL;
int rc;
bool b = false;
pwd = getpwnam("bob");
assert_non_null(pwd);
rc = setuid(pwd->pw_uid);
assert_return_code(rc, errno);
s->ssh.session = ssh_new();
assert_non_null(s->ssh.session);
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
/* Make sure no other configuration options from system will get used */
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROCESS_CONFIG, &b);
assert_ssh_return_code(s->ssh.session, rc);
return 0;
}
static int session_teardown(void **state)
{
struct torture_state *s = *state;
assert_non_null(s);
ssh_disconnect(s->ssh.session);
ssh_free(s->ssh.session);
return 0;
}
static void torture_gssapi_key_exchange_null(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(s->ssh.session, rc);
assert_string_equal(session->current_crypto->kex_methods[SSH_HOSTKEYS],
"null");
torture_teardown_kdc_server(state);
}
static void torture_gssapi_key_exchange_null_pubkey_auth(void **state)
{
struct torture_state *s = *state;
ssh_session session = s->ssh.session;
int rc;
bool t = true;
/* Valid */
torture_setup_kdc_server(
state,
"kadmin.local addprinc -randkey host/server.libssh.site \n"
"kadmin.local ktadd -k $(dirname $0)/d/ssh.keytab host/server.libssh.site \n"
"kadmin.local addprinc -pw bar alice \n"
"kadmin.local list_principals",
"echo bar | kinit alice");
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_GSSAPI_KEY_EXCHANGE, &t);
assert_ssh_return_code(s->ssh.session, rc);
rc = ssh_connect(session);
assert_ssh_return_code(s->ssh.session, rc);
assert_string_equal(session->current_crypto->kex_methods[SSH_HOSTKEYS],
"null");
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
assert_int_equal(rc, SSH_AUTH_SUCCESS);
torture_teardown_kdc_server(state);
}
int torture_run_tests(void)
{
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_null,
session_setup,
session_teardown),
cmocka_unit_test_setup_teardown(torture_gssapi_key_exchange_null_pubkey_auth,
session_setup,
session_teardown),
};
ssh_init();
torture_filter_tests(tests);
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
ssh_finalize();
return rc;
}

View File

@@ -5,3 +5,8 @@
123.0.0.11 testing
fd00::5357:5f0a testing
127.0.0.10 afboth
fd00::5357:5f0a afboth
127.0.0.10 afinet
fd00::5357:5f0a afinet6

11
tests/etc/openssl.cnf Normal file
View File

@@ -0,0 +1,11 @@
openssl_conf = openssl_init
[openssl_init]
providers = provider_sect
[provider_sect]
default = default_sect
pkcs11 = pkcs11_sect
[default_sect]
activate = 1
[pkcs11_sect]
activate = 1
pkcs11-module-assume-fips = true

View File

@@ -88,8 +88,19 @@ target_link_libraries(sntrup761_override
set(SNTRUP761_OVERRIDE_LIBRARY
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}sntrup761_override${CMAKE_SHARED_LIBRARY_SUFFIX})
# mlkem768_override
add_library(mlkem768_override SHARED
mlkem768_override.c
${libssh_SOURCE_DIR}/src/external/libcrux_mlkem768_sha3.c
${override_src}
)
target_link_libraries(mlkem768_override
PRIVATE ${override_libs})
set(MLKEM768_OVERRIDE_LIBRARY
${libssh_BINARY_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}mlkem768_override${CMAKE_SHARED_LIBRARY_SUFFIX})
set(OVERRIDE_LIBRARIES
${CHACHA20_OVERRIDE_LIBRARY}:${POLY1305_OVERRIDE_LIBRARY}:${ED25519_OVERRIDE_LIBRARY}:${CURVE25519_OVERRIDE_LIBRARY}:${SNTRUP761_OVERRIDE_LIBRARY}
${CHACHA20_OVERRIDE_LIBRARY}:${POLY1305_OVERRIDE_LIBRARY}:${ED25519_OVERRIDE_LIBRARY}:${CURVE25519_OVERRIDE_LIBRARY}:${SNTRUP761_OVERRIDE_LIBRARY}:${MLKEM768_OVERRIDE_LIBRARY}
)
if (WITH_MBEDTLS)
@@ -107,6 +118,7 @@ if (WITH_MBEDTLS)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=1")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
elseif (WITH_GCRYPT)
if (HAVE_GCRYPT_CHACHA_POLY)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
@@ -122,6 +134,11 @@ elseif (WITH_GCRYPT)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=1")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=0")
if(HAVE_GCRYPT_MLKEM)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=0")
else()
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
endif()
else ()
if (HAVE_OPENSSL_EVP_CHACHA20)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CHACHAPOLY=0")
@@ -131,6 +148,11 @@ else ()
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_CURVE25519=0")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_ED25519=0")
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_SNTRUP761=1")
if(HAVE_OPENSSL_MLKEM)
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=0")
else()
list(APPEND OVERRIDE_RESULTS "-DSHOULD_CALL_INTERNAL_MLKEM=1")
endif()
endif ()
if (NOT OSX)
@@ -162,6 +184,7 @@ foreach(_OVERRIDE_TEST ${LIBSSH_OVERRIDE_TESTS})
ed25519_override
curve25519_override
sntrup761_override
mlkem768_override
)
if (OSX)

View File

@@ -0,0 +1,83 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2021 - 2025 Red Hat, Inc.
*
* Authors: Anderson Toshiyuki Sasaki
* Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 2.1 of the License, or (at your option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the SSH Library; see the file COPYING. If not,
* see <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <libssh/priv.h>
#include "libssh/mlkem_native.h"
#include "mlkem768_override.h"
static bool internal_function_called = false;
libcrux_ml_kem_mlkem768_MlKem768KeyPair
__wrap_libcrux_ml_kem_mlkem768_portable_generate_key_pair(
uint8_t randomness[64U])
{
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
internal_function_called = true;
return libcrux_ml_kem_mlkem768_portable_generate_key_pair(randomness);
}
bool __wrap_libcrux_ml_kem_mlkem768_portable_validate_public_key(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key)
{
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
internal_function_called = true;
return libcrux_ml_kem_mlkem768_portable_validate_public_key(public_key);
}
tuple_c2 __wrap_libcrux_ml_kem_mlkem768_portable_encapsulate(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
uint8_t randomness[32U])
{
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
internal_function_called = true;
return libcrux_ml_kem_mlkem768_portable_encapsulate(public_key, randomness);
}
void __wrap_libcrux_ml_kem_mlkem768_portable_decapsulate(
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
uint8_t ret[32U])
{
fprintf(stderr, "%s: Internal implementation was called\n", __func__);
internal_function_called = true;
return libcrux_ml_kem_mlkem768_portable_decapsulate(private_key,
ciphertext,
ret);
}
bool internal_mlkem768_function_called(void)
{
return internal_function_called;
}
void reset_mlkem768_function_called(void)
{
internal_function_called = false;
}

View File

@@ -0,0 +1,43 @@
/*
* This file is part of the SSH Library
*
* Copyright (c) 2021 - 2025 Red Hat, Inc.
*
* Authors: Anderson Toshiyuki Sasaki
* Jakub Jelen <jjelen@redhat.com>
*
* The SSH Library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 2.1 of the License, or (at your option) any later version.
*
* The SSH Library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the SSH Library; see the file COPYING. If not,
* see <https://www.gnu.org/licenses/>.
*/
#include "libssh/mlkem_native.h"
libcrux_ml_kem_mlkem768_MlKem768KeyPair
__wrap_libcrux_ml_kem_mlkem768_portable_generate_key_pair(
uint8_t randomness[64U]);
bool __wrap_libcrux_ml_kem_mlkem768_portable_validate_public_key(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key);
tuple_c2 __wrap_libcrux_ml_kem_mlkem768_portable_encapsulate(
libcrux_ml_kem_types_MlKemPublicKey_30 *public_key,
uint8_t randomness[32U]);
void __wrap_libcrux_ml_kem_mlkem768_portable_decapsulate(
libcrux_ml_kem_types_MlKemPrivateKey_d9 *private_key,
libcrux_ml_kem_mlkem768_MlKem768Ciphertext *ciphertext,
uint8_t ret[32U]);
bool internal_mlkem768_function_called(void);
void reset_mlkem768_function_called(void);

View File

@@ -32,6 +32,7 @@
#include "chacha20_override.h"
#include "curve25519_override.h"
#include "ed25519_override.h"
#include "mlkem768_override.h"
#include "poly1305_override.h"
#include "sntrup761_override.h"
@@ -119,6 +120,8 @@ static int session_setup(void **state)
reset_poly1305_function_called();
reset_curve25519_function_called();
reset_ed25519_function_called();
reset_sntrup761_function_called();
reset_mlkem768_function_called();
return 0;
}
@@ -324,6 +327,64 @@ torture_override_ecdh_sntrup761x25519_sha512(void **state)
}
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
#ifdef OPENSSH_MLKEM768X25519_SHA256
static void torture_override_mlkem768x25519_sha256(void **state)
{
struct torture_state *s = *state;
bool internal_curve25519_called;
bool internal_mlkem768_called;
if (ssh_fips_mode()) {
skip();
}
test_algorithm(s->ssh.session,
"mlkem768x25519-sha256",
NULL, /* cipher */
NULL /* hostkey */);
internal_curve25519_called = internal_curve25519_function_called();
internal_mlkem768_called = internal_mlkem768_function_called();
#if SHOULD_CALL_INTERNAL_MLKEM
assert_true(internal_mlkem768_called);
#else
assert_false(internal_mlkem768_called);
#endif
#if SHOULD_CALL_INTERNAL_CURVE25519
assert_true(internal_curve25519_called);
#else
assert_false(internal_curve25519_called);
#endif
}
#endif /* OPENSSH_MLKEM768X25519_SHA256 */
#ifdef OPENSSH_MLKEM768NISTP256_SHA256
static void torture_override_mlkem768nistp256_sha256(void **state)
{
struct torture_state *s = *state;
bool internal_mlkem768_called;
if (ssh_fips_mode()) {
skip();
}
test_algorithm(s->ssh.session,
"mlkem768nistp256-sha256",
NULL, /* cipher */
NULL /* hostkey */);
internal_mlkem768_called = internal_mlkem768_function_called();
#if SHOULD_CALL_INTERNAL_MLKEM
assert_true(internal_mlkem768_called);
#else
assert_false(internal_mlkem768_called);
#endif
}
#endif /* OPENSSH_MLKEM768NISTP256_SHA256 */
#ifdef OPENSSH_SSH_ED25519
static void torture_override_ed25519(void **state)
{
@@ -378,6 +439,16 @@ int torture_run_tests(void)
session_setup,
session_teardown),
#endif /* OPENSSH_SNTRUP761X25519_SHA512 */
#ifdef OPENSSH_MLKEM768X25519_SHA256
cmocka_unit_test_setup_teardown(torture_override_mlkem768x25519_sha256,
session_setup,
session_teardown),
#endif /* OPENSSH_MLKEM768X25519_SHA256 */
#ifdef OPENSSH_MLKEM768NISTP256_SHA256
cmocka_unit_test_setup_teardown(torture_override_mlkem768nistp256_sha256,
session_setup,
session_teardown),
#endif /* OPENSSH_MLKEM768NISTP256_SHA256 */
#ifdef OPENSSH_SSH_ED25519
cmocka_unit_test_setup_teardown(torture_override_ed25519,
session_setup,

View File

@@ -1,10 +1,12 @@
#define _GNU_SOURCE
#include <dlfcn.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
/*******************************************************************************
@@ -211,3 +213,43 @@ statx(int dirfd,
return rc;
}
static int is_file_blocked(const char *pathname)
{
if (pathname == NULL) {
return 0;
}
static const char *blocked_files[] = {
/* Block for torture_gssapi_server_key_exchange_null */
"/etc/ssh/ssh_host_ecdsa_key",
"/etc/ssh/ssh_host_rsa_key",
"/etc/ssh/ssh_host_ed25519_key",
};
for (size_t i = 0; i < sizeof(blocked_files) / sizeof(blocked_files[0]);
i++) {
if (strcmp(pathname, blocked_files[i]) == 0) {
errno = ENOENT; /* No such file or directory */
return 1;
}
}
return 0;
}
#define WRAP_FOPEN(func_name) \
FILE *func_name(const char *pathname, const char *mode) \
{ \
typedef FILE *(*orig_func_t)(const char *pathname, const char *mode); \
static orig_func_t orig_func = NULL; \
if (orig_func == NULL) { \
orig_func = (orig_func_t)dlsym(RTLD_NEXT, #func_name); \
} \
if (is_file_blocked(pathname)) { \
return NULL; \
} \
return orig_func(pathname, mode); \
}
WRAP_FOPEN(fopen)
WRAP_FOPEN(fopen64)

View File

@@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIN1aDCZfQOPArIsxnS8uMipbQtjM/FD77+hpkyo9i95W

View File

@@ -24,10 +24,20 @@
"-o PubkeyAcceptedKeyTypes=" \
OPENSSH_KEYS
#ifdef HAVE_SK_DUMMY
#define SECURITY_KEY_PROVIDER \
"-oSecurityKeyProvider=\"" SK_DUMMY_LIBRARY_PATH "\" "
#else
#define SECURITY_KEY_PROVIDER ""
#endif
/* GlobalKnownHostsFile is just a place holder and won't actually set the hostkey */
#define OPENSSH_CMD_START(hostkey_algos) \
OPENSSH_BINARY " " \
"-o UserKnownHostsFile=/dev/null " \
"-o StrictHostKeyChecking=no " \
SECURITY_KEY_PROVIDER \
"-o GlobalKnownHostsFile=%s " \
"-F /dev/null " \
hostkey_algos " " \
OPENSSH_PKACCEPTED_TYPES " " \
@@ -64,9 +74,11 @@
#define DROPBEAR_BINARY DROPBEAR_EXECUTABLE
#define DROPBEAR_KEYGEN "dropbearkey"
/* HostKeyAlias is just a place holder and won't actually set the hostkey */
#define DROPBEAR_CMD_START \
DROPBEAR_BINARY " " \
"-y -y " \
"-o HostKeyAlias=%s " \
"-i " CLIENT_ID_FILE " " \
"1> %s.out " \
"2> %s.err "
@@ -87,4 +99,22 @@
#define DROPBEAR_MAC_CMD(macs) \
DROPBEAR_CMD_START "-m " macs " " DROPBEAR_CMD_END
/* PuTTY */
#define PUTTY_BINARY PUTTY_EXECUTABLE
#define PUTTY_KEYGEN PUTTYGEN_EXECUTABLE
#define PUTTY_CMD_START \
PUTTY_BINARY " " \
"-batch -ssh -P 1234 " \
"-i " CLIENT_ID_FILE " " \
"-hostkey $(" OPENSSH_KEYGEN \
" -l -f %s.pub -E md5 | awk '{print $2}' | cut -d: -f2-) " \
"1> %s.out 2> %s.err "
#define PUTTY_CMD_END " localhost ls"
#define PUTTY_CMD \
PUTTY_CMD_START PUTTY_CMD_END
#endif /* __PKD_CLIENT_H__ */

View File

@@ -215,81 +215,81 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
*/
#define PKDTESTS_DEFAULT_FIPS(f, client, cmd) \
f(client, rsa_default, cmd, setup_rsa, teardown) \
f(client, ecdsa_256_default, cmd, setup_ecdsa_256, teardown) \
f(client, ecdsa_384_default, cmd, setup_ecdsa_384, teardown) \
f(client, ecdsa_521_default, cmd, setup_ecdsa_521, teardown)
f(client, rsa_default, cmd, setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_default, cmd, setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_default, cmd, setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_default, cmd, setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#define PKDTESTS_DEFAULT(f, client, cmd) \
/* Default passes by server key type. */ \
PKDTESTS_DEFAULT_FIPS(f, client, cmd) \
f(client, ed25519_default, cmd, setup_ed25519, teardown)
f(client, ed25519_default, cmd, setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#define GEX_SHA256 "diffie-hellman-group-exchange-sha256"
#define GEX_SHA1 "diffie-hellman-group-exchange-sha1"
#if defined(WITH_GEX)
#define PKDTESTS_KEX_FIPS(f, client, kexcmd) \
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown) \
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown) \
f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown) \
f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown) \
f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown) \
f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown) \
f(client, rsa_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_rsa, teardown) \
f(client, ecdsa_256_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_521, teardown)
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, rsa_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#else /* !defined(WITH_GEX) */
#define PKDTESTS_KEX_FIPS(f, client, kexcmd) \
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown) \
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown) \
f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown) \
f(client, rsa_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_rsa, teardown) \
f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown) \
f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown) \
f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown)
f(client, rsa_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group14_sha256,kexcmd("diffie-hellman-group14-sha256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group16_sha512,kexcmd("diffie-hellman-group16-sha512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group18_sha512,kexcmd("diffie-hellman-group18-sha512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#endif
#ifdef OPENSSH_SNTRUP761X25519_SHA512_OPENSSH_COM
#define SNTRUP_OPENSSH_NAME "sntrup761x25519-sha512@openssh.com"
#define PKDTESTS_KEX_SNTRUP761_OPENSSH(f, client, kexcmd) \
f(client, rsa_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_rsa, teardown) \
f(client, ecdsa_256_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_521, teardown) \
f(client, ed25519_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ed25519, teardown)
f(client, rsa_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ed25519_sntrup761x25519_sha512_openssh_com, kexcmd(SNTRUP_OPENSSH_NAME), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#else
#define PKDTESTS_KEX_SNTRUP761_OPENSSH(f, client, kexcmd)
#endif
@@ -297,44 +297,44 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
#ifdef OPENSSH_SNTRUP761X25519_SHA512
#define SNTRUP_NAME "sntrup761x25519-sha512"
#define PKDTESTS_KEX_SNTRUP761(f, client, kexcmd) \
f(client, rsa_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_rsa, teardown) \
f(client, ecdsa_256_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_521, teardown) \
f(client, ed25519_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ed25519, teardown)
f(client, rsa_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ed25519_sntrup761x25519_sha512, kexcmd(SNTRUP_NAME), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#else
#define PKDTESTS_KEX_SNTRUP761(f, client, kexcmd)
#endif
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768X25519_SHA256)
#if defined(OPENSSH_MLKEM768X25519_SHA256)
#define PKDTESTS_KEX_MLKEM768X25519(f, client, kexcmd) \
f(client, rsa_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_rsa, teardown) \
f(client, ecdsa_256_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_521, teardown) \
f(client, ed25519_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ed25519, teardown)
f(client, rsa_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ed25519_mlkem768x25519_sha256, kexcmd("mlkem768x25519-sha256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#else
#define PKDTESTS_KEX_MLKEM768X25519(f, client, kexcmd)
#endif
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM768NISTP256_SHA256)
#if defined(OPENSSH_MLKEM768NISTP256_SHA256)
#define PKDTESTS_KEX_MLKEM768NISTP256(f, client, kexcmd) \
f(client, rsa_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_rsa, teardown) \
f(client, ecdsa_256_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_521, teardown) \
f(client, ed25519_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ed25519, teardown)
f(client, rsa_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ed25519_mlkem768nistp256_sha256, kexcmd("mlkem768nistp256-sha256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#else
#define PKDTESTS_KEX_MLKEM768NISTP256(f, client, kexcmd)
#endif
#if defined(HAVE_MLKEM) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
#if defined(HAVE_MLKEM1024) && defined(OPENSSH_MLKEM1024NISTP384_SHA384)
#define PKDTESTS_KEX_MLKEM1024NISTP384(f, client, kexcmd) \
f(client, rsa_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_rsa, teardown) \
f(client, ecdsa_256_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_521, teardown) \
f(client, ed25519_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ed25519, teardown)
f(client, rsa_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ed25519_mlkem1024nistp384_sha384, kexcmd("mlkem1024nistp384-sha384"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#else
#define PKDTESTS_KEX_MLKEM1024NISTP384(f, client, kexcmd)
#endif
@@ -346,146 +346,146 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
PKDTESTS_KEX_MLKEM768X25519(f, client, kexcmd) \
PKDTESTS_KEX_MLKEM768NISTP256(f, client, kexcmd) \
PKDTESTS_KEX_MLKEM1024NISTP384(f, client, kexcmd) \
f(client, rsa_curve25519_sha256, kexcmd("curve25519-sha256"), setup_rsa, teardown) \
f(client, rsa_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_rsa, teardown) \
f(client, rsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_rsa, teardown) \
f(client, rsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_rsa, teardown) \
f(client, ecdsa_256_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_521, teardown)
f(client, rsa_curve25519_sha256, kexcmd("curve25519-sha256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group14_sha1, kexcmd("diffie-hellman-group14-sha1"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#if defined(WITH_GEX)
/* GEX_SHA256 is included in PKDTESTS_KEX_FIPS if available */
#define PKDTESTS_KEX(f, client, kexcmd) \
/* Kex algorithms. */ \
PKDTESTS_KEX_COMMON(f, client, kexcmd) \
f(client, rsa_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_rsa, teardown) \
f(client, ecdsa_256_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_521, teardown)
f(client, rsa_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#else
#define PKDTESTS_KEX(f, client, kexcmd) \
/* Kex algorithms. */ \
f(client, ed25519_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ed25519, teardown) \
f(client, ed25519_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ed25519, teardown) \
f(client, ed25519_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ed25519, teardown) \
f(client, ed25519_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ed25519, teardown) \
f(client, ed25519_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ed25519, teardown) \
f(client, ed25519_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_ed25519, teardown) \
f(client, ed25519_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_ed25519, teardown) \
f(client, ed25519_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_ed25519, teardown) \
f(client, ed25519_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ed25519, teardown) \
f(client, ed25519_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ed25519, teardown) \
f(client, ed25519_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ed25519, teardown)
f(client, ed25519_curve25519_sha256, kexcmd("curve25519-sha256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_curve25519_sha256_libssh_org, kexcmd("curve25519-sha256@libssh.org"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_ecdh_sha2_nistp256, kexcmd("ecdh-sha2-nistp256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_ecdh_sha2_nistp384, kexcmd("ecdh-sha2-nistp384"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_ecdh_sha2_nistp521, kexcmd("ecdh-sha2-nistp521"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_diffie_hellman_group14_sha256, kexcmd("diffie-hellman-group14-sha256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_diffie_hellman_group16_sha512, kexcmd("diffie-hellman-group16-sha512"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_diffie_hellman_group18_sha512, kexcmd("diffie-hellman-group18-sha512"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_diffie_hellman_group1_sha1, kexcmd("diffie-hellman-group1-sha1"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_diffie_hellman_group_exchange_sha256, kexcmd(GEX_SHA256), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_diffie_hellman_group_exchange_sha1, kexcmd(GEX_SHA1), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#endif
#define PKDTESTS_CIPHER_COMMON(f, client, ciphercmd) \
f(client, rsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_rsa, teardown) \
f(client, rsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_rsa, teardown) \
f(client, ecdsa_256_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_521, teardown)
f(client, rsa_aes128_ctr, ciphercmd("aes128-ctr"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_aes256_ctr, ciphercmd("aes256-ctr"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_aes128_ctr, ciphercmd("aes128-ctr"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_aes256_ctr, ciphercmd("aes256-ctr"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#define PKDTESTS_CIPHER_FIPS(f, client, ciphercmd) \
PKDTESTS_CIPHER_COMMON(f, client, ciphercmd) \
f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown) \
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \
f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown)
f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#define CHACHA20 "chacha20-poly1305@openssh.com"
#define PKDTESTS_CIPHER_CHACHA(f, client, ciphercmd) \
f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown) \
f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown) \
f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown)
f(client, rsa_chacha20, ciphercmd(CHACHA20), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ed25519_chacha20, ciphercmd(CHACHA20), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ecdsa_256_chacha20, ciphercmd(CHACHA20), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_chacha20, ciphercmd(CHACHA20), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_chacha20, ciphercmd(CHACHA20), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#define PKDTESTS_CIPHER(f, client, ciphercmd) \
/* Ciphers. */ \
PKDTESTS_CIPHER_COMMON(f, client, ciphercmd) \
PKDTESTS_CIPHER_CHACHA(f, client, ciphercmd) \
f(client, ed25519_aes128_ctr, ciphercmd("aes128-ctr"), setup_ed25519, teardown) \
f(client, ed25519_aes256_ctr, ciphercmd("aes256-ctr"), setup_ed25519, teardown)
f(client, ed25519_aes128_ctr, ciphercmd("aes128-ctr"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_aes256_ctr, ciphercmd("aes256-ctr"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#define AES128_GCM "aes128-gcm@openssh.com"
#define AES256_GCM "aes256-gcm@openssh.com"
#define PKDTESTS_CIPHER_OPENSSHONLY_FIPS(f, client, ciphercmd) \
f(client, rsa_aes128_gcm, ciphercmd(AES128_GCM), setup_rsa, teardown) \
f(client, rsa_aes256_gcm, ciphercmd(AES256_GCM), setup_rsa, teardown) \
f(client, ecdsa_256_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_521, teardown)
f(client, rsa_aes128_gcm, ciphercmd(AES128_GCM), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_aes256_gcm, ciphercmd(AES256_GCM), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ecdsa_256_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_aes128_gcm, ciphercmd(AES128_GCM), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_aes256_gcm, ciphercmd(AES256_GCM), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#define PKDTESTS_CIPHER_OPENSSHONLY(f, client, ciphercmd) \
/* Ciphers. */ \
PKDTESTS_CIPHER_OPENSSHONLY_FIPS(f, client, ciphercmd) \
f(client, rsa_3des_cbc, ciphercmd("3des-cbc"), setup_rsa, teardown) \
f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown) \
f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown) \
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown) \
f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown) \
f(client, ed25519_3des_cbc, ciphercmd("3des-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes128_cbc, ciphercmd("aes128-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes256_cbc, ciphercmd("aes256-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes192_cbc, ciphercmd("aes192-cbc"), setup_ed25519, teardown) \
f(client, ed25519_aes192_ctr, ciphercmd("aes192-ctr"), setup_ed25519, teardown) \
f(client, ed25519_aes128_gcm, ciphercmd(AES128_GCM), setup_ed25519, teardown) \
f(client, ed25519_aes256_gcm, ciphercmd(AES256_GCM), setup_ed25519, teardown) \
f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown)
f(client, rsa_3des_cbc, ciphercmd("3des-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_aes128_cbc, ciphercmd("aes128-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_aes192_cbc, ciphercmd("aes192-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_aes256_cbc, ciphercmd("aes256-cbc"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_aes192_ctr, ciphercmd("aes192-ctr"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, ed25519_3des_cbc, ciphercmd("3des-cbc"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_aes128_cbc, ciphercmd("aes128-cbc"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_aes256_cbc, ciphercmd("aes256-cbc"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_aes192_cbc, ciphercmd("aes192-cbc"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_aes192_ctr, ciphercmd("aes192-ctr"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_aes128_gcm, ciphercmd(AES128_GCM), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_aes256_gcm, ciphercmd(AES256_GCM), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ecdsa_256_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_3des_cbc, ciphercmd("3des-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_aes128_cbc, ciphercmd("aes128-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_aes192_cbc, ciphercmd("aes192-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_aes256_cbc, ciphercmd("aes256-cbc"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_aes192_ctr, ciphercmd("aes192-ctr"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY)
#define PKDTESTS_MAC_FIPS_BASE(f, client, maccmd) \
f(client, ecdsa_256_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_521, teardown) \
f(client, rsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_rsa, teardown)
f(client, ecdsa_256_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, rsa_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY)
#define PKDTESTS_MAC_FIPS_SHA1(f, client, maccmd) \
f(client, ecdsa_256_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_521, teardown) \
f(client, rsa_hmac_sha1, maccmd("hmac-sha1"), setup_rsa, teardown)
f(client, ecdsa_256_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_hmac_sha1, maccmd("hmac-sha1"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, rsa_hmac_sha1, maccmd("hmac-sha1"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY)
#ifdef DROPBEAR_SUPPORTS_HMAC_SHA1
#define PKDTESTS_MAC_FIPS(f, client, maccmd) \
PKDTESTS_MAC_FIPS_BASE(f, client, maccmd) \
PKDTESTS_MAC_FIPS_SHA1(f, client, maccmd) \
f(client, ed25519_hmac_sha1, maccmd("hmac-sha1"), setup_ed25519, teardown)
f(client, ed25519_hmac_sha1, maccmd("hmac-sha1"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#define PKDTESTS_MAC_OPENSSHONLY_FIPS_SHA1(f, client, maccmd)
#else
#define PKDTESTS_MAC_FIPS(f, client, maccmd) \
@@ -496,41 +496,41 @@ static int torture_pkd_setup_ecdsa_521(void **state) {
#define PKDTESTS_MAC_OPENSSHONLY_FIPS(f, client, maccmd) \
PKDTESTS_MAC_OPENSSHONLY_FIPS_SHA1(f, client, maccmd) \
f(client, ecdsa_256_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_256, teardown) \
f(client, ecdsa_256_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_256, teardown) \
f(client, ecdsa_384_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_384, teardown) \
f(client, ecdsa_384_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_384, teardown) \
f(client, ecdsa_521_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_521, teardown) \
f(client, ecdsa_521_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_521, teardown) \
f(client, rsa_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_rsa, teardown) \
f(client, rsa_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_rsa, teardown) \
f(client, rsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_rsa, teardown) \
f(client, rsa_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_rsa, teardown)
f(client, ecdsa_256_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_256_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_256, teardown, LIBSSH_ECDSA_256_TESTKEY) \
f(client, ecdsa_384_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_384_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_384, teardown, LIBSSH_ECDSA_384_TESTKEY) \
f(client, ecdsa_521_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, ecdsa_521_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ecdsa_521, teardown, LIBSSH_ECDSA_521_TESTKEY) \
f(client, rsa_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY)
#define PKDTESTS_MAC(f, client, maccmd) \
/* MACs. */ \
PKDTESTS_MAC_FIPS(f, client, maccmd) \
f(client, ed25519_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ed25519, teardown)
f(client, ed25519_hmac_sha2_256, maccmd("hmac-sha2-256"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#define PKDTESTS_MAC_OPENSSHONLY(f, client, maccmd) \
PKDTESTS_MAC_OPENSSHONLY_FIPS(f, client, maccmd) \
f(client, ed25519_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ed25519, teardown) \
f(client, ed25519_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ed25519, teardown) \
f(client, ed25519_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ed25519, teardown) \
f(client, ed25519_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ed25519, teardown)
f(client, ed25519_hmac_sha1_etm, maccmd("hmac-sha1-etm@openssh.com"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_hmac_sha2_256_etm, maccmd("hmac-sha2-256-etm@openssh.com"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_hmac_sha2_512, maccmd("hmac-sha2-512"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY) \
f(client, ed25519_hmac_sha2_512_etm, maccmd("hmac-sha2-512-etm@openssh.com"), setup_ed25519, teardown, LIBSSH_ED25519_TESTKEY)
#define PKDTESTS_HOSTKEY_OPENSSHONLY_FIPS(f, client, hkcmd) \
f(client, rsa_sha2_256, hkcmd("rsa-sha2-256"), setup_rsa, teardown) \
f(client, rsa_sha2_512, hkcmd("rsa-sha2-512"), setup_rsa, teardown) \
f(client, rsa_sha2_256_512, hkcmd("rsa-sha2-256,rsa-sha2-512"), setup_rsa, teardown) \
f(client, rsa_sha2_512_256, hkcmd("rsa-sha2-512,rsa-sha2-256"), setup_rsa, teardown)
f(client, rsa_sha2_256, hkcmd("rsa-sha2-256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_sha2_512, hkcmd("rsa-sha2-512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_sha2_256_512, hkcmd("rsa-sha2-256,rsa-sha2-512"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY) \
f(client, rsa_sha2_512_256, hkcmd("rsa-sha2-512,rsa-sha2-256"), setup_rsa, teardown, LIBSSH_RSA_TESTKEY)
#define PKDTESTS_HOSTKEY_OPENSSHONLY(f, client, hkcmd) \
PKDTESTS_HOSTKEY_OPENSSHONLY_FIPS(f, client, hkcmd)
@@ -567,12 +567,12 @@ static void torture_pkd_runtest(const char *testname,
* defined here to result in distinct output when running the tests.
*/
#define emit_keytest(client, testname, sshcmd, setup, teardown) \
#define emit_keytest(client, testname, sshcmd, setup, teardown, hostkey) \
static void torture_pkd_## client ## _ ## testname(void **state) { \
const char *tname = "torture_pkd_" #client "_" #testname; \
char testcmd[2048] = { 0 }; \
(void) state; \
snprintf(&testcmd[0], sizeof(testcmd), sshcmd, tname, tname); \
snprintf(&testcmd[0], sizeof(testcmd), sshcmd, hostkey, tname, tname); \
torture_pkd_runtest(tname, testcmd); \
}
@@ -615,6 +615,28 @@ PKDTESTS_MAC(emit_keytest, openssh_ed, OPENSSH_MAC_CMD)
PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_ed, OPENSSH_MAC_CMD)
#undef CLIENT_ID_FILE
#ifdef HAVE_SK_DUMMY
#define CLIENT_ID_FILE OPENSSH_ECDSA_SK_TESTKEY
PKDTESTS_DEFAULT(emit_keytest, openssh_ec_sk, OPENSSH_CMD)
PKDTESTS_DEFAULT(emit_keytest, openssh_cert_ec_sk, OPENSSH_CERT_CMD)
PKDTESTS_KEX(emit_keytest, openssh_ec_sk, OPENSSH_KEX_CMD)
PKDTESTS_CIPHER(emit_keytest, openssh_ec_sk, OPENSSH_CIPHER_CMD)
PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_ec_sk, OPENSSH_CIPHER_CMD)
PKDTESTS_MAC(emit_keytest, openssh_ec_sk, OPENSSH_MAC_CMD)
PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_ec_sk, OPENSSH_MAC_CMD)
#undef CLIENT_ID_FILE
#define CLIENT_ID_FILE OPENSSH_ED25519_SK_TESTKEY
PKDTESTS_DEFAULT(emit_keytest, openssh_ed_sk, OPENSSH_CMD)
PKDTESTS_DEFAULT(emit_keytest, openssh_cert_ed_sk, OPENSSH_CERT_CMD)
PKDTESTS_KEX(emit_keytest, openssh_ed_sk, OPENSSH_KEX_CMD)
PKDTESTS_CIPHER(emit_keytest, openssh_ed_sk, OPENSSH_CIPHER_CMD)
PKDTESTS_CIPHER_OPENSSHONLY(emit_keytest, openssh_ed_sk, OPENSSH_CIPHER_CMD)
PKDTESTS_MAC(emit_keytest, openssh_ed_sk, OPENSSH_MAC_CMD)
PKDTESTS_MAC_OPENSSHONLY(emit_keytest, openssh_ed_sk, OPENSSH_MAC_CMD)
#undef CLIENT_ID_FILE
#endif /* HAVE_SK_DUMMY */
#define CLIENT_ID_FILE DROPBEAR_RSA_TESTKEY
PKDTESTS_DEFAULT(emit_keytest, dropbear_rsa, DROPBEAR_CMD)
PKDTESTS_CIPHER(emit_keytest, dropbear_rsa, DROPBEAR_CIPHER_CMD)
@@ -633,23 +655,35 @@ PKDTESTS_CIPHER(emit_keytest, dropbear_ed, DROPBEAR_CIPHER_CMD)
PKDTESTS_MAC(emit_keytest, dropbear_ed, DROPBEAR_MAC_CMD)
#undef CLIENT_ID_FILE
#define CLIENT_ID_FILE PUTTY_RSA_PPK_TESTKEY
PKDTESTS_DEFAULT(emit_keytest, putty_rsa, PUTTY_CMD)
#undef CLIENT_ID_FILE
#define CLIENT_ID_FILE PUTTY_ED25519_PPK_TESTKEY
PKDTESTS_DEFAULT(emit_keytest, putty_ed, PUTTY_CMD)
#undef CLIENT_ID_FILE
#define CLIENT_ID_FILE PUTTY_ECDSA256_PPK_TESTKEY
PKDTESTS_DEFAULT(emit_keytest, putty_e256, PUTTY_CMD)
#undef CLIENT_ID_FILE
/*
* Define an array of testname strings mapped to their associated
* test function. Enables running tests individually by name from
* the command line.
*/
#define emit_testmap(client, testname, sshcmd, setup, teardown) \
#define emit_testmap(client, testname, sshcmd, setup, teardown, ...) \
{ "torture_pkd_" #client "_" #testname, \
emit_unit_test(client, testname, sshcmd, setup, teardown) },
emit_unit_test(client, testname, sshcmd, setup, teardown, ##__VA_ARGS__) },
#define emit_unit_test(client, testname, sshcmd, setup, teardown) \
#define emit_unit_test(client, testname, sshcmd, setup, teardown, ...) \
cmocka_unit_test_setup_teardown(torture_pkd_ ## client ## _ ## testname, \
torture_pkd_ ## setup, \
torture_pkd_ ## teardown)
#define emit_unit_test_comma(client, testname, sshcmd, setup, teardown) \
emit_unit_test(client, testname, sshcmd, setup, teardown),
#define emit_unit_test_comma(client, testname, sshcmd, setup, teardown, ...) \
emit_unit_test(client, testname, sshcmd, setup, teardown, ##__VA_ARGS__),
struct {
const char *testname;
@@ -696,8 +730,15 @@ struct {
PKDTESTS_CIPHER(emit_testmap, dropbear_ed, DROPBEAR_CIPHER_CMD)
PKDTESTS_MAC(emit_testmap, dropbear_ed, DROPBEAR_MAC_CMD)
/* PuTTY */
PKDTESTS_DEFAULT(emit_testmap, putty_rsa, PUTTY_CMD)
PKDTESTS_DEFAULT(emit_testmap, putty_e256, PUTTY_CMD)
PKDTESTS_DEFAULT(emit_testmap, putty_ed, PUTTY_CMD)
/* Noop */
emit_testmap(client, noop, "", setup_noop, teardown)
emit_testmap(client, noop, "", setup_noop, teardown, NULL)
/* NULL tail entry */
{ .testname = NULL,
@@ -738,6 +779,24 @@ static int pkd_run_tests(void) {
PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_ed, OPENSSH_CIPHER_CMD)
PKDTESTS_MAC(emit_unit_test_comma, openssh_ed, OPENSSH_MAC_CMD)
PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_ed, OPENSSH_MAC_CMD)
#ifdef HAVE_SK_DUMMY
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_ec_sk, OPENSSH_CMD)
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_cert_ec_sk, OPENSSH_CERT_CMD)
PKDTESTS_KEX(emit_unit_test_comma, openssh_ec_sk, OPENSSH_KEX_CMD)
PKDTESTS_CIPHER(emit_unit_test_comma, openssh_ec_sk, OPENSSH_CIPHER_CMD)
PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_ec_sk, OPENSSH_CIPHER_CMD)
PKDTESTS_MAC(emit_unit_test_comma, openssh_ec_sk, OPENSSH_MAC_CMD)
PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_ec_sk, OPENSSH_MAC_CMD)
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_ed_sk, OPENSSH_CMD)
PKDTESTS_DEFAULT(emit_unit_test_comma, openssh_cert_ed_sk, OPENSSH_CERT_CMD)
PKDTESTS_KEX(emit_unit_test_comma, openssh_ed_sk, OPENSSH_KEX_CMD)
PKDTESTS_CIPHER(emit_unit_test_comma, openssh_ed_sk, OPENSSH_CIPHER_CMD)
PKDTESTS_CIPHER_OPENSSHONLY(emit_unit_test_comma, openssh_ed_sk, OPENSSH_CIPHER_CMD)
PKDTESTS_MAC(emit_unit_test_comma, openssh_ed_sk, OPENSSH_MAC_CMD)
PKDTESTS_MAC_OPENSSHONLY(emit_unit_test_comma, openssh_ed_sk, OPENSSH_MAC_CMD)
#endif /* HAVE_SK_DUMMY */
};
/* It is not possible to test hostkey and kex algorithms, because
@@ -758,6 +817,14 @@ static int pkd_run_tests(void) {
PKDTESTS_MAC(emit_unit_test_comma, dropbear_ed, DROPBEAR_MAC_CMD)
};
const struct CMUnitTest putty_tests[] = {
PKDTESTS_DEFAULT(emit_unit_test_comma, putty_rsa, PUTTY_CMD)
PKDTESTS_DEFAULT(emit_unit_test_comma, putty_e256, PUTTY_CMD)
PKDTESTS_DEFAULT(emit_unit_test_comma, putty_ed, PUTTY_CMD)
};
const struct CMUnitTest openssh_fips_tests[] = {
PKDTESTS_DEFAULT_FIPS(emit_unit_test_comma, openssh_rsa, OPENSSH_CMD)
PKDTESTS_DEFAULT_FIPS(emit_unit_test_comma, openssh_sha256_cert_rsa,
@@ -778,12 +845,13 @@ static int pkd_run_tests(void) {
};
const struct CMUnitTest noop_tests[] = {
emit_unit_test(client, noop, "", setup_noop, teardown)
emit_unit_test(client, noop, "", setup_noop, teardown, NULL)
};
/* Test list is populated depending on which clients are enabled. */
struct CMUnitTest all_tests[(sizeof(openssh_tests) / sizeof(openssh_tests[0])) +
(sizeof(dropbear_tests) / sizeof(dropbear_tests[0])) +
(sizeof(putty_tests) / sizeof(putty_tests[0])) +
(sizeof(noop_tests) / sizeof(noop_tests[0]))];
memset(&all_tests[0], 0x0, sizeof(all_tests));
@@ -807,6 +875,14 @@ static int pkd_run_tests(void) {
}
}
if (is_putty_client_enabled()) {
setup_putty_client_keys();
if (!ssh_fips_mode()) {
memcpy(&all_tests[tindex], &putty_tests[0], sizeof(putty_tests));
tindex += (sizeof(putty_tests) / sizeof(putty_tests[0]));
}
}
memcpy(&all_tests[tindex], &noop_tests[0], sizeof(noop_tests));
tindex += (sizeof(noop_tests) / sizeof(noop_tests[0]));
@@ -859,6 +935,10 @@ static int pkd_run_tests(void) {
cleanup_openssh_client_keys();
}
if (is_putty_client_enabled()) {
cleanup_putty_client_keys();
}
/* Clean up any server keys that were generated. */
cleanup_rsa_key();
cleanup_ecdsa_keys();
@@ -1078,4 +1158,4 @@ out_finalize:
#endif
out:
return exit_code;
}
}

View File

@@ -153,6 +153,21 @@ void setup_openssh_client_keys(void) {
}
assert_int_equal(rc, 0);
}
#ifdef HAVE_SK_DUMMY
setenv("SSH_SK_PROVIDER", SK_DUMMY_LIBRARY_PATH, 1);
if (access(OPENSSH_ECDSA_SK_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t ecdsa-sk -q -N \"\" -f "
OPENSSH_ECDSA_SK_TESTKEY);
}
assert_int_equal(rc, 0);
if (access(OPENSSH_ED25519_SK_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t ed25519-sk -q -N \"\" -f "
OPENSSH_ED25519_SK_TESTKEY);
}
assert_int_equal(rc, 0);
#endif
}
void cleanup_openssh_client_keys(void) {
@@ -165,6 +180,10 @@ void cleanup_openssh_client_keys(void) {
if (!ssh_fips_mode()) {
cleanup_key(OPENSSH_ED25519_TESTKEY);
}
#ifdef HAVE_SK_DUMMY
cleanup_key(OPENSSH_ECDSA_SK_TESTKEY);
cleanup_key(OPENSSH_ED25519_SK_TESTKEY);
#endif
}
void setup_dropbear_client_keys(void)
@@ -195,3 +214,56 @@ void cleanup_dropbear_client_keys(void)
cleanup_key(DROPBEAR_ECDSA256_TESTKEY);
cleanup_key(DROPBEAR_ED25519_TESTKEY);
}
void setup_putty_client_keys(void)
{
int rc = 0;
/* RSA Keys */
if (access(PUTTY_RSA_TESTKEY, F_OK) != 0 ||
access(PUTTY_RSA_PPK_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t rsa -q -N \"\" -f "
PUTTY_RSA_TESTKEY);
assert_int_equal(rc, 0);
rc = system_checked(PUTTY_KEYGEN " " PUTTY_RSA_TESTKEY
" -O private -o " PUTTY_RSA_PPK_TESTKEY);
assert_int_equal(rc, 0);
}
/* ECDSA 256 Keys */
if (access(PUTTY_ECDSA256_TESTKEY, F_OK) != 0 ||
access(PUTTY_ECDSA256_PPK_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t ecdsa -b 256 -q -N \"\" -f "
PUTTY_ECDSA256_TESTKEY);
assert_int_equal(rc, 0);
rc = system_checked(PUTTY_KEYGEN " " PUTTY_ECDSA256_TESTKEY
" -O private -o " PUTTY_ECDSA256_PPK_TESTKEY);
assert_int_equal(rc, 0);
}
/* ED25519 Keys */
if (access(PUTTY_ED25519_TESTKEY, F_OK) != 0 ||
access(PUTTY_ED25519_PPK_TESTKEY, F_OK) != 0) {
rc = system_checked(OPENSSH_KEYGEN " -t ed25519 -q -N \"\" -f "
PUTTY_ED25519_TESTKEY);
assert_int_equal(rc, 0);
rc = system_checked(PUTTY_KEYGEN " " PUTTY_ED25519_TESTKEY
" -O private -o " PUTTY_ED25519_PPK_TESTKEY);
assert_int_equal(rc, 0);
}
}
void cleanup_putty_client_keys(void)
{
cleanup_key(PUTTY_RSA_TESTKEY);
cleanup_file(PUTTY_RSA_PPK_TESTKEY);
cleanup_key(PUTTY_ECDSA256_TESTKEY);
cleanup_file(PUTTY_ECDSA256_PPK_TESTKEY);
cleanup_key(PUTTY_ED25519_TESTKEY);
cleanup_file(PUTTY_ED25519_PPK_TESTKEY);
}

View File

@@ -30,17 +30,29 @@ void cleanup_ecdsa_keys(void);
#define OPENSSH_ECDSA521_TESTKEY "openssh_testkey.id_ecdsa521"
#define OPENSSH_ED25519_TESTKEY "openssh_testkey.id_ed25519"
#define OPENSSH_CA_TESTKEY "libssh_testkey.ca"
#define OPENSSH_ECDSA_SK_TESTKEY "openssh_testkey.id_ecdsa-sk"
#define OPENSSH_ED25519_SK_TESTKEY "openssh_testkey.id_ed25519-sk"
#define DROPBEAR_RSA_TESTKEY "dropbear_testkey.id_rsa"
#define DROPBEAR_ECDSA256_TESTKEY "dropbear_testkey.id_ecdsa256"
#define DROPBEAR_ED25519_TESTKEY "dropbear_testkey.id_ed25519"
#define PUTTY_RSA_TESTKEY "putty_testkey.id_rsa"
#define PUTTY_RSA_PPK_TESTKEY "putty_testkey.id_rsa.ppk"
#define PUTTY_ECDSA256_TESTKEY "putty_testkey.id_ecdsa256"
#define PUTTY_ECDSA256_PPK_TESTKEY "putty_testkey.id_ecdsa256.ppk"
#define PUTTY_ED25519_TESTKEY "putty_testkey.id_ed25519"
#define PUTTY_ED25519_PPK_TESTKEY "putty_testkey.id_ed25519.ppk"
void setup_openssh_client_keys(void);
void cleanup_openssh_client_keys(void);
void setup_dropbear_client_keys(void);
void cleanup_dropbear_client_keys(void);
void setup_putty_client_keys(void);
void cleanup_putty_client_keys(void);
#define cleanup_file(name) do {\
if (access((name), F_OK) != -1) {\
unlink((name));\

View File

@@ -112,3 +112,10 @@ int is_openssh_client_enabled(void) {
int is_dropbear_client_enabled(void) {
return (bin_exists(DROPBEAR_BINARY) && bin_exists(DROPBEAR_KEYGEN));
}
int is_putty_client_enabled(void)
{
return (bin_exists(PUTTY_BINARY) &&
bin_exists(PUTTY_KEYGEN) &&
bin_exists(OPENSSH_KEYGEN));
}

View File

@@ -12,5 +12,6 @@ int system_checked(const char *cmd);
/* Is client 'X' enabled? */
int is_openssh_client_enabled(void);
int is_dropbear_client_enabled(void);
int is_putty_client_enabled(void);
#endif /* __PKD_UTIL_H__ */
#endif /* __PKD_UTIL_H__ */

View File

@@ -19,7 +19,10 @@ if (WITH_GSSAPI AND GSSAPI_FOUND AND GSSAPI_TESTING)
${LIBSSH_SERVER_TESTS}
torture_gssapi_server_auth
torture_gssapi_server_auth_cb
torture_gssapi_server_delegation)
torture_gssapi_server_delegation
torture_gssapi_server_key_exchange
torture_gssapi_server_key_exchange_null
torture_gssapi_server_key_exchange_fallback)
endif()
include_directories(${libssh_SOURCE_DIR}/include

View File

@@ -17,10 +17,6 @@ if (WITH_COVERAGE)
append_coverage_compiler_flags_to_target(testserver)
endif (WITH_COVERAGE)
set(LIBSSH_SERVER_TESTS
# torture_server_kbdint
)
include_directories(${libssh_SOURCE_DIR}/include
${libssh_BINARY_DIR}/include
${libssh_BINARY_DIR})

View File

@@ -172,6 +172,65 @@ null_userdata:
return SSH_AUTH_DENIED;
}
static int kbdint_check_response(ssh_session session, struct session_data_st *sdata)
{
int count, cmp;
const char *answer = NULL;
count = ssh_userauth_kbdint_getnanswers(session);
if (count != 2) {
return 0;
}
answer = ssh_userauth_kbdint_getanswer(session, 0);
cmp = strcasecmp(sdata->username, answer);
if (cmp != 0) {
return 0;
}
answer = ssh_userauth_kbdint_getanswer(session, 1);
cmp = strcmp(sdata->password, answer);
if (cmp != 0) {
return 0;
}
return 1;
}
static int
auth_kbdint_cb(ssh_message message, ssh_session session, void *userdata)
{
struct session_data_st *sdata = (struct session_data_st *)userdata;
const char *name = "\n\nKeyboard-Interactive Fancy Authentication\n";
const char *instruction = "Get yourself authenticated";
const char *prompts[2] = {"Username: ", "Password: "};
char echo[] = {1, 0};
if (sdata == NULL) {
fprintf(stderr, "Error: NULL userdata\n");
return SSH_AUTH_DENIED;
}
if (!ssh_message_auth_kbdint_is_response(message)) {
printf("User %s wants to auth with kbdint\n",
ssh_message_auth_user(message));
ssh_message_auth_interactive_request(message,
name,
instruction,
2,
prompts,
echo);
return SSH_AUTH_INFO;
} else {
if (kbdint_check_response(session, sdata)) {
sdata->authenticated = 1;
return SSH_AUTH_SUCCESS;
}
}
return SSH_AUTH_DENIED;
}
#if WITH_GSSAPI
int auth_gssapi_mic_cb(ssh_session session,
UNUSED_PARAM(const char *user),
@@ -783,6 +842,7 @@ struct ssh_server_callbacks_struct *get_default_server_cb(void)
cb->auth_password_function = auth_password_cb;
cb->auth_pubkey_function = auth_pubkey_cb;
cb->channel_open_request_session_function = channel_new_session_cb;
cb->auth_kbdint_function = auth_kbdint_cb;
#if WITH_GSSAPI
cb->auth_gssapi_mic_function = auth_gssapi_mic_cb;
#endif
@@ -912,7 +972,8 @@ void default_handle_session_cb(ssh_event event,
} else {
ssh_set_auth_methods(session,
SSH_AUTH_METHOD_PASSWORD |
SSH_AUTH_METHOD_PUBLICKEY|
SSH_AUTH_METHOD_PUBLICKEY |
SSH_AUTH_METHOD_INTERACTIVE |
SSH_AUTH_METHOD_GSSAPI_MIC);
}

View File

@@ -67,7 +67,7 @@ struct arguments_st {
static void free_arguments(struct arguments_st *arguments)
{
if (arguments == NULL) {
goto end;
return;
}
SAFE_FREE(arguments->address);
@@ -87,9 +87,6 @@ static void free_arguments(struct arguments_st *arguments)
SAFE_FREE(arguments->config_file);
SAFE_FREE(arguments->log_file);
SAFE_FREE(arguments->pid_file);
end:
return;
}
#ifdef HAVE_ARGP_H
@@ -115,6 +112,9 @@ static void print_auth_methods(int auth_methods)
if (auth_methods & SSH_AUTH_METHOD_GSSAPI_MIC) {
printf("\tSSH_AUTH_METHOD_GSSAPI_MIC\n");
}
if (auth_methods & SSH_AUTH_METHOD_GSSAPI_KEYEX) {
printf("\tSSH_AUTH_METHOD_GSSAPI_KEYEX\n");
}
}
static void print_verbosity(int verbosity)
@@ -273,6 +273,7 @@ static int init_server_state(struct server_state_st *state,
} else {
state->auth_methods = SSH_AUTH_METHOD_PASSWORD |
SSH_AUTH_METHOD_PUBLICKEY |
SSH_AUTH_METHOD_INTERACTIVE |
SSH_AUTH_METHOD_GSSAPI_MIC;
}
@@ -295,6 +296,7 @@ static int init_server_state(struct server_state_st *state,
}
state->parse_global_config = arguments->with_global_config;
state->gssapi_key_exchange_algs = NULL;
if (arguments->config_file) {
state->config_file = arguments->config_file;

View File

@@ -145,9 +145,8 @@ int run_server(struct server_state_st *state)
}
if (state->host_key == NULL && state->rsa_key == NULL &&
state->ecdsa_key == NULL && state->ed25519_key) {
state->ecdsa_key == NULL && state->ed25519_key == NULL) {
fprintf(stderr, "Missing host key\n");
goto out;
}
sshbind = ssh_bind_new();
@@ -194,6 +193,30 @@ int run_server(struct server_state_st *state)
goto out;
}
#ifdef WITH_GSSAPI
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE,
&(state->gssapi_key_exchange));
if (rc != 0) {
fprintf(stderr,
"Error setting GSSAPI key exchange: %s\n",
ssh_get_error(sshbind));
goto out;
}
if (state->gssapi_key_exchange_algs != NULL) {
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
state->gssapi_key_exchange_algs);
if (rc != 0) {
fprintf(stderr,
"Error setting GSSAPI key exchange algorithms: %s\n",
ssh_get_error(sshbind));
goto out;
}
}
#endif /* WITH_GSSAPI */
rc = ssh_bind_options_set(sshbind,
SSH_BIND_OPTIONS_BINDPORT,
&(state->port));

View File

@@ -53,6 +53,8 @@ struct server_state_st {
bool parse_global_config;
char *log_file;
bool gssapi_key_exchange;
const char *gssapi_key_exchange_algs;
/* State */
int max_tries;

View File

@@ -160,6 +160,7 @@ setup_default_server(void **state)
ss = tss->ss;
s = tss->state;
setenv("NSS_WRAPPER_HOSTNAME", "server.libssh.site", 1);
/* Start the server using the default values */
pid = fork_run_server(ss, free_test_server_state, &tss);
if (pid < 0) {
@@ -335,7 +336,7 @@ torture_gssapi_server_auth_invalid_host(void **state)
"echo bar | kinit alice");
rc = ssh_userauth_gssapi(session);
assert_int_equal(rc, SSH_AUTH_ERROR);
assert_int_equal(rc, SSH_AUTH_DENIED);
torture_teardown_kdc_server((void **)&s);
}

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