blob: bd172e82fdd6278bcbd4f59f7ce724a8e12df05e [file] [log] [blame]
Radek Krejciac6d3472015-10-22 15:47:18 +02001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client_ssh.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @author Michal Vasko <mvasko@cesnet.cz>
5 * @brief libnetconf2 - SSH specific client session transport functions
Radek Krejciac6d3472015-10-22 15:47:18 +02006 *
7 * This source is compiled only with libssh.
8 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01009 * @copyright
Michal Vasko40899d82021-05-26 12:04:49 +020010 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Radek Krejciac6d3472015-10-22 15:47:18 +020011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * This source code is licensed under BSD 3-Clause License (the "License").
13 * You may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010015 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010016 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejciac6d3472015-10-22 15:47:18 +020017 */
18
Michal Vasko7b62fed2015-10-26 15:39:46 +010019#define _GNU_SOURCE
Michal Vasko7a20d2e2021-05-19 16:40:23 +020020
Radek Krejciac6d3472015-10-22 15:47:18 +020021#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010022#include <errno.h>
23#include <fcntl.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010024#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020025#include <pwd.h>
26#include <stddef.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <termios.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010033#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020034#include <unistd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020035
Michal Vasko7b62fed2015-10-26 15:39:46 +010036#ifdef ENABLE_DNSSEC
Michal Vasko7b62fed2015-10-26 15:39:46 +010037# include <validator/resolver.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020038# include <validator/validator.h>
Michal Vasko40899d82021-05-26 12:04:49 +020039# include <validator/validator-config.h>
40
Michal Vasko7b62fed2015-10-26 15:39:46 +010041# include <validator/validator-compat.h>
42#endif
43
Michal Vasko745ff832015-12-08 14:40:29 +010044#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020045#include <libyang/libyang.h>
46
Michal Vasko7a20d2e2021-05-19 16:40:23 +020047#include "compat.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020048#include "libnetconf.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010049#include "session_client.h"
50#include "session_client_ch.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020051
Radek Krejci62aa0642017-05-25 16:33:49 +020052struct nc_client_context *nc_client_context_location(void);
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{
Michal Vasko9d4cca52022-09-07 11:19:57 +02001148 return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +01001149}
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;
roman6ece9c52022-06-22 09:29:17 +02001175 struct timespec ts_timeout;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001176
1177 ssh_sess = session->ti.libssh.session;
1178
roman6ece9c52022-06-22 09:29:17 +02001179 nc_gettimespec_mono_add(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001180 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1181 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001182 if (nc_difftimespec_mono_cur(&ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001183 break;
1184 }
1185 }
1186 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001187 ERR(session, "SSH connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001188 return 0;
1189 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001190 ERR(session, "Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
1191 DBG(session, "Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001192 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001193 }
1194
Radek Krejci90a84a22017-05-25 13:53:00 +02001195 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko05532772021-06-03 12:12:38 +02001196 ERR(session, "Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001197 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001198 }
1199
Michal Vasko36c7be82017-02-22 13:37:59 +01001200 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001201 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001202 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001203 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1204 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001205 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001206 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001207 }
1208 }
1209 if (ret_auth == SSH_AUTH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001210 ERR(session, "Request authentication methods timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001211 return 0;
1212 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001213 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001214 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001215 } else if (ret_auth == SSH_AUTH_SUCCESS) {
Michal Vasko05532772021-06-03 12:12:38 +02001216 WRN(session, "Server accepts \"none\" authentication method.")
Radek Krejciae813f42018-07-02 13:38:30 +02001217 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001218 }
1219
1220 /* check what authentication methods are available */
1221 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001222
1223 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001224 if (opts->auth_pref[0].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001225 VRB(session, "Interactive SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001226 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001227 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001228 if (opts->auth_pref[1].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001229 VRB(session, "Password SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001230 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001231 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001232 if (opts->auth_pref[2].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001233 VRB(session, "Publickey SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001234 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001235 }
1236
Michal Vasko0190bc32016-03-02 15:47:49 +01001237 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001238 auth = 0;
1239 pref = 0;
1240 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1241 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001242 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001243 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001244 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001245 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001246 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001247 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001248 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001249 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001250 }
1251
Michal Vasko235efdc2015-12-17 12:05:04 +01001252 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001253 if (!attempt) {
Michal Vasko05532772021-06-03 12:12:38 +02001254 ERR(session, "Unable to authenticate to the remote server (no supported authentication methods detected).");
Radek Krejciae813f42018-07-02 13:38:30 +02001255 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001256 ERR(session, "Unable to authenticate to the remote server (all attempts via supported authentication "
1257 "methods failed).");
Radek Krejciae813f42018-07-02 13:38:30 +02001258 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001259 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001260 }
1261
1262 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001263 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001264 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001265 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1266
Michal Vasko05532772021-06-03 12:12:38 +02001267 VRB(session, "Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001268 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001269 if (s == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001270 ERR(session, "Unable to get the password.");
Michal Vasko88583042018-03-29 09:18:58 +02001271 return -1;
1272 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001273
Michal Vasko36c7be82017-02-22 13:37:59 +01001274 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001275 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001276 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001277 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1278 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001279 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001280 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001281 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001282 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001283 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001284 free(s);
1285 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001286
Michal Vasko7b62fed2015-10-26 15:39:46 +01001287 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001288 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1289
Michal Vasko05532772021-06-03 12:12:38 +02001290 VRB(session, "Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001291
Michal Vasko36c7be82017-02-22 13:37:59 +01001292 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001293 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001294 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001295 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) ||
1296 (ret_auth == SSH_AUTH_AGAIN)) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001297 if (ret_auth == SSH_AUTH_AGAIN) {
1298 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001299 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001300 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001301 }
1302 continue;
1303 }
1304
Michal Vasko7b62fed2015-10-26 15:39:46 +01001305 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1306 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001307 if (!prompt) {
1308 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001309 break;
1310 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001311
Michal Vasko30e2c872016-02-18 10:03:21 +01001312 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001313 ssh_userauth_kbdint_getinstruction(ssh_sess),
1314 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001315 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1316 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001317 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001318 break;
1319 }
1320 free(answer);
1321 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001322 if (ret_auth == SSH_AUTH_ERROR) {
1323 break;
1324 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001325 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001326 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001327 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001328 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001329 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001330
Michal Vasko206d3b12015-12-04 11:08:42 +01001331 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001332 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1333
Michal Vasko05532772021-06-03 12:12:38 +02001334 VRB(session, "Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001335
1336 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001337 if (!opts->key_count) {
Michal Vasko05532772021-06-03 12:12:38 +02001338 VRB(session, "No key pair specified.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001339 break;
1340 }
1341
Michal Vasko30e2c872016-02-18 10:03:21 +01001342 for (j = 0; j < opts->key_count; j++) {
Michal Vasko05532772021-06-03 12:12:38 +02001343 VRB(session, "Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001344 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1345 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001346
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001347 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1348 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001349 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001350 continue;
1351 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001352 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001353 continue;
1354 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001355
Michal Vasko36c7be82017-02-22 13:37:59 +01001356 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001357 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001358 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001359 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1360 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001361 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001362 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001363 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001364 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001365 ssh_key_free(pubkey);
1366
1367 if (ret_auth == SSH_AUTH_DENIED) {
1368 continue;
1369 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001370 break;
1371 }
1372
Michal Vasko30e2c872016-02-18 10:03:21 +01001373 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001374 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001375 } else {
1376 s = NULL;
1377 }
1378
Michal Vasko0190bc32016-03-02 15:47:49 +01001379 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001380 if (s) {
1381 memset(s, 0, strlen(s));
1382 free(s);
1383 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001384 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001385 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001386 continue;
1387 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001388 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001389 continue;
1390 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001391
Michal Vasko36c7be82017-02-22 13:37:59 +01001392 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001393 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001394 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001395 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1396 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001397 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001398 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001399 }
1400 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001401 ssh_key_free(privkey);
1402
Michal Vasko0190bc32016-03-02 15:47:49 +01001403 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001404 break;
1405 }
1406 }
1407 break;
1408 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001409
Michal Vasko0190bc32016-03-02 15:47:49 +01001410 switch (ret_auth) {
1411 case SSH_AUTH_AGAIN:
Michal Vasko05532772021-06-03 12:12:38 +02001412 ERR(session, "Authentication response timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001413 return 0;
1414 case SSH_AUTH_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001415 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001416 return -1;
1417 case SSH_AUTH_DENIED:
Michal Vasko05532772021-06-03 12:12:38 +02001418 WRN(session, "Authentication denied.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001419 break;
1420 case SSH_AUTH_PARTIAL:
Michal Vasko05532772021-06-03 12:12:38 +02001421 VRB(session, "Partial authentication success.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001422 break;
1423 case SSH_AUTH_SUCCESS:
Michal Vasko05532772021-06-03 12:12:38 +02001424 VRB(session, "Authentication successful.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001425 break;
1426 case SSH_AUTH_INFO:
1427 ERRINT;
1428 return -1;
1429 }
Radek Krejciae813f42018-07-02 13:38:30 +02001430
1431 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001432 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001433
Michal Vasko0190bc32016-03-02 15:47:49 +01001434 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001435}
1436
1437/* Open new SSH channel and request the 'netconf' subsystem.
1438 * SSH connection is expected to be established.
1439 */
1440static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001441open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001442{
1443 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001444 int ret;
roman6ece9c52022-06-22 09:29:17 +02001445 struct timespec ts_timeout;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001446
1447 ssh_sess = session->ti.libssh.session;
1448
1449 if (!ssh_is_connected(ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001450 ERR(session, "SSH session not connected.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001451 return -1;
1452 }
1453
1454 if (session->ti.libssh.channel) {
Michal Vasko05532772021-06-03 12:12:38 +02001455 ERR(session, "SSH channel already created.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001456 return -1;
1457 }
1458
Michal Vasko7b62fed2015-10-26 15:39:46 +01001459 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001460 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001461 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001462 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001463 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001464 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1465 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001466 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001467 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001468 }
1469 }
1470 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001471 ERR(session, "Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001472 ssh_channel_free(session->ti.libssh.channel);
1473 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001474 return 0;
1475 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001476 ERR(session, "Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001477 ssh_channel_free(session->ti.libssh.channel);
1478 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001479 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001480 }
1481
1482 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001483 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001484 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001485 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001486 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1487 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001488 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001489 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001490 }
1491 }
1492 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001493 ERR(session, "Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001494 ssh_channel_free(session->ti.libssh.channel);
1495 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001496 return 0;
1497 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001498 ERR(session, "Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001499 ssh_channel_free(session->ti.libssh.channel);
1500 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001501 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001502 }
1503
Michal Vasko0190bc32016-03-02 15:47:49 +01001504 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001505}
1506
Michal Vasko30e2c872016-02-18 10:03:21 +01001507static struct nc_session *
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001508_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
1509 struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001510{
Michal Vasko66032bc2019-01-22 15:03:12 +01001511 char *host = NULL, *username = NULL, *ip_host;
Dragos Dan8ce3b7c2021-03-09 09:17:22 +02001512 unsigned int port = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001513 int sock;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001514 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001515 struct nc_session *session = NULL;
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001516 char *buf = NULL;
1517 size_t buf_len = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001518
1519 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001520 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001521 return NULL;
1522 }
1523
1524 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001525 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001526 if (!session) {
1527 ERRMEM;
1528 return NULL;
1529 }
1530 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001531 session->ti_type = NC_TI_LIBSSH;
1532 session->ti.libssh.session = ssh_session;
1533
1534 /* was port set? */
Michal Vasko097fc852021-03-09 08:18:17 +01001535 ssh_options_get_port(ssh_session, &port);
Michal Vasko30e2c872016-02-18 10:03:21 +01001536
1537 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1538 /*
1539 * There is no file descriptor (detected based on the host, there is no way to check
1540 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1541 */
1542
1543 /* remember host */
1544 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001545 if (!host) {
1546 ERRMEM;
1547 goto fail;
1548 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001549 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1550
1551 /* create and connect socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001552 sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001553 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001554 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001555 goto fail;
1556 }
1557 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001558 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko66032bc2019-01-22 15:03:12 +01001559
1560 free(host);
1561 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001562 }
1563
1564 /* was username set? */
1565 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1566
1567 if (!ssh_is_connected(ssh_session)) {
1568 /*
1569 * We are connected, but not SSH authenticated. (Transport layer)
1570 */
1571
1572 /* remember username */
1573 if (!username) {
1574 if (!opts->username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001575 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Michal Vasko30e2c872016-02-18 10:03:21 +01001576 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001577 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001578 goto fail;
1579 }
1580 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001581 free(buf);
Michal Vasko30e2c872016-02-18 10:03:21 +01001582 } else {
1583 username = strdup(opts->username);
1584 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001585 if (!username) {
1586 ERRMEM;
1587 goto fail;
1588 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001589 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1590 }
1591
1592 /* connect and authenticate SSH session */
1593 session->host = host;
1594 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001595 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001596 goto fail;
1597 }
1598 }
1599
1600 /*
1601 * Almost done, open a netconf channel. (Transport layer / application layer)
1602 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001603 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001604 goto fail;
1605 }
1606
1607 /*
1608 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1609 */
1610
Michal Vasko78939072022-12-12 07:43:18 +01001611 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001612 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001613 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001614 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001615
1616 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001617 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001618 goto fail;
1619 }
1620 session->status = NC_STATUS_RUNNING;
1621
1622 if (nc_ctx_check_and_fill(session) == -1) {
1623 goto fail;
1624 }
1625
Michal Vasko93224072021-11-09 12:14:28 +01001626 /* store information if not previously */
1627 session->host = host;
1628 session->port = port;
1629 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001630
1631 return session;
1632
1633fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001634 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001635 return NULL;
1636}
1637
Radek Krejciac6d3472015-10-22 15:47:18 +02001638API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001639nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001640{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001641 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001642 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001643 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001644 char *username, *ip_host = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001645 struct passwd *pw, pw_buf;
Radek Krejciac6d3472015-10-22 15:47:18 +02001646 struct nc_session *session = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001647 char *buf = NULL;
1648 size_t buf_len = 0;
Radek Krejciac6d3472015-10-22 15:47:18 +02001649
1650 /* process parameters */
1651 if (!host || strisempty(host)) {
1652 host = "localhost";
1653 }
1654
1655 if (!port) {
1656 port = NC_PORT_SSH;
1657 }
Michal Vasko55fded62016-02-02 12:19:34 +01001658 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001659
Michal Vasko3031aae2016-01-27 16:07:18 +01001660 if (!ssh_opts.username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001661 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Radek Krejciac6d3472015-10-22 15:47:18 +02001662 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001663 ERR(session, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko1d430d92021-10-11 09:30:43 +02001664 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001665 } else {
1666 username = pw->pw_name;
1667 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001668 } else {
1669 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001670 }
1671
1672 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001673 session = nc_new_session(NC_CLIENT, 0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001674 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001675 ERRMEM;
Michal Vasko1d430d92021-10-11 09:30:43 +02001676 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001677 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001678 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001679
Michal Vasko131120a2018-05-29 15:44:02 +02001680 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001681 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001682 session->ti.libssh.session = ssh_new();
1683 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001684 ERR(session, "Unable to initialize SSH session.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001685 goto fail;
1686 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001687
Michal Vasko7b62fed2015-10-26 15:39:46 +01001688 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001689 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001690 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001691 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001692 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
1693
1694 /* create and assign communication socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001695 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001696 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001697 ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001698 goto fail;
1699 }
1700 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001701 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001702
Michal Vasko93224072021-11-09 12:14:28 +01001703 /* store information for session connection */
1704 session->host = strdup(host);
1705 session->username = strdup(username);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001706 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1) ||
1707 (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001708 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001709 }
1710
Michal Vasko78939072022-12-12 07:43:18 +01001711 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001712 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001713 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001714 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001715
Radek Krejciac6d3472015-10-22 15:47:18 +02001716 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001717 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001718 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001719 }
Michal Vaskoad611702015-12-03 13:41:51 +01001720 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001721
Michal Vaskoef578332016-01-25 13:20:09 +01001722 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001723 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001724 }
1725
Michal Vasko93224072021-11-09 12:14:28 +01001726 /* update information */
1727 free(session->host);
1728 session->host = ip_host;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001729 session->port = port;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001730
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001731 free(buf);
Radek Krejciac6d3472015-10-22 15:47:18 +02001732 return session;
1733
Michal Vasko7b62fed2015-10-26 15:39:46 +01001734fail:
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001735 free(buf);
Michal Vasko66032bc2019-01-22 15:03:12 +01001736 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001737 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001738 return NULL;
1739}
1740
1741API struct nc_session *
1742nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1743{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001744 return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001745}
1746
1747API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001748nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001749{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001750 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001751
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001752 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001753 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001754 return NULL;
1755 }
1756
Michal Vasko7b62fed2015-10-26 15:39:46 +01001757 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001758 new_session = nc_new_session(NC_CLIENT, 1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001759 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001760 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001761 return NULL;
1762 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001763 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001764
Michal Vasko131120a2018-05-29 15:44:02 +02001765 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001766 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001767 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001768 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001769
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001770 /* append to the session ring list */
1771 if (!session->ti.libssh.next) {
1772 session->ti.libssh.next = new_session;
1773 new_session->ti.libssh.next = session;
1774 } else {
1775 ptr = session->ti.libssh.next;
1776 session->ti.libssh.next = new_session;
1777 new_session->ti.libssh.next = ptr;
1778 }
1779
Michal Vasko7b62fed2015-10-26 15:39:46 +01001780 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001781 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001782 goto fail;
1783 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001784 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001785 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001786 }
Michal Vasko131120a2018-05-29 15:44:02 +02001787 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001788
Michal Vasko78939072022-12-12 07:43:18 +01001789 if (nc_client_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001790 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001791 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001792 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001793
Michal Vasko7b62fed2015-10-26 15:39:46 +01001794 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001795 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001796 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001797 }
Michal Vaskoad611702015-12-03 13:41:51 +01001798 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001799
Michal Vaskoef578332016-01-25 13:20:09 +01001800 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001801 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001802 }
1803
Michal Vasko93224072021-11-09 12:14:28 +01001804 /* store information into session */
1805 new_session->host = strdup(session->host);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001806 new_session->port = session->port;
Michal Vasko93224072021-11-09 12:14:28 +01001807 new_session->username = strdup(session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001808
Michal Vasko7b62fed2015-10-26 15:39:46 +01001809 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001810
1811fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001812 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001813 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001814}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001815
Michal Vasko3031aae2016-01-27 16:07:18 +01001816struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001817nc_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 +01001818{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001819 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001820 unsigned int uint_port;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001821 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001822 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001823 ssh_session sess;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001824 char *buf = NULL;
1825 size_t buf_len = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001826
Michal Vasko80cad7f2015-12-08 14:42:27 +01001827 sess = ssh_new();
1828 if (!sess) {
Michal Vasko05532772021-06-03 12:12:38 +02001829 ERR(NULL, "Unable to initialize an SSH session.");
Michal Vasko80cad7f2015-12-08 14:42:27 +01001830 close(sock);
1831 return NULL;
1832 }
1833
1834 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001835 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001836 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001837 uint_port = port;
1838 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001839 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001840 if (!ssh_ch_opts.username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001841 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Michal Vasko3031aae2016-01-27 16:07:18 +01001842 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001843 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko435e5cf2019-04-23 08:48:44 +02001844 ssh_free(sess);
Michal Vasko3031aae2016-01-27 16:07:18 +01001845 return NULL;
1846 }
1847 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001848 free(buf);
Michal Vasko3031aae2016-01-27 16:07:18 +01001849 } else {
1850 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001851 }
Michal Vasko8fd6fca2019-02-04 10:59:49 +01001852 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1853 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1854#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
1855 ssh_options_set(sess, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1856 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1857#endif
Michal Vasko80cad7f2015-12-08 14:42:27 +01001858
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001859 session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
Michal Vasko435e5cf2019-04-23 08:48:44 +02001860 if (!session) {
Michal Vasko457f0532019-08-15 13:59:49 +02001861 /* sess is freed */
Michal Vasko435e5cf2019-04-23 08:48:44 +02001862 return NULL;
Michal Vasko4282fae2016-02-18 10:03:42 +01001863 }
1864
Michal Vasko435e5cf2019-04-23 08:48:44 +02001865 session->flags |= NC_SESSION_CALLHOME;
Michal Vasko30e2c872016-02-18 10:03:21 +01001866 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001867}