aboutsummaryrefslogtreecommitdiffstats
path: root/extcap/ssh-base.c
diff options
context:
space:
mode:
Diffstat (limited to 'extcap/ssh-base.c')
-rw-r--r--extcap/ssh-base.c195
1 files changed, 166 insertions, 29 deletions
diff --git a/extcap/ssh-base.c b/extcap/ssh-base.c
index 37e152a89c..b52dee5d3c 100644
--- a/extcap/ssh-base.c
+++ b/extcap/ssh-base.c
@@ -11,6 +11,7 @@
*/
#include "config.h"
+#define WS_LOG_DOMAIN LOG_DOMAIN_EXTCAP
#include "ssh-base.h"
@@ -20,9 +21,80 @@
#include <ws_attributes.h>
#include <wsutil/wslog.h>
-static void extcap_log(int priority _U_, const char *function, const char *buffer, void *userdata _U_)
+/*
+ * The unreleased 0.11.0 version of libssh has the ability to
+ * add algorithms to the default supported list by prepending
+ * "+" to the configuration list. For older versions, we have
+ * to specify all the algorithms we want, but as long as at
+ * least one succeeds the command won't fail. (That means that
+ * it's possible that we won't actually add support for SHA-1,
+ * say if it's running on a system in FIPS mode. We could parse
+ * the returned list to check.)
+ */
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,11,0)
+#define HOSTKEYS_SHA1 "+ssh-rsa"
+#define KEY_EXCHANGE_SHA1 "+diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1"
+#define HMAC_SHA1 "+hmac-sha1-etm@openssh.com,hmac-sha1"
+#else
+#define HOSTKEYS_SHA1 \
+ "ssh-ed25519," \
+ "ecdsa-sha2-nistp521," \
+ "ecdsa-sha2-nistp384," \
+ "ecdsa-sha2-nistp256," \
+ "sk-ssh-ed25519@openssh.com," \
+ "sk-ecdsa-sha2-nistp256@openssh.com," \
+ "rsa-sha2-512," \
+ "rsa-sha2-256," \
+ "ssh-rsa"
+#define KEY_EXCHANGE_SHA1 \
+ "curve25519-sha256,curve25519-sha256@libssh.org," \
+ "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521," \
+ "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512," \
+ "diffie-hellman-group-exchange-sha256," \
+ "diffie-hellman-group14-sha256," \
+ "diffie-hellman-group-exchange-sha1," \
+ "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
+#define HMAC_SHA1 \
+ "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com," \
+ "hmac-sha2-256,hmac-sha2-512," \
+ "hmac-sha1-etm@openssh.com,hmac-sha1"
+#endif
+
+static void extcap_log(int priority, const char *function, const char *buffer, void *userdata _U_)
{
- ws_debug("[%s] %s", function, buffer);
+ enum ws_log_level level = LOG_LEVEL_DEBUG;
+ switch (priority) {
+ case SSH_LOG_TRACE:
+ level = LOG_LEVEL_NOISY;
+ break;
+ case SSH_LOG_DEBUG:
+ level = LOG_LEVEL_DEBUG;
+ break;
+ case SSH_LOG_INFO:
+ level = LOG_LEVEL_INFO;
+ break;
+ case SSH_LOG_WARN:
+ default:
+ /* Prior to 0.11.0 libssh prints far too much at SSH_LOG_WARN,
+ * including merely informational messages.
+ * Lower them to LOG_LEVEL_INFO, which won't get shown in the GUI
+ * and aren't shown by default. (Anything INFO and below goes to
+ * stdout due to ws_log_console_writer_set_use_stdout in extcap-base.c)
+ * After the following commit libssh only uses LOG_LEVEL_WARN for
+ * serious issues:
+ * https://gitlab.com/libssh/libssh-mirror/-/commit/657d9143d121dfff74f5a63f734d0096c7f37194
+ */
+#if LIBSSH_VERSION_INT < SSH_VERSION_INT(0,11,0)
+ level = LOG_LEVEL_INFO;
+#else
+ level = LOG_LEVEL_WARNING;
+#endif
+ break;
+ }
+ /* We set the libssh log level to specifically ask for this, so don't
+ * both checking the log level a second time.
+ */
+ ws_log_write_always_full("libssh", level, NULL, 0, function, "%s", buffer);
}
void add_libssh_info(extcap_parameters * extcap_conf)
@@ -34,8 +106,8 @@ void add_libssh_info(extcap_parameters * extcap_conf)
ssh_session create_ssh_connection(const ssh_params_t* ssh_params, char** err_info)
{
ssh_session sshs;
- gchar* username = NULL;
- guint port;
+ char* username = NULL;
+ unsigned port;
/* Open session and set options */
sshs = ssh_new();
@@ -50,7 +122,7 @@ ssh_session create_ssh_connection(const ssh_params_t* ssh_params, char** err_inf
}
if (ssh_options_set(sshs, SSH_OPTIONS_HOST, ssh_params->host)) {
- *err_info = g_strdup_printf("Can't set the host: %s", ssh_params->host);
+ *err_info = ws_strdup_printf("Can't set the host: %s", ssh_params->host);
goto failure;
}
@@ -61,30 +133,50 @@ ssh_session create_ssh_connection(const ssh_params_t* ssh_params, char** err_inf
goto failure;
}
- if (ssh_params->debug) {
- int debug_level = SSH_LOG_INFO;
- ssh_options_set(sshs, SSH_OPTIONS_LOG_VERBOSITY, &debug_level);
- ssh_set_log_callback(extcap_log);
+ ssh_options_set(sshs, SSH_OPTIONS_LOG_VERBOSITY, &ssh_params->debug);
+ ssh_set_log_callback(extcap_log);
+
+ if (ssh_params->ssh_sha1) {
+ if (ssh_options_set(sshs, SSH_OPTIONS_HOSTKEYS, HOSTKEYS_SHA1)) {
+ *err_info = ws_strdup_printf("Can't set host keys to allow SHA-1.");
+ goto failure;
+ }
+ if (ssh_options_set(sshs, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, HOSTKEYS_SHA1)) {
+ *err_info = ws_strdup_printf("Can't set public key algorithms to allow SSH-RSA (SHA-1).");
+ goto failure;
+ }
+ if (ssh_options_set(sshs, SSH_OPTIONS_KEY_EXCHANGE, KEY_EXCHANGE_SHA1)) {
+ *err_info = ws_strdup_printf("Can't set key exchange methods to allow SHA-1.");
+ goto failure;
+ }
+ if (ssh_options_set(sshs, SSH_OPTIONS_HMAC_C_S, HMAC_SHA1)) {
+ *err_info = ws_strdup_printf("Can't set MAC client to server algorithms to allow SHA-1.");
+ goto failure;
+ }
+ if (ssh_options_set(sshs, SSH_OPTIONS_HMAC_S_C, HMAC_SHA1)) {
+ *err_info = ws_strdup_printf("Can't set MAC server to client algorithms to allow SHA-1.");
+ goto failure;
+ }
}
if (ssh_params->port != 0) {
port = ssh_params->port;
if (ssh_options_set(sshs, SSH_OPTIONS_PORT, &port)) {
- *err_info = g_strdup_printf("Can't set the port: %u", port);
+ *err_info = ws_strdup_printf("Can't set the port: %u", port);
goto failure;
}
}
if (ssh_params->proxycommand) {
if (ssh_options_set(sshs, SSH_OPTIONS_PROXYCOMMAND, ssh_params->proxycommand)) {
- *err_info = g_strdup_printf("Can't set the ProxyCommand: %s", ssh_params->proxycommand);
+ *err_info = ws_strdup_printf("Can't set the ProxyCommand: %s", ssh_params->proxycommand);
goto failure;
}
}
if (ssh_params->username) {
if (ssh_options_set(sshs, SSH_OPTIONS_USER, ssh_params->username)) {
- *err_info = g_strdup_printf("Can't set the username: %s", ssh_params->username);
+ *err_info = ws_strdup_printf("Can't set the username: %s", ssh_params->username);
goto failure;
}
}
@@ -99,7 +191,7 @@ ssh_session create_ssh_connection(const ssh_params_t* ssh_params, char** err_inf
/* Connect to server */
if (ssh_connect(sshs) != SSH_OK) {
- *err_info = g_strdup_printf("Connection error: %s", ssh_get_error(sshs));
+ *err_info = ws_strdup_printf("Connection error: %s", ssh_get_error(sshs));
goto failure;
}
@@ -111,36 +203,63 @@ ssh_session create_ssh_connection(const ssh_params_t* ssh_params, char** err_inf
ws_info("Connecting using public key in %s...", ssh_params->sshkey_path);
ret = ssh_pki_import_privkey_file(ssh_params->sshkey_path, ssh_params->sshkey_passphrase, NULL, NULL, &pkey);
- if (ret == SSH_OK) {
+ switch (ret) {
+
+ case SSH_OK:
if (ssh_userauth_publickey(sshs, NULL, pkey) == SSH_AUTH_SUCCESS) {
ws_info("done");
ssh_key_free(pkey);
return sshs;
}
+ ws_info("failed (%s)", ssh_get_error(sshs));
+ break;
+ case SSH_EOF:
+ ws_warning("Error importing key from %s. File doesn't exist or permission denied.",
+ ssh_params->sshkey_path);
+ break;
+ case SSH_ERROR:
+ /* Unfortunately we can't call ssh_get_error() on the
+ * key to determine why import failed.
+ */
+ ws_warning("Error importing key from %s. Make sure it is a valid"
+ " private key file and any necessary passphrase is configured.",
+ ssh_params->sshkey_path);
+ break;
+ default:
+ ws_warning("Unknown error from ssh_pki_import_privkey_file");
}
ssh_key_free(pkey);
- ws_info("failed (%s)", ssh_get_error(sshs));
}
- /* Try to authenticate using standard public key */
- ws_info("Connecting using standard public key...");
- if (ssh_userauth_publickey_auto(sshs, NULL, NULL) == SSH_AUTH_SUCCESS) {
- ws_info("done");
- return sshs;
+ /* Workaround: it may happen that libssh closes socket in meantime and any next ssh_ call fails so we should detect it in advance */
+ if (ssh_get_fd(sshs) != (socket_t)-1) {
+ /* If a password has been provided and all previous attempts failed, try to use it */
+ if (ssh_params->password) {
+ ws_info("Connecting using password...");
+ if (ssh_userauth_password(sshs, ssh_params->username, ssh_params->password) == SSH_AUTH_SUCCESS) {
+ ws_info("done");
+ return sshs;
+ }
+ ws_info("failed");
+ }
+ } else {
+ ws_info("ssh connection closed before password authentication");
}
- ws_info("failed");
- /* If a password has been provided and all previous attempts failed, try to use it */
- if (ssh_params->password) {
- ws_info("Connecting using password...");
- if (ssh_userauth_password(sshs, ssh_params->username, ssh_params->password) == SSH_AUTH_SUCCESS) {
+ /* Workaround: it may happen that libssh closes socket in meantime and any next ssh_ call fails so we should detect it in advance */
+ if (ssh_get_fd(sshs) != (socket_t)-1) {
+ /* Try to authenticate using standard public key */
+ ws_info("Connecting using standard public key...");
+ if (ssh_userauth_publickey_auto(sshs, NULL, NULL) == SSH_AUTH_SUCCESS) {
ws_info("done");
return sshs;
}
ws_info("failed");
+ } else {
+ ws_info("ssh connection closed before public key authentication");
}
- *err_info = g_strdup_printf("Can't find a valid authentication. Disconnecting.");
+ *err_info = ws_strdup_printf("Can't find a valid authentication. Disconnecting.");
/* All authentication failed. Disconnect and return */
ssh_disconnect(sshs);
@@ -152,13 +271,14 @@ failure:
int ssh_channel_printf(ssh_channel channel, const char* fmt, ...)
{
- gchar* buf;
+ char* buf;
va_list arg;
int ret = EXIT_SUCCESS;
va_start(arg, fmt);
- buf = g_strdup_vprintf(fmt, arg);
- if (ssh_channel_write(channel, buf, (guint32)strlen(buf)) == SSH_ERROR)
+ buf = ws_strdup_vprintf(fmt, arg);
+ ws_debug("%s", buf);
+ if (ssh_channel_write(channel, buf, (uint32_t)strlen(buf)) == SSH_ERROR)
ret = EXIT_FAILURE;
va_end(arg);
g_free(buf);
@@ -200,6 +320,23 @@ void ssh_params_free(ssh_params_t* ssh_params)
g_free(ssh_params);
}
+void ssh_params_set_log_level(ssh_params_t* ssh_params, enum ws_log_level level)
+{
+ switch (level) {
+ case LOG_LEVEL_NOISY:
+ ssh_params->debug = SSH_LOG_TRACE;
+ break;
+ case LOG_LEVEL_DEBUG:
+ ssh_params->debug = SSH_LOG_DEBUG;
+ break;
+ case LOG_LEVEL_INFO:
+ ssh_params->debug = SSH_LOG_INFO;
+ break;
+ default:
+ ssh_params->debug = SSH_LOG_WARN;
+ }
+}
+
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*