2 * ssh-base has base utility functions to connect to hosts via ssh
4 * Copyright 2016, Dario Lombardo
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
14 #define WS_LOG_DOMAIN LOG_DOMAIN_EXTCAP
18 #include <extcap/extcap-base.h>
20 #include <libssh/callbacks.h>
21 #include <ws_attributes.h>
22 #include <wsutil/wslog.h>
25 * The unreleased 0.11.0 version of libssh has the ability to
26 * add algorithms to the default supported list by prepending
27 * "+" to the configuration list. For older versions, we have
28 * to specify all the algorithms we want, but as long as at
29 * least one succeeds the command won't fail. (That means that
30 * it's possible that we won't actually add support for SHA-1,
31 * say if it's running on a system in FIPS mode. We could parse
32 * the returned list to check.)
34 #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,11,0)
35 #define HOSTKEYS_SHA1 "+ssh-rsa"
36 #define KEY_EXCHANGE_SHA1 "+diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1"
37 #define HMAC_SHA1 "+hmac-sha1-etm@openssh.com,hmac-sha1"
39 #define HOSTKEYS_SHA1 \
41 "ecdsa-sha2-nistp521," \
42 "ecdsa-sha2-nistp384," \
43 "ecdsa-sha2-nistp256," \
44 "sk-ssh-ed25519@openssh.com," \
45 "sk-ecdsa-sha2-nistp256@openssh.com," \
49 #define KEY_EXCHANGE_SHA1 \
50 "curve25519-sha256,curve25519-sha256@libssh.org," \
51 "ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521," \
52 "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512," \
53 "diffie-hellman-group-exchange-sha256," \
54 "diffie-hellman-group14-sha256," \
55 "diffie-hellman-group-exchange-sha1," \
56 "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"
58 "hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com," \
59 "hmac-sha2-256,hmac-sha2-512," \
60 "hmac-sha1-etm@openssh.com,hmac-sha1"
63 static void extcap_log(int priority
, const char *function
, const char *buffer
, void *userdata _U_
)
65 enum ws_log_level level
= LOG_LEVEL_DEBUG
;
68 level
= LOG_LEVEL_NOISY
;
71 level
= LOG_LEVEL_DEBUG
;
74 level
= LOG_LEVEL_INFO
;
78 /* Prior to 0.11.0 libssh prints far too much at SSH_LOG_WARN,
79 * including merely informational messages.
80 * Lower them to LOG_LEVEL_INFO, which won't get shown in the GUI
81 * and aren't shown by default. (Anything INFO and below goes to
82 * stdout due to ws_log_console_writer_set_use_stdout in extcap-base.c)
83 * After the following commit libssh only uses LOG_LEVEL_WARN for
85 * https://gitlab.com/libssh/libssh-mirror/-/commit/657d9143d121dfff74f5a63f734d0096c7f37194
87 #if LIBSSH_VERSION_INT < SSH_VERSION_INT(0,11,0)
88 level
= LOG_LEVEL_INFO
;
90 level
= LOG_LEVEL_WARNING
;
94 /* We set the libssh log level to specifically ask for this, so don't
95 * both checking the log level a second time.
97 ws_log_write_always_full("libssh", level
, NULL
, 0, function
, "%s", buffer
);
100 void add_libssh_info(extcap_parameters
* extcap_conf
)
102 extcap_base_set_compiled_with(extcap_conf
, "libssh version %s", SSH_STRINGIFY(LIBSSH_VERSION
));
103 extcap_base_set_running_with(extcap_conf
, "libssh version %s", ssh_version(0));
106 ssh_session
create_ssh_connection(const ssh_params_t
* ssh_params
, char** err_info
)
109 char* username
= NULL
;
112 /* Open session and set options */
115 *err_info
= g_strdup("Can't create ssh session");
119 if (!ssh_params
->host
) {
120 *err_info
= g_strdup("Hostname needed");
124 if (ssh_options_set(sshs
, SSH_OPTIONS_HOST
, ssh_params
->host
)) {
125 *err_info
= ws_strdup_printf("Can't set the host: %s", ssh_params
->host
);
129 /* Load the configurations already present in the system configuration file. */
130 /* They will be overwritten by the user-provided configurations. */
131 if (ssh_options_parse_config(sshs
, NULL
) != 0) {
132 *err_info
= g_strdup("Unable to load the configuration file");
136 ssh_options_set(sshs
, SSH_OPTIONS_LOG_VERBOSITY
, &ssh_params
->debug
);
137 ssh_set_log_callback(extcap_log
);
139 if (ssh_params
->ssh_sha1
) {
140 if (ssh_options_set(sshs
, SSH_OPTIONS_HOSTKEYS
, HOSTKEYS_SHA1
)) {
141 *err_info
= ws_strdup_printf("Can't set host keys to allow SHA-1.");
144 if (ssh_options_set(sshs
, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
, HOSTKEYS_SHA1
)) {
145 *err_info
= ws_strdup_printf("Can't set public key algorithms to allow SSH-RSA (SHA-1).");
148 if (ssh_options_set(sshs
, SSH_OPTIONS_KEY_EXCHANGE
, KEY_EXCHANGE_SHA1
)) {
149 *err_info
= ws_strdup_printf("Can't set key exchange methods to allow SHA-1.");
152 if (ssh_options_set(sshs
, SSH_OPTIONS_HMAC_C_S
, HMAC_SHA1
)) {
153 *err_info
= ws_strdup_printf("Can't set MAC client to server algorithms to allow SHA-1.");
156 if (ssh_options_set(sshs
, SSH_OPTIONS_HMAC_S_C
, HMAC_SHA1
)) {
157 *err_info
= ws_strdup_printf("Can't set MAC server to client algorithms to allow SHA-1.");
162 if (ssh_params
->port
!= 0) {
163 port
= ssh_params
->port
;
164 if (ssh_options_set(sshs
, SSH_OPTIONS_PORT
, &port
)) {
165 *err_info
= ws_strdup_printf("Can't set the port: %u", port
);
170 if (ssh_params
->proxycommand
) {
171 if (ssh_options_set(sshs
, SSH_OPTIONS_PROXYCOMMAND
, ssh_params
->proxycommand
)) {
172 *err_info
= ws_strdup_printf("Can't set the ProxyCommand: %s", ssh_params
->proxycommand
);
177 if (ssh_params
->username
) {
178 if (ssh_options_set(sshs
, SSH_OPTIONS_USER
, ssh_params
->username
)) {
179 *err_info
= ws_strdup_printf("Can't set the username: %s", ssh_params
->username
);
184 ssh_options_get(sshs
, SSH_OPTIONS_USER
, &username
);
185 ssh_options_get_port(sshs
, &port
);
187 ws_log(LOG_DOMAIN_CAPCHILD
, LOG_LEVEL_INFO
, "Opening ssh connection to %s@%s:%u", username
,
188 ssh_params
->host
, port
);
190 ssh_string_free_char(username
);
192 /* Connect to server */
193 if (ssh_connect(sshs
) != SSH_OK
) {
194 *err_info
= ws_strdup_printf("Connection error: %s", ssh_get_error(sshs
));
198 /* If a public key path has been provided, try to authenticate using it */
199 if (ssh_params
->sshkey_path
) {
200 ssh_key pkey
= ssh_key_new();
203 ws_info("Connecting using public key in %s...", ssh_params
->sshkey_path
);
204 ret
= ssh_pki_import_privkey_file(ssh_params
->sshkey_path
, ssh_params
->sshkey_passphrase
, NULL
, NULL
, &pkey
);
209 if (ssh_userauth_publickey(sshs
, NULL
, pkey
) == SSH_AUTH_SUCCESS
) {
214 ws_info("failed (%s)", ssh_get_error(sshs
));
217 ws_warning("Error importing key from %s. File doesn't exist or permission denied.",
218 ssh_params
->sshkey_path
);
221 /* Unfortunately we can't call ssh_get_error() on the
222 * key to determine why import failed.
224 ws_warning("Error importing key from %s. Make sure it is a valid"
225 " private key file and any necessary passphrase is configured.",
226 ssh_params
->sshkey_path
);
229 ws_warning("Unknown error from ssh_pki_import_privkey_file");
234 /* Workaround: it may happen that libssh closes socket in meantime and any next ssh_ call fails so we should detect it in advance */
235 if (ssh_get_fd(sshs
) != (socket_t
)-1) {
236 /* If a password has been provided and all previous attempts failed, try to use it */
237 if (ssh_params
->password
) {
238 ws_info("Connecting using password...");
239 if (ssh_userauth_password(sshs
, ssh_params
->username
, ssh_params
->password
) == SSH_AUTH_SUCCESS
) {
246 ws_info("ssh connection closed before password authentication");
249 /* Workaround: it may happen that libssh closes socket in meantime and any next ssh_ call fails so we should detect it in advance */
250 if (ssh_get_fd(sshs
) != (socket_t
)-1) {
251 /* Try to authenticate using standard public key */
252 ws_info("Connecting using standard public key...");
253 if (ssh_userauth_publickey_auto(sshs
, NULL
, NULL
) == SSH_AUTH_SUCCESS
) {
259 ws_info("ssh connection closed before public key authentication");
262 *err_info
= ws_strdup_printf("Can't find a valid authentication. Disconnecting.");
264 /* All authentication failed. Disconnect and return */
265 ssh_disconnect(sshs
);
272 int ssh_channel_printf(ssh_channel channel
, const char* fmt
, ...)
276 int ret
= EXIT_SUCCESS
;
279 buf
= ws_strdup_vprintf(fmt
, arg
);
281 if (ssh_channel_write(channel
, buf
, (uint32_t)strlen(buf
)) == SSH_ERROR
)
289 void ssh_cleanup(ssh_session
* sshs
, ssh_channel
* channel
)
292 ssh_channel_send_eof(*channel
);
293 ssh_channel_close(*channel
);
294 ssh_channel_free(*channel
);
299 ssh_disconnect(*sshs
);
305 ssh_params_t
* ssh_params_new(void)
307 return g_new0(ssh_params_t
, 1);
310 void ssh_params_free(ssh_params_t
* ssh_params
)
314 g_free(ssh_params
->host
);
315 g_free(ssh_params
->username
);
316 g_free(ssh_params
->password
);
317 g_free(ssh_params
->sshkey_path
);
318 g_free(ssh_params
->sshkey_passphrase
);
319 g_free(ssh_params
->proxycommand
);
323 void ssh_params_set_log_level(ssh_params_t
* ssh_params
, enum ws_log_level level
)
326 case LOG_LEVEL_NOISY
:
327 ssh_params
->debug
= SSH_LOG_TRACE
;
329 case LOG_LEVEL_DEBUG
:
330 ssh_params
->debug
= SSH_LOG_DEBUG
;
333 ssh_params
->debug
= SSH_LOG_INFO
;
336 ssh_params
->debug
= SSH_LOG_WARN
;
341 * Editor modelines - https://www.wireshark.org/tools/modelines.html
346 * indent-tabs-mode: t
349 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
350 * :indentSize=8:tabSize=8:noTabs=false: