mirror of
https://git.libssh.org/projects/libssh.git
synced 2026-03-24 20:40:09 +09:00
Compare commits
12 Commits
76b14eaed7
...
dc39902006
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc39902006 | ||
|
|
29dd7874cd | ||
|
|
8a134e03db | ||
|
|
39d931f7e5 | ||
|
|
b4f6d8b800 | ||
|
|
c78d2bb8fb | ||
|
|
5b0cee7c1b | ||
|
|
59ed66b684 | ||
|
|
ce0b616bc6 | ||
|
|
31ceec02fe | ||
|
|
a7cf4bb37b | ||
|
|
3dfaa70fcf |
@@ -432,6 +432,7 @@ enum ssh_options_e {
|
||||
SSH_OPTIONS_ADDRESS_FAMILY,
|
||||
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE,
|
||||
SSH_OPTIONS_GSSAPI_KEY_EXCHANGE_ALGS,
|
||||
SSH_OPTIONS_NEXT_IDENTITY,
|
||||
};
|
||||
|
||||
enum {
|
||||
|
||||
@@ -43,8 +43,9 @@ extern "C" {
|
||||
|
||||
/* in misc.c */
|
||||
/* gets the user home dir. */
|
||||
char *ssh_get_user_home_dir(void);
|
||||
char *ssh_get_user_home_dir(ssh_session session);
|
||||
char *ssh_get_local_username(void);
|
||||
char *ssh_get_local_hostname(void);
|
||||
int ssh_file_readaccess_ok(const char *file);
|
||||
int ssh_dir_writeable(const char *path);
|
||||
|
||||
|
||||
@@ -246,13 +246,16 @@ struct ssh_session_struct {
|
||||
struct {
|
||||
struct ssh_list *identity;
|
||||
struct ssh_list *identity_non_exp;
|
||||
struct ssh_iterator *identity_it;
|
||||
struct ssh_list *certificate;
|
||||
struct ssh_list *certificate_non_exp;
|
||||
struct ssh_list *proxy_jumps;
|
||||
struct ssh_list *proxy_jumps_user_cb;
|
||||
char *proxy_jumps_str;
|
||||
char *username;
|
||||
char *host;
|
||||
char *bindaddr; /* bind the client to an ip addr */
|
||||
char *homedir;
|
||||
char *sshdir;
|
||||
char *knownhosts;
|
||||
char *global_knownhosts;
|
||||
|
||||
15
src/config.c
15
src/config.c
@@ -493,6 +493,10 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
||||
bool parse_entry = do_parsing;
|
||||
bool libssh_proxy_jump = ssh_libssh_proxy_jumps();
|
||||
|
||||
if (do_parsing) {
|
||||
SAFE_FREE(session->opts.proxy_jumps_str);
|
||||
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||
}
|
||||
/* Special value none disables the proxy */
|
||||
cmp = strcasecmp(s, "none");
|
||||
if (cmp == 0) {
|
||||
@@ -509,6 +513,17 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing)
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
if (do_parsing) {
|
||||
/* Store the whole string in sesion */
|
||||
SAFE_FREE(session->opts.proxy_jumps_str);
|
||||
session->opts.proxy_jumps_str = strdup(s);
|
||||
if (session->opts.proxy_jumps_str == NULL) {
|
||||
free(c);
|
||||
ssh_set_error_oom(session);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
cp = c;
|
||||
do {
|
||||
endp = strchr(cp, ',');
|
||||
|
||||
@@ -305,6 +305,87 @@ static void ssh_connector_reset_pollevents(ssh_connector connector)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @brief Update the connector's flags after a read-write io
|
||||
* operation
|
||||
*
|
||||
* This should be called after some data is successfully read from
|
||||
* connector's input and written to connector's output.
|
||||
*
|
||||
* @param[in, out] connector Connector for which the io operation occured.
|
||||
*
|
||||
* @warning This does not consider the case when the io indicated failure
|
||||
*
|
||||
* @warning This does not consider the case when the input indicated that
|
||||
* EOF was encountered.
|
||||
*/
|
||||
static void ssh_connector_update_flags_after_io(ssh_connector connector)
|
||||
{
|
||||
/*
|
||||
* With fds we can afford to mark:
|
||||
* - in_available as 0 after an fd read (even if more pending data can be
|
||||
* immediately read from the fd)
|
||||
*
|
||||
* - out_wontblock as 0 after an fd write (even if more data can
|
||||
* be written to the fd without blocking)
|
||||
*
|
||||
* since poll events set on the fd will get raised to indicate
|
||||
* possibility of read/write in case existing situation is apt
|
||||
* (i.e can read/write occur right now) or if situation becomes
|
||||
* apt in future (read data becomes available, write becomes
|
||||
* possible)
|
||||
*/
|
||||
|
||||
/*
|
||||
* On the other hand, with channels we need to be more careful
|
||||
* before claiming read/write not possible because channel callbacks
|
||||
* are called in limited scenarios.
|
||||
*
|
||||
* (e.g. connector callback to indicate read data available on input
|
||||
* channel is called only when new data is received on channel. It is
|
||||
* not called when we have some pending data in channel's buffers but
|
||||
* don't receive any new data on the channel)
|
||||
*
|
||||
* Hence, in case of channels, blindly setting flag associated with
|
||||
* read/write input/output to 0 after a read/write may not be a good
|
||||
* idea as the callback that sets it back to 1 again may not be ever
|
||||
* called again.
|
||||
*/
|
||||
|
||||
uint32_t window_size;
|
||||
|
||||
/* update in_available based on input source (fd or channel) */
|
||||
if (connector->in_fd != SSH_INVALID_SOCKET) {
|
||||
connector->in_available = 0;
|
||||
} else if (connector->in_channel != NULL) {
|
||||
if (ssh_channel_poll_timeout(connector->in_channel, 0, 0) > 0) {
|
||||
connector->in_available = 1;
|
||||
} else {
|
||||
connector->in_available = 0;
|
||||
}
|
||||
} else {
|
||||
/* connector input is invalid ! */
|
||||
return;
|
||||
}
|
||||
|
||||
/* update out_wontblock based on output source (fd or channel) */
|
||||
if (connector->out_fd != SSH_INVALID_SOCKET) {
|
||||
connector->out_wontblock = 0;
|
||||
} else if (connector->out_channel != NULL) {
|
||||
window_size = ssh_channel_window_size(connector->out_channel);
|
||||
if (window_size > 0) {
|
||||
connector->out_wontblock = 1;
|
||||
} else {
|
||||
connector->out_wontblock = 0;
|
||||
}
|
||||
} else {
|
||||
/* connector output is invalid ! */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
@@ -390,8 +471,8 @@ static void ssh_connector_fd_in_cb(ssh_connector connector)
|
||||
ssh_set_error(connector->session, SSH_FATAL, "output socket or channel closed");
|
||||
return;
|
||||
}
|
||||
connector->out_wontblock = 0;
|
||||
connector->in_available = 0;
|
||||
|
||||
ssh_connector_update_flags_after_io(connector);
|
||||
} else {
|
||||
connector->in_available = 1;
|
||||
}
|
||||
@@ -444,8 +525,8 @@ ssh_connector_fd_out_cb(ssh_connector connector)
|
||||
"Output socket or channel closed");
|
||||
return;
|
||||
}
|
||||
connector->in_available = 0;
|
||||
connector->out_wontblock = 0;
|
||||
|
||||
ssh_connector_update_flags_after_io(connector);
|
||||
} else {
|
||||
connector->out_wontblock = 1;
|
||||
}
|
||||
@@ -566,11 +647,7 @@ static int ssh_connector_channel_data_cb(ssh_session session,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
connector->out_wontblock = 0;
|
||||
connector->in_available = 0;
|
||||
if ((unsigned int)w < len) {
|
||||
connector->in_available = 1;
|
||||
}
|
||||
ssh_connector_update_flags_after_io(connector);
|
||||
ssh_connector_reset_pollevents(connector);
|
||||
|
||||
return w;
|
||||
@@ -642,8 +719,8 @@ ssh_connector_channel_write_wontblock_cb(ssh_session session,
|
||||
|
||||
return 0;
|
||||
}
|
||||
connector->in_available = 0;
|
||||
connector->out_wontblock = 0;
|
||||
|
||||
ssh_connector_update_flags_after_io(connector);
|
||||
} else {
|
||||
connector->out_wontblock = 1;
|
||||
}
|
||||
|
||||
19
src/gssapi.c
19
src/gssapi.c
@@ -198,7 +198,7 @@ int
|
||||
ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
uint32_t n_oid, ssh_string *oids)
|
||||
{
|
||||
char hostname[NI_MAXHOST] = {0};
|
||||
char *hostname = NULL;
|
||||
OM_uint32 maj_stat, min_stat;
|
||||
size_t i;
|
||||
gss_OID_set supported; /* oids supported by server */
|
||||
@@ -210,14 +210,6 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
int rc;
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
|
||||
rc = gethostname(hostname, 64);
|
||||
if (rc != 0) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Error getting hostname: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
/* Destroy earlier GSSAPI context if any */
|
||||
ssh_gssapi_free(session);
|
||||
rc = ssh_gssapi_init(session);
|
||||
@@ -284,7 +276,16 @@ ssh_gssapi_handle_userauth(ssh_session session, const char *user,
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
hostname = ssh_get_local_hostname();
|
||||
if (hostname == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Error getting hostname: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_import_name(session->gssapi, hostname);
|
||||
SAFE_FREE(hostname);
|
||||
if (rc != SSH_OK) {
|
||||
ssh_auth_reply_default(session, 0);
|
||||
gss_release_oid_set(&min_stat, &both_supported);
|
||||
|
||||
@@ -421,7 +421,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
gss_name_t client_name = GSS_C_NO_NAME;
|
||||
OM_uint32 ret_flags = 0;
|
||||
gss_buffer_desc mic = GSS_C_EMPTY_BUFFER, msg = GSS_C_EMPTY_BUFFER;
|
||||
char hostname[NI_MAXHOST] = {0};
|
||||
char *hostname = NULL;
|
||||
char err_msg[SSH_ERRNO_MSG_MAX] = {0};
|
||||
|
||||
rc = ssh_buffer_unpack(packet, "S", &otoken);
|
||||
@@ -538,8 +538,8 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
goto error;
|
||||
}
|
||||
|
||||
rc = gethostname(hostname, 64);
|
||||
if (rc != 0) {
|
||||
hostname = ssh_get_local_hostname();
|
||||
if (hostname == NULL) {
|
||||
SSH_LOG(SSH_LOG_TRACE,
|
||||
"Error getting hostname: %s",
|
||||
ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
|
||||
@@ -547,6 +547,7 @@ int ssh_server_gss_kex_process_init(ssh_session session, ssh_buffer packet)
|
||||
}
|
||||
|
||||
rc = ssh_gssapi_import_name(session->gssapi, hostname);
|
||||
SAFE_FREE(hostname);
|
||||
if (rc != SSH_OK) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
@@ -615,10 +615,10 @@ int ssh_publickey_to_file(ssh_session session,
|
||||
FILE *fp = NULL;
|
||||
char *user = NULL;
|
||||
char buffer[1024];
|
||||
char host[256];
|
||||
char *host = NULL;
|
||||
unsigned char *pubkey_64 = NULL;
|
||||
size_t len;
|
||||
int rc;
|
||||
|
||||
if(session==NULL)
|
||||
return SSH_ERROR;
|
||||
if(file==NULL || pubkey==NULL){
|
||||
@@ -636,8 +636,8 @@ int ssh_publickey_to_file(ssh_session session,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = gethostname(host, sizeof(host));
|
||||
if (rc < 0) {
|
||||
host = ssh_get_local_hostname();
|
||||
if (host == NULL) {
|
||||
SAFE_FREE(user);
|
||||
SAFE_FREE(pubkey_64);
|
||||
return SSH_ERROR;
|
||||
@@ -651,6 +651,7 @@ int ssh_publickey_to_file(ssh_session session,
|
||||
|
||||
SAFE_FREE(pubkey_64);
|
||||
SAFE_FREE(user);
|
||||
SAFE_FREE(host);
|
||||
|
||||
SSH_LOG(SSH_LOG_RARE, "Trying to write public key file: %s", file);
|
||||
SSH_LOG(SSH_LOG_PACKET, "public key file content: %s", buffer);
|
||||
|
||||
327
src/misc.c
327
src/misc.c
@@ -108,22 +108,22 @@
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
char *ssh_get_user_home_dir(void)
|
||||
static char *ssh_get_user_home_dir_internal(void)
|
||||
{
|
||||
char tmp[PATH_MAX] = {0};
|
||||
char *szPath = NULL;
|
||||
char tmp[PATH_MAX] = {0};
|
||||
char *szPath = NULL;
|
||||
|
||||
if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
|
||||
szPath = malloc(strlen(tmp) + 1);
|
||||
if (szPath == NULL) {
|
||||
return NULL;
|
||||
if (SHGetSpecialFolderPathA(NULL, tmp, CSIDL_PROFILE, TRUE)) {
|
||||
szPath = malloc(strlen(tmp) + 1);
|
||||
if (szPath == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
strcpy(szPath, tmp);
|
||||
return szPath;
|
||||
}
|
||||
|
||||
strcpy(szPath, tmp);
|
||||
return szPath;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* we have read access on file */
|
||||
@@ -298,7 +298,7 @@ int ssh_is_ipaddr(const char *str)
|
||||
#define NSS_BUFLEN_PASSWD 4096
|
||||
#endif /* NSS_BUFLEN_PASSWD */
|
||||
|
||||
char *ssh_get_user_home_dir(void)
|
||||
static char *ssh_get_user_home_dir_internal(void)
|
||||
{
|
||||
char *szPath = NULL;
|
||||
struct passwd pwd;
|
||||
@@ -313,7 +313,6 @@ char *ssh_get_user_home_dir(void)
|
||||
return NULL;
|
||||
}
|
||||
snprintf(buf, sizeof(buf), "%s", szPath);
|
||||
|
||||
return strdup(buf);
|
||||
}
|
||||
|
||||
@@ -428,6 +427,29 @@ int ssh_is_ipaddr(const char *str)
|
||||
|
||||
#endif /* _WIN32 */
|
||||
|
||||
char *ssh_get_user_home_dir(ssh_session session)
|
||||
{
|
||||
char *szPath = NULL;
|
||||
|
||||
/* If used previously, reuse cached value */
|
||||
if (session != NULL && session->opts.homedir != NULL) {
|
||||
return strdup(session->opts.homedir);
|
||||
}
|
||||
|
||||
szPath = ssh_get_user_home_dir_internal();
|
||||
if (szPath == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (session != NULL) {
|
||||
/* cache it:
|
||||
* failure is not fatal -- at worst we will just not cache it */
|
||||
session->opts.homedir = strdup(szPath);
|
||||
}
|
||||
|
||||
return szPath;
|
||||
}
|
||||
|
||||
char *ssh_lowercase(const char* str)
|
||||
{
|
||||
char *new = NULL, *p = NULL;
|
||||
@@ -468,6 +490,38 @@ char *ssh_hostport(const char *host, int port)
|
||||
return dest;
|
||||
}
|
||||
|
||||
static char *
|
||||
ssh_get_hexa_internal(const unsigned char *what, size_t len, bool colons)
|
||||
{
|
||||
const char h[] = "0123456789abcdef";
|
||||
char *hexa = NULL;
|
||||
size_t i;
|
||||
size_t bytes_per_byte = 2 + (colons ? 1 : 0);
|
||||
size_t hlen = len * bytes_per_byte;
|
||||
|
||||
if (len > (UINT_MAX - 1) / bytes_per_byte) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hexa = calloc(hlen + 1, sizeof(char));
|
||||
if (hexa == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
hexa[i * bytes_per_byte] = h[(what[i] >> 4) & 0xF];
|
||||
hexa[i * bytes_per_byte + 1] = h[what[i] & 0xF];
|
||||
if (colons) {
|
||||
hexa[i * bytes_per_byte + 2] = ':';
|
||||
}
|
||||
}
|
||||
if (colons) {
|
||||
hexa[hlen - 1] = '\0';
|
||||
}
|
||||
|
||||
return hexa;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a buffer into a colon separated hex string.
|
||||
* The caller has to free the memory.
|
||||
@@ -483,28 +537,7 @@ char *ssh_hostport(const char *host, int port)
|
||||
*/
|
||||
char *ssh_get_hexa(const unsigned char *what, size_t len)
|
||||
{
|
||||
const char h[] = "0123456789abcdef";
|
||||
char *hexa = NULL;
|
||||
size_t i;
|
||||
size_t hlen = len * 3;
|
||||
|
||||
if (len > (UINT_MAX - 1) / 3) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
hexa = malloc(hlen + 1);
|
||||
if (hexa == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
hexa[i * 3] = h[(what[i] >> 4) & 0xF];
|
||||
hexa[i * 3 + 1] = h[what[i] & 0xF];
|
||||
hexa[i * 3 + 2] = ':';
|
||||
}
|
||||
hexa[hlen - 1] = '\0';
|
||||
|
||||
return hexa;
|
||||
return ssh_get_hexa_internal(what, len, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1189,7 +1222,7 @@ char *ssh_path_expand_tilde(const char *d)
|
||||
} else {
|
||||
ld = strlen(d);
|
||||
p = (char *) d;
|
||||
h = ssh_get_user_home_dir();
|
||||
h = ssh_get_user_home_dir(NULL);
|
||||
}
|
||||
if (h == NULL) {
|
||||
return NULL;
|
||||
@@ -1211,15 +1244,105 @@ char *ssh_path_expand_tilde(const char *d)
|
||||
return r;
|
||||
}
|
||||
|
||||
char *ssh_get_local_hostname(void)
|
||||
{
|
||||
char host[NI_MAXHOST] = {0};
|
||||
int rc;
|
||||
|
||||
rc = gethostname(host, sizeof(host));
|
||||
if (rc != 0) {
|
||||
return NULL;
|
||||
}
|
||||
return strdup(host);
|
||||
}
|
||||
|
||||
static char *get_connection_hash(ssh_session session)
|
||||
{
|
||||
unsigned char conn_hash[SHA_DIGEST_LENGTH];
|
||||
char *local_hostname = NULL;
|
||||
SHACTX ctx = sha1_init();
|
||||
char strport[10] = {0};
|
||||
unsigned int port;
|
||||
int rc;
|
||||
|
||||
if (session == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ctx == NULL) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Local hostname %l */
|
||||
local_hostname = ssh_get_local_hostname();
|
||||
if (local_hostname == NULL) {
|
||||
goto err;
|
||||
}
|
||||
rc = sha1_update(ctx, local_hostname, strlen(local_hostname));
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
SAFE_FREE(local_hostname);
|
||||
|
||||
/* Remote hostname %h */
|
||||
rc = sha1_update(ctx, session->opts.host, strlen(session->opts.host));
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Remote port %p */
|
||||
ssh_options_get_port(session, &port);
|
||||
snprintf(strport, sizeof(strport), "%d", port);
|
||||
rc = sha1_update(ctx, strport, strlen(strport));
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* The remote username %r */
|
||||
rc = sha1_update(ctx,
|
||||
session->opts.username,
|
||||
strlen(session->opts.username));
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* ProxyJump */
|
||||
if (session->opts.proxy_jumps_str != NULL) {
|
||||
rc = sha1_update(ctx,
|
||||
session->opts.proxy_jumps_str,
|
||||
strlen(session->opts.proxy_jumps_str));
|
||||
}
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Frees context */
|
||||
rc = sha1_final(conn_hash, ctx);
|
||||
if (rc != SSH_OK) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
return ssh_get_hexa_internal(conn_hash, SHA_DIGEST_LENGTH, false);
|
||||
|
||||
err:
|
||||
free(local_hostname);
|
||||
sha1_ctx_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** @internal
|
||||
* @brief expands a string in function of session options
|
||||
*
|
||||
* @param[in] s Format string to expand. Known parameters:
|
||||
* %d SSH configuration directory (~/.ssh)
|
||||
* %h target host name
|
||||
* %u local username
|
||||
* %l local hostname
|
||||
* %r remote username
|
||||
* %p remote port
|
||||
* - %d user home directory (~)
|
||||
* - %h target host name
|
||||
* - %u local username
|
||||
* - %l local hostname
|
||||
* - %r remote username
|
||||
* - %p remote port
|
||||
* - %j proxyjump string
|
||||
* - %C Hash of %l%h%p%r%j
|
||||
*
|
||||
* @returns Expanded string. The caller needs to free the memory using
|
||||
* ssh_string_free_char().
|
||||
*
|
||||
@@ -1227,7 +1350,6 @@ char *ssh_path_expand_tilde(const char *d)
|
||||
*/
|
||||
char *ssh_path_expand_escape(ssh_session session, const char *s)
|
||||
{
|
||||
char host[NI_MAXHOST] = {0};
|
||||
char *buf = NULL;
|
||||
char *r = NULL;
|
||||
char *x = NULL;
|
||||
@@ -1276,65 +1398,67 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
|
||||
}
|
||||
|
||||
switch (*p) {
|
||||
case '%':
|
||||
goto escape;
|
||||
case 'd':
|
||||
if (session->opts.sshdir) {
|
||||
x = strdup(session->opts.sshdir);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Cannot expand sshdir");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
x = ssh_get_local_username();
|
||||
break;
|
||||
case 'l':
|
||||
if (gethostname(host, sizeof(host) == 0)) {
|
||||
x = strdup(host);
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
if (session->opts.host) {
|
||||
x = strdup(session->opts.host);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Cannot expand host");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (session->opts.username) {
|
||||
x = strdup(session->opts.username);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Cannot expand username");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
{
|
||||
char tmp[6];
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "%hu",
|
||||
(uint16_t)(session->opts.port > 0 ? session->opts.port
|
||||
: 22));
|
||||
x = strdup(tmp);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"Wrong escape sequence detected");
|
||||
case '%':
|
||||
goto escape;
|
||||
case 'd':
|
||||
x = ssh_get_user_home_dir(session);
|
||||
if (x == NULL) {
|
||||
ssh_set_error(session, SSH_FATAL, "Cannot expand homedir");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'u':
|
||||
x = ssh_get_local_username();
|
||||
break;
|
||||
case 'l':
|
||||
x = ssh_get_local_hostname();
|
||||
break;
|
||||
case 'h':
|
||||
if (session->opts.host) {
|
||||
x = strdup(session->opts.host);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL, "Cannot expand host");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
if (session->opts.username) {
|
||||
x = strdup(session->opts.username);
|
||||
} else {
|
||||
ssh_set_error(session, SSH_FATAL, "Cannot expand username");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case 'p': {
|
||||
char tmp[6];
|
||||
unsigned int port;
|
||||
|
||||
ssh_options_get_port(session, &port);
|
||||
snprintf(tmp, sizeof(tmp), "%u", port);
|
||||
x = strdup(tmp);
|
||||
break;
|
||||
}
|
||||
case 'j':
|
||||
if (session->opts.proxy_jumps_str != NULL) {
|
||||
x = strdup(session->opts.proxy_jumps_str);
|
||||
} else {
|
||||
x = strdup("");
|
||||
}
|
||||
break;
|
||||
case 'C':
|
||||
x = get_connection_hash(session);
|
||||
break;
|
||||
default:
|
||||
ssh_set_error(session, SSH_FATAL, "Wrong escape sequence detected");
|
||||
free(buf);
|
||||
free(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (x == NULL) {
|
||||
@@ -1346,8 +1470,7 @@ char *ssh_path_expand_escape(ssh_session session, const char *s)
|
||||
|
||||
i += strlen(x);
|
||||
if (i >= MAX_BUF_SIZE) {
|
||||
ssh_set_error(session, SSH_FATAL,
|
||||
"String too long");
|
||||
ssh_set_error(session, SSH_FATAL, "String too long");
|
||||
free(buf);
|
||||
free(x);
|
||||
free(r);
|
||||
|
||||
@@ -1197,7 +1197,6 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
|
||||
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;
|
||||
@@ -1562,7 +1561,16 @@ int ssh_options_get_port(ssh_session session, unsigned int* port_target) {
|
||||
* - SSH_OPTIONS_IDENTITY:
|
||||
* Get the first identity file name (const char *).\n
|
||||
* \n
|
||||
* By default id_rsa, id_ecdsa and id_ed25519 files are used.
|
||||
* By default `id_rsa`, `id_ecdsa`, `id_ed25519`, `id_ecdsa_sk`
|
||||
* and `id_ed25519_sk` (when SK support is built in) files are
|
||||
* used.
|
||||
*
|
||||
* - SSH_OPTIONS_NEXT_IDENTITY:
|
||||
* Get the next identity file name (const char *).\n
|
||||
* \n
|
||||
* Repeat calls to get all key paths. SSH_EOF is returned when
|
||||
* the end of list is reached. Another call will start another
|
||||
* iteration over the same list.
|
||||
*
|
||||
* - SSH_OPTIONS_PROXYCOMMAND:
|
||||
* Get the proxycommand necessary to log into the
|
||||
@@ -1658,6 +1666,30 @@ int ssh_options_get(ssh_session session, enum ssh_options_e type, char** value)
|
||||
break;
|
||||
}
|
||||
|
||||
case SSH_OPTIONS_NEXT_IDENTITY: {
|
||||
if (session->opts.identity_it != NULL) {
|
||||
/* Move to the next item */
|
||||
session->opts.identity_it = session->opts.identity_it->next;
|
||||
if (session->opts.identity_it == NULL) {
|
||||
*value = NULL;
|
||||
return SSH_EOF;
|
||||
}
|
||||
} else {
|
||||
/* Get iterator from opts */
|
||||
struct ssh_iterator *it = NULL;
|
||||
it = ssh_list_get_iterator(session->opts.identity);
|
||||
if (it == NULL) {
|
||||
it = ssh_list_get_iterator(session->opts.identity_non_exp);
|
||||
}
|
||||
if (it == NULL) {
|
||||
return SSH_ERROR;
|
||||
}
|
||||
session->opts.identity_it = it;
|
||||
}
|
||||
src = ssh_iterator_value(char *, session->opts.identity_it);
|
||||
break;
|
||||
}
|
||||
|
||||
case SSH_OPTIONS_PROXYCOMMAND:
|
||||
src = session->opts.ProxyCommand;
|
||||
break;
|
||||
@@ -1963,7 +1995,7 @@ int ssh_options_parse_config(ssh_session session, const char *filename)
|
||||
|
||||
/* set default filename */
|
||||
if (filename == NULL) {
|
||||
expanded_filename = ssh_path_expand_escape(session, "%d/config");
|
||||
expanded_filename = ssh_path_expand_escape(session, "%d/.ssh/config");
|
||||
} else {
|
||||
expanded_filename = ssh_path_expand_escape(session, filename);
|
||||
}
|
||||
@@ -2021,7 +2053,7 @@ int ssh_options_apply(ssh_session session)
|
||||
|
||||
if ((session->opts.exp_flags & SSH_OPT_EXP_FLAG_KNOWNHOSTS) == 0) {
|
||||
if (session->opts.knownhosts == NULL) {
|
||||
tmp = ssh_path_expand_escape(session, "%d/known_hosts");
|
||||
tmp = ssh_path_expand_escape(session, "%d/.ssh/known_hosts");
|
||||
} else {
|
||||
tmp = ssh_path_expand_escape(session, session->opts.knownhosts);
|
||||
}
|
||||
|
||||
@@ -2684,7 +2684,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
|
||||
const char *filename)
|
||||
{
|
||||
char key_buf[MAX_LINE_SIZE];
|
||||
char host[256];
|
||||
char *host = NULL;
|
||||
char *b64_key = NULL;
|
||||
char *user = NULL;
|
||||
FILE *fp = NULL;
|
||||
@@ -2699,8 +2699,8 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
rc = gethostname(host, sizeof(host));
|
||||
if (rc < 0) {
|
||||
host = ssh_get_local_hostname();
|
||||
if (host == NULL) {
|
||||
free(user);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
@@ -2708,6 +2708,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
|
||||
rc = ssh_pki_export_pubkey_base64(key, &b64_key);
|
||||
if (rc < 0) {
|
||||
free(user);
|
||||
free(host);
|
||||
return SSH_ERROR;
|
||||
}
|
||||
|
||||
@@ -2718,6 +2719,7 @@ int ssh_pki_export_pubkey_file(const ssh_key key,
|
||||
user,
|
||||
host);
|
||||
free(user);
|
||||
free(host);
|
||||
free(b64_key);
|
||||
if (rc < 0) {
|
||||
return SSH_ERROR;
|
||||
|
||||
@@ -168,7 +168,7 @@ ssh_session ssh_new(void)
|
||||
}
|
||||
#endif /* WITH_GSSAPI */
|
||||
|
||||
id = strdup("%d/id_ed25519");
|
||||
id = strdup("%d/.ssh/id_ed25519");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -179,7 +179,7 @@ ssh_session ssh_new(void)
|
||||
}
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
id = strdup("%d/id_ecdsa");
|
||||
id = strdup("%d/.ssh/id_ecdsa");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ ssh_session ssh_new(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
id = strdup("%d/id_rsa");
|
||||
id = strdup("%d/.ssh/id_rsa");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -200,7 +200,7 @@ ssh_session ssh_new(void)
|
||||
|
||||
#ifdef WITH_FIDO2
|
||||
/* Add security key identities */
|
||||
id = strdup("%d/id_ed25519_sk");
|
||||
id = strdup("%d/.ssh/id_ed25519_sk");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -210,7 +210,7 @@ ssh_session ssh_new(void)
|
||||
}
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
id = strdup("%d/id_ecdsa_sk");
|
||||
id = strdup("%d/.ssh/id_ecdsa_sk");
|
||||
if (id == NULL) {
|
||||
goto err;
|
||||
}
|
||||
@@ -384,6 +384,7 @@ void ssh_free(ssh_session session)
|
||||
ssh_proxyjumps_free(session->opts.proxy_jumps);
|
||||
SSH_LIST_FREE(session->opts.proxy_jumps);
|
||||
SSH_LIST_FREE(session->opts.proxy_jumps_user_cb);
|
||||
SAFE_FREE(session->opts.proxy_jumps_str);
|
||||
|
||||
while ((b = ssh_list_pop_head(struct ssh_buffer_struct *,
|
||||
session->out_queue)) != NULL) {
|
||||
@@ -405,6 +406,7 @@ void ssh_free(ssh_session session)
|
||||
SAFE_FREE(session->opts.bindaddr);
|
||||
SAFE_FREE(session->opts.username);
|
||||
SAFE_FREE(session->opts.host);
|
||||
SAFE_FREE(session->opts.homedir);
|
||||
SAFE_FREE(session->opts.sshdir);
|
||||
SAFE_FREE(session->opts.knownhosts);
|
||||
SAFE_FREE(session->opts.global_knownhosts);
|
||||
|
||||
@@ -376,7 +376,7 @@ torture_auth_autopubkey_protected_auth_function (const char *prompt, char *buf,
|
||||
assert_int_equal(echo, 0);
|
||||
assert_int_equal(verify, 0);
|
||||
|
||||
expected_id = ssh_path_expand_escape(data->session, "%d/id_rsa_protected");
|
||||
expected_id = ssh_path_expand_escape(data->session, "%d/.ssh/id_rsa_protected");
|
||||
assert_true(expected_id != NULL);
|
||||
|
||||
rc = ssh_userauth_publickey_auto_get_current_identity(data->session, &id);
|
||||
@@ -429,7 +429,7 @@ static void torture_auth_autopubkey_protected(void **state) {
|
||||
|
||||
/* Try id_rsa_protected first.
|
||||
*/
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "%d/id_rsa_protected");
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_IDENTITY, "%d/.ssh/id_rsa_protected");
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
rc = ssh_connect(session);
|
||||
|
||||
@@ -100,13 +100,10 @@ static int session_setup(void **state)
|
||||
static int session_setup_ssh_dir(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
const char *no_home = "~/.no_ssh";
|
||||
int rc;
|
||||
|
||||
session_setup(state);
|
||||
|
||||
rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_SSH_DIR, no_home);
|
||||
assert_ssh_return_code(s->ssh.session, rc);
|
||||
s->ssh.session->opts.homedir = strdup("~/.no_ssh");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <errno.h>
|
||||
#include "torture.h"
|
||||
#include "libssh/session.h"
|
||||
#include "libssh/options.h"
|
||||
#include "libssh/misc.h"
|
||||
|
||||
#define LIBSSH_SSH_CONFIG "libssh_config"
|
||||
@@ -59,6 +60,23 @@ static int setup_config_files(void **state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int setup_session(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int verbosity;
|
||||
|
||||
s->ssh.session = ssh_new();
|
||||
assert_non_null(s->ssh.session);
|
||||
|
||||
verbosity = torture_libssh_verbosity();
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity);
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
||||
|
||||
setenv("NSS_WRAPPER_HOSTNAME", "client.libssh.site", 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
@@ -80,6 +98,16 @@ static int teardown(void **state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int teardown_session(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
|
||||
ssh_disconnect(s->ssh.session);
|
||||
ssh_free(s->ssh.session);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This tests makes sure that parsing both system-wide and per-user
|
||||
* configuration files retains OpenSSH semantics (the per-user overrides
|
||||
* the system-wide values).
|
||||
@@ -210,10 +238,207 @@ static void torture_client_config_suppress(void **state)
|
||||
assert_string_equal(s->ssh.session->opts.username, "bob");
|
||||
}
|
||||
|
||||
static void torture_client_config_expand(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
int ret = 0;
|
||||
|
||||
/* TEST: user home directory */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%d");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
BINARYDIR "/tests/home");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: target host name */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%h");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, TORTURE_SSH_SERVER);
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: local username */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%u");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "root");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: local hostname */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "client.libssh.site");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: remote username */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, "alice");
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%r");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "alice");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: remote port */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_PORT_STR, "2222");
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%p");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "2222");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: empty proxyjump */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%j");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
/* No proxyjump string should not explode */
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts, "");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: proxyjump string present */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%j");
|
||||
ssh_options_set(s->ssh.session,
|
||||
SSH_OPTIONS_PROXYJUMP,
|
||||
"user@" TORTURE_SSH_SERVER ":22");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"user@" TORTURE_SSH_SERVER ":22");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: separate list %l-%h-%p-%r-%j with empty ProxyJump */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l-%h-%p-%r-%j");
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_PROXYJUMP, "none");
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_PORT_STR, "22");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
// Tested by
|
||||
// ret = system(SSH_EXECUTABLE
|
||||
// " -p 22 -o UserKnownHostsFile=/dev/null"
|
||||
// " -o KnownHostsCommand='/bin/touch \"/tmp/%l-%h-%p-%r-%j\"'"
|
||||
// " alice@" TORTURE_SSH_SERVER);
|
||||
// assert_return_code(ret, errno);
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"client.libssh.site-127.0.0.10-22-alice-");
|
||||
|
||||
|
||||
/* TEST: hash of %l%h%p%r%j with empty ProxyJump */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%C");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
// Tested by
|
||||
// ret = system(SSH_EXECUTABLE
|
||||
// " -p 22 -o UserKnownHostsFile=/dev/null"
|
||||
// " -o KnownHostsCommand='/bin/touch \"/tmp/%C\"'"
|
||||
// " alice@" TORTURE_SSH_SERVER);
|
||||
// assert_return_code(ret, errno);
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"133e3957ff9d01fdcf1f6c7f83325a8ce49bf850");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: separate list %l-%h-%p-%r-%j */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%l-%h-%p-%r-%j");
|
||||
ssh_options_set(s->ssh.session,
|
||||
SSH_OPTIONS_PROXYJUMP,
|
||||
"user@" TORTURE_SSH_SERVER ":22");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
// Tested by
|
||||
// ret = system(SSH_EXECUTABLE
|
||||
// " -p 22 -oProxyJump=user@" TORTURE_SSH_SERVER ":22"
|
||||
// " -o UserKnownHostsFile=/dev/null"
|
||||
// " -o KnownHostsCommand='/bin/touch \"/tmp/%l-%h-%p-%r-%j\"'"
|
||||
// " alice@" TORTURE_SSH_SERVER);
|
||||
// assert_return_code(ret, errno);
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"client.libssh.site-127.0.0.10-22-alice-user@"
|
||||
TORTURE_SSH_SERVER ":22");
|
||||
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
|
||||
/* TEST: hash of %l%h%p%r%j */
|
||||
ssh_options_set(s->ssh.session, SSH_OPTIONS_KNOWNHOSTS, "%C");
|
||||
|
||||
ret = ssh_options_apply(s->ssh.session);
|
||||
assert_ssh_return_code(s->ssh.session, ret);
|
||||
|
||||
// Tested by
|
||||
// ret = system(SSH_EXECUTABLE
|
||||
// " -p 22 -oProxyJump=user@" TORTURE_SSH_SERVER ":22"
|
||||
// " -o UserKnownHostsFile=/dev/null"
|
||||
// " -o KnownHostsCommand='/bin/touch \"/tmp/%C\"'"
|
||||
// " alice@" TORTURE_SSH_SERVER);
|
||||
// assert_return_code(ret, errno);
|
||||
assert_string_equal(s->ssh.session->opts.knownhosts,
|
||||
"adf0b7c4e71a0fee85fd97506507ba8591f3663b");
|
||||
|
||||
/* Reset the flag so we can repeat the test */
|
||||
s->ssh.session->opts.exp_flags &= ~SSH_OPT_EXP_FLAG_KNOWNHOSTS;
|
||||
|
||||
}
|
||||
|
||||
|
||||
int torture_run_tests(void) {
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
/* Keep this first -- following setup is changing user to bob, which we
|
||||
* do not want */
|
||||
cmocka_unit_test_setup_teardown(torture_client_config_expand,
|
||||
setup_session,
|
||||
teardown_session),
|
||||
cmocka_unit_test_setup_teardown(torture_client_config_system,
|
||||
setup_config_files,
|
||||
teardown),
|
||||
|
||||
@@ -11,19 +11,37 @@
|
||||
|
||||
#define MAX_XFER_BUF_SIZE 16384
|
||||
|
||||
#define DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(TEST_NAME) \
|
||||
{ \
|
||||
#TEST_NAME, \
|
||||
TEST_NAME, \
|
||||
session_setup, \
|
||||
session_teardown, \
|
||||
NULL \
|
||||
}, \
|
||||
{ \
|
||||
#TEST_NAME"_proxyjump", \
|
||||
TEST_NAME, \
|
||||
session_proxyjump_setup, \
|
||||
session_teardown, \
|
||||
NULL \
|
||||
}
|
||||
|
||||
static int sshd_setup(void **state)
|
||||
{
|
||||
torture_setup_sshd_server(state, false);
|
||||
torture_setup_sshd_servers(state, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sshd_teardown(void **state)
|
||||
{
|
||||
/* this will take care of the server1 teardown too */
|
||||
torture_teardown_sshd_server(state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_setup(void **state)
|
||||
static int session_setup_helper(void **state, bool with_proxyjump)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
struct passwd *pwd = NULL;
|
||||
@@ -35,11 +53,15 @@ static int session_setup(void **state)
|
||||
rc = setuid(pwd->pw_uid);
|
||||
assert_return_code(rc, errno);
|
||||
|
||||
s->ssh.session = torture_ssh_session(s,
|
||||
TORTURE_SSH_SERVER,
|
||||
NULL,
|
||||
TORTURE_SSH_USER_ALICE,
|
||||
NULL);
|
||||
if (with_proxyjump) {
|
||||
s->ssh.session = torture_ssh_session_proxyjump();
|
||||
} else {
|
||||
s->ssh.session = torture_ssh_session(s,
|
||||
TORTURE_SSH_SERVER,
|
||||
NULL,
|
||||
TORTURE_SSH_USER_ALICE,
|
||||
NULL);
|
||||
}
|
||||
assert_non_null(s->ssh.session);
|
||||
|
||||
s->ssh.tsftp = torture_sftp_session(s->ssh.session);
|
||||
@@ -48,6 +70,16 @@ static int session_setup(void **state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int session_setup(void **state)
|
||||
{
|
||||
return session_setup_helper(state, false);
|
||||
}
|
||||
|
||||
static int session_proxyjump_setup(void **state)
|
||||
{
|
||||
return session_setup_helper(state, true);
|
||||
}
|
||||
|
||||
static int session_teardown(void **state)
|
||||
{
|
||||
struct torture_state *s = *state;
|
||||
@@ -722,37 +754,18 @@ int torture_run_tests(void)
|
||||
{
|
||||
int rc;
|
||||
struct CMUnitTest tests[] = {
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_file,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_more_than_cap,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_file,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_more_than_cap,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_negative,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_negative,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_read_unordered_wait,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
|
||||
cmocka_unit_test_setup_teardown(torture_sftp_aio_write_unordered_wait,
|
||||
session_setup,
|
||||
session_teardown),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_read_file),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
|
||||
torture_sftp_aio_read_more_than_cap),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_write_file),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
|
||||
torture_sftp_aio_write_more_than_cap),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_read_negative),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(torture_sftp_aio_write_negative),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
|
||||
torture_sftp_aio_read_unordered_wait),
|
||||
DIRECT_AND_PROXYJUMP_SETUP_TEARDOWN(
|
||||
torture_sftp_aio_write_unordered_wait),
|
||||
};
|
||||
|
||||
ssh_init();
|
||||
|
||||
@@ -2,4 +2,4 @@ users:x:9000:
|
||||
sshd:x:65531:
|
||||
nobody:x:65533:
|
||||
nogroup:x:65534:nobody
|
||||
root:x:65532:
|
||||
root:x:0:
|
||||
|
||||
@@ -5,5 +5,5 @@ 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
|
||||
root:x:0:0:root gecos:@HOMEDIR@:/bin/false
|
||||
@LOCAL_USER@:x:@LOCAL_UID@:9000:local user:@HOMEDIR@:/bin/false
|
||||
|
||||
137
tests/torture.c
137
tests/torture.c
@@ -389,6 +389,143 @@ failed:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* always return verification successful */
|
||||
static int verify_knownhost_trust_all(UNUSED_PARAM(ssh_session jump_session),
|
||||
UNUSED_PARAM(void *user))
|
||||
{
|
||||
return SSH_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Create a session connected to server via proxyjump
|
||||
*
|
||||
* @param[in] state A pointer to a pointer to an initialized torture_state
|
||||
* structure
|
||||
*
|
||||
* @warning It is expected that both sshd servers are setup before calling
|
||||
* this, see torture_setup_sshd_server() and
|
||||
* torture_setup_sshd_servers()
|
||||
*
|
||||
* TODO: If needed, in future, we can extend this function to:
|
||||
* - allow caller to pass server host port, user, password similar to
|
||||
* torture_ssh_session() or club this with that function
|
||||
*
|
||||
* - allow caller to customize jump hosts and callbacks for each of them
|
||||
*/
|
||||
ssh_session torture_ssh_session_proxyjump(void)
|
||||
{
|
||||
/*
|
||||
* We'll setup the connection chain:
|
||||
* - client
|
||||
* - jump host 1: doe (sshd server, IPV4)
|
||||
* - jump host 2: alice (sshd server1, IPV6)
|
||||
* - server: alice (sshd server, IPV4)
|
||||
*/
|
||||
char jump_host_list[1024] = {0};
|
||||
int jump_host_count = 2;
|
||||
const char *jump_host_1_address = torture_server_address(AF_INET);
|
||||
const char *jump_host_2_address = torture_server1_address(AF_INET6);
|
||||
struct ssh_jump_callbacks_struct jump_host_callbacks = {
|
||||
.before_connection = NULL,
|
||||
.verify_knownhost = verify_knownhost_trust_all,
|
||||
.authenticate = NULL,
|
||||
};
|
||||
|
||||
ssh_session session = NULL;
|
||||
bool process_config = false;
|
||||
int rc, i;
|
||||
|
||||
session = ssh_new();
|
||||
if (session == NULL) {
|
||||
fprintf(stderr, "Failed to create new ssh session\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to set session host: %s\n",
|
||||
ssh_get_error(session));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to set session user: %s\n",
|
||||
ssh_get_error(session));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to set process config option: %s\n",
|
||||
ssh_get_error(session));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = snprintf(jump_host_list,
|
||||
sizeof(jump_host_list),
|
||||
"doe@%s:22,alice@%s:22",
|
||||
jump_host_1_address,
|
||||
jump_host_2_address);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr, "snprintf failed: %s\n", strerror(errno));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
if (rc >= (int)sizeof(jump_host_list)) {
|
||||
fprintf(stderr, "Insufficient jump host list buffer size\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_PROXYJUMP, jump_host_list);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to set jump hosts for the session: %s\n",
|
||||
ssh_get_error(session));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
for (i = 0; i < jump_host_count; ++i) {
|
||||
rc = ssh_options_set(session,
|
||||
SSH_OPTIONS_PROXYJUMP_CB_LIST_APPEND,
|
||||
&jump_host_callbacks);
|
||||
if (rc < 0) {
|
||||
fprintf(stderr,
|
||||
"Failed to set jump callbacks for jump host %d: %s\n",
|
||||
i + 1,
|
||||
ssh_get_error(session));
|
||||
goto failed;
|
||||
}
|
||||
}
|
||||
|
||||
rc = ssh_connect(session);
|
||||
if (rc != SSH_OK) {
|
||||
fprintf(stderr,
|
||||
"Failed to connect to ssh server: %s\n",
|
||||
ssh_get_error(session));
|
||||
goto failed;
|
||||
}
|
||||
|
||||
rc = ssh_userauth_publickey_auto(session, NULL, NULL);
|
||||
if (rc != SSH_AUTH_SUCCESS) {
|
||||
fprintf(stderr, "Public key authentication did not succeed\n");
|
||||
goto failed;
|
||||
}
|
||||
|
||||
return session;
|
||||
|
||||
failed:
|
||||
if (ssh_is_connected(session)) {
|
||||
ssh_disconnect(session);
|
||||
}
|
||||
ssh_free(session);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef WITH_SERVER
|
||||
|
||||
ssh_bind torture_ssh_bind(const char *addr,
|
||||
|
||||
@@ -115,6 +115,8 @@ ssh_session torture_ssh_session(struct torture_state *s,
|
||||
const char *user,
|
||||
const char *password);
|
||||
|
||||
ssh_session torture_ssh_session_proxyjump(void);
|
||||
|
||||
ssh_bind torture_ssh_bind(const char *addr,
|
||||
const unsigned int port,
|
||||
enum ssh_keytypes_e key_type,
|
||||
|
||||
@@ -25,7 +25,7 @@ extern LIBSSH_THREAD int ssh_log_level;
|
||||
#define HOSTKEYALGORITHMS "ssh-ed25519,ecdsa-sha2-nistp521,ssh-rsa"
|
||||
#define PUBKEYACCEPTEDTYPES "rsa-sha2-512,ssh-rsa,ecdsa-sha2-nistp521"
|
||||
#define MACS "hmac-sha1,hmac-sha2-256,hmac-sha2-512,hmac-sha1-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com"
|
||||
#define USER_KNOWN_HOSTS "%d/my_known_hosts"
|
||||
#define USER_KNOWN_HOSTS "%d/.ssh/my_known_hosts"
|
||||
#define GLOBAL_KNOWN_HOSTS "/etc/ssh/my_ssh_known_hosts"
|
||||
#define BIND_ADDRESS "::1"
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ static void torture_get_user_home_dir(void **state) {
|
||||
|
||||
(void) state;
|
||||
|
||||
user = ssh_get_user_home_dir();
|
||||
user = ssh_get_user_home_dir(NULL);
|
||||
assert_non_null(user);
|
||||
#ifndef _WIN32
|
||||
assert_string_equal(user, pwd->pw_dir);
|
||||
@@ -288,7 +288,8 @@ static void torture_path_expand_escape(void **state) {
|
||||
const char *s = "%d/%h/%p/by/%r";
|
||||
char *e;
|
||||
|
||||
session->opts.sshdir = strdup("guru");
|
||||
/* Set the homedir here to prevent querying the NSS DB */
|
||||
session->opts.homedir = strdup("guru");
|
||||
session->opts.host = strdup("meditation");
|
||||
session->opts.port = 0;
|
||||
session->opts.username = strdup("root");
|
||||
@@ -310,9 +311,10 @@ static void torture_path_expand_known_hosts(void **state) {
|
||||
ssh_session session = *state;
|
||||
char *tmp;
|
||||
|
||||
session->opts.sshdir = strdup("/home/guru/.ssh");
|
||||
/* Set the homedir here to prevent querying the NSS DB */
|
||||
session->opts.homedir = strdup("/home/guru");
|
||||
|
||||
tmp = ssh_path_expand_escape(session, "%d/known_hosts");
|
||||
tmp = ssh_path_expand_escape(session, "%d/.ssh/known_hosts");
|
||||
assert_non_null(tmp);
|
||||
assert_string_equal(tmp, "/home/guru/.ssh/known_hosts");
|
||||
free(tmp);
|
||||
@@ -322,9 +324,10 @@ static void torture_path_expand_percent(void **state) {
|
||||
ssh_session session = *state;
|
||||
char *tmp;
|
||||
|
||||
session->opts.sshdir = strdup("/home/guru/.ssh");
|
||||
/* Set the homedir here to prevent querying the NSS DB */
|
||||
session->opts.homedir = strdup("/home/guru");
|
||||
|
||||
tmp = ssh_path_expand_escape(session, "%d/config%%1");
|
||||
tmp = ssh_path_expand_escape(session, "%d/.ssh/config%%1");
|
||||
assert_non_null(tmp);
|
||||
assert_string_equal(tmp, "/home/guru/.ssh/config%1");
|
||||
free(tmp);
|
||||
|
||||
@@ -839,6 +839,7 @@ static void torture_options_get_identity(void **state)
|
||||
char *identity = NULL;
|
||||
int rc;
|
||||
|
||||
/* This adds an identity to the head of the list and returns */
|
||||
rc = ssh_options_set(session, SSH_OPTIONS_ADD_IDENTITY, "identity1");
|
||||
assert_true(rc == 0);
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_IDENTITY, &identity);
|
||||
@@ -856,6 +857,48 @@ static void torture_options_get_identity(void **state)
|
||||
assert_non_null(identity);
|
||||
assert_string_equal(identity, "identity2");
|
||||
ssh_string_free_char(identity);
|
||||
|
||||
/* Iterate over all of the identities */
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_string_equal(identity, "identity2");
|
||||
ssh_string_free_char(identity);
|
||||
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_string_equal(identity, "identity1");
|
||||
SAFE_FREE(identity);
|
||||
|
||||
/* here are the default identities */
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_string_equal(identity, "%d/.ssh/id_ed25519");
|
||||
ssh_string_free_char(identity);
|
||||
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_string_equal(identity, "%d/.ssh/id_ecdsa");
|
||||
ssh_string_free_char(identity);
|
||||
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_string_equal(identity, "%d/.ssh/id_rsa");
|
||||
ssh_string_free_char(identity);
|
||||
|
||||
#ifdef WITH_FIDO2
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_string_equal(identity, "%d/.ssh/id_ed25519_sk");
|
||||
ssh_string_free_char(identity);
|
||||
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
assert_string_equal(identity, "%d/.ssh/id_ecdsa_sk");
|
||||
ssh_string_free_char(identity);
|
||||
#endif /* WITH_FIDO2 */
|
||||
|
||||
rc = ssh_options_get(session, SSH_OPTIONS_NEXT_IDENTITY, &identity);
|
||||
assert_int_equal(rc, SSH_EOF);
|
||||
}
|
||||
|
||||
static void torture_options_set_global_knownhosts(void **state)
|
||||
@@ -2067,25 +2110,25 @@ static void torture_options_apply (void **state)
|
||||
rc = ssh_list_append(awaited_list, id);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
/* append the defaults; this list is copied from ssh_new@src/session.c */
|
||||
id = ssh_path_expand_escape(session, "%d/id_ed25519");
|
||||
id = ssh_path_expand_escape(session, "%d/.ssh/id_ed25519");
|
||||
rc = ssh_list_append(awaited_list, id);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
#ifdef HAVE_ECC
|
||||
id = ssh_path_expand_escape(session, "%d/id_ecdsa");
|
||||
id = ssh_path_expand_escape(session, "%d/.ssh/id_ecdsa");
|
||||
rc = ssh_list_append(awaited_list, id);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
#endif
|
||||
id = ssh_path_expand_escape(session, "%d/id_rsa");
|
||||
id = ssh_path_expand_escape(session, "%d/.ssh/id_rsa");
|
||||
rc = ssh_list_append(awaited_list, id);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
#ifdef WITH_FIDO2
|
||||
/* Add security key identities */
|
||||
id = ssh_path_expand_escape(session, "%d/id_ed25519_sk");
|
||||
id = ssh_path_expand_escape(session, "%d/.ssh/id_ed25519_sk");
|
||||
rc = ssh_list_append(awaited_list, id);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
|
||||
#ifdef HAVE_ECC
|
||||
id = ssh_path_expand_escape(session, "%d/id_ecdsa_sk");
|
||||
id = ssh_path_expand_escape(session, "%d/.ssh/id_ecdsa_sk");
|
||||
rc = ssh_list_append(awaited_list, id);
|
||||
assert_int_equal(rc, SSH_OK);
|
||||
#endif /* HAVE_ECC */
|
||||
|
||||
Reference in New Issue
Block a user