Compare commits

...

10 Commits

Author SHA1 Message Date
Jakub Jelen
76b14eaed7 Update list of implemented RFCs and drafts
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
Jakub Jelen
8e8f091aba connector: Simplify handling of out/err channels
Based on stale MR !461.

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

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

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

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

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

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

Signed-off-by: Eshan Kelkar <eshankelkar@galorithm.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
2026-02-04 18:08:25 +01:00
11 changed files with 250 additions and 119 deletions

View File

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

View File

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

View File

@@ -542,16 +542,8 @@ static int ssh_connector_channel_data_cb(ssh_session session,
window_len = MIN(window, len);
/* Route the data to the right exception channel */
if (is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR)) {
w = ssh_channel_write_stderr(connector->out_channel,
data,
window_len);
} else if (!is_stderr &&
(connector->out_flags & SSH_CONNECTOR_STDOUT)) {
w = ssh_channel_write(connector->out_channel,
data,
window_len);
} else if (connector->out_flags & SSH_CONNECTOR_STDOUT) {
if (connector->out_flags & SSH_CONNECTOR_STDOUT &&
!(is_stderr && (connector->out_flags & SSH_CONNECTOR_STDERR))) {
w = ssh_channel_write(connector->out_channel,
data,
window_len);

View File

@@ -814,6 +814,16 @@ static struct ssh_iterator *ssh_iterator_new(const void *data)
return iterator;
}
/**
* @internal
*
* @brief Appends an element to the end of the list.
*
* @param[in] list The list to append the element
* @param[in] data The element to append
*
* @return `SSH_OK` on success, `SSH_ERROR` on error
*/
int ssh_list_append(struct ssh_list *list, const void *data)
{
struct ssh_iterator *iterator = NULL;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -833,7 +833,7 @@ torture_setup_create_sshd_config(void **state, bool pam, bool second_sshd)
"TrustedUserCAKeys %s\n"
"\n"
"LogLevel DEBUG3\n"
"Subsystem sftp %s -l DEBUG2\n"
"Subsystem sftp %s -l DEBUG3 -e\n"
"\n"
"PasswordAuthentication yes\n"
"PubkeyAuthentication yes\n"
@@ -873,7 +873,7 @@ torture_setup_create_sshd_config(void **state, bool pam, bool second_sshd)
"TrustedUserCAKeys %s\n" /* Trusted CA */
"\n"
"LogLevel DEBUG3\n"
"Subsystem sftp %s -l DEBUG2\n" /* SFTP server */
"Subsystem sftp %s -l DEBUG3 -e\n" /* SFTP server */
"\n"
"PasswordAuthentication yes\n"
"PubkeyAuthentication yes\n"

View File

@@ -159,6 +159,8 @@ extern LIBSSH_THREAD int ssh_log_level;
"\tProxyJump jumpbox:2222\n" \
"Host two-step\n" \
"\tProxyJump u1@first:222,u2@second:33\n" \
"Host three-step\n" \
"\tProxyJump u1@first:222,u2@second:33,u3@third:444\n" \
"Host none\n" \
"\tProxyJump none\n" \
"Host only-command\n" \
@@ -1172,6 +1174,23 @@ static void torture_config_proxyjump(void **state,
"u1",
"222");
/* Three step jump */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "three-step");
_parse_config(session, file, string, SSH_OK);
helper_proxy_jump_check(session->opts.proxy_jumps->root,
"third",
"u3",
"444");
helper_proxy_jump_check(session->opts.proxy_jumps->root->next,
"second",
"u2",
"33");
helper_proxy_jump_check(session->opts.proxy_jumps->root->next->next,
"first",
"u1",
"222");
/* none */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "none");
@@ -1237,6 +1256,13 @@ static void torture_config_proxyjump(void **state,
assert_string_equal(session->opts.ProxyCommand,
"ssh -l u1 -p 222 -J u2@second:33 -W '[%h]:%p' first");
/* Three step jump */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "three-step");
_parse_config(session, file, string, SSH_OK);
assert_string_equal(session->opts.ProxyCommand,
"ssh -l u1 -p 222 -J u2@second:33,u3@third:444 -W '[%h]:%p' first");
/* none */
torture_reset_config(session);
ssh_options_set(session, SSH_OPTIONS_HOST, "none");