blob: 362e79bf0d37cd949888fa1ab22683e5a58d5af8 [file] [log] [blame]
Radek Krejciac6d3472015-10-22 15:47:18 +02001/**
Michal Vasko086311b2016-01-08 09:53:11 +01002 * \file session_client_ssh.c
Radek Krejciac6d3472015-10-22 15:47:18 +02003 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko086311b2016-01-08 09:53:11 +01004 * \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 Vasko40899d82021-05-26 12:04:49 +02009 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Radek Krejciac6d3472015-10-22 15:47:18 +020010 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010011 * This source code is licensed under BSD 3-Clause License (the "License").
12 * You may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010014 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010015 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejciac6d3472015-10-22 15:47:18 +020016 */
17
Michal Vasko7b62fed2015-10-26 15:39:46 +010018#define _GNU_SOURCE
Michal Vasko7a20d2e2021-05-19 16:40:23 +020019
Radek Krejciac6d3472015-10-22 15:47:18 +020020#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010021#include <errno.h>
22#include <fcntl.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010023#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020024#include <pwd.h>
25#include <stddef.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include <sys/stat.h>
30#include <sys/types.h>
31#include <termios.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010032#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020033#include <unistd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020034
Michal Vasko7b62fed2015-10-26 15:39:46 +010035#ifdef ENABLE_DNSSEC
Michal Vasko7b62fed2015-10-26 15:39:46 +010036# include <validator/resolver.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020037# include <validator/validator.h>
Michal Vasko40899d82021-05-26 12:04:49 +020038# include <validator/validator-config.h>
39
Michal Vasko7b62fed2015-10-26 15:39:46 +010040# include <validator/validator-compat.h>
41#endif
42
Michal Vasko745ff832015-12-08 14:40:29 +010043#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020044#include <libyang/libyang.h>
45
Michal Vasko7a20d2e2021-05-19 16:40:23 +020046#include "compat.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020047#include "libnetconf.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010048#include "session_client.h"
49#include "session_client_ch.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020050
Radek Krejci62aa0642017-05-25 16:33:49 +020051struct nc_client_context *nc_client_context_location(void);
Radek Krejcifd5b6682017-06-13 15:52:53 +020052int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx);
Michal Vasko30e2c872016-02-18 10:03:21 +010053
Radek Krejci62aa0642017-05-25 16:33:49 +020054#define client_opts nc_client_context_location()->opts
55#define ssh_opts nc_client_context_location()->ssh_opts
56#define ssh_ch_opts nc_client_context_location()->ssh_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010057
Michal Vaskoa43b8e32017-05-12 11:46:20 +020058static FILE *
59open_tty_noecho(const char *path, struct termios *oldterm)
60{
61 struct termios newterm;
62 FILE *ret;
63
64 if (!(ret = fopen(path, "r"))) {
Michal Vasko05532772021-06-03 12:12:38 +020065 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020066 return NULL;
67 }
68
69 if (tcgetattr(fileno(ret), oldterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020070 ERR(NULL, "Unable to get terminal \"%s\" settings (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020071 fclose(ret);
72 return NULL;
73 }
74
75 newterm = *oldterm;
76 newterm.c_lflag &= ~ECHO;
77 newterm.c_lflag &= ~ICANON;
78 tcflush(fileno(ret), TCIFLUSH);
79 if (tcsetattr(fileno(ret), TCSANOW, &newterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020080 ERR(NULL, "Unable to change terminal \"%s\" settings for hiding password (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020081 fclose(ret);
82 return NULL;
83 }
84
85 return ret;
86}
87
Michal Vasko51228ac2018-03-29 14:57:53 +020088static FILE *
89nc_open_in(int echo, struct termios *oldterm)
Michal Vaskoa43b8e32017-05-12 11:46:20 +020090{
Michal Vasko51228ac2018-03-29 14:57:53 +020091 char buf[512];
92 int buflen = 512, ret;
93 FILE *in;
94
95 if (!echo) {
96 in = open_tty_noecho("/dev/tty", oldterm);
97 } else {
98 in = fopen("/dev/tty", "r");
99 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200100 ERR(NULL, "Unable to open terminal \"/dev/tty\" for reading (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200101 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200102 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200103
104 if (!in) {
105 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200106 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200107 return NULL;
108 }
109
110 if (!echo) {
111 in = open_tty_noecho(buf, oldterm);
112 } else {
113 in = fopen(buf, "r");
114 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200115 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200116 }
117 }
118 }
119
120 return in;
121}
122
123static FILE *
124nc_open_out(void)
125{
126 char buf[512];
127 int buflen = 512, ret;
128 FILE *out;
129
130 out = fopen("/dev/tty", "w");
131 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200132 ERR(NULL, "Unable to open terminal \"/dev/tty\" for writing (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200133
134 if ((ret = ttyname_r(STDOUT_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200135 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200136 return NULL;
137 }
138
139 out = fopen(buf, "w");
140 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200141 ERR(NULL, "Unable to open terminal \"%s\" for writing (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200142 }
143 }
144
145 return out;
146}
147
148static void
149nc_close_inout(FILE *inout, int echo, struct termios *oldterm)
150{
151 if (inout) {
152 if (!echo && (tcsetattr(fileno(inout), TCSANOW, oldterm) != 0)) {
Michal Vasko05532772021-06-03 12:12:38 +0200153 ERR(NULL, "Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200154 }
155 fclose(inout);
156 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200157}
158
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400159void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100160_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +0100161{
162 int i;
163
Michal Vaskoe22c6732016-01-29 11:03:02 +0100164 for (i = 0; i < opts->key_count; ++i) {
165 free(opts->keys[i].pubkey_path);
166 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +0100167 }
Michal Vaskoe22c6732016-01-29 11:03:02 +0100168 free(opts->keys);
169 free(opts->username);
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200170 opts->keys = NULL;
171 opts->username = NULL;
Michal Vaskoe22c6732016-01-29 11:03:02 +0100172}
Michal Vasko990089e2015-10-27 15:05:25 +0100173
Michal Vaskob7558c52016-02-26 15:04:19 +0100174void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100175nc_client_ssh_destroy_opts(void)
176{
177 _nc_client_ssh_destroy_opts(&ssh_opts);
178 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +0100179}
180
Michal Vaskoef112d72016-02-18 13:28:25 +0100181#ifdef ENABLE_DNSSEC
182
183/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
184/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
185static int
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200186sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg)
187{
Michal Vaskoef112d72016-02-18 13:28:25 +0100188 ns_msg handle;
189 ns_rr rr;
190 val_status_t val_status;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200191 const unsigned char *rdata;
Michal Vaskoef112d72016-02-18 13:28:25 +0100192 unsigned char buf[4096];
193 int buf_len = 4096;
194 int ret = 0, i, j, len;
195
196 /* class 1 - internet, type 44 - SSHFP */
197 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
198
199 if ((len < 0) || !val_istrusted(val_status)) {
200 ret = 2;
201 goto finish;
202 }
203
204 if (ns_initparse(buf, len, &handle) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200205 ERR(NULL, "Failed to initialize DNSSEC response parser.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100206 ret = 2;
207 goto finish;
208 }
209
210 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
Michal Vasko05532772021-06-03 12:12:38 +0200211 ERR(NULL, "DNSSEC query returned %d.", i);
Michal Vaskoef112d72016-02-18 13:28:25 +0100212 ret = 2;
213 goto finish;
214 }
215
216 if (!libsres_msg_getflag(handle, ns_f_ad)) {
217 /* response not secured by DNSSEC */
218 ret = 1;
219 }
220
221 /* query section */
222 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
Michal Vasko05532772021-06-03 12:12:38 +0200223 ERR(NULL, "DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100224 ret = 2;
225 goto finish;
226 }
227
228 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200229 ERR(NULL, "DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100230 ret = 2;
231 goto finish;
232 }
233
234 /* answer section */
235 i = 0;
236 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
237 if (ns_rr_type(rr) != 44) {
238 ++i;
239 continue;
240 }
241
242 rdata = ns_rr_rdata(rr);
243 if (rdata[0] != type) {
244 ++i;
245 continue;
246 }
247 if (rdata[1] != alg) {
248 ++i;
249 continue;
250 }
251
252 /* we found the correct SSHFP entry */
253 rdata += 2;
254 for (j = 0; j < 20; ++j) {
255 if (rdata[j] != (unsigned char)sha1hash[j]) {
256 ret = 2;
257 goto finish;
258 }
259 }
260
261 /* server fingerprint is supported by a DNS entry,
262 * we have already determined if DNSSEC was used or not
263 */
264 goto finish;
265 }
266
267 /* no match */
268 ret = 2;
269
270finish:
271 val_free_validator_state();
272 return ret;
273}
274
275#endif /* ENABLE_DNSSEC */
276
Radek Krejci62aa0642017-05-25 16:33:49 +0200277int
Radek Krejci90a84a22017-05-25 13:53:00 +0200278sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(priv))
Michal Vaskoef112d72016-02-18 13:28:25 +0100279{
Michal Vasko51228ac2018-03-29 14:57:53 +0200280 char *hexa = NULL;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200281
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800282#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
283 int c, ret;
284 enum ssh_known_hosts_e state;
285#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100286 int c, state, ret;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800287#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100288 ssh_key srv_pubkey;
289 unsigned char *hash_sha1 = NULL;
290 size_t hlen;
291 enum ssh_keytypes_e srv_pubkey_type;
292 char answer[5];
Michal Vasko51228ac2018-03-29 14:57:53 +0200293 FILE *out = NULL, *in = NULL;
Michal Vaskoef112d72016-02-18 13:28:25 +0100294
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800295#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
296 state = ssh_session_is_known_server(session);
297#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100298 state = ssh_is_server_known(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800299#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100300
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800301#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 0))
302 ret = ssh_get_server_publickey(session, &srv_pubkey);
303#else
Michal Vaskocc0aa7d2016-05-31 12:48:42 +0200304 ret = ssh_get_publickey(session, &srv_pubkey);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800305#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100306 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200307 ERR(NULL, "Unable to get server public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100308 return -1;
309 }
310
311 srv_pubkey_type = ssh_key_type(srv_pubkey);
312 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
313 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
319 hexa = ssh_get_hexa(hash_sha1, hlen);
320
321 switch (state) {
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800322#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
323 case SSH_KNOWN_HOSTS_OK:
324#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100325 case SSH_SERVER_KNOWN_OK:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800326#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100327 break; /* ok */
328
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800329#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
330 case SSH_KNOWN_HOSTS_CHANGED:
331#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100332 case SSH_SERVER_KNOWN_CHANGED:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800333#endif
Michal Vasko05532772021-06-03 12:12:38 +0200334 ERR(NULL, "Remote host key changed, the connection will be terminated!");
Michal Vasko51228ac2018-03-29 14:57:53 +0200335 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100336
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800337#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
338 case SSH_KNOWN_HOSTS_OTHER:
339#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100340 case SSH_SERVER_FOUND_OTHER:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800341#endif
Michal Vasko05532772021-06-03 12:12:38 +0200342 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 +0100343 goto hostkey_not_known;
344
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800345#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
346 case SSH_KNOWN_HOSTS_NOT_FOUND:
347#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100348 case SSH_SERVER_FILE_NOT_FOUND:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800349#endif
Michal Vasko05532772021-06-03 12:12:38 +0200350 WRN(NULL, "Could not find the known hosts file.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100351 goto hostkey_not_known;
352
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800353#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
354 case SSH_KNOWN_HOSTS_UNKNOWN:
355#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100356 case SSH_SERVER_NOT_KNOWN:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800357#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100358hostkey_not_known:
359#ifdef ENABLE_DNSSEC
Michal Vaskod1792612021-05-28 11:33:47 +0200360 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) && (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
Michal Vaskoef112d72016-02-18 13:28:25 +0100361 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
Michal Vasko650011a2016-02-25 14:49:29 +0100362 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100363 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100364 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100365 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100366 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100367 }
368
369 /* DNSSEC SSHFP check successful, that's enough */
370 if (!ret) {
Michal Vasko05532772021-06-03 12:12:38 +0200371 VRB(NULL, "DNSSEC SSHFP check successful.");
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800372#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
373 ssh_session_update_known_hosts(session);
374#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100375 ssh_write_knownhost(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800376#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100377 ssh_clean_pubkey_hash(&hash_sha1);
378 ssh_string_free_char(hexa);
379 return 0;
380 }
381 }
382#endif
383
Michal Vasko51228ac2018-03-29 14:57:53 +0200384 if (!(in = nc_open_in(1, NULL))) {
385 goto error;
386 }
387 if (!(out = nc_open_out())) {
388 goto error;
389 }
390
Michal Vaskoef112d72016-02-18 13:28:25 +0100391 /* try to get result from user */
Michal Vasko51228ac2018-03-29 14:57:53 +0200392 if (fprintf(out, "The authenticity of the host \'%s\' cannot be established.\n", hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200393 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200394 goto error;
395 }
396 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 +0200397 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200398 goto error;
399 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100400
401#ifdef ENABLE_DNSSEC
402 if (ret == 2) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200403 if (fprintf(out, "No matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200404 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200405 goto error;
406 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100407 } else if (ret == 1) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200408 if (fprintf(out, "Matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200409 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200410 goto error;
411 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100412 }
413#endif
414
Michal Vasko51228ac2018-03-29 14:57:53 +0200415 if (fprintf(out, "Are you sure you want to continue connecting (yes/no)? ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200416 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200417 goto error;
418 }
419 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100420
421 do {
Michal Vasko51228ac2018-03-29 14:57:53 +0200422 if (fscanf(in, "%4s", answer) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200423 ERR(NULL, "Reading from input failed (%s).", feof(in) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200424 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100425 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200426 while (((c = getc(in)) != EOF) && (c != '\n')) {}
Michal Vaskoef112d72016-02-18 13:28:25 +0100427
Michal Vasko51228ac2018-03-29 14:57:53 +0200428 fflush(in);
Michal Vaskoef112d72016-02-18 13:28:25 +0100429 if (!strcmp("yes", answer)) {
430 /* store the key into the host file */
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800431#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
432 ret = ssh_session_update_known_hosts(session);
433#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100434 ret = ssh_write_knownhost(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800435#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100436 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +0200437 WRN(NULL, "Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
Michal Vaskoef112d72016-02-18 13:28:25 +0100438 }
439 } else if (!strcmp("no", answer)) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200440 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100441 } else {
Michal Vasko51228ac2018-03-29 14:57:53 +0200442 if (fprintf(out, "Please type 'yes' or 'no': ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200443 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200444 goto error;
445 }
Michal Vasko5fb5f992020-11-26 15:19:31 +0100446 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100447 }
448 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
449
450 break;
451
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800452#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
453 case SSH_KNOWN_HOSTS_ERROR:
454#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100455 case SSH_SERVER_ERROR:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800456#endif
Michal Vasko05532772021-06-03 12:12:38 +0200457 ERR(NULL, "SSH error: %s", ssh_get_error(session));
Michal Vasko51228ac2018-03-29 14:57:53 +0200458 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100459 }
460
Michal Vasko51228ac2018-03-29 14:57:53 +0200461 nc_close_inout(in, 1, NULL);
462 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100463 ssh_clean_pubkey_hash(&hash_sha1);
464 ssh_string_free_char(hexa);
465 return 0;
466
Michal Vasko51228ac2018-03-29 14:57:53 +0200467error:
468 nc_close_inout(in, 1, NULL);
469 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100470 ssh_clean_pubkey_hash(&hash_sha1);
471 ssh_string_free_char(hexa);
472 return -1;
473}
474
Radek Krejci62aa0642017-05-25 16:33:49 +0200475char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200476sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200477{
Michal Vasko51228ac2018-03-29 14:57:53 +0200478 char *buf = NULL;
479 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200480 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200481 FILE *in = NULL, *out = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200482
Michal Vasko11d142a2016-01-19 15:58:24 +0100483 buf = malloc(buflen * sizeof *buf);
484 if (!buf) {
485 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100486 return NULL;
487 }
488
Michal Vasko51228ac2018-03-29 14:57:53 +0200489 if (!(in = nc_open_in(0, &oldterm))) {
490 goto error;
491 }
492 if (!(out = nc_open_out())) {
493 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200494 }
495
Michal Vasko51228ac2018-03-29 14:57:53 +0200496 if (fprintf(out, "%s@%s password: ", username, hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200497 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200498 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200499 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200500 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200501
502 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200503 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100504 if (len >= buflen - 1) {
505 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100506 buf = nc_realloc(buf, buflen * sizeof *buf);
507 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100508 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200509 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100510 }
511 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100512 buf[len++] = (char)c;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100513 }
514 buf[len++] = 0; /* terminating null byte */
515
Michal Vasko51228ac2018-03-29 14:57:53 +0200516 fprintf(out, "\n");
517
518 nc_close_inout(in, 0, &oldterm);
519 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100520 return buf;
Michal Vasko93f26d92018-02-01 09:08:35 +0100521
Michal Vasko51228ac2018-03-29 14:57:53 +0200522error:
523 nc_close_inout(in, 0, &oldterm);
524 nc_close_inout(out, 1, NULL);
Michal Vasko93f26d92018-02-01 09:08:35 +0100525 free(buf);
526 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100527}
528
Radek Krejci62aa0642017-05-25 16:33:49 +0200529char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200530sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100531{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200532 unsigned int buflen = 64, cur_len;
Michal Vasko51228ac2018-03-29 14:57:53 +0200533 int c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200534 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200535 char *buf = NULL;
536 FILE *in = NULL, *out = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100537
538 buf = malloc(buflen * sizeof *buf);
539 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100540 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100541 return NULL;
542 }
543
Michal Vasko51228ac2018-03-29 14:57:53 +0200544 if (!(in = nc_open_in(echo, &oldterm))) {
545 goto error;
546 }
547 if (!(out = nc_open_out())) {
548 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100549 }
550
Michal Vasko51228ac2018-03-29 14:57:53 +0200551 if (auth_name && (fprintf(out, "%s\n", auth_name) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200552 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200553 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100554 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200555 if (instruction && (fprintf(out, "%s\n", instruction) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200556 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200557 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100558 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200559 if (fputs(prompt, out) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200560 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200561 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200562 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200563 fflush(out);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100564
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200565 cur_len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200566 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200567 if (cur_len >= buflen - 1) {
568 buflen *= 2;
569 buf = nc_realloc(buf, buflen * sizeof *buf);
570 if (!buf) {
571 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200572 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200573 }
574 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100575 buf[cur_len++] = (char)c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200576 }
577 /* terminating null byte */
578 buf[cur_len] = '\0';
579
Michal Vasko51228ac2018-03-29 14:57:53 +0200580 fprintf(out, "\n");
581
582 nc_close_inout(in, echo, &oldterm);
583 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200584 return buf;
585
Michal Vasko51228ac2018-03-29 14:57:53 +0200586error:
587 nc_close_inout(in, echo, &oldterm);
588 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200589 free(buf);
590 return NULL;
591}
592
Radek Krejci62aa0642017-05-25 16:33:49 +0200593char *
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200594sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200595{
Michal Vasko51228ac2018-03-29 14:57:53 +0200596 char *buf = NULL;
597 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200598 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200599 FILE *in = NULL, *out = NULL;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200600
601 buf = malloc(buflen * sizeof *buf);
602 if (!buf) {
603 ERRMEM;
604 return NULL;
605 }
606
Michal Vasko51228ac2018-03-29 14:57:53 +0200607 if (!(in = nc_open_in(0, &oldterm))) {
608 goto error;
609 }
610 if (!(out = nc_open_out())) {
611 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200612 }
613
Michal Vasko51228ac2018-03-29 14:57:53 +0200614 if (fprintf(out, "Enter passphrase for the key '%s': ", privkey_path) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200615 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200616 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200617 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200618 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200619
620 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200621 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100622 if (len >= buflen - 1) {
623 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100624 buf = nc_realloc(buf, buflen * sizeof *buf);
625 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100626 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200627 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100628 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100629 }
630 buf[len++] = (char)c;
631 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200632 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100633
Michal Vasko51228ac2018-03-29 14:57:53 +0200634 fprintf(out, "\n");
635
636 nc_close_inout(in, 0, &oldterm);
637 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100638 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100639
Michal Vasko51228ac2018-03-29 14:57:53 +0200640error:
641 nc_close_inout(in, 0, &oldterm);
642 nc_close_inout(out, 1, NULL);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100643 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100644 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100645}
646
Michal Vaskoef112d72016-02-18 13:28:25 +0100647static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200648_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200649 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100650{
Michal Vaskoef112d72016-02-18 13:28:25 +0100651 if (auth_hostkey_check) {
652 opts->auth_hostkey_check = auth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200653 opts->auth_hostkey_check_priv = priv;
Michal Vaskoef112d72016-02-18 13:28:25 +0100654 } else {
655 opts->auth_hostkey_check = sshauth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200656 opts->auth_hostkey_check_priv = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100657 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100658}
659
Radek Krejci90a84a22017-05-25 13:53:00 +0200660static void
661_nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200662 void **priv, struct nc_client_ssh_opts *opts)
Michal Vaskoef112d72016-02-18 13:28:25 +0100663{
Radek Krejci90a84a22017-05-25 13:53:00 +0200664 if (auth_hostkey_check) {
665 (*auth_hostkey_check) = opts->auth_hostkey_check == sshauth_hostkey_check ? NULL : opts->auth_hostkey_check;
666 }
667 if (priv) {
668 (*priv) = opts->auth_hostkey_check_priv;
669 }
670}
671
Radek Krejci90a84a22017-05-25 13:53:00 +0200672API void
673nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200674 void *priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200675{
676 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100677}
678
679API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200680nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200681 void *priv)
Michal Vaskoef112d72016-02-18 13:28:25 +0100682{
Radek Krejci90a84a22017-05-25 13:53:00 +0200683 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100684}
685
Radek Krejci90a84a22017-05-25 13:53:00 +0200686API void
687nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200688 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200689{
690 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
691}
692
693API void
694nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200695 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200696{
697 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
698}
Michal Vaskoef112d72016-02-18 13:28:25 +0100699
Michal Vasko30e2c872016-02-18 10:03:21 +0100700static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200701_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 +0200702 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100703{
704 if (auth_password) {
705 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200706 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100707 } else {
708 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200709 opts->auth_password_priv = NULL;
710 }
711}
712
713static void
714_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 +0200715 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200716{
717 if (auth_password) {
718 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
719 }
720 if (priv) {
721 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100722 }
723}
724
725API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200726nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200727 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100728{
Radek Krejci90a84a22017-05-25 13:53:00 +0200729 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100730}
731
732API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200733nc_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 +0200734 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100735{
Radek Krejci90a84a22017-05-25 13:53:00 +0200736 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
737}
738
739API void
740nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200741 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200742{
743 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
744}
745
746API void
747nc_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 +0200748 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200749{
750 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100751}
752
753static void
754_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200755 const char *prompt, int echo, void *priv),
756 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100757{
758 if (auth_interactive) {
759 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200760 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100761 } else {
762 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200763 opts->auth_interactive_priv = NULL;
764 }
765}
766
767static void
768_nc_client_ssh_get_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, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200771{
772 if (auth_interactive) {
773 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
774 }
775 if (priv) {
776 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100777 }
778}
779
780API void
781nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200782 const char *prompt, int echo, void *priv),
783 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100784{
Radek Krejci90a84a22017-05-25 13:53:00 +0200785 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100786}
787
788API void
789nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200790 const char *prompt, int echo, void *priv),
791 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100792{
Radek Krejci90a84a22017-05-25 13:53:00 +0200793 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
794}
795
796API void
797nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200798 const char *prompt, int echo, void *priv),
799 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200800{
801 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
802}
803
804API void
805nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200806 const char *prompt, int echo, void *priv),
807 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200808{
809 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100810}
811
812static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200813_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 +0200814 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100815{
816 if (auth_privkey_passphrase) {
817 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200818 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100819 } else {
820 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200821 opts->auth_privkey_passphrase_priv = NULL;
822 }
823}
824
825static void
826_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 +0200827 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200828{
829 if (auth_privkey_passphrase) {
830 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
831 }
832 if (priv) {
833 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100834 }
835}
836
837API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200838nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200839 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100840{
Radek Krejci90a84a22017-05-25 13:53:00 +0200841 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100842}
843
844API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200845nc_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 +0200846 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100847{
Radek Krejci90a84a22017-05-25 13:53:00 +0200848 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
849}
850
851API void
852nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200853 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200854{
855 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
856}
857
858API void
859nc_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 +0200860 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200861{
862 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100863}
864
Michal Vasko3031aae2016-01-27 16:07:18 +0100865static int
866_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 +0100867{
868 int i;
869 FILE *key;
870 char line[128];
871
Michal Vasko45e53ae2016-04-07 11:46:03 +0200872 if (!pub_key) {
873 ERRARG("pub_key");
874 return -1;
875 } else if (!priv_key) {
876 ERRARG("priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100877 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100878 }
879
Michal Vasko3031aae2016-01-27 16:07:18 +0100880 for (i = 0; i < opts->key_count; ++i) {
881 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
882 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200883 WRN(NULL, "Private key \"%s\" found with another public key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200884 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100885 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100886 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200887 WRN(NULL, "Public key \"%s\" found with another private key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200888 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100889 continue;
890 }
891
Michal Vasko05532772021-06-03 12:12:38 +0200892 ERR(NULL, "SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100893 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100894 }
895 }
896
Michal Vasko3031aae2016-01-27 16:07:18 +0100897 /* add the keys */
898 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100899 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
900 if (!opts->keys) {
901 ERRMEM;
902 return -1;
903 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100904 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
905 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
906 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100907
Michal Vasko4eb3c312016-03-01 14:09:37 +0100908 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
909 ERRMEM;
910 return -1;
911 }
912
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100913 /* check encryption */
914 if ((key = fopen(priv_key, "r"))) {
915 /* 1st line - key type */
916 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100917 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200918 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100919 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100920 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100921 /* 2nd line - encryption information or key */
922 if (!fgets(line, sizeof line, key)) {
923 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200924 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100925 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100926 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100927 fclose(key);
928 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100929 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100930 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100931 }
932
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100933 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100934}
935
936API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100937nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100938{
Michal Vasko3031aae2016-01-27 16:07:18 +0100939 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
940}
941
942API int
943nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
944{
945 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
946}
947
948static int
949_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
950{
951 if (idx >= opts->key_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200952 ERRARG("idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100953 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100954 }
955
Michal Vasko3031aae2016-01-27 16:07:18 +0100956 free(opts->keys[idx].pubkey_path);
957 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100958
Michal Vasko3031aae2016-01-27 16:07:18 +0100959 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100960 if (idx < opts->key_count) {
961 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
962 }
963 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100964 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
965 if (!opts->keys) {
966 ERRMEM;
967 return -1;
968 }
Michal Vaskoc0256492016-02-02 12:19:06 +0100969 } else {
970 free(opts->keys);
971 opts->keys = NULL;
972 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100973
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100974 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100975}
976
977API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100978nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100979{
Michal Vasko3031aae2016-01-27 16:07:18 +0100980 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100981}
982
983API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100984nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100985{
Michal Vasko3031aae2016-01-27 16:07:18 +0100986 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
987}
988
989static int
990_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
991{
992 return opts->key_count;
993}
994
995API int
996nc_client_ssh_get_keypair_count(void)
997{
998 return _nc_client_ssh_get_keypair_count(&ssh_opts);
999}
1000
1001API int
1002nc_client_ssh_ch_get_keypair_count(void)
1003{
1004 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
1005}
1006
1007static int
1008_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
1009{
Michal Vasko45e53ae2016-04-07 11:46:03 +02001010 if (idx >= opts->key_count) {
1011 ERRARG("idx");
1012 return -1;
1013 } else if (!pub_key && !priv_key) {
1014 ERRARG("pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001015 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001016 }
1017
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001018 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001019 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001020 }
1021 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001022 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001023 }
1024
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001025 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001026}
1027
Michal Vasko3031aae2016-01-27 16:07:18 +01001028API int
1029nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
1030{
1031 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
1032}
1033
1034API int
1035nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
1036{
1037 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
1038}
1039
1040static void
1041_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 +01001042{
1043 if (pref < 0) {
1044 pref = -1;
1045 }
1046
1047 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001048 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001049 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001050 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001051 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001052 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001053 }
1054}
1055
Michal Vasko3031aae2016-01-27 16:07:18 +01001056API void
1057nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001058{
Michal Vasko3031aae2016-01-27 16:07:18 +01001059 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
1060}
1061
1062API void
1063nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
1064{
1065 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
1066}
1067
1068static int16_t
1069_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
1070{
1071 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001072
1073 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001074 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001075 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001076 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001077 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001078 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001079 }
1080
1081 return pref;
1082}
1083
Michal Vasko3031aae2016-01-27 16:07:18 +01001084API int16_t
1085nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1086{
1087 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
1088}
1089
1090API int16_t
1091nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1092{
1093 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
1094}
1095
1096static int
1097_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
1098{
1099 if (opts->username) {
1100 free(opts->username);
1101 }
1102 if (username) {
1103 opts->username = strdup(username);
1104 if (!opts->username) {
1105 ERRMEM;
1106 return -1;
1107 }
1108 } else {
1109 opts->username = NULL;
1110 }
1111
1112 return 0;
1113}
1114
1115API int
1116nc_client_ssh_set_username(const char *username)
1117{
1118 return _nc_client_ssh_set_username(username, &ssh_opts);
1119}
1120
1121API int
1122nc_client_ssh_ch_set_username(const char *username)
1123{
1124 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1125}
1126
Michal Vaskoe22c6732016-01-29 11:03:02 +01001127static const char *
1128_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1129{
1130 return opts->username;
1131}
1132
1133API const char *
1134nc_client_ssh_get_username(void)
1135{
1136 return _nc_client_ssh_get_username(&ssh_opts);
1137}
1138
1139API const char *
1140nc_client_ssh_ch_get_username(void)
1141{
1142 return _nc_client_ssh_get_username(&ssh_ch_opts);
1143}
1144
Michal Vasko3031aae2016-01-27 16:07:18 +01001145API int
1146nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1147{
1148 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
1149}
1150
1151API int
1152nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1153{
1154 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1155}
1156
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001157/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001158 * Host, port, username, and a connected socket is expected to be set.
Radek Krejciae813f42018-07-02 13:38:30 +02001159 *
1160 * return values
1161 * -1 failure
1162 * 0 try again
1163 * 1 success
Michal Vasko7b62fed2015-10-26 15:39:46 +01001164 */
1165static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001166connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001167{
Radek Krejciae813f42018-07-02 13:38:30 +02001168 int j, ret_auth, userauthlist, ret, attempt = 0;
Michal Vasko235efdc2015-12-17 12:05:04 +01001169 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001170 int16_t pref;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001171 const char *prompt;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001172 char *s, *answer, echo;
1173 ssh_key pubkey, privkey;
1174 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001175 struct timespec ts_timeout, ts_cur;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001176
1177 ssh_sess = session->ti.libssh.session;
1178
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001179 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001180 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001181 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1182 usleep(NC_TIMEOUT_STEP);
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001183 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001184 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001185 break;
1186 }
1187 }
1188 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001189 ERR(session, "SSH connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001190 return 0;
1191 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001192 ERR(session, "Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
1193 DBG(session, "Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001194 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001195 }
1196
Radek Krejci90a84a22017-05-25 13:53:00 +02001197 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko05532772021-06-03 12:12:38 +02001198 ERR(session, "Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001199 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001200 }
1201
Michal Vasko36c7be82017-02-22 13:37:59 +01001202 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001203 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001204 nc_addtimespec(&ts_timeout, timeout);
1205 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001206 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1207 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001208 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001209 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001210 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1211 break;
1212 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001213 }
1214 }
1215 if (ret_auth == SSH_AUTH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001216 ERR(session, "Request authentication methods timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001217 return 0;
1218 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001219 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001220 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001221 } else if (ret_auth == SSH_AUTH_SUCCESS) {
Michal Vasko05532772021-06-03 12:12:38 +02001222 WRN(session, "Server accepts \"none\" authentication method.")
Radek Krejciae813f42018-07-02 13:38:30 +02001223 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001224 }
1225
1226 /* check what authentication methods are available */
1227 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001228
1229 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001230 if (opts->auth_pref[0].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001231 VRB(session, "Interactive SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001232 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001233 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001234 if (opts->auth_pref[1].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001235 VRB(session, "Password SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001236 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001237 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001238 if (opts->auth_pref[2].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001239 VRB(session, "Publickey SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001240 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001241 }
1242
Michal Vasko0190bc32016-03-02 15:47:49 +01001243 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001244 auth = 0;
1245 pref = 0;
1246 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1247 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001248 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001249 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001250 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001251 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001252 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001253 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001254 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001255 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001256 }
1257
Michal Vasko235efdc2015-12-17 12:05:04 +01001258 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001259 if (!attempt) {
Michal Vasko05532772021-06-03 12:12:38 +02001260 ERR(session, "Unable to authenticate to the remote server (no supported authentication methods detected).");
Radek Krejciae813f42018-07-02 13:38:30 +02001261 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001262 ERR(session, "Unable to authenticate to the remote server (all attempts via supported authentication "
1263 "methods failed).");
Radek Krejciae813f42018-07-02 13:38:30 +02001264 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001265 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001266 }
1267
1268 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001269 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001270 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001271 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1272
Michal Vasko05532772021-06-03 12:12:38 +02001273 VRB(session, "Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001274 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001275 if (s == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001276 ERR(session, "Unable to get the password.");
Michal Vasko88583042018-03-29 09:18:58 +02001277 return -1;
1278 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001279
Michal Vasko36c7be82017-02-22 13:37:59 +01001280 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001281 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001282 nc_addtimespec(&ts_timeout, timeout);
1283 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001284 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1285 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001286 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001287 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001288 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1289 break;
1290 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001291 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001292 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001293 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001294 free(s);
1295 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001296
Michal Vasko7b62fed2015-10-26 15:39:46 +01001297 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001298 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1299
Michal Vasko05532772021-06-03 12:12:38 +02001300 VRB(session, "Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001301
Michal Vasko36c7be82017-02-22 13:37:59 +01001302 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001303 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001304 nc_addtimespec(&ts_timeout, timeout);
1305 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001306 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) ||
1307 (ret_auth == SSH_AUTH_AGAIN)) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001308 if (ret_auth == SSH_AUTH_AGAIN) {
1309 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001310 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001311 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001312 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1313 break;
1314 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001315 }
1316 continue;
1317 }
1318
Michal Vasko7b62fed2015-10-26 15:39:46 +01001319 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1320 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001321 if (!prompt) {
1322 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001323 break;
1324 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001325
1326 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1327 echo = 0;
1328
Michal Vasko30e2c872016-02-18 10:03:21 +01001329 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001330 ssh_userauth_kbdint_getinstruction(ssh_sess),
1331 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001332 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1333 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001334 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001335 break;
1336 }
1337 free(answer);
1338 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001339 if (ret_auth == SSH_AUTH_ERROR) {
1340 break;
1341 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001342 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001343 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001344 nc_addtimespec(&ts_timeout, timeout);
1345 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001346 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001347 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001348
Michal Vasko206d3b12015-12-04 11:08:42 +01001349 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001350 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1351
Michal Vasko05532772021-06-03 12:12:38 +02001352 VRB(session, "Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001353
1354 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001355 if (!opts->key_count) {
Michal Vasko05532772021-06-03 12:12:38 +02001356 VRB(session, "No key pair specified.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001357 break;
1358 }
1359
Michal Vasko30e2c872016-02-18 10:03:21 +01001360 for (j = 0; j < opts->key_count; j++) {
Michal Vasko05532772021-06-03 12:12:38 +02001361 VRB(session, "Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001362 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1363 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001364
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001365 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1366 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001367 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001368 continue;
1369 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001370 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001371 continue;
1372 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001373
Michal Vasko36c7be82017-02-22 13:37:59 +01001374 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001375 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001376 nc_addtimespec(&ts_timeout, timeout);
1377 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001378 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1379 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001380 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001381 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001382 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1383 break;
1384 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001385 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001386 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001387 ssh_key_free(pubkey);
1388
1389 if (ret_auth == SSH_AUTH_DENIED) {
1390 continue;
1391 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001392 break;
1393 }
1394
Michal Vasko30e2c872016-02-18 10:03:21 +01001395 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001396 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001397 } else {
1398 s = NULL;
1399 }
1400
Michal Vasko0190bc32016-03-02 15:47:49 +01001401 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001402 if (s) {
1403 memset(s, 0, strlen(s));
1404 free(s);
1405 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001406 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001407 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001408 continue;
1409 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001410 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001411 continue;
1412 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001413
Michal Vasko36c7be82017-02-22 13:37:59 +01001414 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001415 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001416 nc_addtimespec(&ts_timeout, timeout);
1417 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001418 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1419 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001420 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001421 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001422 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1423 break;
1424 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001425 }
1426 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001427 ssh_key_free(privkey);
1428
Michal Vasko0190bc32016-03-02 15:47:49 +01001429 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001430 break;
1431 }
1432 }
1433 break;
1434 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001435
Michal Vasko0190bc32016-03-02 15:47:49 +01001436 switch (ret_auth) {
1437 case SSH_AUTH_AGAIN:
Michal Vasko05532772021-06-03 12:12:38 +02001438 ERR(session, "Authentication response timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001439 return 0;
1440 case SSH_AUTH_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001441 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001442 return -1;
1443 case SSH_AUTH_DENIED:
Michal Vasko05532772021-06-03 12:12:38 +02001444 WRN(session, "Authentication denied.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001445 break;
1446 case SSH_AUTH_PARTIAL:
Michal Vasko05532772021-06-03 12:12:38 +02001447 VRB(session, "Partial authentication success.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001448 break;
1449 case SSH_AUTH_SUCCESS:
Michal Vasko05532772021-06-03 12:12:38 +02001450 VRB(session, "Authentication successful.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001451 break;
1452 case SSH_AUTH_INFO:
1453 ERRINT;
1454 return -1;
1455 }
Radek Krejciae813f42018-07-02 13:38:30 +02001456
1457 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001458 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001459
Michal Vasko0190bc32016-03-02 15:47:49 +01001460 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001461}
1462
1463/* Open new SSH channel and request the 'netconf' subsystem.
1464 * SSH connection is expected to be established.
1465 */
1466static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001467open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001468{
1469 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001470 int ret;
1471 struct timespec ts_timeout, ts_cur;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001472
1473 ssh_sess = session->ti.libssh.session;
1474
1475 if (!ssh_is_connected(ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001476 ERR(session, "SSH session not connected.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001477 return -1;
1478 }
1479
1480 if (session->ti.libssh.channel) {
Michal Vasko05532772021-06-03 12:12:38 +02001481 ERR(session, "SSH channel already created.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001482 return -1;
1483 }
1484
Michal Vasko7b62fed2015-10-26 15:39:46 +01001485 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001486 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001487 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001488 nc_addtimespec(&ts_timeout, timeout);
1489 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001490 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001491 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1492 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001493 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001494 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001495 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1496 break;
1497 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001498 }
1499 }
1500 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001501 ERR(session, "Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001502 ssh_channel_free(session->ti.libssh.channel);
1503 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001504 return 0;
1505 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001506 ERR(session, "Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001507 ssh_channel_free(session->ti.libssh.channel);
1508 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001509 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001510 }
1511
1512 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001513 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001514 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001515 nc_addtimespec(&ts_timeout, timeout);
1516 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001517 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1518 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001519 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001520 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001521 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1522 break;
1523 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001524 }
1525 }
1526 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001527 ERR(session, "Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001528 ssh_channel_free(session->ti.libssh.channel);
1529 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001530 return 0;
1531 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001532 ERR(session, "Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001533 ssh_channel_free(session->ti.libssh.channel);
1534 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001535 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001536 }
1537
Michal Vasko0190bc32016-03-02 15:47:49 +01001538 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001539}
1540
Michal Vasko30e2c872016-02-18 10:03:21 +01001541static struct nc_session *
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001542_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
1543 struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001544{
Michal Vasko66032bc2019-01-22 15:03:12 +01001545 char *host = NULL, *username = NULL, *ip_host;
Dragos Dan8ce3b7c2021-03-09 09:17:22 +02001546 unsigned int port = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001547 int sock;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001548 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001549 struct nc_session *session = NULL;
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001550 char *buf = NULL;
1551 size_t buf_len = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001552
1553 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001554 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001555 return NULL;
1556 }
1557
1558 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001559 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001560 if (!session) {
1561 ERRMEM;
1562 return NULL;
1563 }
1564 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001565 session->ti_type = NC_TI_LIBSSH;
1566 session->ti.libssh.session = ssh_session;
1567
1568 /* was port set? */
Michal Vasko097fc852021-03-09 08:18:17 +01001569 ssh_options_get_port(ssh_session, &port);
Michal Vasko30e2c872016-02-18 10:03:21 +01001570
1571 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1572 /*
1573 * There is no file descriptor (detected based on the host, there is no way to check
1574 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1575 */
1576
1577 /* remember host */
1578 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001579 if (!host) {
1580 ERRMEM;
1581 goto fail;
1582 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001583 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1584
1585 /* create and connect socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001586 sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001587 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001588 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001589 goto fail;
1590 }
1591 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001592 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko66032bc2019-01-22 15:03:12 +01001593
1594 free(host);
1595 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001596 }
1597
1598 /* was username set? */
1599 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1600
1601 if (!ssh_is_connected(ssh_session)) {
1602 /*
1603 * We are connected, but not SSH authenticated. (Transport layer)
1604 */
1605
1606 /* remember username */
1607 if (!username) {
1608 if (!opts->username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001609 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Michal Vasko30e2c872016-02-18 10:03:21 +01001610 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001611 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001612 goto fail;
1613 }
1614 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001615 free(buf);
Michal Vasko30e2c872016-02-18 10:03:21 +01001616 } else {
1617 username = strdup(opts->username);
1618 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001619 if (!username) {
1620 ERRMEM;
1621 goto fail;
1622 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001623 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1624 }
1625
1626 /* connect and authenticate SSH session */
1627 session->host = host;
1628 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001629 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001630 goto fail;
1631 }
1632 }
1633
1634 /*
1635 * Almost done, open a netconf channel. (Transport layer / application layer)
1636 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001637 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001638 goto fail;
1639 }
1640
1641 /*
1642 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1643 */
1644
Radek Krejcifd5b6682017-06-13 15:52:53 +02001645 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1646 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001647 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001648 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001649
1650 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001651 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001652 goto fail;
1653 }
1654 session->status = NC_STATUS_RUNNING;
1655
1656 if (nc_ctx_check_and_fill(session) == -1) {
1657 goto fail;
1658 }
1659
Michal Vasko395900e2021-11-09 12:14:28 +01001660 /* store information if not previously */
1661 session->host = host;
1662 session->port = port;
1663 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001664
1665 return session;
1666
1667fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001668 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001669 return NULL;
1670}
1671
Radek Krejciac6d3472015-10-22 15:47:18 +02001672API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001673nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001674{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001675 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001676 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001677 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001678 char *username, *ip_host = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001679 struct passwd *pw, pw_buf;
Radek Krejciac6d3472015-10-22 15:47:18 +02001680 struct nc_session *session = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001681 char *buf = NULL;
1682 size_t buf_len = 0;
Radek Krejciac6d3472015-10-22 15:47:18 +02001683
1684 /* process parameters */
1685 if (!host || strisempty(host)) {
1686 host = "localhost";
1687 }
1688
1689 if (!port) {
1690 port = NC_PORT_SSH;
1691 }
Michal Vasko55fded62016-02-02 12:19:34 +01001692 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001693
Michal Vasko3031aae2016-01-27 16:07:18 +01001694 if (!ssh_opts.username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001695 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Radek Krejciac6d3472015-10-22 15:47:18 +02001696 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001697 ERR(session, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko1d430d92021-10-11 09:30:43 +02001698 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001699 } else {
1700 username = pw->pw_name;
1701 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001702 } else {
1703 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001704 }
1705
1706 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001707 session = nc_new_session(NC_CLIENT, 0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001708 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001709 ERRMEM;
Michal Vasko1d430d92021-10-11 09:30:43 +02001710 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001711 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001712 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001713
Michal Vasko131120a2018-05-29 15:44:02 +02001714 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001715 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001716 session->ti.libssh.session = ssh_new();
1717 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001718 ERR(session, "Unable to initialize SSH session.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001719 goto fail;
1720 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001721
Michal Vasko7b62fed2015-10-26 15:39:46 +01001722 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001723 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001724 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001725 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001726 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
1727
1728 /* create and assign communication socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001729 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001730 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001731 ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001732 goto fail;
1733 }
1734 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001735 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001736
Michal Vasko395900e2021-11-09 12:14:28 +01001737 /* store information for session connection */
1738 session->host = strdup(host);
1739 session->username = strdup(username);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001740 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1) ||
1741 (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001742 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001743 }
1744
Radek Krejcifd5b6682017-06-13 15:52:53 +02001745 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1746 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001747 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001748 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001749
Radek Krejciac6d3472015-10-22 15:47:18 +02001750 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001751 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001752 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001753 }
Michal Vaskoad611702015-12-03 13:41:51 +01001754 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001755
Michal Vaskoef578332016-01-25 13:20:09 +01001756 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001757 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001758 }
1759
Michal Vasko395900e2021-11-09 12:14:28 +01001760 /* update information */
1761 free(session->host);
1762 session->host = ip_host;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001763 session->port = port;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001764
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001765 free(buf);
Radek Krejciac6d3472015-10-22 15:47:18 +02001766 return session;
1767
Michal Vasko7b62fed2015-10-26 15:39:46 +01001768fail:
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001769 free(buf);
Michal Vasko66032bc2019-01-22 15:03:12 +01001770 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001771 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001772 return NULL;
1773}
1774
1775API struct nc_session *
1776nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1777{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001778 return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001779}
1780
1781API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001782nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001783{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001784 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001785
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001786 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001787 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001788 return NULL;
1789 }
1790
Michal Vasko7b62fed2015-10-26 15:39:46 +01001791 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001792 new_session = nc_new_session(NC_CLIENT, 1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001793 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001794 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001795 return NULL;
1796 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001797 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001798
Michal Vasko131120a2018-05-29 15:44:02 +02001799 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001800 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001801 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001802 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001803
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001804 /* append to the session ring list */
1805 if (!session->ti.libssh.next) {
1806 session->ti.libssh.next = new_session;
1807 new_session->ti.libssh.next = session;
1808 } else {
1809 ptr = session->ti.libssh.next;
1810 session->ti.libssh.next = new_session;
1811 new_session->ti.libssh.next = ptr;
1812 }
1813
Michal Vasko7b62fed2015-10-26 15:39:46 +01001814 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001815 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001816 goto fail;
1817 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001818 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001819 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001820 }
Michal Vasko131120a2018-05-29 15:44:02 +02001821 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001822
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001823 if (nc_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001824 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001825 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001826 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001827
Michal Vasko7b62fed2015-10-26 15:39:46 +01001828 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001829 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001830 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001831 }
Michal Vaskoad611702015-12-03 13:41:51 +01001832 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001833
Michal Vaskoef578332016-01-25 13:20:09 +01001834 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001835 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001836 }
1837
Michal Vasko395900e2021-11-09 12:14:28 +01001838 /* store information into session */
1839 new_session->host = strdup(session->host);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001840 new_session->port = session->port;
Michal Vasko395900e2021-11-09 12:14:28 +01001841 new_session->username = strdup(session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001842
Michal Vasko7b62fed2015-10-26 15:39:46 +01001843 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001844
1845fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001846 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001847 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001848}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001849
Michal Vasko3031aae2016-01-27 16:07:18 +01001850struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001851nc_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 +01001852{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001853 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001854 unsigned int uint_port;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001855 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001856 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001857 ssh_session sess;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001858 char *buf = NULL;
1859 size_t buf_len = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001860
Michal Vasko80cad7f2015-12-08 14:42:27 +01001861 sess = ssh_new();
1862 if (!sess) {
Michal Vasko05532772021-06-03 12:12:38 +02001863 ERR(NULL, "Unable to initialize an SSH session.");
Michal Vasko80cad7f2015-12-08 14:42:27 +01001864 close(sock);
1865 return NULL;
1866 }
1867
1868 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001869 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001870 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001871 uint_port = port;
1872 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001873 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001874 if (!ssh_ch_opts.username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001875 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Michal Vasko3031aae2016-01-27 16:07:18 +01001876 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001877 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko435e5cf2019-04-23 08:48:44 +02001878 ssh_free(sess);
Michal Vasko3031aae2016-01-27 16:07:18 +01001879 return NULL;
1880 }
1881 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001882 free(buf);
Michal Vasko3031aae2016-01-27 16:07:18 +01001883 } else {
1884 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001885 }
Michal Vasko8fd6fca2019-02-04 10:59:49 +01001886 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1887 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1888#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
1889 ssh_options_set(sess, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1890 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1891#endif
Michal Vasko80cad7f2015-12-08 14:42:27 +01001892
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001893 session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
Michal Vasko435e5cf2019-04-23 08:48:44 +02001894 if (!session) {
Michal Vasko457f0532019-08-15 13:59:49 +02001895 /* sess is freed */
Michal Vasko435e5cf2019-04-23 08:48:44 +02001896 return NULL;
Michal Vasko4282fae2016-02-18 10:03:42 +01001897 }
1898
Michal Vasko435e5cf2019-04-23 08:48:44 +02001899 session->flags |= NC_SESSION_CALLHOME;
Michal Vasko30e2c872016-02-18 10:03:21 +01001900 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001901}