diff options
Diffstat (limited to 'extcap/ssh-base.c')
-rw-r--r-- | extcap/ssh-base.c | 195 |
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 * |