Compare commits

...

5 Commits

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

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

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

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

Signed-off-by: Manas Trivedi <manas.trivedi.020@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
2026-03-24 10:15:05 +01:00
10 changed files with 303 additions and 61 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

180
src/pki.c
View File

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

View File

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

View File

@@ -39,10 +39,29 @@ static void torture_channel_select(void **state)
close(fd);
}
static void torture_channel_null_session(void **state)
{
ssh_channel channel = NULL;
(void)state;
channel = calloc(1, sizeof(struct ssh_channel_struct));
assert_non_null(channel);
channel->state = SSH_CHANNEL_STATE_OPEN;
channel->session = NULL;
assert_int_equal(ssh_channel_is_open(channel), 0);
free(channel);
}
int torture_run_tests(void) {
int rc;
struct CMUnitTest tests[] = {
cmocka_unit_test(torture_channel_select),
cmocka_unit_test(torture_channel_null_session),
};
ssh_init();

View File

@@ -107,6 +107,76 @@ static void torture_pki_signature(void **state)
ssh_signature_free(sig);
}
static void torture_pki_type_and_hash_from_signature_name(void **state)
{
enum ssh_keytypes_e type;
enum ssh_digest_e hash_type;
int rc;
(void)state; /* unused */
/* Test NULL input */
rc = ssh_key_type_and_hash_from_signature_name(NULL, &type, &hash_type);
assert_int_equal(rc, SSH_ERROR);
/* Test NULL output pointers */
rc = ssh_key_type_and_hash_from_signature_name("ssh-rsa", NULL, &hash_type);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_key_type_and_hash_from_signature_name("ssh-rsa", &type, NULL);
assert_int_equal(rc, SSH_ERROR);
/* Test unknown name */
rc = ssh_key_type_and_hash_from_signature_name("unknown-algo",
&type,
&hash_type);
assert_int_equal(rc, SSH_ERROR);
/* Test valid RSA signatures */
rc =
ssh_key_type_and_hash_from_signature_name("ssh-rsa", &type, &hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_RSA);
assert_int_equal(hash_type, SSH_DIGEST_SHA1);
rc = ssh_key_type_and_hash_from_signature_name("rsa-sha2-256",
&type,
&hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_RSA);
assert_int_equal(hash_type, SSH_DIGEST_SHA256);
rc = ssh_key_type_and_hash_from_signature_name("rsa-sha2-512",
&type,
&hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_RSA);
assert_int_equal(hash_type, SSH_DIGEST_SHA512);
/* Test valid ECDSA signatures */
rc = ssh_key_type_and_hash_from_signature_name("ecdsa-sha2-nistp256",
&type,
&hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_ECDSA_P256);
assert_int_equal(hash_type, SSH_DIGEST_SHA256);
/* Test valid ED25519 signature */
rc = ssh_key_type_and_hash_from_signature_name("ssh-ed25519",
&type,
&hash_type);
assert_int_equal(rc, SSH_OK);
assert_int_equal(type, SSH_KEYTYPE_ED25519);
assert_int_equal(hash_type, SSH_DIGEST_AUTO);
/* Test that loose key names are rejected */
rc = ssh_key_type_and_hash_from_signature_name("ecdsa", &type, &hash_type);
assert_int_equal(rc, SSH_ERROR);
rc = ssh_key_type_and_hash_from_signature_name("rsa", &type, &hash_type);
assert_int_equal(rc, SSH_ERROR);
}
struct key_attrs {
int sign;
int verify;
@@ -415,6 +485,7 @@ int torture_run_tests(void) {
struct CMUnitTest tests[] = {
cmocka_unit_test(torture_pki_keytype),
cmocka_unit_test(torture_pki_signature),
cmocka_unit_test(torture_pki_type_and_hash_from_signature_name),
cmocka_unit_test_setup_teardown(torture_pki_verify_mismatch,
setup_cert_dir,
teardown_cert_dir),