mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-07-31 00:03:07 +03:00
feat: implement proxy jump using libssh
tests: modify proxyjump tests to check for ssh_jump_info_struct tests: add proxyjump functionality test feat: add SSH_OPTIONS_PROXYJUMP tests: proxyjump, check authentication fix: ssh_socket_connect_proxyjump add exit label to exit on error feat: implement io forwarding using pthread feat: proxyjump: use threading instead of forking feat: proxyjump: cancel forwarding threads on ssh_disconnect fix: proxyjump remove ProxyJump bool and put pthread ifdefs feat: use ssh_event for io forwarding instead of threads reformat: tests to use assert_int_not_equal fix: link to pthread refactor: make function to free proxy jump list docs: add comment for proxy jump channel feat: add env variable to enable libssh proxy jump feat: open channel for proxyjump like OpenSSH feat: add more tests for proxy jump fix: use a global variable to close io forwarding, this prevents segfaults fix: handle proxy list in thread without creating copy Signed-off-by: Gauravsingh Sisodia <xaerru@gmail.com> Reviewed-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Eshan Kelkar <eshankelkar@galorithm.com>
This commit is contained in:
committed by
Sahana Prasad
parent
fe53cdfabd
commit
6d1ed76c7a
@ -1074,6 +1074,45 @@ LIBSSH_API int ssh_set_log_callback(ssh_logging_callback cb);
|
|||||||
*/
|
*/
|
||||||
LIBSSH_API ssh_logging_callback ssh_get_log_callback(void);
|
LIBSSH_API ssh_logging_callback ssh_get_log_callback(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SSH proxyjump before connection callback. Called before calling
|
||||||
|
* ssh_connect()
|
||||||
|
* @param session Jump session handler
|
||||||
|
* @param userdata Userdata to be passed to the callback function.
|
||||||
|
*
|
||||||
|
* @return 0 on success, < 0 on error.
|
||||||
|
*/
|
||||||
|
typedef int (*ssh_jump_before_connection_callback)(ssh_session session,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SSH proxyjump verify knownhost callback. Verify the host.
|
||||||
|
* If not specified default function will be used.
|
||||||
|
* @param session Jump session handler
|
||||||
|
* @param userdata Userdata to be passed to the callback function.
|
||||||
|
*
|
||||||
|
* @return 0 on success, < 0 on error.
|
||||||
|
*/
|
||||||
|
typedef int (*ssh_jump_verify_knownhost_callback)(ssh_session session,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief SSH proxyjump user authentication callback. Authenticate the user.
|
||||||
|
* @param session Jump session handler
|
||||||
|
* @param userdata Userdata to be passed to the callback function.
|
||||||
|
*
|
||||||
|
* @return 0 on success, < 0 on error.
|
||||||
|
*/
|
||||||
|
typedef int (*ssh_jump_authenticate_callback)(ssh_session session,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
struct ssh_jump_callbacks_struct {
|
||||||
|
void *userdata;
|
||||||
|
ssh_jump_before_connection_callback before_connection;
|
||||||
|
ssh_jump_verify_knownhost_callback verify_knownhost;
|
||||||
|
ssh_jump_authenticate_callback authenticate;
|
||||||
|
};
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -30,6 +30,7 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "libssh/libssh.h"
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
char *ssh_config_get_cmd(char **str);
|
char *ssh_config_get_cmd(char **str);
|
||||||
@ -63,6 +64,21 @@ int ssh_config_parse_uri(const char *tok,
|
|||||||
char **port,
|
char **port,
|
||||||
bool ignore_port);
|
bool ignore_port);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief: Parse the ProxyJump configuration line and if parsing,
|
||||||
|
* stores the result in the configuration option
|
||||||
|
*
|
||||||
|
* @param[in] session The ssh session
|
||||||
|
* @param[in] s The string to be parsed.
|
||||||
|
* @param[in] do_parsing Whether to parse or not.
|
||||||
|
*
|
||||||
|
* @returns SSH_OK if the provided string is formatted and parsed correctly
|
||||||
|
* SSH_ERROR on failure
|
||||||
|
*/
|
||||||
|
int ssh_config_parse_proxy_jump(ssh_session session,
|
||||||
|
const char *s,
|
||||||
|
bool do_parsing);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -417,6 +417,8 @@ enum ssh_options_e {
|
|||||||
SSH_OPTIONS_CONTROL_MASTER,
|
SSH_OPTIONS_CONTROL_MASTER,
|
||||||
SSH_OPTIONS_CONTROL_PATH,
|
SSH_OPTIONS_CONTROL_PATH,
|
||||||
SSH_OPTIONS_CERTIFICATE,
|
SSH_OPTIONS_CERTIFICATE,
|
||||||
|
SSH_OPTIONS_PROXYJUMP,
|
||||||
|
SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
#else
|
#else
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
@ -65,6 +66,12 @@ struct ssh_iterator {
|
|||||||
const void *data;
|
const void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ssh_jump_info_struct {
|
||||||
|
char *hostname;
|
||||||
|
char *username;
|
||||||
|
int port;
|
||||||
|
};
|
||||||
|
|
||||||
struct ssh_timestamp {
|
struct ssh_timestamp {
|
||||||
long seconds;
|
long seconds;
|
||||||
long useconds;
|
long useconds;
|
||||||
@ -100,6 +107,9 @@ const void *_ssh_list_pop_head(struct ssh_list *list);
|
|||||||
#define ssh_list_pop_head(type, ssh_list)\
|
#define ssh_list_pop_head(type, ssh_list)\
|
||||||
((type)_ssh_list_pop_head(ssh_list))
|
((type)_ssh_list_pop_head(ssh_list))
|
||||||
|
|
||||||
|
#define SSH_LIST_FREE(x) \
|
||||||
|
do { if ((x) != NULL) { ssh_list_free(x); (x) = NULL; } } while(0)
|
||||||
|
|
||||||
int ssh_make_milliseconds(unsigned long sec, unsigned long usec);
|
int ssh_make_milliseconds(unsigned long sec, unsigned long usec);
|
||||||
void ssh_timestamp_init(struct ssh_timestamp *ts);
|
void ssh_timestamp_init(struct ssh_timestamp *ts);
|
||||||
int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout);
|
int ssh_timeout_elapsed(struct ssh_timestamp *ts, int timeout);
|
||||||
@ -123,6 +133,9 @@ ssize_t ssh_writen(int fd, const void *buf, size_t nbytes);
|
|||||||
int ssh_check_hostname_syntax(const char *hostname);
|
int ssh_check_hostname_syntax(const char *hostname);
|
||||||
int ssh_check_username_syntax(const char *username);
|
int ssh_check_username_syntax(const char *username);
|
||||||
|
|
||||||
|
void ssh_proxyjumps_free(struct ssh_list *proxy_jump_list);
|
||||||
|
bool ssh_libssh_proxy_jumps(void);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -140,6 +140,7 @@ struct ssh_session_struct {
|
|||||||
uint32_t send_seq;
|
uint32_t send_seq;
|
||||||
uint32_t recv_seq;
|
uint32_t recv_seq;
|
||||||
struct ssh_timestamp last_rekey_time;
|
struct ssh_timestamp last_rekey_time;
|
||||||
|
bool proxy_root;
|
||||||
|
|
||||||
int connected;
|
int connected;
|
||||||
/* !=0 when the user got a session handle */
|
/* !=0 when the user got a session handle */
|
||||||
@ -239,6 +240,8 @@ struct ssh_session_struct {
|
|||||||
struct ssh_list *identity_non_exp;
|
struct ssh_list *identity_non_exp;
|
||||||
struct ssh_list *certificate;
|
struct ssh_list *certificate;
|
||||||
struct ssh_list *certificate_non_exp;
|
struct ssh_list *certificate_non_exp;
|
||||||
|
struct ssh_list *proxy_jumps;
|
||||||
|
struct ssh_list *proxy_jumps_user_cb;
|
||||||
char *username;
|
char *username;
|
||||||
char *host;
|
char *host;
|
||||||
char *bindaddr; /* bind the client to an ip addr */
|
char *bindaddr; /* bind the client to an ip addr */
|
||||||
|
@ -41,6 +41,7 @@ int ssh_socket_unix(ssh_socket s, const char *path);
|
|||||||
void ssh_execute_command(const char *command, socket_t in, socket_t out);
|
void ssh_execute_command(const char *command, socket_t in, socket_t out);
|
||||||
int ssh_socket_connect_proxycommand(ssh_socket s, const char *command);
|
int ssh_socket_connect_proxycommand(ssh_socket s, const char *command);
|
||||||
#endif
|
#endif
|
||||||
|
int ssh_socket_connect_proxyjump(ssh_socket s);
|
||||||
void ssh_socket_close(ssh_socket s);
|
void ssh_socket_close(ssh_socket s);
|
||||||
int ssh_socket_write(ssh_socket s,const void *buffer, uint32_t len);
|
int ssh_socket_write(ssh_socket s,const void *buffer, uint32_t len);
|
||||||
int ssh_socket_is_open(ssh_socket s);
|
int ssh_socket_is_open(ssh_socket s);
|
||||||
|
16
src/client.c
16
src/client.c
@ -589,6 +589,13 @@ int ssh_connect(ssh_session session)
|
|||||||
session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
|
session->session_state = SSH_SESSION_STATE_SOCKET_CONNECTED;
|
||||||
ssh_socket_set_fd(session->socket, session->opts.fd);
|
ssh_socket_set_fd(session->socket, session->opts.fd);
|
||||||
ret = SSH_OK;
|
ret = SSH_OK;
|
||||||
|
#ifndef _WIN32
|
||||||
|
#ifdef HAVE_PTHREAD
|
||||||
|
} else if (ssh_libssh_proxy_jumps() &&
|
||||||
|
ssh_list_count(session->opts.proxy_jumps) != 0) {
|
||||||
|
ret = ssh_socket_connect_proxyjump(session->socket);
|
||||||
|
#endif /* HAVE_PTHREAD */
|
||||||
|
#endif /* _WIN32 */
|
||||||
} else if (session->opts.ProxyCommand != NULL) {
|
} else if (session->opts.ProxyCommand != NULL) {
|
||||||
#ifdef WITH_EXEC
|
#ifdef WITH_EXEC
|
||||||
ret = ssh_socket_connect_proxycommand(session->socket,
|
ret = ssh_socket_connect_proxycommand(session->socket,
|
||||||
@ -758,6 +765,7 @@ ssh_session_set_disconnect_message(ssh_session session, const char *message)
|
|||||||
return SSH_OK;
|
return SSH_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern int proxy_disconnect;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Disconnect from a session (client or server).
|
* @brief Disconnect from a session (client or server).
|
||||||
@ -780,6 +788,14 @@ ssh_disconnect(ssh_session session)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
/* Only send the disconnect to all other threads when the root session calls
|
||||||
|
* ssh_disconnect() */
|
||||||
|
if (session->proxy_root) {
|
||||||
|
proxy_disconnect = 1;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (session->disconnect_message == NULL) {
|
if (session->disconnect_message == NULL) {
|
||||||
session->disconnect_message = strdup("Bye Bye") ;
|
session->disconnect_message = strdup("Bye Bye") ;
|
||||||
if (session->disconnect_message == NULL) {
|
if (session->disconnect_message == NULL) {
|
||||||
|
77
src/config.c
77
src/config.c
@ -434,10 +434,18 @@ ssh_match_exec(ssh_session session, const char *command, bool negate)
|
|||||||
}
|
}
|
||||||
#endif /* WITH_EXEC */
|
#endif /* WITH_EXEC */
|
||||||
|
|
||||||
/* @brief: Parse the ProxyJump configuration line and if parsing,
|
/**
|
||||||
|
* @brief: Parse the ProxyJump configuration line and if parsing,
|
||||||
* stores the result in the configuration option
|
* stores the result in the configuration option
|
||||||
|
*
|
||||||
|
* @param[in] session The ssh session
|
||||||
|
* @param[in] s The string to be parsed.
|
||||||
|
* @param[in] do_parsing Whether to parse or not.
|
||||||
|
*
|
||||||
|
* @returns SSH_OK if the provided string is formatted and parsed correctly
|
||||||
|
* SSH_ERROR on failure
|
||||||
*/
|
*/
|
||||||
static int
|
int
|
||||||
ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
||||||
{
|
{
|
||||||
char *c = NULL, *cp = NULL, *endp = NULL;
|
char *c = NULL, *cp = NULL, *endp = NULL;
|
||||||
@ -446,12 +454,16 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
|||||||
char *port = NULL;
|
char *port = NULL;
|
||||||
char *next = NULL;
|
char *next = NULL;
|
||||||
int cmp, rv = SSH_ERROR;
|
int cmp, rv = SSH_ERROR;
|
||||||
|
struct ssh_jump_info_struct *jump_host = NULL;
|
||||||
bool parse_entry = do_parsing;
|
bool parse_entry = do_parsing;
|
||||||
|
bool libssh_proxy_jump = ssh_libssh_proxy_jumps();
|
||||||
|
|
||||||
/* Special value none disables the proxy */
|
/* Special value none disables the proxy */
|
||||||
cmp = strcasecmp(s, "none");
|
cmp = strcasecmp(s, "none");
|
||||||
if (cmp == 0 && do_parsing) {
|
if (cmp == 0) {
|
||||||
|
if (!libssh_proxy_jump && do_parsing) {
|
||||||
ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, s);
|
ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, s);
|
||||||
|
}
|
||||||
return SSH_OK;
|
return SSH_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -469,25 +481,65 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
|||||||
/* Split out the token */
|
/* Split out the token */
|
||||||
*endp = '\0';
|
*endp = '\0';
|
||||||
}
|
}
|
||||||
if (parse_entry) {
|
if (parse_entry && libssh_proxy_jump) {
|
||||||
|
jump_host = calloc(1, sizeof(struct ssh_jump_info_struct));
|
||||||
|
if (jump_host == NULL) {
|
||||||
|
ssh_set_error_oom(session);
|
||||||
|
rv = SSH_ERROR;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = ssh_config_parse_uri(cp,
|
||||||
|
&jump_host->username,
|
||||||
|
&jump_host->hostname,
|
||||||
|
&port,
|
||||||
|
false);
|
||||||
|
if (rv != SSH_OK) {
|
||||||
|
ssh_set_error_invalid(session);
|
||||||
|
SAFE_FREE(jump_host);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (port == NULL) {
|
||||||
|
jump_host->port = 22;
|
||||||
|
} else {
|
||||||
|
jump_host->port = strtol(port, NULL, 10);
|
||||||
|
SAFE_FREE(port);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Prepend because we will recursively proxy jump */
|
||||||
|
rv = ssh_list_prepend(session->opts.proxy_jumps, jump_host);
|
||||||
|
if (rv != SSH_OK) {
|
||||||
|
ssh_set_error_oom(session);
|
||||||
|
SAFE_FREE(jump_host);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
} else if (parse_entry) {
|
||||||
/* We actually care only about the first item */
|
/* We actually care only about the first item */
|
||||||
rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
|
rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false);
|
||||||
|
if (rv != SSH_OK) {
|
||||||
|
ssh_set_error_invalid(session);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
/* The rest of the list needs to be passed on */
|
/* The rest of the list needs to be passed on */
|
||||||
if (endp != NULL) {
|
if (endp != NULL) {
|
||||||
next = strdup(endp + 1);
|
next = strdup(endp + 1);
|
||||||
if (next == NULL) {
|
if (next == NULL) {
|
||||||
ssh_set_error_oom(session);
|
ssh_set_error_oom(session);
|
||||||
rv = SSH_ERROR;
|
rv = SSH_ERROR;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* The rest is just sanity-checked to avoid failures later */
|
/* The rest is just sanity-checked to avoid failures later */
|
||||||
rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
|
rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false);
|
||||||
}
|
|
||||||
if (rv != SSH_OK) {
|
if (rv != SSH_OK) {
|
||||||
|
ssh_set_error_invalid(session);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (!libssh_proxy_jump) {
|
||||||
parse_entry = 0;
|
parse_entry = 0;
|
||||||
|
}
|
||||||
if (endp != NULL) {
|
if (endp != NULL) {
|
||||||
cp = endp + 1;
|
cp = endp + 1;
|
||||||
} else {
|
} else {
|
||||||
@ -495,7 +547,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
|||||||
}
|
}
|
||||||
} while (cp != NULL);
|
} while (cp != NULL);
|
||||||
|
|
||||||
if (hostname != NULL && do_parsing) {
|
if (!libssh_proxy_jump && hostname != NULL && do_parsing) {
|
||||||
char com[512] = {0};
|
char com[512] = {0};
|
||||||
|
|
||||||
rv = snprintf(com, sizeof(com), "ssh%s%s%s%s%s%s -W '[%%h]:%%p' %s",
|
rv = snprintf(com, sizeof(com), "ssh%s%s%s%s%s%s -W '[%%h]:%%p' %s",
|
||||||
@ -511,11 +563,19 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
|||||||
rv = SSH_ERROR;
|
rv = SSH_ERROR;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, com);
|
rv = ssh_options_set(session, SSH_OPTIONS_PROXYCOMMAND, com);
|
||||||
|
if (rv != SSH_OK) {
|
||||||
|
ssh_set_error_oom(session);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rv = SSH_OK;
|
rv = SSH_OK;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
if (rv != SSH_OK) {
|
||||||
|
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||||
|
}
|
||||||
SAFE_FREE(username);
|
SAFE_FREE(username);
|
||||||
SAFE_FREE(hostname);
|
SAFE_FREE(hostname);
|
||||||
SAFE_FREE(port);
|
SAFE_FREE(port);
|
||||||
@ -1063,7 +1123,8 @@ ssh_config_parse_line(ssh_session session,
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
/* We share the seen value with the ProxyCommand */
|
/* We share the seen value with the ProxyCommand */
|
||||||
rv = ssh_config_parse_proxy_jump(session, p,
|
rv = ssh_config_parse_proxy_jump(session,
|
||||||
|
p,
|
||||||
(*parsing && !seen[SOC_PROXYCOMMAND]));
|
(*parsing && !seen[SOC_PROXYCOMMAND]));
|
||||||
if (rv != SSH_OK) {
|
if (rv != SSH_OK) {
|
||||||
SAFE_FREE(x);
|
SAFE_FREE(x);
|
||||||
|
42
src/misc.c
42
src/misc.c
@ -2200,4 +2200,46 @@ int ssh_check_username_syntax(const char *username)
|
|||||||
|
|
||||||
return SSH_OK;
|
return SSH_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Free proxy jump list
|
||||||
|
*
|
||||||
|
* Frees everything in a proxy jump list, but doesn't free the ssh_list
|
||||||
|
*
|
||||||
|
* @param proxy_jump_list
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ssh_proxyjumps_free(struct ssh_list *proxy_jump_list)
|
||||||
|
{
|
||||||
|
struct ssh_jump_info_struct *jump = NULL;
|
||||||
|
|
||||||
|
for (jump =
|
||||||
|
ssh_list_pop_head(struct ssh_jump_info_struct *, proxy_jump_list);
|
||||||
|
jump != NULL;
|
||||||
|
jump = ssh_list_pop_head(struct ssh_jump_info_struct *,
|
||||||
|
proxy_jump_list)) {
|
||||||
|
SAFE_FREE(jump->hostname);
|
||||||
|
SAFE_FREE(jump->username);
|
||||||
|
SAFE_FREE(jump);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if libssh proxy jumps is enabled
|
||||||
|
*
|
||||||
|
* If env variable OPENSSH_PROXYJUMP is set to 1 then proxyjump will be
|
||||||
|
* through the OpenSSH binary.
|
||||||
|
*
|
||||||
|
* @return false if OPENSSH_PROXYJUMP=1
|
||||||
|
* true otherwise
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
ssh_libssh_proxy_jumps(void)
|
||||||
|
{
|
||||||
|
const char *t = getenv("OPENSSH_PROXYJUMP");
|
||||||
|
|
||||||
|
return !(t != NULL && t[0] == '1');
|
||||||
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -512,6 +512,20 @@ int ssh_options_set_algo(ssh_session session,
|
|||||||
* Set the command to be executed in order to connect to
|
* Set the command to be executed in order to connect to
|
||||||
* server (const char *).
|
* server (const char *).
|
||||||
*
|
*
|
||||||
|
* - SSH_OPTIONS_PROXYJUMP:
|
||||||
|
* Set the comma separated jump hosts in order to connect to
|
||||||
|
* server (const char *). Set to "none" to disable.
|
||||||
|
* Example:
|
||||||
|
* "alice@127.0.0.1:5555,bob@127.0.0.2"
|
||||||
|
*
|
||||||
|
* If environment variable OPENSSH_PROXYJUMP is set to 1 then proxyjump will be
|
||||||
|
* handled by the OpenSSH binary.
|
||||||
|
*
|
||||||
|
* - SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND:
|
||||||
|
* Append the callbacks struct for a jump in order of
|
||||||
|
* SSH_OPTIONS_PROXYJUMP. Append as many times
|
||||||
|
* as the number of jumps (struct ssh_jump_callbacks_struct *).
|
||||||
|
*
|
||||||
* - SSH_OPTIONS_GSSAPI_SERVER_IDENTITY
|
* - SSH_OPTIONS_GSSAPI_SERVER_IDENTITY
|
||||||
* Set it to specify the GSSAPI server identity that libssh
|
* Set it to specify the GSSAPI server identity that libssh
|
||||||
* should expect when connecting to the server (const char *).
|
* should expect when connecting to the server (const char *).
|
||||||
@ -637,6 +651,7 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
|||||||
unsigned int u;
|
unsigned int u;
|
||||||
int rc;
|
int rc;
|
||||||
char **wanted_methods = session->opts.wanted_methods;
|
char **wanted_methods = session->opts.wanted_methods;
|
||||||
|
struct ssh_jump_callbacks_struct *j = NULL;
|
||||||
|
|
||||||
if (session == NULL) {
|
if (session == NULL) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -1123,6 +1138,32 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SSH_OPTIONS_PROXYJUMP:
|
||||||
|
v = value;
|
||||||
|
if (v == NULL || v[0] == '\0') {
|
||||||
|
ssh_set_error_invalid(session);
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||||
|
rc = ssh_config_parse_proxy_jump(session, v, true);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND:
|
||||||
|
j = (struct ssh_jump_callbacks_struct *)value;
|
||||||
|
if (j == NULL) {
|
||||||
|
ssh_set_error_invalid(session);
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
rc = ssh_list_prepend(session->opts.proxy_jumps_user_cb, j);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
ssh_set_error_oom(session);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
case SSH_OPTIONS_GSSAPI_SERVER_IDENTITY:
|
case SSH_OPTIONS_GSSAPI_SERVER_IDENTITY:
|
||||||
v = value;
|
v = value;
|
||||||
if (v == NULL || v[0] == '\0') {
|
if (v == NULL || v[0] == '\0') {
|
||||||
|
@ -96,6 +96,7 @@ ssh_session ssh_new(void)
|
|||||||
session->auth.supported_methods = 0;
|
session->auth.supported_methods = 0;
|
||||||
ssh_set_blocking(session, 1);
|
ssh_set_blocking(session, 1);
|
||||||
session->maxchannel = FIRST_CHANNEL;
|
session->maxchannel = FIRST_CHANNEL;
|
||||||
|
session->proxy_root = true;
|
||||||
|
|
||||||
session->agent = ssh_agent_new(session);
|
session->agent = ssh_agent_new(session);
|
||||||
if (session->agent == NULL) {
|
if (session->agent == NULL) {
|
||||||
@ -138,6 +139,16 @@ ssh_session ssh_new(void)
|
|||||||
/* the default certificates are loaded automatically from the default
|
/* the default certificates are loaded automatically from the default
|
||||||
* identities later */
|
* identities later */
|
||||||
|
|
||||||
|
session->opts.proxy_jumps = ssh_list_new();
|
||||||
|
if (session->opts.proxy_jumps == NULL) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
session->opts.proxy_jumps_user_cb = ssh_list_new();
|
||||||
|
if (session->opts.proxy_jumps_user_cb == NULL) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
id = strdup("%d/id_ed25519");
|
id = strdup("%d/id_ed25519");
|
||||||
if (id == NULL) {
|
if (id == NULL) {
|
||||||
goto err;
|
goto err;
|
||||||
@ -321,6 +332,10 @@ void ssh_free(ssh_session session)
|
|||||||
ssh_list_free(session->opts.certificate_non_exp);
|
ssh_list_free(session->opts.certificate_non_exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||||
|
SSH_LIST_FREE(session->opts.proxy_jumps);
|
||||||
|
SSH_LIST_FREE(session->opts.proxy_jumps_user_cb);
|
||||||
|
|
||||||
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
|
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
|
||||||
session->out_queue)) != NULL) {
|
session->out_queue)) != NULL) {
|
||||||
SSH_BUFFER_FREE(b);
|
SSH_BUFFER_FREE(b);
|
||||||
|
303
src/socket.c
303
src/socket.c
@ -44,6 +44,9 @@ struct sockaddr_un {
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#include <sys/un.h>
|
#include <sys/un.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#ifdef HAVE_PTHREAD
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
#endif /* _WIN32 */
|
#endif /* _WIN32 */
|
||||||
|
|
||||||
#include "libssh/priv.h"
|
#include "libssh/priv.h"
|
||||||
@ -90,6 +93,15 @@ struct ssh_socket_struct {
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef HAVE_PTHREAD
|
||||||
|
struct jump_thread_data_struct {
|
||||||
|
ssh_session session;
|
||||||
|
socket_t fd;
|
||||||
|
};
|
||||||
|
|
||||||
|
int proxy_disconnect = 0;
|
||||||
|
#endif /* HAVE_PTHREAD */
|
||||||
|
|
||||||
static int sockets_initialized = 0;
|
static int sockets_initialized = 0;
|
||||||
|
|
||||||
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
|
static ssize_t ssh_socket_unbuffered_read(ssh_socket s,
|
||||||
@ -987,4 +999,295 @@ ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
|
|||||||
return SSH_OK;
|
return SSH_OK;
|
||||||
}
|
}
|
||||||
#endif /* WITH_EXEC */
|
#endif /* WITH_EXEC */
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
#ifdef HAVE_PTHREAD
|
||||||
|
static int
|
||||||
|
verify_knownhost(ssh_session session)
|
||||||
|
{
|
||||||
|
enum ssh_known_hosts_e state;
|
||||||
|
|
||||||
|
state = ssh_session_is_known_server(session);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case SSH_KNOWN_HOSTS_OK:
|
||||||
|
break; /* ok */
|
||||||
|
default:
|
||||||
|
SSH_LOG(SSH_LOG_WARN, "Couldn't verify knownhost during proxyjump.");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void *
|
||||||
|
jump_thread_func(void *arg)
|
||||||
|
{
|
||||||
|
struct jump_thread_data_struct *jump_thread_data = NULL;
|
||||||
|
struct ssh_jump_info_struct *jis = NULL;
|
||||||
|
struct ssh_jump_callbacks_struct *cb = NULL;
|
||||||
|
ssh_session jump_session = NULL;
|
||||||
|
ssh_channel caa = NULL;
|
||||||
|
int rc;
|
||||||
|
ssh_event event = NULL;
|
||||||
|
ssh_connector connector_in = NULL, connector_out = NULL;
|
||||||
|
ssh_session session = NULL;
|
||||||
|
int next_port;
|
||||||
|
char *next_hostname = NULL;
|
||||||
|
|
||||||
|
jump_thread_data = (struct jump_thread_data_struct *)arg;
|
||||||
|
session = jump_thread_data->session;
|
||||||
|
|
||||||
|
next_port = session->opts.port;
|
||||||
|
next_hostname = strdup(session->opts.host);
|
||||||
|
|
||||||
|
jump_session = ssh_new();
|
||||||
|
if (jump_session == NULL) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
rc = cb->before_connection(jump_session, cb->userdata);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
rc = ssh_connect(jump_session);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use the callback or default implementation for verifying knownhost */
|
||||||
|
if (cb != NULL && cb->verify_knownhost != NULL) {
|
||||||
|
rc = cb->verify_knownhost(jump_session, cb->userdata);
|
||||||
|
} else {
|
||||||
|
rc = verify_knownhost(jump_session);
|
||||||
|
}
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use the callback or publickey method to authenticate */
|
||||||
|
if (cb != NULL && cb->authenticate != NULL) {
|
||||||
|
rc = cb->authenticate(jump_session, cb->userdata);
|
||||||
|
} else {
|
||||||
|
rc = ssh_userauth_publickey_auto(jump_session, NULL, NULL);
|
||||||
|
}
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
SSH_LOG(SSH_LOG_WARN, "%s", ssh_get_error(jump_session));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
caa = ssh_channel_new(jump_session);
|
||||||
|
if (caa == NULL) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
/* The origin hostname and port are set to match OpenSSH implementation
|
||||||
|
* they are only used for logging on the server */
|
||||||
|
rc = ssh_channel_open_forward(caa,
|
||||||
|
next_hostname,
|
||||||
|
next_port,
|
||||||
|
"127.0.0.1",
|
||||||
|
65535);
|
||||||
|
if (rc != SSH_OK) {
|
||||||
|
SSH_LOG(SSH_LOG_WARN,
|
||||||
|
"Error opening port forwarding channel: %s",
|
||||||
|
ssh_get_error(jump_session));
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
event = ssh_event_new();
|
||||||
|
if (event == NULL) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
connector_in = ssh_connector_new(jump_session);
|
||||||
|
if (connector_in == NULL) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ssh_connector_set_out_channel(connector_in, caa, SSH_CONNECTOR_STDINOUT);
|
||||||
|
ssh_connector_set_in_fd(connector_in, jump_thread_data->fd);
|
||||||
|
ssh_event_add_connector(event, connector_in);
|
||||||
|
|
||||||
|
connector_out = ssh_connector_new(jump_session);
|
||||||
|
if (connector_out == NULL) {
|
||||||
|
goto exit;
|
||||||
|
}
|
||||||
|
ssh_connector_set_out_fd(connector_out, jump_thread_data->fd);
|
||||||
|
ssh_connector_set_in_channel(connector_out, caa, SSH_CONNECTOR_STDINOUT);
|
||||||
|
ssh_event_add_connector(event, connector_out);
|
||||||
|
|
||||||
|
while (ssh_channel_is_open(caa)) {
|
||||||
|
if (proxy_disconnect == 1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
rc = ssh_event_dopoll(event, 60000);
|
||||||
|
if (rc == SSH_ERROR) {
|
||||||
|
SSH_LOG(SSH_LOG_WARN,
|
||||||
|
"Error in ssh_event_dopoll() during proxy jump");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exit:
|
||||||
|
if (connector_in != NULL) {
|
||||||
|
ssh_event_remove_connector(event, connector_in);
|
||||||
|
ssh_connector_free(connector_in);
|
||||||
|
}
|
||||||
|
if (connector_out != NULL) {
|
||||||
|
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);
|
||||||
|
|
||||||
|
SAFE_FREE(jump_thread_data);
|
||||||
|
|
||||||
|
pthread_exit(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
ssh_socket_connect_proxyjump(ssh_socket s)
|
||||||
|
{
|
||||||
|
ssh_poll_handle h = NULL;
|
||||||
|
int rc;
|
||||||
|
pthread_t jump_thread;
|
||||||
|
struct jump_thread_data_struct *jump_thread_data = NULL;
|
||||||
|
socket_t pair[2];
|
||||||
|
|
||||||
|
if (s->state != SSH_SOCKET_NONE) {
|
||||||
|
ssh_set_error(
|
||||||
|
s->session,
|
||||||
|
SSH_FATAL,
|
||||||
|
"ssh_socket_connect_proxyjump called on socket not unconnected");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
jump_thread_data = calloc(1, sizeof(struct jump_thread_data_struct));
|
||||||
|
if (jump_thread_data == NULL) {
|
||||||
|
ssh_set_error_oom(s->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_FATAL,
|
||||||
|
"Creating socket pair failed: %s",
|
||||||
|
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||||
|
SAFE_FREE(jump_thread_data);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
jump_thread_data->session = s->session;
|
||||||
|
jump_thread_data->fd = pair[0];
|
||||||
|
|
||||||
|
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_FATAL,
|
||||||
|
"Creating new thread failed: %s",
|
||||||
|
ssh_strerror(rc, err_msg, SSH_ERRNO_MSG_MAX));
|
||||||
|
SAFE_FREE(jump_thread_data);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
rc = pthread_detach(jump_thread);
|
||||||
|
if (rc != 0) {
|
||||||
|
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||||
|
|
||||||
|
ssh_set_error(s->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;
|
||||||
|
}
|
||||||
|
|
||||||
|
SSH_LOG(SSH_LOG_DEBUG,
|
||||||
|
"ProxyJump connection pipe: [%d,%d]",
|
||||||
|
pair[0],
|
||||||
|
pair[1]);
|
||||||
|
ssh_socket_set_fd(s, pair[1]);
|
||||||
|
s->fd_is_socket = 1;
|
||||||
|
h = ssh_socket_get_poll_handle(s);
|
||||||
|
if (h == NULL) {
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
ssh_socket_set_connected(s, h);
|
||||||
|
if (s->callbacks && s->callbacks->connected) {
|
||||||
|
s->callbacks->connected(SSH_SOCKET_CONNECTED_OK,
|
||||||
|
0,
|
||||||
|
s->callbacks->userdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_PTHREAD */
|
||||||
|
|
||||||
|
#endif /* _WIN32 */
|
||||||
/** @} */
|
/** @} */
|
||||||
|
@ -18,6 +18,12 @@ set(TORTURE_LINK_LIBRARIES
|
|||||||
${CMOCKA_LIBRARY}
|
${CMOCKA_LIBRARY}
|
||||||
ssh::static)
|
ssh::static)
|
||||||
|
|
||||||
|
if (NOT WIN32)
|
||||||
|
set(TORTURE_LINK_LIBRARIES
|
||||||
|
${TORTURE_LINK_LIBRARIES}
|
||||||
|
pthread)
|
||||||
|
endif(NOT WIN32)
|
||||||
|
|
||||||
# create test library
|
# create test library
|
||||||
add_library(${TORTURE_LIBRARY}
|
add_library(${TORTURE_LIBRARY}
|
||||||
STATIC
|
STATIC
|
||||||
@ -257,7 +263,7 @@ if (CLIENT_TESTING OR SERVER_TESTING)
|
|||||||
# ssh_ping
|
# ssh_ping
|
||||||
add_executable(ssh_ping ssh_ping.c)
|
add_executable(ssh_ping ssh_ping.c)
|
||||||
target_compile_options(ssh_ping PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
|
target_compile_options(ssh_ping PRIVATE ${DEFAULT_C_COMPILE_FLAGS})
|
||||||
target_link_libraries(ssh_ping ssh::static)
|
target_link_libraries(ssh_ping ssh::static pthread)
|
||||||
|
|
||||||
# homedir will be used in passwd
|
# homedir will be used in passwd
|
||||||
set(HOMEDIR ${CMAKE_CURRENT_BINARY_DIR}/home)
|
set(HOMEDIR ${CMAKE_CURRENT_BINARY_DIR}/home)
|
||||||
|
@ -14,4 +14,4 @@ include_directories(${libssh_BINARY_DIR})
|
|||||||
|
|
||||||
add_executable(benchmarks ${benchmarks_SRCS})
|
add_executable(benchmarks ${benchmarks_SRCS})
|
||||||
|
|
||||||
target_link_libraries(benchmarks ssh::static)
|
target_link_libraries(benchmarks ssh::static pthread)
|
||||||
|
@ -33,6 +33,12 @@ if (WITH_PKCS11_URI)
|
|||||||
torture_auth_pkcs11)
|
torture_auth_pkcs11)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (HAVE_PTHREAD)
|
||||||
|
set(LIBSSH_CLIENT_TESTS
|
||||||
|
${LIBSSH_CLIENT_TESTS}
|
||||||
|
torture_proxyjump)
|
||||||
|
endif()
|
||||||
|
|
||||||
if (DEFAULT_C_NO_DEPRECATION_FLAGS)
|
if (DEFAULT_C_NO_DEPRECATION_FLAGS)
|
||||||
set_source_files_properties(torture_knownhosts.c
|
set_source_files_properties(torture_knownhosts.c
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
|
@ -93,7 +93,7 @@ static void torture_options_set_proxycommand(void **state)
|
|||||||
#ifdef WITH_EXEC
|
#ifdef WITH_EXEC
|
||||||
assert_ssh_return_code(session, rc);
|
assert_ssh_return_code(session, rc);
|
||||||
fd = ssh_get_fd(session);
|
fd = ssh_get_fd(session);
|
||||||
assert_true(fd != SSH_INVALID_SOCKET);
|
assert_int_not_equal(fd, SSH_INVALID_SOCKET);
|
||||||
rc = fcntl(fd, F_GETFL);
|
rc = fcntl(fd, F_GETFL);
|
||||||
assert_int_equal(rc & O_RDWR, O_RDWR);
|
assert_int_equal(rc & O_RDWR, O_RDWR);
|
||||||
#else
|
#else
|
||||||
@ -145,7 +145,7 @@ static void torture_options_set_proxycommand_ssh(void **state)
|
|||||||
#ifdef WITH_EXEC
|
#ifdef WITH_EXEC
|
||||||
assert_ssh_return_code(session, rc);
|
assert_ssh_return_code(session, rc);
|
||||||
fd = ssh_get_fd(session);
|
fd = ssh_get_fd(session);
|
||||||
assert_true(fd != SSH_INVALID_SOCKET);
|
assert_int_not_equal(fd, SSH_INVALID_SOCKET);
|
||||||
rc = fcntl(fd, F_GETFL);
|
rc = fcntl(fd, F_GETFL);
|
||||||
assert_int_equal(rc & O_RDWR, O_RDWR);
|
assert_int_equal(rc & O_RDWR, O_RDWR);
|
||||||
#else
|
#else
|
||||||
@ -176,7 +176,7 @@ static void torture_options_set_proxycommand_ssh_stderr(void **state)
|
|||||||
#ifdef WITH_EXEC
|
#ifdef WITH_EXEC
|
||||||
assert_ssh_return_code(session, rc);
|
assert_ssh_return_code(session, rc);
|
||||||
fd = ssh_get_fd(session);
|
fd = ssh_get_fd(session);
|
||||||
assert_true(fd != SSH_INVALID_SOCKET);
|
assert_int_not_equal(fd, SSH_INVALID_SOCKET);
|
||||||
rc = fcntl(fd, F_GETFL);
|
rc = fcntl(fd, F_GETFL);
|
||||||
assert_int_equal(rc & O_RDWR, O_RDWR);
|
assert_int_equal(rc & O_RDWR, O_RDWR);
|
||||||
#else
|
#else
|
||||||
|
222
tests/client/torture_proxyjump.c
Normal file
222
tests/client/torture_proxyjump.c
Normal file
@ -0,0 +1,222 @@
|
|||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#define LIBSSH_STATIC
|
||||||
|
|
||||||
|
#include "torture.h"
|
||||||
|
#include <libssh/libssh.h>
|
||||||
|
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
|
||||||
|
static int
|
||||||
|
sshd_setup(void **state)
|
||||||
|
{
|
||||||
|
torture_setup_sshd_server(state, false);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
sshd_teardown(void **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);
|
||||||
|
|
||||||
|
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||||
|
assert_ssh_return_code(s->ssh.session, rc);
|
||||||
|
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
||||||
|
assert_ssh_return_code(s->ssh.session, rc);
|
||||||
|
rc = ssh_options_set(s->ssh.session,
|
||||||
|
SSH_OPTIONS_USER,
|
||||||
|
TORTURE_SSH_USER_ALICE);
|
||||||
|
assert_ssh_return_code(s->ssh.session, rc);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
unsetenv("SSH_AUTH_SOCK");
|
||||||
|
unsetenv("SSH_AGENT_PID");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
session_teardown(void **state)
|
||||||
|
{
|
||||||
|
struct torture_state *s = *state;
|
||||||
|
|
||||||
|
ssh_disconnect(s->ssh.session);
|
||||||
|
ssh_free(s->ssh.session);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
torture_proxyjump_single_jump(void **state)
|
||||||
|
{
|
||||||
|
struct torture_state *s = *state;
|
||||||
|
ssh_session session = s->ssh.session;
|
||||||
|
char proxyjump_buf[500] = {0};
|
||||||
|
const char *address = torture_server_address(AF_INET);
|
||||||
|
int rc;
|
||||||
|
socket_t fd;
|
||||||
|
|
||||||
|
rc = snprintf(proxyjump_buf, sizeof(proxyjump_buf), "alice@%s:22", address);
|
||||||
|
if (rc < 0 || rc >= (int)sizeof(proxyjump_buf)) {
|
||||||
|
fail_msg("snprintf failed");
|
||||||
|
}
|
||||||
|
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP, proxyjump_buf);
|
||||||
|
assert_ssh_return_code(session, rc);
|
||||||
|
|
||||||
|
rc = ssh_connect(session);
|
||||||
|
assert_ssh_return_code(session, rc);
|
||||||
|
|
||||||
|
fd = ssh_get_fd(session);
|
||||||
|
assert_int_not_equal(fd, SSH_INVALID_SOCKET);
|
||||||
|
|
||||||
|
rc = fcntl(fd, F_GETFL);
|
||||||
|
assert_int_equal(rc & O_RDWR, O_RDWR);
|
||||||
|
|
||||||
|
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
|
||||||
|
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
before_connection(ssh_session jump_session, void *user)
|
||||||
|
{
|
||||||
|
(void)jump_session;
|
||||||
|
(void)user;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
verify_knownhost(ssh_session jump_session, void *user)
|
||||||
|
{
|
||||||
|
(void)jump_session;
|
||||||
|
(void)user;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
authenticate(ssh_session jump_session, void *user)
|
||||||
|
{
|
||||||
|
(void)user;
|
||||||
|
|
||||||
|
return ssh_userauth_publickey_auto(jump_session, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
torture_proxyjump_multiple_jump(void **state)
|
||||||
|
{
|
||||||
|
struct torture_state *s = *state;
|
||||||
|
ssh_session session = s->ssh.session;
|
||||||
|
char proxyjump_buf[500] = {0};
|
||||||
|
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
|
||||||
|
};
|
||||||
|
|
||||||
|
rc = snprintf(proxyjump_buf,
|
||||||
|
sizeof(proxyjump_buf),
|
||||||
|
"alice@%s:22,alice@%s:22",
|
||||||
|
address,
|
||||||
|
address);
|
||||||
|
if (rc < 0 || rc >= (int)sizeof(proxyjump_buf)) {
|
||||||
|
fail_msg("snprintf failed");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
assert_ssh_return_code(session, rc);
|
||||||
|
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND, &c);
|
||||||
|
assert_ssh_return_code(session, rc);
|
||||||
|
|
||||||
|
rc = ssh_connect(session);
|
||||||
|
assert_ssh_return_code(session, rc);
|
||||||
|
|
||||||
|
fd = ssh_get_fd(session);
|
||||||
|
assert_int_not_equal(fd, SSH_INVALID_SOCKET);
|
||||||
|
|
||||||
|
rc = fcntl(fd, F_GETFL);
|
||||||
|
assert_int_equal(rc & O_RDWR, O_RDWR);
|
||||||
|
|
||||||
|
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
|
||||||
|
assert_int_equal(rc, SSH_AUTH_SUCCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
torture_proxyjump_invalid_jump(void **state)
|
||||||
|
{
|
||||||
|
struct torture_state *s = *state;
|
||||||
|
ssh_session session = s->ssh.session;
|
||||||
|
char proxyjump_buf[500] = {0};
|
||||||
|
const char *address = torture_server_address(AF_INET);
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
rc = snprintf(proxyjump_buf,
|
||||||
|
sizeof(proxyjump_buf),
|
||||||
|
"doesnotexist@%s:54",
|
||||||
|
address);
|
||||||
|
if (rc < 0 || rc >= (int)sizeof(proxyjump_buf)) {
|
||||||
|
fail_msg("snprintf failed");
|
||||||
|
}
|
||||||
|
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP, proxyjump_buf);
|
||||||
|
assert_ssh_return_code(session, rc);
|
||||||
|
|
||||||
|
rc = ssh_connect(session);
|
||||||
|
assert_ssh_return_code_equal(session, rc, SSH_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
torture_run_tests(void)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
struct CMUnitTest tests[] = {
|
||||||
|
cmocka_unit_test_setup_teardown(torture_proxyjump_single_jump,
|
||||||
|
session_setup,
|
||||||
|
session_teardown),
|
||||||
|
cmocka_unit_test_setup_teardown(torture_proxyjump_multiple_jump,
|
||||||
|
session_setup,
|
||||||
|
session_teardown),
|
||||||
|
cmocka_unit_test_setup_teardown(torture_proxyjump_invalid_jump,
|
||||||
|
session_setup,
|
||||||
|
session_teardown),
|
||||||
|
};
|
||||||
|
|
||||||
|
ssh_init();
|
||||||
|
|
||||||
|
torture_filter_tests(tests);
|
||||||
|
rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
|
||||||
|
ssh_finalize();
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
@ -4,7 +4,7 @@ macro(fuzzer name)
|
|||||||
add_executable(${name} ${name}.c)
|
add_executable(${name} ${name}.c)
|
||||||
target_link_libraries(${name}
|
target_link_libraries(${name}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
ssh::static)
|
ssh::static pthread)
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
set_target_properties(${name}
|
set_target_properties(${name}
|
||||||
PROPERTIES
|
PROPERTIES
|
||||||
|
@ -1653,6 +1653,9 @@ void torture_write_file(const char *filename, const char *data){
|
|||||||
void torture_reset_config(ssh_session session)
|
void torture_reset_config(ssh_session session)
|
||||||
{
|
{
|
||||||
memset(session->opts.options_seen, 0, sizeof(session->opts.options_seen));
|
memset(session->opts.options_seen, 0, sizeof(session->opts.options_seen));
|
||||||
|
if (ssh_libssh_proxy_jumps()) {
|
||||||
|
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void torture_unsetenv(const char *variable)
|
void torture_unsetenv(const char *variable)
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "match.c"
|
#include "match.c"
|
||||||
#include "config.c"
|
#include "config.c"
|
||||||
#include "libssh/socket.h"
|
#include "libssh/socket.h"
|
||||||
|
#include "libssh/misc.h"
|
||||||
|
|
||||||
extern LIBSSH_THREAD int ssh_log_level;
|
extern LIBSSH_THREAD int ssh_log_level;
|
||||||
|
|
||||||
@ -691,6 +692,34 @@ static void torture_config_auth_methods_string(void **state)
|
|||||||
torture_config_auth_methods(state, NULL, LIBSSH_TESTCONFIG_STRING8);
|
torture_config_auth_methods(state, NULL, LIBSSH_TESTCONFIG_STRING8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Helper for checking hostname, username and port of ssh_jump_info_struct
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
helper_proxy_jump_check(struct ssh_iterator *jump,
|
||||||
|
const char *hostname,
|
||||||
|
const char *username,
|
||||||
|
const char *port)
|
||||||
|
{
|
||||||
|
struct ssh_jump_info_struct *jis =
|
||||||
|
ssh_iterator_value(struct ssh_jump_info_struct *, jump);
|
||||||
|
|
||||||
|
assert_string_equal(jis->hostname, hostname);
|
||||||
|
|
||||||
|
if (username != NULL) {
|
||||||
|
assert_string_equal(jis->username, username);
|
||||||
|
} else {
|
||||||
|
assert_null(jis->username);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (port != NULL) {
|
||||||
|
int iport = strtol(port, NULL, 10);
|
||||||
|
assert_int_equal(jis->port, iport);
|
||||||
|
} else {
|
||||||
|
assert_int_equal(jis->port, 22);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Verify the configuration parser does not choke on unknown
|
* @brief Verify the configuration parser does not choke on unknown
|
||||||
* or unsupported configuration options
|
* or unsupported configuration options
|
||||||
@ -702,6 +731,8 @@ static void torture_config_unknown(void **state,
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* test corner cases */
|
/* test corner cases */
|
||||||
|
/* Without libssh proxy jump */
|
||||||
|
torture_setenv("OPENSSH_PROXYJUMP", "1");
|
||||||
_parse_config(session, file, string, SSH_OK);
|
_parse_config(session, file, string, SSH_OK);
|
||||||
assert_string_equal(session->opts.ProxyCommand,
|
assert_string_equal(session->opts.ProxyCommand,
|
||||||
"ssh -W '[%h]:%p' many-spaces.com");
|
"ssh -W '[%h]:%p' many-spaces.com");
|
||||||
@ -711,6 +742,7 @@ static void torture_config_unknown(void **state,
|
|||||||
assert_true(ret == 0);
|
assert_true(ret == 0);
|
||||||
ret = ssh_config_parse_file(session, GLOBAL_CLIENT_CONFIG);
|
ret = ssh_config_parse_file(session, GLOBAL_CLIENT_CONFIG);
|
||||||
assert_true(ret == 0);
|
assert_true(ret == 0);
|
||||||
|
torture_unsetenv("OPENSSH_PROXYJUMP");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -994,8 +1026,89 @@ static void torture_config_proxyjump(void **state,
|
|||||||
const char *file, const char *string)
|
const char *file, const char *string)
|
||||||
{
|
{
|
||||||
ssh_session session = *state;
|
ssh_session session = *state;
|
||||||
|
|
||||||
const char *config;
|
const char *config;
|
||||||
|
|
||||||
|
|
||||||
|
/* Tests for libssh based proxyjump */
|
||||||
|
/* Simplest version with just a hostname */
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "simple");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root,
|
||||||
|
"jumpbox",
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* With username */
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "user");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root,
|
||||||
|
"jumpbox",
|
||||||
|
"user",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* With port */
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "port");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root,
|
||||||
|
"jumpbox",
|
||||||
|
NULL,
|
||||||
|
"2222");
|
||||||
|
|
||||||
|
/* Two step jump */
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "two-step");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root,
|
||||||
|
"second",
|
||||||
|
"u2",
|
||||||
|
"33");
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root->next,
|
||||||
|
"first",
|
||||||
|
"u1",
|
||||||
|
"222");
|
||||||
|
|
||||||
|
/* none */
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "none");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
assert_int_equal(ssh_list_count(session->opts.proxy_jumps), 0);
|
||||||
|
|
||||||
|
/* If also ProxyCommand is specified, the first is applied */
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "only-command");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
assert_string_equal(session->opts.ProxyCommand, PROXYCMD);
|
||||||
|
assert_int_equal(ssh_list_count(session->opts.proxy_jumps), 0);
|
||||||
|
|
||||||
|
/* If also ProxyCommand is specified, the first is applied */
|
||||||
|
torture_reset_config(session);
|
||||||
|
SAFE_FREE(session->opts.ProxyCommand);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "only-jump");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
assert_null(session->opts.ProxyCommand);
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root,
|
||||||
|
"jumpbox",
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* IPv6 address */
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "ipv6");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root,
|
||||||
|
"2620:52:0::fed",
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
torture_reset_config(session);
|
||||||
|
|
||||||
|
/* Tests for proxycommand based proxyjump */
|
||||||
|
torture_setenv("OPENSSH_PROXYJUMP", "1");
|
||||||
|
|
||||||
/* Simplest version with just a hostname */
|
/* Simplest version with just a hostname */
|
||||||
torture_reset_config(session);
|
torture_reset_config(session);
|
||||||
ssh_options_set(session, SSH_OPTIONS_HOST, "simple");
|
ssh_options_set(session, SSH_OPTIONS_HOST, "simple");
|
||||||
@ -1049,6 +1162,7 @@ static void torture_config_proxyjump(void **state,
|
|||||||
assert_string_equal(session->opts.ProxyCommand,
|
assert_string_equal(session->opts.ProxyCommand,
|
||||||
"ssh -W '[%h]:%p' 2620:52:0::fed");
|
"ssh -W '[%h]:%p' 2620:52:0::fed");
|
||||||
|
|
||||||
|
|
||||||
/* Multiple @ is allowed in second jump */
|
/* Multiple @ is allowed in second jump */
|
||||||
config = "Host allowed-hostname\n"
|
config = "Host allowed-hostname\n"
|
||||||
"\tProxyJump localhost,user@principal.com@jumpbox:22\n";
|
"\tProxyJump localhost,user@principal.com@jumpbox:22\n";
|
||||||
@ -1076,8 +1190,44 @@ static void torture_config_proxyjump(void **state,
|
|||||||
_parse_config(session, file, string, SSH_OK);
|
_parse_config(session, file, string, SSH_OK);
|
||||||
assert_string_equal(session->opts.ProxyCommand,
|
assert_string_equal(session->opts.ProxyCommand,
|
||||||
"ssh -l user@principal.com -p 22 -W '[%h]:%p' jumpbox");
|
"ssh -l user@principal.com -p 22 -W '[%h]:%p' jumpbox");
|
||||||
|
torture_unsetenv("OPENSSH_PROXYJUMP");
|
||||||
|
|
||||||
|
/* Tests for libssh based proxyjump */
|
||||||
|
/* Multiple @ is allowed in second jump */
|
||||||
|
config = "Host allowed-hostname\n"
|
||||||
|
"\tProxyJump localhost,user@principal.com@jumpbox:22\n";
|
||||||
|
if (file != NULL) {
|
||||||
|
torture_write_file(file, config);
|
||||||
|
} else {
|
||||||
|
string = config;
|
||||||
|
}
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "allowed-hostname");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root,
|
||||||
|
"jumpbox",
|
||||||
|
"user@principal.com",
|
||||||
|
"22");
|
||||||
|
|
||||||
|
/* Multiple @ is allowed */
|
||||||
|
config = "Host allowed-hostname\n"
|
||||||
|
"\tProxyJump user@principal.com@jumpbox:22\n";
|
||||||
|
if (file != NULL) {
|
||||||
|
torture_write_file(file, config);
|
||||||
|
} else {
|
||||||
|
string = config;
|
||||||
|
}
|
||||||
|
torture_reset_config(session);
|
||||||
|
ssh_options_set(session, SSH_OPTIONS_HOST, "allowed-hostname");
|
||||||
|
_parse_config(session, file, string, SSH_OK);
|
||||||
|
helper_proxy_jump_check(session->opts.proxy_jumps->root,
|
||||||
|
"jumpbox",
|
||||||
|
"user@principal.com",
|
||||||
|
"22");
|
||||||
|
torture_reset_config(session);
|
||||||
|
|
||||||
/* In this part, we try various other config files and strings. */
|
/* In this part, we try various other config files and strings. */
|
||||||
|
torture_setenv("OPENSSH_PROXYJUMP", "1");
|
||||||
|
|
||||||
/* Try to create some invalid configurations */
|
/* Try to create some invalid configurations */
|
||||||
/* Non-numeric port */
|
/* Non-numeric port */
|
||||||
@ -1223,6 +1373,8 @@ static void torture_config_proxyjump(void **state,
|
|||||||
torture_reset_config(session);
|
torture_reset_config(session);
|
||||||
ssh_options_set(session, SSH_OPTIONS_HOST, "no-port");
|
ssh_options_set(session, SSH_OPTIONS_HOST, "no-port");
|
||||||
_parse_config(session, file, string, SSH_ERROR);
|
_parse_config(session, file, string, SSH_ERROR);
|
||||||
|
|
||||||
|
torture_unsetenv("OPENSSH_PROXYJUMP");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Reference in New Issue
Block a user