blob: 95f9a0c8bd69404ea472628dd3d8c9cf6c798420 [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 Vasko36c7be82017-02-22 13:37:59 +010024#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020025#include <pwd.h>
26#include <stddef.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <termios.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010033#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020034#include <unistd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020035
Michal Vasko7b62fed2015-10-26 15:39:46 +010036#ifdef ENABLE_DNSSEC
Michal Vasko7b62fed2015-10-26 15:39:46 +010037# include <validator/resolver.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020038# include <validator/validator.h>
Michal Vasko40899d82021-05-26 12:04:49 +020039# include <validator/validator-config.h>
40
Michal Vasko7b62fed2015-10-26 15:39:46 +010041# include <validator/validator-compat.h>
42#endif
43
Michal Vasko745ff832015-12-08 14:40:29 +010044#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020045#include <libyang/libyang.h>
46
Michal Vasko7a20d2e2021-05-19 16:40:23 +020047#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020048#include "config.h"
49#include "log_p.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010050#include "session_client.h"
51#include "session_client_ch.h"
roman3f9b65c2023-06-05 14:26:58 +020052#include "session_p.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020053
Radek Krejci62aa0642017-05-25 16:33:49 +020054struct nc_client_context *nc_client_context_location(void);
Michal Vasko30e2c872016-02-18 10:03:21 +010055
Radek Krejci62aa0642017-05-25 16:33:49 +020056#define client_opts nc_client_context_location()->opts
57#define ssh_opts nc_client_context_location()->ssh_opts
58#define ssh_ch_opts nc_client_context_location()->ssh_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010059
Michal Vaskoa43b8e32017-05-12 11:46:20 +020060static FILE *
61open_tty_noecho(const char *path, struct termios *oldterm)
62{
63 struct termios newterm;
64 FILE *ret;
65
66 if (!(ret = fopen(path, "r"))) {
Michal Vasko05532772021-06-03 12:12:38 +020067 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020068 return NULL;
69 }
70
71 if (tcgetattr(fileno(ret), oldterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020072 ERR(NULL, "Unable to get terminal \"%s\" settings (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020073 fclose(ret);
74 return NULL;
75 }
76
77 newterm = *oldterm;
78 newterm.c_lflag &= ~ECHO;
79 newterm.c_lflag &= ~ICANON;
80 tcflush(fileno(ret), TCIFLUSH);
81 if (tcsetattr(fileno(ret), TCSANOW, &newterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020082 ERR(NULL, "Unable to change terminal \"%s\" settings for hiding password (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020083 fclose(ret);
84 return NULL;
85 }
86
87 return ret;
88}
89
Michal Vasko51228ac2018-03-29 14:57:53 +020090static FILE *
91nc_open_in(int echo, struct termios *oldterm)
Michal Vaskoa43b8e32017-05-12 11:46:20 +020092{
Michal Vasko51228ac2018-03-29 14:57:53 +020093 char buf[512];
94 int buflen = 512, ret;
95 FILE *in;
96
97 if (!echo) {
98 in = open_tty_noecho("/dev/tty", oldterm);
99 } else {
100 in = fopen("/dev/tty", "r");
101 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200102 ERR(NULL, "Unable to open terminal \"/dev/tty\" for reading (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200103 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200104 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200105
106 if (!in) {
107 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200108 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200109 return NULL;
110 }
111
112 if (!echo) {
113 in = open_tty_noecho(buf, oldterm);
114 } else {
115 in = fopen(buf, "r");
116 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200117 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200118 }
119 }
120 }
121
122 return in;
123}
124
125static FILE *
126nc_open_out(void)
127{
128 char buf[512];
129 int buflen = 512, ret;
130 FILE *out;
131
132 out = fopen("/dev/tty", "w");
133 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200134 ERR(NULL, "Unable to open terminal \"/dev/tty\" for writing (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200135
136 if ((ret = ttyname_r(STDOUT_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200137 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200138 return NULL;
139 }
140
141 out = fopen(buf, "w");
142 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200143 ERR(NULL, "Unable to open terminal \"%s\" for writing (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200144 }
145 }
146
147 return out;
148}
149
150static void
151nc_close_inout(FILE *inout, int echo, struct termios *oldterm)
152{
153 if (inout) {
154 if (!echo && (tcsetattr(fileno(inout), TCSANOW, oldterm) != 0)) {
Michal Vasko05532772021-06-03 12:12:38 +0200155 ERR(NULL, "Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200156 }
157 fclose(inout);
158 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200159}
160
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400161void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100162_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +0100163{
164 int i;
165
Michal Vaskoe22c6732016-01-29 11:03:02 +0100166 for (i = 0; i < opts->key_count; ++i) {
167 free(opts->keys[i].pubkey_path);
168 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +0100169 }
Michal Vaskoe22c6732016-01-29 11:03:02 +0100170 free(opts->keys);
171 free(opts->username);
romanf6e32012023-04-24 15:51:26 +0200172 free(opts->knownhosts_path);
romanc1d2b092023-02-02 08:58:27 +0100173 opts->key_count = 0;
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200174 opts->keys = NULL;
175 opts->username = NULL;
romanf6e32012023-04-24 15:51:26 +0200176 opts->knownhosts_path = NULL;
Michal Vaskoe22c6732016-01-29 11:03:02 +0100177}
Michal Vasko990089e2015-10-27 15:05:25 +0100178
Michal Vaskob7558c52016-02-26 15:04:19 +0100179void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100180nc_client_ssh_destroy_opts(void)
181{
182 _nc_client_ssh_destroy_opts(&ssh_opts);
183 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +0100184}
185
Michal Vaskoef112d72016-02-18 13:28:25 +0100186#ifdef ENABLE_DNSSEC
187
188/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
189/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
190static int
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200191sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg)
192{
Michal Vaskoef112d72016-02-18 13:28:25 +0100193 ns_msg handle;
194 ns_rr rr;
195 val_status_t val_status;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200196 const unsigned char *rdata;
Michal Vaskoef112d72016-02-18 13:28:25 +0100197 unsigned char buf[4096];
198 int buf_len = 4096;
199 int ret = 0, i, j, len;
200
201 /* class 1 - internet, type 44 - SSHFP */
202 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
203
204 if ((len < 0) || !val_istrusted(val_status)) {
205 ret = 2;
206 goto finish;
207 }
208
209 if (ns_initparse(buf, len, &handle) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200210 ERR(NULL, "Failed to initialize DNSSEC response parser.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100211 ret = 2;
212 goto finish;
213 }
214
215 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
Michal Vasko05532772021-06-03 12:12:38 +0200216 ERR(NULL, "DNSSEC query returned %d.", i);
Michal Vaskoef112d72016-02-18 13:28:25 +0100217 ret = 2;
218 goto finish;
219 }
220
221 if (!libsres_msg_getflag(handle, ns_f_ad)) {
222 /* response not secured by DNSSEC */
223 ret = 1;
224 }
225
226 /* query section */
227 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
Michal Vasko05532772021-06-03 12:12:38 +0200228 ERR(NULL, "DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100229 ret = 2;
230 goto finish;
231 }
232
233 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200234 ERR(NULL, "DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100235 ret = 2;
236 goto finish;
237 }
238
239 /* answer section */
240 i = 0;
241 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
242 if (ns_rr_type(rr) != 44) {
243 ++i;
244 continue;
245 }
246
247 rdata = ns_rr_rdata(rr);
248 if (rdata[0] != type) {
249 ++i;
250 continue;
251 }
252 if (rdata[1] != alg) {
253 ++i;
254 continue;
255 }
256
257 /* we found the correct SSHFP entry */
258 rdata += 2;
259 for (j = 0; j < 20; ++j) {
260 if (rdata[j] != (unsigned char)sha1hash[j]) {
261 ret = 2;
262 goto finish;
263 }
264 }
265
266 /* server fingerprint is supported by a DNS entry,
267 * we have already determined if DNSSEC was used or not
268 */
269 goto finish;
270 }
271
272 /* no match */
273 ret = 2;
274
275finish:
276 val_free_validator_state();
277 return ret;
278}
279
280#endif /* ENABLE_DNSSEC */
281
romanf6e32012023-04-24 15:51:26 +0200282static int
283nc_client_ssh_update_known_hosts(ssh_session session, const char *hostname)
Michal Vaskoef112d72016-02-18 13:28:25 +0100284{
romanf6e32012023-04-24 15:51:26 +0200285 int ret;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200286
romanf6e32012023-04-24 15:51:26 +0200287 ret = ssh_session_update_known_hosts(session);
romanf6e32012023-04-24 15:51:26 +0200288 if (ret != SSH_OK) {
289 WRN(NULL, "Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
290 }
291
292 return ret;
293}
294
295static int
296nc_client_ssh_get_srv_pubkey_data(ssh_session session, enum ssh_keytypes_e *srv_pubkey_type, char **hexa, unsigned char **hash_sha1)
297{
298 int ret;
Michal Vaskoef112d72016-02-18 13:28:25 +0100299 ssh_key srv_pubkey;
Michal Vaskoef112d72016-02-18 13:28:25 +0100300 size_t hlen;
Michal Vaskoef112d72016-02-18 13:28:25 +0100301
romanf6e32012023-04-24 15:51:26 +0200302 *hexa = NULL;
303 *hash_sha1 = NULL;
Michal Vaskoef112d72016-02-18 13:28:25 +0100304
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800305 ret = ssh_get_server_publickey(session, &srv_pubkey);
Michal Vaskoef112d72016-02-18 13:28:25 +0100306 if (ret < 0) {
roman6e5fd702023-04-27 14:30:27 +0200307 ERR(NULL, "Unable to get server's public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100308 return -1;
309 }
310
romanf6e32012023-04-24 15:51:26 +0200311 *srv_pubkey_type = ssh_key_type(srv_pubkey);
312 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, hash_sha1, &hlen);
Michal Vaskoef112d72016-02-18 13:28:25 +0100313 ssh_key_free(srv_pubkey);
314 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200315 ERR(NULL, "Failed to calculate SHA1 hash of the server public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100316 return -1;
317 }
318
romanf6e32012023-04-24 15:51:26 +0200319 *hexa = ssh_get_hexa(*hash_sha1, hlen);
320 if (!*hexa) {
321 ERR(NULL, "Getting the hostkey's hex string failed.");
322 return -1;
323 }
324
325 return 0;
326}
327
328#ifdef ENABLE_DNSSEC
329static int
330nc_client_ssh_do_dnssec_sshfp_check(ssh_session session, enum ssh_keytypes_e srv_pubkey_type, const char *hostname, unsigned char *hash_sha1)
331{
roman2eab4742023-06-06 10:00:26 +0200332 int ret = 0;
romanf6e32012023-04-24 15:51:26 +0200333
334 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) && (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
335 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
romanf6e32012023-04-24 15:51:26 +0200336 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
337 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
338 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
339 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
340 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
roman3a95bb22023-10-26 11:07:17 +0200341 } else {
342 /* other key types not supported */
343 ret = 1;
romanf6e32012023-04-24 15:51:26 +0200344 }
345
346 /* DNSSEC SSHFP check successful, that's enough */
347 if (!ret) {
348 VRB(NULL, "DNSSEC SSHFP check successful.");
romanf6e32012023-04-24 15:51:26 +0200349 ssh_session_update_known_hosts(session);
romanf6e32012023-04-24 15:51:26 +0200350 }
351
352 return ret;
353 }
354
355 return 1;
356}
357
358#endif
359
360static int
361nc_client_ssh_auth_hostkey_check(const char *hostname, uint16_t port, ssh_session session)
362{
363 char *hexa = NULL;
364 unsigned char *hash_sha1 = NULL;
365 NC_SSH_KNOWNHOSTS_MODE knownhosts_mode = ssh_opts.knownhosts_mode;
366 enum ssh_keytypes_e srv_pubkey_type;
367 char answer[5];
368 FILE *out = NULL, *in = NULL;
369 int c, state;
370
371#ifdef ENABLE_DNSSEC
372 int dnssec_ret;
373#endif
374
375 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_SKIP) {
376 /* skip all hostkey checks */
377 return 0;
378 }
379
380 if (nc_client_ssh_get_srv_pubkey_data(session, &srv_pubkey_type, &hexa, &hash_sha1)) {
381 goto error;
382 }
383
romanf6e32012023-04-24 15:51:26 +0200384 state = ssh_session_is_known_server(session);
Michal Vaskoef112d72016-02-18 13:28:25 +0100385 switch (state) {
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800386 case SSH_KNOWN_HOSTS_OK:
Michal Vaskoef112d72016-02-18 13:28:25 +0100387 break; /* ok */
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800388 case SSH_KNOWN_HOSTS_CHANGED:
romanf6e32012023-04-24 15:51:26 +0200389 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT) {
390 /* is the mode is set to accept, then accept any connection even if the remote key changed */
romanfa609bd2023-10-26 12:14:12 +0200391 WRN(NULL, "Remote host key changed!");
romanf6e32012023-04-24 15:51:26 +0200392 break;
393 } else {
394 ERR(NULL, "Remote host key changed, the connection will be terminated!");
395 goto error;
396 }
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800397 case SSH_KNOWN_HOSTS_OTHER:
Michal Vasko05532772021-06-03 12:12:38 +0200398 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 +0100399 goto hostkey_not_known;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800400 case SSH_KNOWN_HOSTS_NOT_FOUND:
Michal Vasko05532772021-06-03 12:12:38 +0200401 WRN(NULL, "Could not find the known hosts file.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100402 goto hostkey_not_known;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800403 case SSH_KNOWN_HOSTS_UNKNOWN:
Michal Vaskoef112d72016-02-18 13:28:25 +0100404hostkey_not_known:
405#ifdef ENABLE_DNSSEC
romanf6e32012023-04-24 15:51:26 +0200406 /* do dnssec check, if it's ok then we're done otherwise continue */
407 dnssec_ret = nc_client_ssh_do_dnssec_sshfp_check(session, srv_pubkey_type, hostname, hash_sha1);
408 if (!dnssec_ret) {
409 ssh_clean_pubkey_hash(&hash_sha1);
410 ssh_string_free_char(hexa);
411 return 0;
Michal Vaskoef112d72016-02-18 13:28:25 +0100412 }
413#endif
414
romanf6e32012023-04-24 15:51:26 +0200415 /* open the files for reading/writing */
Michal Vasko51228ac2018-03-29 14:57:53 +0200416 if (!(in = nc_open_in(1, NULL))) {
417 goto error;
418 }
romanf6e32012023-04-24 15:51:26 +0200419
Michal Vasko51228ac2018-03-29 14:57:53 +0200420 if (!(out = nc_open_out())) {
421 goto error;
422 }
423
romanf6e32012023-04-24 15:51:26 +0200424 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_STRICT) {
425 /* do not connect if the hostkey is not present in known_hosts file in this mode */
romanfa609bd2023-10-26 12:14:12 +0200426 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 +0200427 goto error;
428 } else if ((knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT_NEW) || (knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT)) {
429 /* add a new entry to the known_hosts file without prompting */
430 if (nc_client_ssh_update_known_hosts(session, hostname)) {
431 goto error;
432 }
433
434 VRB(NULL, "Permanently added '[%s]:%hu' (%s) to the list of known hosts.", hostname, port, ssh_key_type_to_char(srv_pubkey_type));
435
436 break;
437 }
438
Michal Vaskoef112d72016-02-18 13:28:25 +0100439 /* try to get result from user */
Michal Vasko51228ac2018-03-29 14:57:53 +0200440 if (fprintf(out, "The authenticity of the host \'%s\' cannot be established.\n", hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200441 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200442 goto error;
443 }
444 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 +0200445 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200446 goto error;
447 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100448
449#ifdef ENABLE_DNSSEC
romanf6e32012023-04-24 15:51:26 +0200450 if (dnssec_ret == 2) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200451 if (fprintf(out, "No matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200452 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200453 goto error;
454 }
romanf6e32012023-04-24 15:51:26 +0200455 } else if (dnssec_ret == 1) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200456 if (fprintf(out, "Matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200457 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200458 goto error;
459 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100460 }
461#endif
462
Michal Vasko51228ac2018-03-29 14:57:53 +0200463 if (fprintf(out, "Are you sure you want to continue connecting (yes/no)? ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200464 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200465 goto error;
466 }
467 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100468
469 do {
Michal Vasko51228ac2018-03-29 14:57:53 +0200470 if (fscanf(in, "%4s", answer) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200471 ERR(NULL, "Reading from input failed (%s).", feof(in) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200472 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100473 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200474 while (((c = getc(in)) != EOF) && (c != '\n')) {}
Michal Vaskoef112d72016-02-18 13:28:25 +0100475
Michal Vasko51228ac2018-03-29 14:57:53 +0200476 fflush(in);
Michal Vaskoef112d72016-02-18 13:28:25 +0100477 if (!strcmp("yes", answer)) {
romanf6e32012023-04-24 15:51:26 +0200478 /* store the key into the known_hosts file */
479 nc_client_ssh_update_known_hosts(session, hostname);
Michal Vaskoef112d72016-02-18 13:28:25 +0100480 } else if (!strcmp("no", answer)) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200481 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100482 } else {
Michal Vasko51228ac2018-03-29 14:57:53 +0200483 if (fprintf(out, "Please type 'yes' or 'no': ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200484 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200485 goto error;
486 }
Michal Vasko5fb5f992020-11-26 15:19:31 +0100487 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100488 }
489 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
490
491 break;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800492 case SSH_KNOWN_HOSTS_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +0200493 ERR(NULL, "SSH error: %s", ssh_get_error(session));
Michal Vasko51228ac2018-03-29 14:57:53 +0200494 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100495 }
496
Michal Vasko51228ac2018-03-29 14:57:53 +0200497 nc_close_inout(in, 1, NULL);
498 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100499 ssh_clean_pubkey_hash(&hash_sha1);
500 ssh_string_free_char(hexa);
501 return 0;
502
Michal Vasko51228ac2018-03-29 14:57:53 +0200503error:
504 nc_close_inout(in, 1, NULL);
505 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100506 ssh_clean_pubkey_hash(&hash_sha1);
507 ssh_string_free_char(hexa);
508 return -1;
509}
510
Radek Krejci62aa0642017-05-25 16:33:49 +0200511char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200512sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200513{
Michal Vasko51228ac2018-03-29 14:57:53 +0200514 char *buf = NULL;
515 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200516 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200517 FILE *in = NULL, *out = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200518
Michal Vasko11d142a2016-01-19 15:58:24 +0100519 buf = malloc(buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200520 NC_CHECK_ERRMEM_RET(!buf, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100521
Michal Vasko51228ac2018-03-29 14:57:53 +0200522 if (!(in = nc_open_in(0, &oldterm))) {
523 goto error;
524 }
525 if (!(out = nc_open_out())) {
526 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200527 }
528
Michal Vasko51228ac2018-03-29 14:57:53 +0200529 if (fprintf(out, "%s@%s password: ", username, hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200530 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200531 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200532 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200533 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200534
535 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200536 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100537 if (len >= buflen - 1) {
538 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100539 buf = nc_realloc(buf, buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200540 NC_CHECK_ERRMEM_GOTO(!buf,; , error);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100541 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100542 buf[len++] = (char)c;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100543 }
544 buf[len++] = 0; /* terminating null byte */
545
Michal Vasko51228ac2018-03-29 14:57:53 +0200546 fprintf(out, "\n");
547
548 nc_close_inout(in, 0, &oldterm);
549 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100550 return buf;
Michal Vasko93f26d92018-02-01 09:08:35 +0100551
Michal Vasko51228ac2018-03-29 14:57:53 +0200552error:
553 nc_close_inout(in, 0, &oldterm);
554 nc_close_inout(out, 1, NULL);
Michal Vasko93f26d92018-02-01 09:08:35 +0100555 free(buf);
556 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100557}
558
Radek Krejci62aa0642017-05-25 16:33:49 +0200559char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200560sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100561{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200562 unsigned int buflen = 64, cur_len;
Michal Vasko51228ac2018-03-29 14:57:53 +0200563 int c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200564 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200565 char *buf = NULL;
566 FILE *in = NULL, *out = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100567
568 buf = malloc(buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200569 NC_CHECK_ERRMEM_RET(!buf, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100570
Michal Vasko51228ac2018-03-29 14:57:53 +0200571 if (!(in = nc_open_in(echo, &oldterm))) {
572 goto error;
573 }
574 if (!(out = nc_open_out())) {
575 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100576 }
577
Michal Vasko51228ac2018-03-29 14:57:53 +0200578 if (auth_name && (fprintf(out, "%s\n", auth_name) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200579 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200580 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100581 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200582 if (instruction && (fprintf(out, "%s\n", instruction) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200583 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200584 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100585 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200586 if (fputs(prompt, out) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200587 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200588 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200589 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200590 fflush(out);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100591
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200592 cur_len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200593 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200594 if (cur_len >= buflen - 1) {
595 buflen *= 2;
596 buf = nc_realloc(buf, buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200597 NC_CHECK_ERRMEM_GOTO(!buf,; , error);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200598 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100599 buf[cur_len++] = (char)c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200600 }
601 /* terminating null byte */
602 buf[cur_len] = '\0';
603
Michal Vasko51228ac2018-03-29 14:57:53 +0200604 fprintf(out, "\n");
605
606 nc_close_inout(in, echo, &oldterm);
607 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200608 return buf;
609
Michal Vasko51228ac2018-03-29 14:57:53 +0200610error:
611 nc_close_inout(in, echo, &oldterm);
612 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200613 free(buf);
614 return NULL;
615}
616
Radek Krejci62aa0642017-05-25 16:33:49 +0200617char *
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200618sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200619{
Michal Vasko51228ac2018-03-29 14:57:53 +0200620 char *buf = NULL;
621 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200622 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200623 FILE *in = NULL, *out = NULL;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200624
625 buf = malloc(buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200626 NC_CHECK_ERRMEM_RET(!buf, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200627
Michal Vasko51228ac2018-03-29 14:57:53 +0200628 if (!(in = nc_open_in(0, &oldterm))) {
629 goto error;
630 }
631 if (!(out = nc_open_out())) {
632 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200633 }
634
Michal Vasko51228ac2018-03-29 14:57:53 +0200635 if (fprintf(out, "Enter passphrase for the key '%s': ", privkey_path) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200636 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200637 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200638 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200639 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200640
641 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200642 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100643 if (len >= buflen - 1) {
644 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100645 buf = nc_realloc(buf, buflen * sizeof *buf);
roman3a95bb22023-10-26 11:07:17 +0200646 NC_CHECK_ERRMEM_GOTO(!buf,; , error);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100647 }
648 buf[len++] = (char)c;
649 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200650 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100651
Michal Vasko51228ac2018-03-29 14:57:53 +0200652 fprintf(out, "\n");
653
654 nc_close_inout(in, 0, &oldterm);
655 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100656 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100657
Michal Vasko51228ac2018-03-29 14:57:53 +0200658error:
659 nc_close_inout(in, 0, &oldterm);
660 nc_close_inout(out, 1, NULL);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100661 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100662 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100663}
664
romanf6e32012023-04-24 15:51:26 +0200665API int
666nc_client_ssh_set_knownhosts_path(const char *path)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100667{
romanf6e32012023-04-24 15:51:26 +0200668 free(ssh_opts.knownhosts_path);
669
670 if (!path) {
671 ssh_opts.knownhosts_path = NULL;
672 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100673 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100674
romanf6e32012023-04-24 15:51:26 +0200675 ssh_opts.knownhosts_path = strdup(path);
roman3a95bb22023-10-26 11:07:17 +0200676 NC_CHECK_ERRMEM_RET(!ssh_opts.knownhosts_path, 1);
romanf6e32012023-04-24 15:51:26 +0200677
678 return 0;
Radek Krejci90a84a22017-05-25 13:53:00 +0200679}
680
Radek Krejci90a84a22017-05-25 13:53:00 +0200681API void
romanf6e32012023-04-24 15:51:26 +0200682nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode)
Radek Krejci90a84a22017-05-25 13:53:00 +0200683{
romanf6e32012023-04-24 15:51:26 +0200684 ssh_opts.knownhosts_mode = mode;
Radek Krejci90a84a22017-05-25 13:53:00 +0200685}
Michal Vaskoef112d72016-02-18 13:28:25 +0100686
Michal Vasko30e2c872016-02-18 10:03:21 +0100687static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200688_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 +0200689 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100690{
691 if (auth_password) {
692 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200693 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100694 } else {
695 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200696 opts->auth_password_priv = NULL;
697 }
698}
699
700static void
701_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 +0200702 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200703{
704 if (auth_password) {
705 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
706 }
707 if (priv) {
708 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100709 }
710}
711
712API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200713nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200714 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100715{
Radek Krejci90a84a22017-05-25 13:53:00 +0200716 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100717}
718
719API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200720nc_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 +0200721 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100722{
Radek Krejci90a84a22017-05-25 13:53:00 +0200723 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
724}
725
726API void
727nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200728 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200729{
730 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
731}
732
733API void
734nc_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 +0200735 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200736{
737 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100738}
739
740static void
741_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200742 const char *prompt, int echo, void *priv),
743 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100744{
745 if (auth_interactive) {
746 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200747 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100748 } else {
749 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200750 opts->auth_interactive_priv = NULL;
751 }
752}
753
754static void
755_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200756 const char *prompt, int echo, void *priv),
757 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200758{
759 if (auth_interactive) {
760 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
761 }
762 if (priv) {
763 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100764 }
765}
766
767API void
768nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200769 const char *prompt, int echo, void *priv),
770 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100771{
Radek Krejci90a84a22017-05-25 13:53:00 +0200772 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100773}
774
775API void
776nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200777 const char *prompt, int echo, void *priv),
778 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100779{
Radek Krejci90a84a22017-05-25 13:53:00 +0200780 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
781}
782
783API void
784nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200785 const char *prompt, int echo, void *priv),
786 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200787{
788 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
789}
790
791API void
792nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200793 const char *prompt, int echo, void *priv),
794 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200795{
796 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100797}
798
799static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200800_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 +0200801 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100802{
803 if (auth_privkey_passphrase) {
804 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200805 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100806 } else {
807 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200808 opts->auth_privkey_passphrase_priv = NULL;
809 }
810}
811
812static void
813_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 +0200814 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200815{
816 if (auth_privkey_passphrase) {
817 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
818 }
819 if (priv) {
820 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100821 }
822}
823
824API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200825nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200826 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100827{
Radek Krejci90a84a22017-05-25 13:53:00 +0200828 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100829}
830
831API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200832nc_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 +0200833 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100834{
Radek Krejci90a84a22017-05-25 13:53:00 +0200835 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
836}
837
838API void
839nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200840 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200841{
842 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
843}
844
845API void
846nc_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 +0200847 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200848{
849 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100850}
851
Michal Vasko3031aae2016-01-27 16:07:18 +0100852static int
853_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 +0100854{
855 int i;
856 FILE *key;
857 char line[128];
858
roman40672412023-05-04 11:10:22 +0200859 NC_CHECK_ARG_RET(NULL, pub_key, priv_key, -1);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100860
Michal Vasko3031aae2016-01-27 16:07:18 +0100861 for (i = 0; i < opts->key_count; ++i) {
862 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
863 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200864 WRN(NULL, "Private key \"%s\" found with another public key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200865 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100866 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100867 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200868 WRN(NULL, "Public key \"%s\" found with another private key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200869 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100870 continue;
871 }
872
Michal Vasko05532772021-06-03 12:12:38 +0200873 ERR(NULL, "SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100874 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100875 }
876 }
877
Michal Vasko3031aae2016-01-27 16:07:18 +0100878 /* add the keys */
879 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100880 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
roman3a95bb22023-10-26 11:07:17 +0200881 NC_CHECK_ERRMEM_RET(!opts->keys, -1);
Michal Vasko3031aae2016-01-27 16:07:18 +0100882 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
883 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
884 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100885
roman3a95bb22023-10-26 11:07:17 +0200886 NC_CHECK_ERRMEM_RET(!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path, -1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100887
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100888 /* check encryption */
889 if ((key = fopen(priv_key, "r"))) {
890 /* 1st line - key type */
891 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100892 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200893 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100894 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100895 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100896 /* 2nd line - encryption information or key */
897 if (!fgets(line, sizeof line, key)) {
898 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200899 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100900 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100901 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100902 fclose(key);
903 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100904 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100905 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100906 }
907
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100908 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100909}
910
911API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100912nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100913{
Michal Vasko3031aae2016-01-27 16:07:18 +0100914 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
915}
916
917API int
918nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
919{
920 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
921}
922
923static int
924_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
925{
926 if (idx >= opts->key_count) {
roman40672412023-05-04 11:10:22 +0200927 ERRARG(NULL, "idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100928 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100929 }
930
Michal Vasko3031aae2016-01-27 16:07:18 +0100931 free(opts->keys[idx].pubkey_path);
932 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100933
Michal Vasko3031aae2016-01-27 16:07:18 +0100934 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100935 if (idx < opts->key_count) {
936 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
937 }
938 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100939 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
roman3a95bb22023-10-26 11:07:17 +0200940 NC_CHECK_ERRMEM_RET(!opts->keys, -1);
Michal Vaskoc0256492016-02-02 12:19:06 +0100941 } else {
942 free(opts->keys);
943 opts->keys = NULL;
944 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100945
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100946 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100947}
948
949API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100950nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100951{
Michal Vasko3031aae2016-01-27 16:07:18 +0100952 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100953}
954
955API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100956nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100957{
Michal Vasko3031aae2016-01-27 16:07:18 +0100958 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
959}
960
961static int
962_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
963{
964 return opts->key_count;
965}
966
967API int
968nc_client_ssh_get_keypair_count(void)
969{
970 return _nc_client_ssh_get_keypair_count(&ssh_opts);
971}
972
973API int
974nc_client_ssh_ch_get_keypair_count(void)
975{
976 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
977}
978
979static int
980_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
981{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200982 if (idx >= opts->key_count) {
roman40672412023-05-04 11:10:22 +0200983 ERRARG(NULL, "idx");
Michal Vasko45e53ae2016-04-07 11:46:03 +0200984 return -1;
985 } else if (!pub_key && !priv_key) {
roman40672412023-05-04 11:10:22 +0200986 ERRARG(NULL, "pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100987 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100988 }
989
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100990 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100991 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100992 }
993 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100994 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100995 }
996
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100997 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100998}
999
Michal Vasko3031aae2016-01-27 16:07:18 +01001000API int
1001nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
1002{
1003 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
1004}
1005
1006API int
1007nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
1008{
1009 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
1010}
1011
1012static void
1013_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 +01001014{
1015 if (pref < 0) {
1016 pref = -1;
1017 }
1018
1019 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001020 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001021 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001022 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001023 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001024 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001025 }
1026}
1027
Michal Vasko3031aae2016-01-27 16:07:18 +01001028API void
1029nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001030{
Michal Vasko3031aae2016-01-27 16:07:18 +01001031 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
1032}
1033
1034API void
1035nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
1036{
1037 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
1038}
1039
1040static int16_t
1041_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
1042{
1043 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001044
1045 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001046 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001047 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001048 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001049 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001050 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001051 }
1052
1053 return pref;
1054}
1055
Michal Vasko3031aae2016-01-27 16:07:18 +01001056API int16_t
1057nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1058{
1059 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
1060}
1061
1062API int16_t
1063nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1064{
1065 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
1066}
1067
1068static int
1069_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
1070{
1071 if (opts->username) {
1072 free(opts->username);
1073 }
1074 if (username) {
1075 opts->username = strdup(username);
roman3a95bb22023-10-26 11:07:17 +02001076 NC_CHECK_ERRMEM_RET(!opts->username, -1);
Michal Vasko3031aae2016-01-27 16:07:18 +01001077 } else {
1078 opts->username = NULL;
1079 }
1080
1081 return 0;
1082}
1083
1084API int
1085nc_client_ssh_set_username(const char *username)
1086{
1087 return _nc_client_ssh_set_username(username, &ssh_opts);
1088}
1089
1090API int
1091nc_client_ssh_ch_set_username(const char *username)
1092{
1093 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1094}
1095
Michal Vaskoe22c6732016-01-29 11:03:02 +01001096static const char *
1097_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1098{
1099 return opts->username;
1100}
1101
1102API const char *
1103nc_client_ssh_get_username(void)
1104{
1105 return _nc_client_ssh_get_username(&ssh_opts);
1106}
1107
1108API const char *
1109nc_client_ssh_ch_get_username(void)
1110{
1111 return _nc_client_ssh_get_username(&ssh_ch_opts);
1112}
1113
Michal Vasko3031aae2016-01-27 16:07:18 +01001114API int
1115nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1116{
Michal Vasko9d4cca52022-09-07 11:19:57 +02001117 return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +01001118}
1119
1120API int
1121nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1122{
1123 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1124}
1125
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001126/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001127 * Host, port, username, and a connected socket is expected to be set.
Radek Krejciae813f42018-07-02 13:38:30 +02001128 *
1129 * return values
1130 * -1 failure
1131 * 0 try again
1132 * 1 success
Michal Vasko7b62fed2015-10-26 15:39:46 +01001133 */
1134static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001135connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001136{
Radek Krejciae813f42018-07-02 13:38:30 +02001137 int j, ret_auth, userauthlist, ret, attempt = 0;
Michal Vasko235efdc2015-12-17 12:05:04 +01001138 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001139 int16_t pref;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001140 const char *prompt;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001141 char *s, *answer, echo;
1142 ssh_key pubkey, privkey;
1143 ssh_session ssh_sess;
roman6ece9c52022-06-22 09:29:17 +02001144 struct timespec ts_timeout;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001145
1146 ssh_sess = session->ti.libssh.session;
1147
Michal Vaskod8a74192023-02-06 15:51:50 +01001148 nc_timeouttime_get(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001149 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1150 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001151 if (nc_timeouttime_cur_diff(&ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001152 break;
1153 }
1154 }
1155 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001156 ERR(session, "SSH connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001157 return 0;
1158 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001159 ERR(session, "Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
1160 DBG(session, "Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001161 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001162 }
1163
romanf6e32012023-04-24 15:51:26 +02001164 if (nc_client_ssh_auth_hostkey_check(session->host, session->port, ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001165 ERR(session, "Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001166 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001167 }
1168
Michal Vasko36c7be82017-02-22 13:37:59 +01001169 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001170 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001171 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001172 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1173 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001174 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001175 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001176 }
1177 }
1178 if (ret_auth == SSH_AUTH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001179 ERR(session, "Request authentication methods timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001180 return 0;
1181 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001182 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001183 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001184 } else if (ret_auth == SSH_AUTH_SUCCESS) {
Radek Krejciae813f42018-07-02 13:38:30 +02001185 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001186 }
1187
1188 /* check what authentication methods are available */
1189 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001190
1191 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001192 if (opts->auth_pref[0].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001193 VRB(session, "Interactive SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001194 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001195 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001196 if (opts->auth_pref[1].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001197 VRB(session, "Password SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001198 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001199 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001200 if (opts->auth_pref[2].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001201 VRB(session, "Publickey SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001202 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001203 }
1204
Michal Vasko0190bc32016-03-02 15:47:49 +01001205 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001206 auth = 0;
1207 pref = 0;
1208 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1209 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001210 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001211 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001212 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001213 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001214 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001215 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001216 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001217 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001218 }
1219
Michal Vasko235efdc2015-12-17 12:05:04 +01001220 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001221 if (!attempt) {
Michal Vasko05532772021-06-03 12:12:38 +02001222 ERR(session, "Unable to authenticate to the remote server (no supported authentication methods detected).");
Radek Krejciae813f42018-07-02 13:38:30 +02001223 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001224 ERR(session, "Unable to authenticate to the remote server (all attempts via supported authentication "
1225 "methods failed).");
Radek Krejciae813f42018-07-02 13:38:30 +02001226 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001227 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001228 }
1229
1230 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001231 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001232 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001233 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1234
Michal Vasko05532772021-06-03 12:12:38 +02001235 VRB(session, "Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001236 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001237 if (s == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001238 ERR(session, "Unable to get the password.");
Michal Vasko88583042018-03-29 09:18:58 +02001239 return -1;
1240 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001241
Michal Vasko36c7be82017-02-22 13:37:59 +01001242 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001243 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001244 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001245 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1246 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001247 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001248 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001249 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001250 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001251 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001252 free(s);
1253 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001254
Michal Vasko7b62fed2015-10-26 15:39:46 +01001255 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001256 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1257
Michal Vasko05532772021-06-03 12:12:38 +02001258 VRB(session, "Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001259
Michal Vasko36c7be82017-02-22 13:37:59 +01001260 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001261 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001262 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001263 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) ||
1264 (ret_auth == SSH_AUTH_AGAIN)) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001265 if (ret_auth == SSH_AUTH_AGAIN) {
1266 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001267 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001268 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001269 }
1270 continue;
1271 }
1272
Michal Vasko7b62fed2015-10-26 15:39:46 +01001273 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1274 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001275 if (!prompt) {
1276 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001277 break;
1278 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001279
Michal Vasko30e2c872016-02-18 10:03:21 +01001280 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001281 ssh_userauth_kbdint_getinstruction(ssh_sess),
1282 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001283 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1284 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001285 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001286 break;
1287 }
1288 free(answer);
1289 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001290 if (ret_auth == SSH_AUTH_ERROR) {
1291 break;
1292 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001293 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001294 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001295 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001296 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001297 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001298
Michal Vasko206d3b12015-12-04 11:08:42 +01001299 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001300 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1301
Michal Vasko05532772021-06-03 12:12:38 +02001302 VRB(session, "Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001303
1304 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001305 if (!opts->key_count) {
Michal Vasko05532772021-06-03 12:12:38 +02001306 VRB(session, "No key pair specified.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001307 break;
1308 }
1309
Michal Vasko30e2c872016-02-18 10:03:21 +01001310 for (j = 0; j < opts->key_count; j++) {
Michal Vasko05532772021-06-03 12:12:38 +02001311 VRB(session, "Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001312 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1313 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001314
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001315 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1316 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001317 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001318 continue;
1319 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001320 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001321 continue;
1322 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001323
Michal Vasko36c7be82017-02-22 13:37:59 +01001324 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001325 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001326 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001327 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1328 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001329 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001330 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001331 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001332 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001333 ssh_key_free(pubkey);
1334
1335 if (ret_auth == SSH_AUTH_DENIED) {
1336 continue;
1337 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001338 break;
1339 }
1340
Michal Vasko30e2c872016-02-18 10:03:21 +01001341 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001342 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001343 } else {
1344 s = NULL;
1345 }
1346
Michal Vasko0190bc32016-03-02 15:47:49 +01001347 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001348 if (s) {
1349 memset(s, 0, strlen(s));
1350 free(s);
1351 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001352 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001353 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001354 continue;
1355 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001356 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001357 continue;
1358 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001359
Michal Vasko36c7be82017-02-22 13:37:59 +01001360 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001361 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001362 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001363 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1364 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001365 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001366 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001367 }
1368 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001369 ssh_key_free(privkey);
1370
Michal Vasko0190bc32016-03-02 15:47:49 +01001371 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001372 break;
1373 }
1374 }
1375 break;
1376 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001377
Michal Vasko0190bc32016-03-02 15:47:49 +01001378 switch (ret_auth) {
1379 case SSH_AUTH_AGAIN:
Michal Vasko05532772021-06-03 12:12:38 +02001380 ERR(session, "Authentication response timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001381 return 0;
1382 case SSH_AUTH_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001383 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001384 return -1;
1385 case SSH_AUTH_DENIED:
Michal Vasko05532772021-06-03 12:12:38 +02001386 WRN(session, "Authentication denied.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001387 break;
1388 case SSH_AUTH_PARTIAL:
Michal Vasko05532772021-06-03 12:12:38 +02001389 VRB(session, "Partial authentication success.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001390 break;
1391 case SSH_AUTH_SUCCESS:
Michal Vasko05532772021-06-03 12:12:38 +02001392 VRB(session, "Authentication successful.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001393 break;
1394 case SSH_AUTH_INFO:
1395 ERRINT;
1396 return -1;
1397 }
Radek Krejciae813f42018-07-02 13:38:30 +02001398
1399 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001400 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001401
Michal Vasko0190bc32016-03-02 15:47:49 +01001402 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001403}
1404
1405/* Open new SSH channel and request the 'netconf' subsystem.
1406 * SSH connection is expected to be established.
1407 */
1408static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001409open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001410{
1411 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001412 int ret;
roman6ece9c52022-06-22 09:29:17 +02001413 struct timespec ts_timeout;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001414
1415 ssh_sess = session->ti.libssh.session;
1416
1417 if (!ssh_is_connected(ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001418 ERR(session, "SSH session not connected.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001419 return -1;
1420 }
1421
1422 if (session->ti.libssh.channel) {
Michal Vasko05532772021-06-03 12:12:38 +02001423 ERR(session, "SSH channel already created.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001424 return -1;
1425 }
1426
Michal Vasko7b62fed2015-10-26 15:39:46 +01001427 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001428 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001429 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001430 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001431 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001432 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1433 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001434 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001435 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001436 }
1437 }
1438 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001439 ERR(session, "Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001440 ssh_channel_free(session->ti.libssh.channel);
1441 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001442 return 0;
1443 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001444 ERR(session, "Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001445 ssh_channel_free(session->ti.libssh.channel);
1446 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001447 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001448 }
1449
1450 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001451 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001452 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001453 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001454 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1455 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001456 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001457 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001458 }
1459 }
1460 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001461 ERR(session, "Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001462 ssh_channel_free(session->ti.libssh.channel);
1463 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001464 return 0;
1465 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001466 ERR(session, "Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001467 ssh_channel_free(session->ti.libssh.channel);
1468 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001469 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001470 }
1471
Michal Vasko0190bc32016-03-02 15:47:49 +01001472 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001473}
1474
Michal Vasko30e2c872016-02-18 10:03:21 +01001475static struct nc_session *
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001476_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
1477 struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001478{
Michal Vasko66032bc2019-01-22 15:03:12 +01001479 char *host = NULL, *username = NULL, *ip_host;
Dragos Dan8ce3b7c2021-03-09 09:17:22 +02001480 unsigned int port = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001481 int sock;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001482 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001483 struct nc_session *session = NULL;
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001484 char *buf = NULL;
1485 size_t buf_len = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001486
roman40672412023-05-04 11:10:22 +02001487 NC_CHECK_ARG_RET(NULL, ssh_session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001488
1489 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001490 session = nc_new_session(NC_CLIENT, 0);
roman3a95bb22023-10-26 11:07:17 +02001491 NC_CHECK_ERRMEM_RET(!session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001492 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001493 session->ti_type = NC_TI_LIBSSH;
1494 session->ti.libssh.session = ssh_session;
1495
1496 /* was port set? */
Michal Vasko097fc852021-03-09 08:18:17 +01001497 ssh_options_get_port(ssh_session, &port);
Michal Vasko30e2c872016-02-18 10:03:21 +01001498
1499 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1500 /*
1501 * There is no file descriptor (detected based on the host, there is no way to check
1502 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1503 */
1504
1505 /* remember host */
1506 host = strdup("localhost");
roman3a95bb22023-10-26 11:07:17 +02001507 NC_CHECK_ERRMEM_GOTO(!host,; , fail);
Michal Vasko30e2c872016-02-18 10:03:21 +01001508 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1509
1510 /* create and connect socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001511 sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001512 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001513 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001514 goto fail;
1515 }
1516 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001517 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko66032bc2019-01-22 15:03:12 +01001518
1519 free(host);
1520 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001521 }
1522
1523 /* was username set? */
1524 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1525
1526 if (!ssh_is_connected(ssh_session)) {
1527 /*
1528 * We are connected, but not SSH authenticated. (Transport layer)
1529 */
1530
1531 /* remember username */
1532 if (!username) {
1533 if (!opts->username) {
romanf6e32012023-04-24 15:51:26 +02001534 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Michal Vasko30e2c872016-02-18 10:03:21 +01001535 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001536 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001537 goto fail;
1538 }
1539 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001540 free(buf);
Michal Vasko30e2c872016-02-18 10:03:21 +01001541 } else {
1542 username = strdup(opts->username);
1543 }
roman3a95bb22023-10-26 11:07:17 +02001544 NC_CHECK_ERRMEM_GOTO(!username,; , fail);
Michal Vasko30e2c872016-02-18 10:03:21 +01001545 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1546 }
1547
1548 /* connect and authenticate SSH session */
1549 session->host = host;
1550 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001551 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001552 goto fail;
1553 }
1554 }
1555
1556 /*
1557 * Almost done, open a netconf channel. (Transport layer / application layer)
1558 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001559 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001560 goto fail;
1561 }
1562
1563 /*
1564 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1565 */
1566
Michal Vasko78939072022-12-12 07:43:18 +01001567 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001568 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001569 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001570 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001571
1572 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001573 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001574 goto fail;
1575 }
1576 session->status = NC_STATUS_RUNNING;
1577
1578 if (nc_ctx_check_and_fill(session) == -1) {
1579 goto fail;
1580 }
1581
Michal Vasko93224072021-11-09 12:14:28 +01001582 /* store information if not previously */
1583 session->host = host;
1584 session->port = port;
1585 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001586
1587 return session;
1588
1589fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001590 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001591 return NULL;
1592}
1593
Radek Krejciac6d3472015-10-22 15:47:18 +02001594API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001595nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001596{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001597 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001598 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001599 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001600 char *username, *ip_host = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001601 struct passwd *pw, pw_buf;
Radek Krejciac6d3472015-10-22 15:47:18 +02001602 struct nc_session *session = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001603 char *buf = NULL;
1604 size_t buf_len = 0;
romanf6e32012023-04-24 15:51:26 +02001605 char *known_hosts_path = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001606
1607 /* process parameters */
roman3f9b65c2023-06-05 14:26:58 +02001608 if (!host || (host[0] == '\0')) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001609 host = "localhost";
1610 }
1611
1612 if (!port) {
1613 port = NC_PORT_SSH;
1614 }
Michal Vasko55fded62016-02-02 12:19:34 +01001615 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001616
Michal Vasko3031aae2016-01-27 16:07:18 +01001617 if (!ssh_opts.username) {
romanf6e32012023-04-24 15:51:26 +02001618 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Radek Krejciac6d3472015-10-22 15:47:18 +02001619 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001620 ERR(session, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko1d430d92021-10-11 09:30:43 +02001621 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001622 } else {
1623 username = pw->pw_name;
1624 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001625 } else {
1626 username = ssh_opts.username;
romanf6e32012023-04-24 15:51:26 +02001627
1628 pw = nc_getpw(0, username, &pw_buf, &buf, &buf_len);
1629 }
1630
1631 if (ssh_opts.knownhosts_path) {
1632 /* known_hosts file path was set so use it */
1633 known_hosts_path = strdup(ssh_opts.knownhosts_path);
roman3a95bb22023-10-26 11:07:17 +02001634 NC_CHECK_ERRMEM_GOTO(!known_hosts_path,; , fail);
romanf6e32012023-04-24 15:51:26 +02001635 } else if (pw) {
1636 /* path not set explicitly, but current user's username found in /etc/passwd, so create the path */
roman3a95bb22023-10-26 11:07:17 +02001637 NC_CHECK_ERRMEM_GOTO(asprintf(&known_hosts_path, "%s/.ssh/known_hosts", pw->pw_dir) == -1,; , fail);
Radek Krejciac6d3472015-10-22 15:47:18 +02001638 }
1639
1640 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001641 session = nc_new_session(NC_CLIENT, 0);
roman3a95bb22023-10-26 11:07:17 +02001642 NC_CHECK_ERRMEM_GOTO(!session,; , fail);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001643 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001644
Michal Vasko131120a2018-05-29 15:44:02 +02001645 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001646 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001647 session->ti.libssh.session = ssh_new();
1648 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001649 ERR(session, "Unable to initialize SSH session.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001650 goto fail;
1651 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001652
Michal Vasko7b62fed2015-10-26 15:39:46 +01001653 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001654 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001655 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001656 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001657 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
romanf6e32012023-04-24 15:51:26 +02001658 if (known_hosts_path) {
1659 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_path);
1660 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001661
1662 /* create and assign communication socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001663 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001664 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001665 ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001666 goto fail;
1667 }
1668 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001669 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001670
Michal Vasko93224072021-11-09 12:14:28 +01001671 /* store information for session connection */
1672 session->host = strdup(host);
1673 session->username = strdup(username);
romanf6e32012023-04-24 15:51:26 +02001674 session->port = port;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001675 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1) ||
1676 (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001677 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001678 }
1679
Michal Vasko78939072022-12-12 07:43:18 +01001680 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001681 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001682 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001683 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001684
Radek Krejciac6d3472015-10-22 15:47:18 +02001685 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001686 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001687 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001688 }
Michal Vaskoad611702015-12-03 13:41:51 +01001689 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001690
Michal Vaskoef578332016-01-25 13:20:09 +01001691 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001692 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001693 }
1694
Michal Vasko93224072021-11-09 12:14:28 +01001695 /* update information */
1696 free(session->host);
1697 session->host = ip_host;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001698 session->port = port;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001699
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001700 free(buf);
romanf6e32012023-04-24 15:51:26 +02001701 free(known_hosts_path);
Radek Krejciac6d3472015-10-22 15:47:18 +02001702 return session;
1703
Michal Vasko7b62fed2015-10-26 15:39:46 +01001704fail:
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001705 free(buf);
romanf6e32012023-04-24 15:51:26 +02001706 free(known_hosts_path);
Michal Vasko66032bc2019-01-22 15:03:12 +01001707 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001708 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001709 return NULL;
1710}
1711
1712API struct nc_session *
1713nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1714{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001715 return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001716}
1717
1718API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001719nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001720{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001721 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001722
roman40672412023-05-04 11:10:22 +02001723 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001724
Michal Vasko7b62fed2015-10-26 15:39:46 +01001725 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001726 new_session = nc_new_session(NC_CLIENT, 1);
roman3a95bb22023-10-26 11:07:17 +02001727 NC_CHECK_ERRMEM_RET(!new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001728 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001729
Michal Vasko131120a2018-05-29 15:44:02 +02001730 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001731 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001732 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001733 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001734
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001735 /* append to the session ring list */
1736 if (!session->ti.libssh.next) {
1737 session->ti.libssh.next = new_session;
1738 new_session->ti.libssh.next = session;
1739 } else {
1740 ptr = session->ti.libssh.next;
1741 session->ti.libssh.next = new_session;
1742 new_session->ti.libssh.next = ptr;
1743 }
1744
Michal Vasko7b62fed2015-10-26 15:39:46 +01001745 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001746 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001747 goto fail;
1748 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001749 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001750 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001751 }
Michal Vasko131120a2018-05-29 15:44:02 +02001752 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001753
Michal Vasko78939072022-12-12 07:43:18 +01001754 if (nc_client_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001755 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001756 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001757 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001758
Michal Vasko7b62fed2015-10-26 15:39:46 +01001759 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001760 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001761 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001762 }
Michal Vaskoad611702015-12-03 13:41:51 +01001763 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001764
Michal Vaskoef578332016-01-25 13:20:09 +01001765 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001766 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001767 }
1768
Michal Vasko93224072021-11-09 12:14:28 +01001769 /* store information into session */
1770 new_session->host = strdup(session->host);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001771 new_session->port = session->port;
Michal Vasko93224072021-11-09 12:14:28 +01001772 new_session->username = strdup(session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001773
Michal Vasko7b62fed2015-10-26 15:39:46 +01001774 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001775
1776fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001777 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001778 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001779}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001780
Michal Vasko3031aae2016-01-27 16:07:18 +01001781struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001782nc_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 +01001783{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001784 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001785 unsigned int uint_port;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001786 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001787 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001788 ssh_session sess;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001789 char *buf = NULL;
1790 size_t buf_len = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001791
Michal Vasko80cad7f2015-12-08 14:42:27 +01001792 sess = ssh_new();
1793 if (!sess) {
Michal Vasko05532772021-06-03 12:12:38 +02001794 ERR(NULL, "Unable to initialize an SSH session.");
Michal Vasko80cad7f2015-12-08 14:42:27 +01001795 close(sock);
1796 return NULL;
1797 }
1798
1799 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001800 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001801 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001802 uint_port = port;
1803 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001804 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001805 if (!ssh_ch_opts.username) {
romanf6e32012023-04-24 15:51:26 +02001806 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Michal Vasko3031aae2016-01-27 16:07:18 +01001807 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001808 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko435e5cf2019-04-23 08:48:44 +02001809 ssh_free(sess);
Michal Vasko3031aae2016-01-27 16:07:18 +01001810 return NULL;
1811 }
1812 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001813 free(buf);
Michal Vasko3031aae2016-01-27 16:07:18 +01001814 } else {
1815 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001816 }
Michal Vasko8fd6fca2019-02-04 10:59:49 +01001817 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1818 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1819#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
1820 ssh_options_set(sess, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1821 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1822#endif
Michal Vasko80cad7f2015-12-08 14:42:27 +01001823
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001824 session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
Michal Vasko435e5cf2019-04-23 08:48:44 +02001825 if (!session) {
Michal Vasko457f0532019-08-15 13:59:49 +02001826 /* sess is freed */
Michal Vasko435e5cf2019-04-23 08:48:44 +02001827 return NULL;
Michal Vasko4282fae2016-02-18 10:03:42 +01001828 }
1829
Michal Vasko435e5cf2019-04-23 08:48:44 +02001830 session->flags |= NC_SESSION_CALLHOME;
Michal Vasko30e2c872016-02-18 10:03:21 +01001831 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001832}