1 /* ssh-base.c
2  * ssh-base has base utility functions to connect to hosts via ssh
3  *
4  * Copyright 2016, Dario Lombardo
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include "config.h"
14 
15 #include "ssh-base.h"
16 
17 #include <extcap/extcap-base.h>
18 #include <string.h>
19 #include <libssh/callbacks.h>
20 #include <ws_attributes.h>
21 #include <wsutil/wslog.h>
22 
extcap_log(int priority _U_,const char * function,const char * buffer,void * userdata _U_)23 static void extcap_log(int priority _U_, const char *function, const char *buffer, void *userdata _U_)
24 {
25 	ws_debug("[%s] %s", function, buffer);
26 }
27 
add_libssh_info(extcap_parameters * extcap_conf)28 void add_libssh_info(extcap_parameters * extcap_conf)
29 {
30 	extcap_base_set_compiled_with(extcap_conf, "libssh version %s", SSH_STRINGIFY(LIBSSH_VERSION));
31 	extcap_base_set_running_with(extcap_conf, "libssh version %s", ssh_version(0));
32 }
33 
create_ssh_connection(const ssh_params_t * ssh_params,char ** err_info)34 ssh_session create_ssh_connection(const ssh_params_t* ssh_params, char** err_info)
35 {
36 	ssh_session sshs;
37 	gchar* username = NULL;
38 	guint port;
39 
40 	/* Open session and set options */
41 	sshs = ssh_new();
42 	if (sshs == NULL) {
43 		*err_info = g_strdup("Can't create ssh session");
44 		return NULL;
45 	}
46 
47 	if (!ssh_params->host) {
48 		*err_info = g_strdup("Hostname needed");
49 		goto failure;
50 	}
51 
52 	if (ssh_options_set(sshs, SSH_OPTIONS_HOST, ssh_params->host)) {
53 		*err_info = g_strdup_printf("Can't set the host: %s", ssh_params->host);
54 		goto failure;
55 	}
56 
57 	/* Load the configurations already present in the system configuration file. */
58 	/* They will be overwritten by the user-provided configurations. */
59 	if (ssh_options_parse_config(sshs, NULL) != 0) {
60 		*err_info = g_strdup("Unable to load the configuration file");
61 		goto failure;
62 	}
63 
64 	if (ssh_params->debug) {
65 		int debug_level = SSH_LOG_INFO;
66 		ssh_options_set(sshs, SSH_OPTIONS_LOG_VERBOSITY, &debug_level);
67 		ssh_set_log_callback(extcap_log);
68 	}
69 
70 	if (ssh_params->port != 0) {
71 		port = ssh_params->port;
72 		if (ssh_options_set(sshs, SSH_OPTIONS_PORT, &port)) {
73 			*err_info = g_strdup_printf("Can't set the port: %u", port);
74 			goto failure;
75 		}
76 	}
77 
78 	if (ssh_params->proxycommand) {
79 		if (ssh_options_set(sshs, SSH_OPTIONS_PROXYCOMMAND, ssh_params->proxycommand)) {
80 			*err_info = g_strdup_printf("Can't set the ProxyCommand: %s", ssh_params->proxycommand);
81 			goto failure;
82 		}
83 	}
84 
85 	if (ssh_params->username) {
86 		if (ssh_options_set(sshs, SSH_OPTIONS_USER, ssh_params->username)) {
87 			*err_info = g_strdup_printf("Can't set the username: %s", ssh_params->username);
88 			goto failure;
89 		}
90 	}
91 
92 	ssh_options_get(sshs, SSH_OPTIONS_USER, &username);
93 	ssh_options_get_port(sshs, &port);
94 
95 	ws_log(LOG_DOMAIN_CAPCHILD, LOG_LEVEL_INFO, "Opening ssh connection to %s@%s:%u", username,
96 		ssh_params->host, port);
97 
98 	ssh_string_free_char(username);
99 
100 	/* Connect to server */
101 	if (ssh_connect(sshs) != SSH_OK) {
102 		*err_info = g_strdup_printf("Connection error: %s", ssh_get_error(sshs));
103 		goto failure;
104 	}
105 
106 	/* If a public key path has been provided, try to authenticate using it */
107 	if (ssh_params->sshkey_path) {
108 		ssh_key pkey = ssh_key_new();
109 		int ret;
110 
111 		ws_info("Connecting using public key in %s...", ssh_params->sshkey_path);
112 		ret = ssh_pki_import_privkey_file(ssh_params->sshkey_path, ssh_params->sshkey_passphrase, NULL, NULL, &pkey);
113 
114 		if (ret == SSH_OK) {
115 			if (ssh_userauth_publickey(sshs, NULL, pkey) == SSH_AUTH_SUCCESS) {
116 				ws_info("done");
117 				ssh_key_free(pkey);
118 				return sshs;
119 			}
120 		}
121 		ssh_key_free(pkey);
122 		ws_info("failed (%s)", ssh_get_error(sshs));
123 	}
124 
125 	/* Try to authenticate using standard public key */
126 	ws_info("Connecting using standard public key...");
127 	if (ssh_userauth_publickey_auto(sshs, NULL, NULL) == SSH_AUTH_SUCCESS) {
128 		ws_info("done");
129 		return sshs;
130 	}
131 	ws_info("failed");
132 
133 	/* If a password has been provided and all previous attempts failed, try to use it */
134 	if (ssh_params->password) {
135 		ws_info("Connecting using password...");
136 		if (ssh_userauth_password(sshs, ssh_params->username, ssh_params->password) == SSH_AUTH_SUCCESS) {
137 			ws_info("done");
138 			return sshs;
139 		}
140 		ws_info("failed");
141 	}
142 
143 	*err_info = g_strdup_printf("Can't find a valid authentication. Disconnecting.");
144 
145 	/* All authentication failed. Disconnect and return */
146 	ssh_disconnect(sshs);
147 
148 failure:
149 	ssh_free(sshs);
150 	return NULL;
151 }
152 
ssh_channel_printf(ssh_channel channel,const char * fmt,...)153 int ssh_channel_printf(ssh_channel channel, const char* fmt, ...)
154 {
155 	gchar* buf;
156 	va_list arg;
157 	int ret = EXIT_SUCCESS;
158 
159 	va_start(arg, fmt);
160 	buf = g_strdup_vprintf(fmt, arg);
161 	if (ssh_channel_write(channel, buf, (guint32)strlen(buf)) == SSH_ERROR)
162 		ret = EXIT_FAILURE;
163 	va_end(arg);
164 	g_free(buf);
165 
166 	return ret;
167 }
168 
ssh_cleanup(ssh_session * sshs,ssh_channel * channel)169 void ssh_cleanup(ssh_session* sshs, ssh_channel* channel)
170 {
171 	if (*channel) {
172 		ssh_channel_send_eof(*channel);
173 		ssh_channel_close(*channel);
174 		ssh_channel_free(*channel);
175 		*channel = NULL;
176 	}
177 
178 	if (*sshs) {
179 		ssh_disconnect(*sshs);
180 		ssh_free(*sshs);
181 		*sshs = NULL;
182 	}
183 }
184 
ssh_params_new(void)185 ssh_params_t* ssh_params_new(void)
186 {
187 	return g_new0(ssh_params_t, 1);
188 }
189 
ssh_params_free(ssh_params_t * ssh_params)190 void ssh_params_free(ssh_params_t* ssh_params)
191 {
192 	if (!ssh_params)
193 		return;
194 	g_free(ssh_params->host);
195 	g_free(ssh_params->username);
196 	g_free(ssh_params->password);
197 	g_free(ssh_params->sshkey_path);
198 	g_free(ssh_params->sshkey_passphrase);
199 	g_free(ssh_params->proxycommand);
200 	g_free(ssh_params);
201 }
202 
203 /*
204  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
205  *
206  * Local variables:
207  * c-basic-offset: 8
208  * tab-width: 8
209  * indent-tabs-mode: t
210  * End:
211  *
212  * vi: set shiftwidth=8 tabstop=8 noexpandtab:
213  * :indentSize=8:tabSize=8:noTabs=false:
214  */
215