blob: 4b68fe3140541fea119144f802d89e35b25ec2b8 [file] [log] [blame]
Radek Krejciac6d3472015-10-22 15:47:18 +02001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client_ssh.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @author Michal Vasko <mvasko@cesnet.cz>
5 * @brief libnetconf2 - SSH specific client session transport functions
Radek Krejciac6d3472015-10-22 15:47:18 +02006 *
7 * This source is compiled only with libssh.
8 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01009 * @copyright
Michal Vasko40899d82021-05-26 12:04:49 +020010 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Radek Krejciac6d3472015-10-22 15:47:18 +020011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * This source code is licensed under BSD 3-Clause License (the "License").
13 * You may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010015 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010016 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejciac6d3472015-10-22 15:47:18 +020017 */
18
Michal Vasko7b62fed2015-10-26 15:39:46 +010019#define _GNU_SOURCE
Michal Vasko7a20d2e2021-05-19 16:40:23 +020020
Radek Krejciac6d3472015-10-22 15:47:18 +020021#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010022#include <errno.h>
23#include <fcntl.h>
Michal Vaskoacd4ba82024-02-02 14:11:13 +010024#include <limits.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010025#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020026#include <pwd.h>
27#include <stddef.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <termios.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010034#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020035#include <unistd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020036
Michal Vasko7b62fed2015-10-26 15:39:46 +010037#ifdef ENABLE_DNSSEC
Michal Vasko7b62fed2015-10-26 15:39:46 +010038# include <validator/resolver.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020039# include <validator/validator.h>
Michal Vasko40899d82021-05-26 12:04:49 +020040# include <validator/validator-config.h>
41
Michal Vasko7b62fed2015-10-26 15:39:46 +010042# include <validator/validator-compat.h>
43#endif
44
Michal Vasko745ff832015-12-08 14:40:29 +010045#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020046#include <libyang/libyang.h>
47
Michal Vasko7a20d2e2021-05-19 16:40:23 +020048#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020049#include "config.h"
50#include "log_p.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010051#include "session_client.h"
52#include "session_client_ch.h"
roman3f9b65c2023-06-05 14:26:58 +020053#include "session_p.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020054
Radek Krejci62aa0642017-05-25 16:33:49 +020055struct nc_client_context *nc_client_context_location(void);
Michal Vasko30e2c872016-02-18 10:03:21 +010056
Radek Krejci62aa0642017-05-25 16:33:49 +020057#define client_opts nc_client_context_location()->opts
58#define ssh_opts nc_client_context_location()->ssh_opts
59#define ssh_ch_opts nc_client_context_location()->ssh_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010060
Michal Vaskoa43b8e32017-05-12 11:46:20 +020061static FILE *
62open_tty_noecho(const char *path, struct termios *oldterm)
63{
64 struct termios newterm;
65 FILE *ret;
66
67 if (!(ret = fopen(path, "r"))) {
Michal Vasko05532772021-06-03 12:12:38 +020068 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020069 return NULL;
70 }
71
72 if (tcgetattr(fileno(ret), oldterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020073 ERR(NULL, "Unable to get terminal \"%s\" settings (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020074 fclose(ret);
75 return NULL;
76 }
77
78 newterm = *oldterm;
79 newterm.c_lflag &= ~ECHO;
80 newterm.c_lflag &= ~ICANON;
81 tcflush(fileno(ret), TCIFLUSH);
82 if (tcsetattr(fileno(ret), TCSANOW, &newterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020083 ERR(NULL, "Unable to change terminal \"%s\" settings for hiding password (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020084 fclose(ret);
85 return NULL;
86 }
87
88 return ret;
89}
90
Michal Vasko51228ac2018-03-29 14:57:53 +020091static FILE *
92nc_open_in(int echo, struct termios *oldterm)
Michal Vaskoa43b8e32017-05-12 11:46:20 +020093{
Michal Vasko51228ac2018-03-29 14:57:53 +020094 char buf[512];
95 int buflen = 512, ret;
96 FILE *in;
97
98 if (!echo) {
99 in = open_tty_noecho("/dev/tty", oldterm);
100 } else {
101 in = fopen("/dev/tty", "r");
102 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200103 ERR(NULL, "Unable to open terminal \"/dev/tty\" for reading (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200104 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200105 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200106
107 if (!in) {
108 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200109 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200110 return NULL;
111 }
112
113 if (!echo) {
114 in = open_tty_noecho(buf, oldterm);
115 } else {
116 in = fopen(buf, "r");
117 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200118 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200119 }
120 }
121 }
122
123 return in;
124}
125
126static FILE *
127nc_open_out(void)
128{
129 char buf[512];
130 int buflen = 512, ret;
131 FILE *out;
132
133 out = fopen("/dev/tty", "w");
134 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200135 ERR(NULL, "Unable to open terminal \"/dev/tty\" for writing (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200136
137 if ((ret = ttyname_r(STDOUT_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200138 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200139 return NULL;
140 }
141
142 out = fopen(buf, "w");
143 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200144 ERR(NULL, "Unable to open terminal \"%s\" for writing (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200145 }
146 }
147
148 return out;
149}
150
151static void
152nc_close_inout(FILE *inout, int echo, struct termios *oldterm)
153{
154 if (inout) {
155 if (!echo && (tcsetattr(fileno(inout), TCSANOW, oldterm) != 0)) {
Michal Vasko05532772021-06-03 12:12:38 +0200156 ERR(NULL, "Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200157 }
158 fclose(inout);
159 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200160}
161
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400162void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100163_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +0100164{
165 int i;
166
Michal Vaskoe22c6732016-01-29 11:03:02 +0100167 for (i = 0; i < opts->key_count; ++i) {
168 free(opts->keys[i].pubkey_path);
169 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +0100170 }
Michal Vaskoe22c6732016-01-29 11:03:02 +0100171 free(opts->keys);
172 free(opts->username);
romanf6e32012023-04-24 15:51:26 +0200173 free(opts->knownhosts_path);
romanc1d2b092023-02-02 08:58:27 +0100174 opts->key_count = 0;
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200175 opts->keys = NULL;
176 opts->username = NULL;
romanf6e32012023-04-24 15:51:26 +0200177 opts->knownhosts_path = NULL;
Michal Vaskoe22c6732016-01-29 11:03:02 +0100178}
Michal Vasko990089e2015-10-27 15:05:25 +0100179
Michal Vaskob7558c52016-02-26 15:04:19 +0100180void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100181nc_client_ssh_destroy_opts(void)
182{
183 _nc_client_ssh_destroy_opts(&ssh_opts);
184 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +0100185}
186
Michal Vaskoef112d72016-02-18 13:28:25 +0100187#ifdef ENABLE_DNSSEC
188
189/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
190/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
191static int
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200192sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg)
193{
Michal Vaskoef112d72016-02-18 13:28:25 +0100194 ns_msg handle;
195 ns_rr rr;
196 val_status_t val_status;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200197 const unsigned char *rdata;
Michal Vaskoef112d72016-02-18 13:28:25 +0100198 unsigned char buf[4096];
199 int buf_len = 4096;
200 int ret = 0, i, j, len;
201
202 /* class 1 - internet, type 44 - SSHFP */
203 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
204
205 if ((len < 0) || !val_istrusted(val_status)) {
206 ret = 2;
207 goto finish;
208 }
209
210 if (ns_initparse(buf, len, &handle) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200211 ERR(NULL, "Failed to initialize DNSSEC response parser.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100212 ret = 2;
213 goto finish;
214 }
215
216 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
Michal Vasko05532772021-06-03 12:12:38 +0200217 ERR(NULL, "DNSSEC query returned %d.", i);
Michal Vaskoef112d72016-02-18 13:28:25 +0100218 ret = 2;
219 goto finish;
220 }
221
222 if (!libsres_msg_getflag(handle, ns_f_ad)) {
223 /* response not secured by DNSSEC */
224 ret = 1;
225 }
226
227 /* query section */
228 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
Michal Vasko05532772021-06-03 12:12:38 +0200229 ERR(NULL, "DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100230 ret = 2;
231 goto finish;
232 }
233
234 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200235 ERR(NULL, "DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100236 ret = 2;
237 goto finish;
238 }
239
240 /* answer section */
241 i = 0;
242 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
243 if (ns_rr_type(rr) != 44) {
244 ++i;
245 continue;
246 }
247
248 rdata = ns_rr_rdata(rr);
249 if (rdata[0] != type) {
250 ++i;
251 continue;
252 }
253 if (rdata[1] != alg) {
254 ++i;
255 continue;
256 }
257
258 /* we found the correct SSHFP entry */
259 rdata += 2;
260 for (j = 0; j < 20; ++j) {
261 if (rdata[j] != (unsigned char)sha1hash[j]) {
262 ret = 2;
263 goto finish;
264 }
265 }
266
267 /* server fingerprint is supported by a DNS entry,
268 * we have already determined if DNSSEC was used or not
269 */
270 goto finish;
271 }
272
273 /* no match */
274 ret = 2;
275
276finish:
277 val_free_validator_state();
278 return ret;
279}
280
281#endif /* ENABLE_DNSSEC */
282
romanf6e32012023-04-24 15:51:26 +0200283static int
284nc_client_ssh_update_known_hosts(ssh_session session, const char *hostname)
Michal Vaskoef112d72016-02-18 13:28:25 +0100285{
romanf6e32012023-04-24 15:51:26 +0200286 int ret;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200287
romanf6e32012023-04-24 15:51:26 +0200288 ret = ssh_session_update_known_hosts(session);
romanf6e32012023-04-24 15:51:26 +0200289 if (ret != SSH_OK) {
290 WRN(NULL, "Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
291 }
292
293 return ret;
294}
295
296static int
297nc_client_ssh_get_srv_pubkey_data(ssh_session session, enum ssh_keytypes_e *srv_pubkey_type, char **hexa, unsigned char **hash_sha1)
298{
299 int ret;
Michal Vaskoef112d72016-02-18 13:28:25 +0100300 ssh_key srv_pubkey;
Michal Vaskoef112d72016-02-18 13:28:25 +0100301 size_t hlen;
Michal Vaskoef112d72016-02-18 13:28:25 +0100302
romanf6e32012023-04-24 15:51:26 +0200303 *hexa = NULL;
304 *hash_sha1 = NULL;
Michal Vaskoef112d72016-02-18 13:28:25 +0100305
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800306 ret = ssh_get_server_publickey(session, &srv_pubkey);
Michal Vaskoef112d72016-02-18 13:28:25 +0100307 if (ret < 0) {
roman6e5fd702023-04-27 14:30:27 +0200308 ERR(NULL, "Unable to get server's public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100309 return -1;
310 }
311
romanf6e32012023-04-24 15:51:26 +0200312 *srv_pubkey_type = ssh_key_type(srv_pubkey);
313 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, hash_sha1, &hlen);
Michal Vaskoef112d72016-02-18 13:28:25 +0100314 ssh_key_free(srv_pubkey);
315 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200316 ERR(NULL, "Failed to calculate SHA1 hash of the server public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100317 return -1;
318 }
319
romanf6e32012023-04-24 15:51:26 +0200320 *hexa = ssh_get_hexa(*hash_sha1, hlen);
321 if (!*hexa) {
322 ERR(NULL, "Getting the hostkey's hex string failed.");
323 return -1;
324 }
325
326 return 0;
327}
328
329#ifdef ENABLE_DNSSEC
330static int
331nc_client_ssh_do_dnssec_sshfp_check(ssh_session session, enum ssh_keytypes_e srv_pubkey_type, const char *hostname, unsigned char *hash_sha1)
332{
roman2eab4742023-06-06 10:00:26 +0200333 int ret = 0;
romanf6e32012023-04-24 15:51:26 +0200334
335 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) && (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
336 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
romanf6e32012023-04-24 15:51:26 +0200337 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
338 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
339 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
340 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
341 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
roman3a95bb22023-10-26 11:07:17 +0200342 } else {
343 /* other key types not supported */
344 ret = 1;
romanf6e32012023-04-24 15:51:26 +0200345 }
346
347 /* DNSSEC SSHFP check successful, that's enough */
348 if (!ret) {
349 VRB(NULL, "DNSSEC SSHFP check successful.");
romanf6e32012023-04-24 15:51:26 +0200350 ssh_session_update_known_hosts(session);
romanf6e32012023-04-24 15:51:26 +0200351 }
352
353 return ret;
354 }
355
356 return 1;
357}
358
359#endif
360
361static int
362nc_client_ssh_auth_hostkey_check(const char *hostname, uint16_t port, ssh_session session)
363{
364 char *hexa = NULL;
365 unsigned char *hash_sha1 = NULL;
366 NC_SSH_KNOWNHOSTS_MODE knownhosts_mode = ssh_opts.knownhosts_mode;
367 enum ssh_keytypes_e srv_pubkey_type;
368 char answer[5];
369 FILE *out = NULL, *in = NULL;
370 int c, state;
371
372#ifdef ENABLE_DNSSEC
373 int dnssec_ret;
374#endif
375
376 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_SKIP) {
377 /* skip all hostkey checks */
378 return 0;
379 }
380
381 if (nc_client_ssh_get_srv_pubkey_data(session, &srv_pubkey_type, &hexa, &hash_sha1)) {
382 goto error;
383 }
384
romanf6e32012023-04-24 15:51:26 +0200385 state = ssh_session_is_known_server(session);
Michal Vaskoef112d72016-02-18 13:28:25 +0100386 switch (state) {
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800387 case SSH_KNOWN_HOSTS_OK:
Michal Vaskoef112d72016-02-18 13:28:25 +0100388 break; /* ok */
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800389 case SSH_KNOWN_HOSTS_CHANGED:
romanf6e32012023-04-24 15:51:26 +0200390 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT) {
391 /* is the mode is set to accept, then accept any connection even if the remote key changed */
romanfa609bd2023-10-26 12:14:12 +0200392 WRN(NULL, "Remote host key changed!");
romanf6e32012023-04-24 15:51:26 +0200393 break;
394 } else {
395 ERR(NULL, "Remote host key changed, the connection will be terminated!");
396 goto error;
397 }
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800398 case SSH_KNOWN_HOSTS_OTHER:
Michal Vasko05532772021-06-03 12:12:38 +0200399 WRN(NULL, "Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100400 goto hostkey_not_known;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800401 case SSH_KNOWN_HOSTS_NOT_FOUND:
Michal Vasko05532772021-06-03 12:12:38 +0200402 WRN(NULL, "Could not find the known hosts file.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100403 goto hostkey_not_known;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800404 case SSH_KNOWN_HOSTS_UNKNOWN:
Michal Vaskoef112d72016-02-18 13:28:25 +0100405hostkey_not_known:
406#ifdef ENABLE_DNSSEC
romanf6e32012023-04-24 15:51:26 +0200407 /* do dnssec check, if it's ok then we're done otherwise continue */
408 dnssec_ret = nc_client_ssh_do_dnssec_sshfp_check(session, srv_pubkey_type, hostname, hash_sha1);
409 if (!dnssec_ret) {
410 ssh_clean_pubkey_hash(&hash_sha1);
411 ssh_string_free_char(hexa);
412 return 0;
Michal Vaskoef112d72016-02-18 13:28:25 +0100413 }
414#endif
415
romanf6e32012023-04-24 15:51:26 +0200416 /* open the files for reading/writing */
Michal Vasko51228ac2018-03-29 14:57:53 +0200417 if (!(in = nc_open_in(1, NULL))) {
418 goto error;
419 }
romanf6e32012023-04-24 15:51:26 +0200420
Michal Vasko51228ac2018-03-29 14:57:53 +0200421 if (!(out = nc_open_out())) {
422 goto error;
423 }
424
romanf6e32012023-04-24 15:51:26 +0200425 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_STRICT) {
426 /* do not connect if the hostkey is not present in known_hosts file in this mode */
romanfa609bd2023-10-26 12:14:12 +0200427 ERR(NULL, "No %s host key is known for [%s]:%hu.\n", ssh_key_type_to_char(srv_pubkey_type), hostname, port);
romanf6e32012023-04-24 15:51:26 +0200428 goto error;
429 } else if ((knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT_NEW) || (knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT)) {
430 /* add a new entry to the known_hosts file without prompting */
431 if (nc_client_ssh_update_known_hosts(session, hostname)) {
432 goto error;
433 }
434
435 VRB(NULL, "Permanently added '[%s]:%hu' (%s) to the list of known hosts.", hostname, port, ssh_key_type_to_char(srv_pubkey_type));
436
437 break;
438 }
439
Michal Vaskoef112d72016-02-18 13:28:25 +0100440 /* try to get result from user */
Michal Vasko51228ac2018-03-29 14:57:53 +0200441 if (fprintf(out, "The authenticity of the host \'%s\' cannot be established.\n", hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200442 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200443 goto error;
444 }
445 if (fprintf(out, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200446 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200447 goto error;
448 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100449
450#ifdef ENABLE_DNSSEC
romanf6e32012023-04-24 15:51:26 +0200451 if (dnssec_ret == 2) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200452 if (fprintf(out, "No matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200453 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200454 goto error;
455 }
romanf6e32012023-04-24 15:51:26 +0200456 } else if (dnssec_ret == 1) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200457 if (fprintf(out, "Matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200458 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200459 goto error;
460 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100461 }
462#endif
463
Michal Vasko51228ac2018-03-29 14:57:53 +0200464 if (fprintf(out, "Are you sure you want to continue connecting (yes/no)? ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200465 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200466 goto error;
467 }
468 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100469
470 do {
Michal Vasko51228ac2018-03-29 14:57:53 +0200471 if (fscanf(in, "%4s", answer) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200472 ERR(NULL, "Reading from input failed (%s).", feof(in) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200473 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100474 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200475 while (((c = getc(in)) != EOF) && (c != '\n')) {}
Michal Vaskoef112d72016-02-18 13:28:25 +0100476
Michal Vasko51228ac2018-03-29 14:57:53 +0200477 fflush(in);
Michal Vaskoef112d72016-02-18 13:28:25 +0100478 if (!strcmp("yes", answer)) {
romanf6e32012023-04-24 15:51:26 +0200479 /* store the key into the known_hosts file */
480 nc_client_ssh_update_known_hosts(session, hostname);
Michal Vaskoef112d72016-02-18 13:28:25 +0100481 } else if (!strcmp("no", answer)) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200482 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100483 } else {
Michal Vasko51228ac2018-03-29 14:57:53 +0200484 if (fprintf(out, "Please type 'yes' or 'no': ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200485 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200486 goto error;
487 }
Michal Vasko5fb5f992020-11-26 15:19:31 +0100488 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100489 }
490 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
491
492 break;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800493 case SSH_KNOWN_HOSTS_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +0200494 ERR(NULL, "SSH error: %s", ssh_get_error(session));
Michal Vasko51228ac2018-03-29 14:57:53 +0200495 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100496 }
497
Michal Vasko51228ac2018-03-29 14:57:53 +0200498 nc_close_inout(in, 1, NULL);
499 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100500 ssh_clean_pubkey_hash(&hash_sha1);
501 ssh_string_free_char(hexa);
502 return 0;
503
Michal Vasko51228ac2018-03-29 14:57:53 +0200504error:
505 nc_close_inout(in, 1, NULL);
506 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100507 ssh_clean_pubkey_hash(&hash_sha1);
508 ssh_string_free_char(hexa);
509 return -1;
510}
511
Radek Krejci62aa0642017-05-25 16:33:49 +0200512char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200513sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200514{
Michal Vasko51228ac2018-03-29 14:57:53 +0200515 char *buf = NULL;
516 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200517 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200518 FILE *in = NULL, *out = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200519
Michal Vasko11d142a2016-01-19 15:58:24 +0100520 buf = malloc(buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200521 NC_CHECK_ERRMEM_RET(!buf, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100522
Michal Vasko51228ac2018-03-29 14:57:53 +0200523 if (!(in = nc_open_in(0, &oldterm))) {
524 goto error;
525 }
526 if (!(out = nc_open_out())) {
527 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200528 }
529
Michal Vasko51228ac2018-03-29 14:57:53 +0200530 if (fprintf(out, "%s@%s password: ", username, hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200531 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200532 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200533 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200534 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200535
536 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200537 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100538 if (len >= buflen - 1) {
539 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100540 buf = nc_realloc(buf, buflen * sizeof *buf);
roman124a4362023-10-26 15:36:22 +0200541 NC_CHECK_ERRMEM_GOTO(!buf, , error);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100542 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100543 buf[len++] = (char)c;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100544 }
545 buf[len++] = 0; /* terminating null byte */
546
Michal Vasko51228ac2018-03-29 14:57:53 +0200547 fprintf(out, "\n");
548
549 nc_close_inout(in, 0, &oldterm);
550 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100551 return buf;
Michal Vasko93f26d92018-02-01 09:08:35 +0100552
Michal Vasko51228ac2018-03-29 14:57:53 +0200553error:
554 nc_close_inout(in, 0, &oldterm);
555 nc_close_inout(out, 1, NULL);
Michal Vasko93f26d92018-02-01 09:08:35 +0100556 free(buf);
557 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100558}
559
Radek Krejci62aa0642017-05-25 16:33:49 +0200560char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200561sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100562{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200563 unsigned int buflen = 64, cur_len;
Michal Vasko51228ac2018-03-29 14:57:53 +0200564 int c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200565 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200566 char *buf = NULL;
567 FILE *in = NULL, *out = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100568
569 buf = malloc(buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200570 NC_CHECK_ERRMEM_RET(!buf, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100571
Michal Vasko51228ac2018-03-29 14:57:53 +0200572 if (!(in = nc_open_in(echo, &oldterm))) {
573 goto error;
574 }
575 if (!(out = nc_open_out())) {
576 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100577 }
578
Michal Vasko51228ac2018-03-29 14:57:53 +0200579 if (auth_name && (fprintf(out, "%s\n", auth_name) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200580 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200581 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100582 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200583 if (instruction && (fprintf(out, "%s\n", instruction) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200584 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200585 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100586 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200587 if (fputs(prompt, out) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200588 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200589 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200590 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200591 fflush(out);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100592
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200593 cur_len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200594 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200595 if (cur_len >= buflen - 1) {
596 buflen *= 2;
597 buf = nc_realloc(buf, buflen * sizeof *buf);
roman124a4362023-10-26 15:36:22 +0200598 NC_CHECK_ERRMEM_GOTO(!buf, , error);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200599 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100600 buf[cur_len++] = (char)c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200601 }
602 /* terminating null byte */
603 buf[cur_len] = '\0';
604
Michal Vasko51228ac2018-03-29 14:57:53 +0200605 fprintf(out, "\n");
606
607 nc_close_inout(in, echo, &oldterm);
608 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200609 return buf;
610
Michal Vasko51228ac2018-03-29 14:57:53 +0200611error:
612 nc_close_inout(in, echo, &oldterm);
613 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200614 free(buf);
615 return NULL;
616}
617
Radek Krejci62aa0642017-05-25 16:33:49 +0200618char *
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200619sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200620{
Michal Vasko51228ac2018-03-29 14:57:53 +0200621 char *buf = NULL;
622 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200623 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200624 FILE *in = NULL, *out = NULL;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200625
626 buf = malloc(buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200627 NC_CHECK_ERRMEM_RET(!buf, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200628
Michal Vasko51228ac2018-03-29 14:57:53 +0200629 if (!(in = nc_open_in(0, &oldterm))) {
630 goto error;
631 }
632 if (!(out = nc_open_out())) {
633 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200634 }
635
Michal Vasko51228ac2018-03-29 14:57:53 +0200636 if (fprintf(out, "Enter passphrase for the key '%s': ", privkey_path) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200637 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200638 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200639 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200640 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200641
642 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200643 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100644 if (len >= buflen - 1) {
645 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100646 buf = nc_realloc(buf, buflen * sizeof *buf);
roman124a4362023-10-26 15:36:22 +0200647 NC_CHECK_ERRMEM_GOTO(!buf, , error);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100648 }
649 buf[len++] = (char)c;
650 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200651 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100652
Michal Vasko51228ac2018-03-29 14:57:53 +0200653 fprintf(out, "\n");
654
655 nc_close_inout(in, 0, &oldterm);
656 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100657 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100658
Michal Vasko51228ac2018-03-29 14:57:53 +0200659error:
660 nc_close_inout(in, 0, &oldterm);
661 nc_close_inout(out, 1, NULL);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100662 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100663 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100664}
665
romanf6e32012023-04-24 15:51:26 +0200666API int
667nc_client_ssh_set_knownhosts_path(const char *path)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100668{
romanf6e32012023-04-24 15:51:26 +0200669 free(ssh_opts.knownhosts_path);
670
671 if (!path) {
672 ssh_opts.knownhosts_path = NULL;
673 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100674 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100675
romanf6e32012023-04-24 15:51:26 +0200676 ssh_opts.knownhosts_path = strdup(path);
roman3a95bb22023-10-26 11:07:17 +0200677 NC_CHECK_ERRMEM_RET(!ssh_opts.knownhosts_path, 1);
romanf6e32012023-04-24 15:51:26 +0200678
679 return 0;
Radek Krejci90a84a22017-05-25 13:53:00 +0200680}
681
Radek Krejci90a84a22017-05-25 13:53:00 +0200682API void
romanf6e32012023-04-24 15:51:26 +0200683nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode)
Radek Krejci90a84a22017-05-25 13:53:00 +0200684{
romanf6e32012023-04-24 15:51:26 +0200685 ssh_opts.knownhosts_mode = mode;
Radek Krejci90a84a22017-05-25 13:53:00 +0200686}
Michal Vaskoef112d72016-02-18 13:28:25 +0100687
Michal Vasko30e2c872016-02-18 10:03:21 +0100688static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200689_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200690 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100691{
692 if (auth_password) {
693 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200694 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100695 } else {
696 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200697 opts->auth_password_priv = NULL;
698 }
699}
700
701static void
702_nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200703 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200704{
705 if (auth_password) {
706 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
707 }
708 if (priv) {
709 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100710 }
711}
712
713API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200714nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200715 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100716{
Radek Krejci90a84a22017-05-25 13:53:00 +0200717 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100718}
719
720API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200721nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200722 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100723{
Radek Krejci90a84a22017-05-25 13:53:00 +0200724 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
725}
726
727API void
728nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200729 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200730{
731 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
732}
733
734API void
735nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200736 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200737{
738 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100739}
740
741static void
742_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200743 const char *prompt, int echo, void *priv),
744 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100745{
746 if (auth_interactive) {
747 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200748 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100749 } else {
750 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200751 opts->auth_interactive_priv = NULL;
752 }
753}
754
755static void
756_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200757 const char *prompt, int echo, void *priv),
758 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200759{
760 if (auth_interactive) {
761 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
762 }
763 if (priv) {
764 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100765 }
766}
767
768API void
769nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200770 const char *prompt, int echo, void *priv),
771 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100772{
Radek Krejci90a84a22017-05-25 13:53:00 +0200773 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100774}
775
776API void
777nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200778 const char *prompt, int echo, void *priv),
779 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100780{
Radek Krejci90a84a22017-05-25 13:53:00 +0200781 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
782}
783
784API void
785nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200786 const char *prompt, int echo, void *priv),
787 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200788{
789 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
790}
791
792API void
793nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200794 const char *prompt, int echo, void *priv),
795 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200796{
797 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100798}
799
800static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200801_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200802 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100803{
804 if (auth_privkey_passphrase) {
805 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200806 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100807 } else {
808 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200809 opts->auth_privkey_passphrase_priv = NULL;
810 }
811}
812
813static void
814_nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200815 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200816{
817 if (auth_privkey_passphrase) {
818 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
819 }
820 if (priv) {
821 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100822 }
823}
824
825API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200826nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200827 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100828{
Radek Krejci90a84a22017-05-25 13:53:00 +0200829 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100830}
831
832API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200833nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200834 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100835{
Radek Krejci90a84a22017-05-25 13:53:00 +0200836 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
837}
838
839API void
840nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200841 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200842{
843 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
844}
845
846API void
847nc_client_ssh_ch_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200848 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200849{
850 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100851}
852
Michal Vasko3031aae2016-01-27 16:07:18 +0100853static int
854_nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100855{
856 int i;
857 FILE *key;
858 char line[128];
859
roman40672412023-05-04 11:10:22 +0200860 NC_CHECK_ARG_RET(NULL, pub_key, priv_key, -1);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100861
Michal Vasko3031aae2016-01-27 16:07:18 +0100862 for (i = 0; i < opts->key_count; ++i) {
863 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
864 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200865 WRN(NULL, "Private key \"%s\" found with another public key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200866 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100867 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100868 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200869 WRN(NULL, "Public key \"%s\" found with another private key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200870 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100871 continue;
872 }
873
Michal Vasko05532772021-06-03 12:12:38 +0200874 ERR(NULL, "SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100875 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100876 }
877 }
878
Michal Vasko3031aae2016-01-27 16:07:18 +0100879 /* add the keys */
Michal Vaskoacd4ba82024-02-02 14:11:13 +0100880 opts->keys = nc_realloc(opts->keys, (opts->key_count + 1) * sizeof *opts->keys);
roman3a95bb22023-10-26 11:07:17 +0200881 NC_CHECK_ERRMEM_RET(!opts->keys, -1);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100882
Michal Vaskoacd4ba82024-02-02 14:11:13 +0100883 opts->keys[opts->key_count].pubkey_path = realpath(pub_key, NULL);
884 if (!opts->keys[opts->key_count].pubkey_path) {
885 ERR(NULL, "Invalid public key path \"%s\" (%s).", pub_key, strerror(errno));
886 return -1;
887 }
888 opts->keys[opts->key_count].privkey_path = realpath(priv_key, NULL);
889 if (!opts->keys[opts->key_count].privkey_path) {
890 ERR(NULL, "Invalid private key path \"%s\" (%s).", priv_key, strerror(errno));
891 free(opts->keys[opts->key_count].pubkey_path);
892 return -1;
893 }
894 opts->keys[opts->key_count].privkey_crypt = 0;
895 ++opts->key_count;
896
897 /* use normalized path */
898 priv_key = opts->keys[opts->key_count - 1].privkey_path;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100899
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100900 /* check encryption */
901 if ((key = fopen(priv_key, "r"))) {
902 /* 1st line - key type */
903 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100904 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200905 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100906 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100907 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100908 /* 2nd line - encryption information or key */
909 if (!fgets(line, sizeof line, key)) {
910 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200911 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100912 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100913 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100914 fclose(key);
915 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100916 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100917 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100918 }
919
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100920 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100921}
922
923API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100924nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100925{
Michal Vasko3031aae2016-01-27 16:07:18 +0100926 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
927}
928
929API int
930nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
931{
932 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
933}
934
935static int
936_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
937{
938 if (idx >= opts->key_count) {
roman40672412023-05-04 11:10:22 +0200939 ERRARG(NULL, "idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100940 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100941 }
942
Michal Vasko3031aae2016-01-27 16:07:18 +0100943 free(opts->keys[idx].pubkey_path);
944 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100945
Michal Vasko3031aae2016-01-27 16:07:18 +0100946 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100947 if (idx < opts->key_count) {
948 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
949 }
950 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100951 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
roman3a95bb22023-10-26 11:07:17 +0200952 NC_CHECK_ERRMEM_RET(!opts->keys, -1);
Michal Vaskoc0256492016-02-02 12:19:06 +0100953 } else {
954 free(opts->keys);
955 opts->keys = NULL;
956 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100957
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100958 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100959}
960
961API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100962nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100963{
Michal Vasko3031aae2016-01-27 16:07:18 +0100964 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100965}
966
967API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100968nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100969{
Michal Vasko3031aae2016-01-27 16:07:18 +0100970 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
971}
972
973static int
974_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
975{
976 return opts->key_count;
977}
978
979API int
980nc_client_ssh_get_keypair_count(void)
981{
982 return _nc_client_ssh_get_keypair_count(&ssh_opts);
983}
984
985API int
986nc_client_ssh_ch_get_keypair_count(void)
987{
988 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
989}
990
991static int
992_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
993{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200994 if (idx >= opts->key_count) {
roman40672412023-05-04 11:10:22 +0200995 ERRARG(NULL, "idx");
Michal Vasko45e53ae2016-04-07 11:46:03 +0200996 return -1;
997 } else if (!pub_key && !priv_key) {
roman40672412023-05-04 11:10:22 +0200998 ERRARG(NULL, "pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100999 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001000 }
1001
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001002 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001003 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001004 }
1005 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001006 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001007 }
1008
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001009 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001010}
1011
Michal Vasko3031aae2016-01-27 16:07:18 +01001012API int
1013nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
1014{
1015 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
1016}
1017
1018API int
1019nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
1020{
1021 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
1022}
1023
1024static void
1025_nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref, struct nc_client_ssh_opts *opts)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001026{
1027 if (pref < 0) {
1028 pref = -1;
1029 }
1030
1031 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001032 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001033 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001034 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001035 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001036 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001037 }
1038}
1039
Michal Vasko3031aae2016-01-27 16:07:18 +01001040API void
1041nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001042{
Michal Vasko3031aae2016-01-27 16:07:18 +01001043 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
1044}
1045
1046API void
1047nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
1048{
1049 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
1050}
1051
1052static int16_t
1053_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
1054{
1055 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001056
1057 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001058 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001059 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001060 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001061 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001062 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001063 }
1064
1065 return pref;
1066}
1067
Michal Vasko3031aae2016-01-27 16:07:18 +01001068API int16_t
1069nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1070{
1071 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
1072}
1073
1074API int16_t
1075nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1076{
1077 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
1078}
1079
1080static int
1081_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
1082{
1083 if (opts->username) {
1084 free(opts->username);
1085 }
1086 if (username) {
1087 opts->username = strdup(username);
roman3a95bb22023-10-26 11:07:17 +02001088 NC_CHECK_ERRMEM_RET(!opts->username, -1);
Michal Vasko3031aae2016-01-27 16:07:18 +01001089 } else {
1090 opts->username = NULL;
1091 }
1092
1093 return 0;
1094}
1095
1096API int
1097nc_client_ssh_set_username(const char *username)
1098{
1099 return _nc_client_ssh_set_username(username, &ssh_opts);
1100}
1101
1102API int
1103nc_client_ssh_ch_set_username(const char *username)
1104{
1105 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1106}
1107
Michal Vaskoe22c6732016-01-29 11:03:02 +01001108static const char *
1109_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1110{
1111 return opts->username;
1112}
1113
1114API const char *
1115nc_client_ssh_get_username(void)
1116{
1117 return _nc_client_ssh_get_username(&ssh_opts);
1118}
1119
1120API const char *
1121nc_client_ssh_ch_get_username(void)
1122{
1123 return _nc_client_ssh_get_username(&ssh_ch_opts);
1124}
1125
Michal Vasko3031aae2016-01-27 16:07:18 +01001126API int
1127nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1128{
Michal Vasko9d4cca52022-09-07 11:19:57 +02001129 return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +01001130}
1131
1132API int
1133nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1134{
1135 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1136}
1137
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001138/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001139 * Host, port, username, and a connected socket is expected to be set.
Radek Krejciae813f42018-07-02 13:38:30 +02001140 *
1141 * return values
1142 * -1 failure
1143 * 0 try again
1144 * 1 success
Michal Vasko7b62fed2015-10-26 15:39:46 +01001145 */
1146static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001147connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001148{
Radek Krejciae813f42018-07-02 13:38:30 +02001149 int j, ret_auth, userauthlist, ret, attempt = 0;
Michal Vasko235efdc2015-12-17 12:05:04 +01001150 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001151 int16_t pref;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001152 const char *prompt;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001153 char *s, *answer, echo;
1154 ssh_key pubkey, privkey;
1155 ssh_session ssh_sess;
roman6ece9c52022-06-22 09:29:17 +02001156 struct timespec ts_timeout;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001157
1158 ssh_sess = session->ti.libssh.session;
1159
Michal Vaskod8a74192023-02-06 15:51:50 +01001160 nc_timeouttime_get(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001161 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1162 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001163 if (nc_timeouttime_cur_diff(&ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001164 break;
1165 }
1166 }
1167 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001168 ERR(session, "SSH connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001169 return 0;
1170 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001171 ERR(session, "Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
1172 DBG(session, "Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001173 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001174 }
1175
romanf6e32012023-04-24 15:51:26 +02001176 if (nc_client_ssh_auth_hostkey_check(session->host, session->port, ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001177 ERR(session, "Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001178 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001179 }
1180
Michal Vasko36c7be82017-02-22 13:37:59 +01001181 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001182 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001183 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001184 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1185 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001186 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001187 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001188 }
1189 }
1190 if (ret_auth == SSH_AUTH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001191 ERR(session, "Request authentication methods timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001192 return 0;
1193 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001194 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001195 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001196 } else if (ret_auth == SSH_AUTH_SUCCESS) {
Radek Krejciae813f42018-07-02 13:38:30 +02001197 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001198 }
1199
1200 /* check what authentication methods are available */
1201 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001202
1203 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001204 if (opts->auth_pref[0].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001205 VRB(session, "Interactive SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001206 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001207 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001208 if (opts->auth_pref[1].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001209 VRB(session, "Password SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001210 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001211 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001212 if (opts->auth_pref[2].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001213 VRB(session, "Publickey SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001214 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001215 }
1216
Michal Vasko0190bc32016-03-02 15:47:49 +01001217 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001218 auth = 0;
1219 pref = 0;
1220 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1221 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001222 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001223 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001224 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001225 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001226 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001227 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001228 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001229 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001230 }
1231
Michal Vasko235efdc2015-12-17 12:05:04 +01001232 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001233 if (!attempt) {
Michal Vasko05532772021-06-03 12:12:38 +02001234 ERR(session, "Unable to authenticate to the remote server (no supported authentication methods detected).");
Radek Krejciae813f42018-07-02 13:38:30 +02001235 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001236 ERR(session, "Unable to authenticate to the remote server (all attempts via supported authentication "
1237 "methods failed).");
Radek Krejciae813f42018-07-02 13:38:30 +02001238 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001239 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001240 }
1241
1242 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001243 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001244 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001245 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1246
Michal Vasko05532772021-06-03 12:12:38 +02001247 VRB(session, "Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001248 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001249 if (s == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001250 ERR(session, "Unable to get the password.");
Michal Vasko88583042018-03-29 09:18:58 +02001251 return -1;
1252 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001253
Michal Vasko36c7be82017-02-22 13:37:59 +01001254 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001255 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001256 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001257 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1258 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001259 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001260 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001261 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001262 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001263 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001264 free(s);
1265 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001266
Michal Vasko7b62fed2015-10-26 15:39:46 +01001267 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001268 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1269
Michal Vasko05532772021-06-03 12:12:38 +02001270 VRB(session, "Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001271
Michal Vasko36c7be82017-02-22 13:37:59 +01001272 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001273 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001274 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001275 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) ||
1276 (ret_auth == SSH_AUTH_AGAIN)) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001277 if (ret_auth == SSH_AUTH_AGAIN) {
1278 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001279 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001280 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001281 }
1282 continue;
1283 }
1284
Michal Vasko7b62fed2015-10-26 15:39:46 +01001285 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1286 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001287 if (!prompt) {
1288 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001289 break;
1290 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001291
Michal Vasko30e2c872016-02-18 10:03:21 +01001292 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001293 ssh_userauth_kbdint_getinstruction(ssh_sess),
1294 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001295 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1296 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001297 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001298 break;
1299 }
1300 free(answer);
1301 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001302 if (ret_auth == SSH_AUTH_ERROR) {
1303 break;
1304 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001305 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001306 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001307 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001308 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001309 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001310
Michal Vasko206d3b12015-12-04 11:08:42 +01001311 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001312 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1313
Michal Vasko05532772021-06-03 12:12:38 +02001314 VRB(session, "Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001315
1316 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001317 if (!opts->key_count) {
Michal Vasko05532772021-06-03 12:12:38 +02001318 VRB(session, "No key pair specified.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001319 break;
1320 }
1321
Michal Vasko30e2c872016-02-18 10:03:21 +01001322 for (j = 0; j < opts->key_count; j++) {
Michal Vasko05532772021-06-03 12:12:38 +02001323 VRB(session, "Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001324 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1325 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001326
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001327 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1328 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001329 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001330 continue;
1331 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001332 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001333 continue;
1334 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001335
Michal Vasko36c7be82017-02-22 13:37:59 +01001336 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001337 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001338 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001339 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1340 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001341 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001342 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001343 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001344 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001345 ssh_key_free(pubkey);
1346
1347 if (ret_auth == SSH_AUTH_DENIED) {
1348 continue;
1349 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001350 break;
1351 }
1352
Michal Vasko30e2c872016-02-18 10:03:21 +01001353 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001354 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001355 } else {
1356 s = NULL;
1357 }
1358
Michal Vasko0190bc32016-03-02 15:47:49 +01001359 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001360 if (s) {
1361 memset(s, 0, strlen(s));
1362 free(s);
1363 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001364 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001365 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001366 continue;
1367 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001368 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001369 continue;
1370 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001371
Michal Vasko36c7be82017-02-22 13:37:59 +01001372 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001373 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001374 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001375 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1376 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001377 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001378 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001379 }
1380 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001381 ssh_key_free(privkey);
1382
Michal Vasko0190bc32016-03-02 15:47:49 +01001383 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001384 break;
1385 }
1386 }
1387 break;
1388 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001389
Michal Vasko0190bc32016-03-02 15:47:49 +01001390 switch (ret_auth) {
1391 case SSH_AUTH_AGAIN:
Michal Vasko05532772021-06-03 12:12:38 +02001392 ERR(session, "Authentication response timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001393 return 0;
1394 case SSH_AUTH_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001395 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001396 return -1;
1397 case SSH_AUTH_DENIED:
Michal Vasko05532772021-06-03 12:12:38 +02001398 WRN(session, "Authentication denied.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001399 break;
1400 case SSH_AUTH_PARTIAL:
Michal Vasko05532772021-06-03 12:12:38 +02001401 VRB(session, "Partial authentication success.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001402 break;
1403 case SSH_AUTH_SUCCESS:
Michal Vasko05532772021-06-03 12:12:38 +02001404 VRB(session, "Authentication successful.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001405 break;
1406 case SSH_AUTH_INFO:
1407 ERRINT;
1408 return -1;
1409 }
Radek Krejciae813f42018-07-02 13:38:30 +02001410
1411 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001412 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001413
Michal Vasko0190bc32016-03-02 15:47:49 +01001414 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001415}
1416
1417/* Open new SSH channel and request the 'netconf' subsystem.
1418 * SSH connection is expected to be established.
1419 */
1420static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001421open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001422{
1423 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001424 int ret;
roman6ece9c52022-06-22 09:29:17 +02001425 struct timespec ts_timeout;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001426
1427 ssh_sess = session->ti.libssh.session;
1428
1429 if (!ssh_is_connected(ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001430 ERR(session, "SSH session not connected.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001431 return -1;
1432 }
1433
1434 if (session->ti.libssh.channel) {
Michal Vasko05532772021-06-03 12:12:38 +02001435 ERR(session, "SSH channel already created.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001436 return -1;
1437 }
1438
Michal Vasko7b62fed2015-10-26 15:39:46 +01001439 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001440 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001441 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001442 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001443 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001444 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1445 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001446 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001447 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001448 }
1449 }
1450 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001451 ERR(session, "Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001452 ssh_channel_free(session->ti.libssh.channel);
1453 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001454 return 0;
1455 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001456 ERR(session, "Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001457 ssh_channel_free(session->ti.libssh.channel);
1458 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001459 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001460 }
1461
1462 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001463 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001464 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001465 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001466 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1467 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001468 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001469 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001470 }
1471 }
1472 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001473 ERR(session, "Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001474 ssh_channel_free(session->ti.libssh.channel);
1475 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001476 return 0;
1477 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001478 ERR(session, "Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001479 ssh_channel_free(session->ti.libssh.channel);
1480 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001481 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001482 }
1483
Michal Vasko0190bc32016-03-02 15:47:49 +01001484 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001485}
1486
Michal Vasko30e2c872016-02-18 10:03:21 +01001487static struct nc_session *
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001488_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
1489 struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001490{
Michal Vasko66032bc2019-01-22 15:03:12 +01001491 char *host = NULL, *username = NULL, *ip_host;
Dragos Dan8ce3b7c2021-03-09 09:17:22 +02001492 unsigned int port = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001493 int sock;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001494 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001495 struct nc_session *session = NULL;
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001496 char *buf = NULL;
1497 size_t buf_len = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001498
roman40672412023-05-04 11:10:22 +02001499 NC_CHECK_ARG_RET(NULL, ssh_session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001500
1501 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001502 session = nc_new_session(NC_CLIENT, 0);
roman3a95bb22023-10-26 11:07:17 +02001503 NC_CHECK_ERRMEM_RET(!session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001504 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001505 session->ti_type = NC_TI_LIBSSH;
1506 session->ti.libssh.session = ssh_session;
1507
1508 /* was port set? */
Michal Vasko097fc852021-03-09 08:18:17 +01001509 ssh_options_get_port(ssh_session, &port);
Michal Vasko30e2c872016-02-18 10:03:21 +01001510
1511 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1512 /*
1513 * There is no file descriptor (detected based on the host, there is no way to check
1514 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1515 */
1516
1517 /* remember host */
1518 host = strdup("localhost");
roman124a4362023-10-26 15:36:22 +02001519 NC_CHECK_ERRMEM_GOTO(!host, , fail);
Michal Vaskob3ea50d2024-02-07 08:50:32 +01001520
1521 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host) != SSH_OK) {
1522 ERR(NULL, "Failed to use hostname \"%s\".", host);
1523 free(host);
1524 goto fail;
1525 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001526
1527 /* create and connect socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001528 sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001529 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001530 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko5d712ac2024-02-07 09:02:20 +01001531 free(host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001532 goto fail;
1533 }
1534 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001535 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko5d712ac2024-02-07 09:02:20 +01001536
1537 free(host);
Michal Vasko66032bc2019-01-22 15:03:12 +01001538 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001539 }
1540
1541 /* was username set? */
1542 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1543
1544 if (!ssh_is_connected(ssh_session)) {
1545 /*
1546 * We are connected, but not SSH authenticated. (Transport layer)
1547 */
1548
1549 /* remember username */
1550 if (!username) {
1551 if (!opts->username) {
romanf6e32012023-04-24 15:51:26 +02001552 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Michal Vasko30e2c872016-02-18 10:03:21 +01001553 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001554 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001555 goto fail;
1556 }
1557 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001558 free(buf);
Michal Vasko30e2c872016-02-18 10:03:21 +01001559 } else {
1560 username = strdup(opts->username);
1561 }
roman124a4362023-10-26 15:36:22 +02001562 NC_CHECK_ERRMEM_GOTO(!username, , fail);
Michal Vasko30e2c872016-02-18 10:03:21 +01001563 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1564 }
1565
1566 /* connect and authenticate SSH session */
1567 session->host = host;
1568 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001569 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001570 goto fail;
1571 }
1572 }
1573
1574 /*
1575 * Almost done, open a netconf channel. (Transport layer / application layer)
1576 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001577 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001578 goto fail;
1579 }
1580
1581 /*
1582 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1583 */
1584
Michal Vasko78939072022-12-12 07:43:18 +01001585 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001586 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001587 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001588 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001589
1590 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001591 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001592 goto fail;
1593 }
1594 session->status = NC_STATUS_RUNNING;
1595
1596 if (nc_ctx_check_and_fill(session) == -1) {
1597 goto fail;
1598 }
1599
Michal Vasko93224072021-11-09 12:14:28 +01001600 /* store information if not previously */
1601 session->host = host;
1602 session->port = port;
1603 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001604
1605 return session;
1606
1607fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001608 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001609 return NULL;
1610}
1611
Radek Krejciac6d3472015-10-22 15:47:18 +02001612API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001613nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001614{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001615 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001616 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001617 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001618 char *username, *ip_host = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001619 struct passwd *pw, pw_buf;
Radek Krejciac6d3472015-10-22 15:47:18 +02001620 struct nc_session *session = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001621 char *buf = NULL;
1622 size_t buf_len = 0;
romanf6e32012023-04-24 15:51:26 +02001623 char *known_hosts_path = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001624
1625 /* process parameters */
roman3f9b65c2023-06-05 14:26:58 +02001626 if (!host || (host[0] == '\0')) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001627 host = "localhost";
1628 }
1629
1630 if (!port) {
1631 port = NC_PORT_SSH;
1632 }
Michal Vasko55fded62016-02-02 12:19:34 +01001633 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001634
Michal Vasko3031aae2016-01-27 16:07:18 +01001635 if (!ssh_opts.username) {
romanf6e32012023-04-24 15:51:26 +02001636 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Radek Krejciac6d3472015-10-22 15:47:18 +02001637 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001638 ERR(session, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko1d430d92021-10-11 09:30:43 +02001639 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001640 } else {
1641 username = pw->pw_name;
1642 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001643 } else {
1644 username = ssh_opts.username;
romanf6e32012023-04-24 15:51:26 +02001645
1646 pw = nc_getpw(0, username, &pw_buf, &buf, &buf_len);
1647 }
1648
1649 if (ssh_opts.knownhosts_path) {
1650 /* known_hosts file path was set so use it */
1651 known_hosts_path = strdup(ssh_opts.knownhosts_path);
roman124a4362023-10-26 15:36:22 +02001652 NC_CHECK_ERRMEM_GOTO(!known_hosts_path, , fail);
romanf6e32012023-04-24 15:51:26 +02001653 } else if (pw) {
1654 /* path not set explicitly, but current user's username found in /etc/passwd, so create the path */
roman124a4362023-10-26 15:36:22 +02001655 NC_CHECK_ERRMEM_GOTO(asprintf(&known_hosts_path, "%s/.ssh/known_hosts", pw->pw_dir) == -1, , fail);
Radek Krejciac6d3472015-10-22 15:47:18 +02001656 }
1657
1658 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001659 session = nc_new_session(NC_CLIENT, 0);
roman124a4362023-10-26 15:36:22 +02001660 NC_CHECK_ERRMEM_GOTO(!session, , fail);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001661 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001662
Michal Vasko131120a2018-05-29 15:44:02 +02001663 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001664 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001665 session->ti.libssh.session = ssh_new();
1666 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001667 ERR(session, "Unable to initialize SSH session.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001668 goto fail;
1669 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001670
Michal Vasko7b62fed2015-10-26 15:39:46 +01001671 /* set some basic SSH session options */
Michal Vaskob3ea50d2024-02-07 08:50:32 +01001672 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host) != SSH_OK) {
1673 ERR(session, "Failed to use hostname \"%s\".", host);
1674 goto fail;
1675 }
Michal Vasko55fded62016-02-02 12:19:34 +01001676 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001677 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001678 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
romanf6e32012023-04-24 15:51:26 +02001679 if (known_hosts_path) {
1680 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_path);
1681 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001682
1683 /* create and assign communication socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001684 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001685 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001686 ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001687 goto fail;
1688 }
1689 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001690 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001691
Michal Vasko93224072021-11-09 12:14:28 +01001692 /* store information for session connection */
1693 session->host = strdup(host);
1694 session->username = strdup(username);
romanf6e32012023-04-24 15:51:26 +02001695 session->port = port;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001696 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1) ||
1697 (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001698 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001699 }
1700
Michal Vasko78939072022-12-12 07:43:18 +01001701 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001702 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001703 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001704 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001705
Radek Krejciac6d3472015-10-22 15:47:18 +02001706 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001707 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001708 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001709 }
Michal Vaskoad611702015-12-03 13:41:51 +01001710 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001711
Michal Vaskoef578332016-01-25 13:20:09 +01001712 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001713 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001714 }
1715
Michal Vasko93224072021-11-09 12:14:28 +01001716 /* update information */
1717 free(session->host);
1718 session->host = ip_host;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001719 session->port = port;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001720
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001721 free(buf);
romanf6e32012023-04-24 15:51:26 +02001722 free(known_hosts_path);
Radek Krejciac6d3472015-10-22 15:47:18 +02001723 return session;
1724
Michal Vasko7b62fed2015-10-26 15:39:46 +01001725fail:
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001726 free(buf);
romanf6e32012023-04-24 15:51:26 +02001727 free(known_hosts_path);
Michal Vasko66032bc2019-01-22 15:03:12 +01001728 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001729 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001730 return NULL;
1731}
1732
1733API struct nc_session *
1734nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1735{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001736 return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001737}
1738
1739API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001740nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001741{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001742 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001743
roman40672412023-05-04 11:10:22 +02001744 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001745
Michal Vasko7b62fed2015-10-26 15:39:46 +01001746 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001747 new_session = nc_new_session(NC_CLIENT, 1);
roman3a95bb22023-10-26 11:07:17 +02001748 NC_CHECK_ERRMEM_RET(!new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001749 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001750
Michal Vasko131120a2018-05-29 15:44:02 +02001751 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001752 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001753 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001754 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001755
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001756 /* append to the session ring list */
1757 if (!session->ti.libssh.next) {
1758 session->ti.libssh.next = new_session;
1759 new_session->ti.libssh.next = session;
1760 } else {
1761 ptr = session->ti.libssh.next;
1762 session->ti.libssh.next = new_session;
1763 new_session->ti.libssh.next = ptr;
1764 }
1765
Michal Vasko7b62fed2015-10-26 15:39:46 +01001766 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001767 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001768 goto fail;
1769 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001770 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001771 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001772 }
Michal Vasko131120a2018-05-29 15:44:02 +02001773 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001774
Michal Vasko78939072022-12-12 07:43:18 +01001775 if (nc_client_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001776 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001777 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001778 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001779
Michal Vasko7b62fed2015-10-26 15:39:46 +01001780 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001781 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001782 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001783 }
Michal Vaskoad611702015-12-03 13:41:51 +01001784 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001785
Michal Vaskoef578332016-01-25 13:20:09 +01001786 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001787 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001788 }
1789
Michal Vasko93224072021-11-09 12:14:28 +01001790 /* store information into session */
1791 new_session->host = strdup(session->host);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001792 new_session->port = session->port;
Michal Vasko93224072021-11-09 12:14:28 +01001793 new_session->username = strdup(session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001794
Michal Vasko7b62fed2015-10-26 15:39:46 +01001795 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001796
1797fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001798 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001799 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001800}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001801
Michal Vasko3031aae2016-01-27 16:07:18 +01001802struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001803nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001804{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001805 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001806 unsigned int uint_port;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001807 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001808 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001809 ssh_session sess;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001810 char *buf = NULL;
1811 size_t buf_len = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001812
Michal Vasko80cad7f2015-12-08 14:42:27 +01001813 sess = ssh_new();
1814 if (!sess) {
Michal Vasko05532772021-06-03 12:12:38 +02001815 ERR(NULL, "Unable to initialize an SSH session.");
Michal Vasko80cad7f2015-12-08 14:42:27 +01001816 close(sock);
1817 return NULL;
1818 }
1819
1820 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001821 ssh_set_blocking(sess, 0);
Michal Vaskob3ea50d2024-02-07 08:50:32 +01001822 if (ssh_options_set(sess, SSH_OPTIONS_HOST, host) != SSH_OK) {
1823 ERR(NULL, "Failed to use hostname \"%s\".", host);
1824 ssh_free(sess);
1825 return NULL;
1826 }
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001827 uint_port = port;
1828 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001829 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vaskob3ea50d2024-02-07 08:50:32 +01001830
Michal Vasko3031aae2016-01-27 16:07:18 +01001831 if (!ssh_ch_opts.username) {
romanf6e32012023-04-24 15:51:26 +02001832 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Michal Vasko3031aae2016-01-27 16:07:18 +01001833 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001834 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko435e5cf2019-04-23 08:48:44 +02001835 ssh_free(sess);
Michal Vasko3031aae2016-01-27 16:07:18 +01001836 return NULL;
1837 }
1838 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001839 free(buf);
Michal Vasko3031aae2016-01-27 16:07:18 +01001840 } else {
1841 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001842 }
Michal Vaskob3ea50d2024-02-07 08:50:32 +01001843
Michal Vasko8fd6fca2019-02-04 10:59:49 +01001844 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1845 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1846#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
1847 ssh_options_set(sess, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1848 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1849#endif
Michal Vasko80cad7f2015-12-08 14:42:27 +01001850
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001851 session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
Michal Vasko435e5cf2019-04-23 08:48:44 +02001852 if (!session) {
Michal Vasko457f0532019-08-15 13:59:49 +02001853 /* sess is freed */
Michal Vasko435e5cf2019-04-23 08:48:44 +02001854 return NULL;
Michal Vasko4282fae2016-02-18 10:03:42 +01001855 }
1856
Michal Vasko435e5cf2019-04-23 08:48:44 +02001857 session->flags |= NC_SESSION_CALLHOME;
Michal Vasko30e2c872016-02-18 10:03:21 +01001858 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001859}