blob: 500fdbe43e89095c348e49a3a89193d1d33b4d2a [file] [log] [blame]
Radek Krejciac6d3472015-10-22 15:47:18 +02001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client_ssh.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @author Michal Vasko <mvasko@cesnet.cz>
5 * @brief libnetconf2 - SSH specific client session transport functions
Radek Krejciac6d3472015-10-22 15:47:18 +02006 *
7 * This source is compiled only with libssh.
8 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01009 * @copyright
Michal Vasko40899d82021-05-26 12:04:49 +020010 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Radek Krejciac6d3472015-10-22 15:47:18 +020011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * This source code is licensed under BSD 3-Clause License (the "License").
13 * You may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010015 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010016 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejciac6d3472015-10-22 15:47:18 +020017 */
18
Michal Vasko7b62fed2015-10-26 15:39:46 +010019#define _GNU_SOURCE
Michal Vasko7a20d2e2021-05-19 16:40:23 +020020
Radek Krejciac6d3472015-10-22 15:47:18 +020021#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010022#include <errno.h>
23#include <fcntl.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010024#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020025#include <pwd.h>
26#include <stddef.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <termios.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010033#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020034#include <unistd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020035
Michal Vasko7b62fed2015-10-26 15:39:46 +010036#ifdef ENABLE_DNSSEC
Michal Vasko7b62fed2015-10-26 15:39:46 +010037# include <validator/resolver.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020038# include <validator/validator.h>
Michal Vasko40899d82021-05-26 12:04:49 +020039# include <validator/validator-config.h>
40
Michal Vasko7b62fed2015-10-26 15:39:46 +010041# include <validator/validator-compat.h>
42#endif
43
Michal Vasko745ff832015-12-08 14:40:29 +010044#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020045#include <libyang/libyang.h>
46
Michal Vasko7a20d2e2021-05-19 16:40:23 +020047#include "compat.h"
roman3f9b65c2023-06-05 14:26:58 +020048#include "config.h"
49#include "log_p.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010050#include "session_client.h"
51#include "session_client_ch.h"
roman3f9b65c2023-06-05 14:26:58 +020052#include "session_p.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020053
Radek Krejci62aa0642017-05-25 16:33:49 +020054struct nc_client_context *nc_client_context_location(void);
Michal Vasko30e2c872016-02-18 10:03:21 +010055
Radek Krejci62aa0642017-05-25 16:33:49 +020056#define client_opts nc_client_context_location()->opts
57#define ssh_opts nc_client_context_location()->ssh_opts
58#define ssh_ch_opts nc_client_context_location()->ssh_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010059
Michal Vaskoa43b8e32017-05-12 11:46:20 +020060static FILE *
61open_tty_noecho(const char *path, struct termios *oldterm)
62{
63 struct termios newterm;
64 FILE *ret;
65
66 if (!(ret = fopen(path, "r"))) {
Michal Vasko05532772021-06-03 12:12:38 +020067 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020068 return NULL;
69 }
70
71 if (tcgetattr(fileno(ret), oldterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020072 ERR(NULL, "Unable to get terminal \"%s\" settings (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020073 fclose(ret);
74 return NULL;
75 }
76
77 newterm = *oldterm;
78 newterm.c_lflag &= ~ECHO;
79 newterm.c_lflag &= ~ICANON;
80 tcflush(fileno(ret), TCIFLUSH);
81 if (tcsetattr(fileno(ret), TCSANOW, &newterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020082 ERR(NULL, "Unable to change terminal \"%s\" settings for hiding password (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020083 fclose(ret);
84 return NULL;
85 }
86
87 return ret;
88}
89
Michal Vasko51228ac2018-03-29 14:57:53 +020090static FILE *
91nc_open_in(int echo, struct termios *oldterm)
Michal Vaskoa43b8e32017-05-12 11:46:20 +020092{
Michal Vasko51228ac2018-03-29 14:57:53 +020093 char buf[512];
94 int buflen = 512, ret;
95 FILE *in;
96
97 if (!echo) {
98 in = open_tty_noecho("/dev/tty", oldterm);
99 } else {
100 in = fopen("/dev/tty", "r");
101 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200102 ERR(NULL, "Unable to open terminal \"/dev/tty\" for reading (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200103 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200104 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200105
106 if (!in) {
107 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200108 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200109 return NULL;
110 }
111
112 if (!echo) {
113 in = open_tty_noecho(buf, oldterm);
114 } else {
115 in = fopen(buf, "r");
116 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200117 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200118 }
119 }
120 }
121
122 return in;
123}
124
125static FILE *
126nc_open_out(void)
127{
128 char buf[512];
129 int buflen = 512, ret;
130 FILE *out;
131
132 out = fopen("/dev/tty", "w");
133 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200134 ERR(NULL, "Unable to open terminal \"/dev/tty\" for writing (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200135
136 if ((ret = ttyname_r(STDOUT_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200137 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200138 return NULL;
139 }
140
141 out = fopen(buf, "w");
142 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200143 ERR(NULL, "Unable to open terminal \"%s\" for writing (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200144 }
145 }
146
147 return out;
148}
149
150static void
151nc_close_inout(FILE *inout, int echo, struct termios *oldterm)
152{
153 if (inout) {
154 if (!echo && (tcsetattr(fileno(inout), TCSANOW, oldterm) != 0)) {
Michal Vasko05532772021-06-03 12:12:38 +0200155 ERR(NULL, "Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200156 }
157 fclose(inout);
158 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200159}
160
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400161void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100162_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +0100163{
164 int i;
165
Michal Vaskoe22c6732016-01-29 11:03:02 +0100166 for (i = 0; i < opts->key_count; ++i) {
167 free(opts->keys[i].pubkey_path);
168 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +0100169 }
Michal Vaskoe22c6732016-01-29 11:03:02 +0100170 free(opts->keys);
171 free(opts->username);
romanf6e32012023-04-24 15:51:26 +0200172 free(opts->knownhosts_path);
romanc1d2b092023-02-02 08:58:27 +0100173 opts->key_count = 0;
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200174 opts->keys = NULL;
175 opts->username = NULL;
romanf6e32012023-04-24 15:51:26 +0200176 opts->knownhosts_path = NULL;
Michal Vaskoe22c6732016-01-29 11:03:02 +0100177}
Michal Vasko990089e2015-10-27 15:05:25 +0100178
Michal Vaskob7558c52016-02-26 15:04:19 +0100179void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100180nc_client_ssh_destroy_opts(void)
181{
182 _nc_client_ssh_destroy_opts(&ssh_opts);
183 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +0100184}
185
Michal Vaskoef112d72016-02-18 13:28:25 +0100186#ifdef ENABLE_DNSSEC
187
188/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
189/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
190static int
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200191sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg)
192{
Michal Vaskoef112d72016-02-18 13:28:25 +0100193 ns_msg handle;
194 ns_rr rr;
195 val_status_t val_status;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200196 const unsigned char *rdata;
Michal Vaskoef112d72016-02-18 13:28:25 +0100197 unsigned char buf[4096];
198 int buf_len = 4096;
199 int ret = 0, i, j, len;
200
201 /* class 1 - internet, type 44 - SSHFP */
202 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
203
204 if ((len < 0) || !val_istrusted(val_status)) {
205 ret = 2;
206 goto finish;
207 }
208
209 if (ns_initparse(buf, len, &handle) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200210 ERR(NULL, "Failed to initialize DNSSEC response parser.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100211 ret = 2;
212 goto finish;
213 }
214
215 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
Michal Vasko05532772021-06-03 12:12:38 +0200216 ERR(NULL, "DNSSEC query returned %d.", i);
Michal Vaskoef112d72016-02-18 13:28:25 +0100217 ret = 2;
218 goto finish;
219 }
220
221 if (!libsres_msg_getflag(handle, ns_f_ad)) {
222 /* response not secured by DNSSEC */
223 ret = 1;
224 }
225
226 /* query section */
227 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
Michal Vasko05532772021-06-03 12:12:38 +0200228 ERR(NULL, "DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100229 ret = 2;
230 goto finish;
231 }
232
233 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200234 ERR(NULL, "DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100235 ret = 2;
236 goto finish;
237 }
238
239 /* answer section */
240 i = 0;
241 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
242 if (ns_rr_type(rr) != 44) {
243 ++i;
244 continue;
245 }
246
247 rdata = ns_rr_rdata(rr);
248 if (rdata[0] != type) {
249 ++i;
250 continue;
251 }
252 if (rdata[1] != alg) {
253 ++i;
254 continue;
255 }
256
257 /* we found the correct SSHFP entry */
258 rdata += 2;
259 for (j = 0; j < 20; ++j) {
260 if (rdata[j] != (unsigned char)sha1hash[j]) {
261 ret = 2;
262 goto finish;
263 }
264 }
265
266 /* server fingerprint is supported by a DNS entry,
267 * we have already determined if DNSSEC was used or not
268 */
269 goto finish;
270 }
271
272 /* no match */
273 ret = 2;
274
275finish:
276 val_free_validator_state();
277 return ret;
278}
279
280#endif /* ENABLE_DNSSEC */
281
romanf6e32012023-04-24 15:51:26 +0200282static int
283nc_client_ssh_update_known_hosts(ssh_session session, const char *hostname)
Michal Vaskoef112d72016-02-18 13:28:25 +0100284{
romanf6e32012023-04-24 15:51:26 +0200285 int ret;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200286
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800287#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
romanf6e32012023-04-24 15:51:26 +0200288 ret = ssh_session_update_known_hosts(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800289#else
romanf6e32012023-04-24 15:51:26 +0200290 ret = ssh_write_knownhost(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800291#endif
romanf6e32012023-04-24 15:51:26 +0200292
293 if (ret != SSH_OK) {
294 WRN(NULL, "Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
295 }
296
297 return ret;
298}
299
300static int
301nc_client_ssh_get_srv_pubkey_data(ssh_session session, enum ssh_keytypes_e *srv_pubkey_type, char **hexa, unsigned char **hash_sha1)
302{
303 int ret;
Michal Vaskoef112d72016-02-18 13:28:25 +0100304 ssh_key srv_pubkey;
Michal Vaskoef112d72016-02-18 13:28:25 +0100305 size_t hlen;
Michal Vaskoef112d72016-02-18 13:28:25 +0100306
romanf6e32012023-04-24 15:51:26 +0200307 *hexa = NULL;
308 *hash_sha1 = NULL;
Michal Vaskoef112d72016-02-18 13:28:25 +0100309
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800310#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 0))
311 ret = ssh_get_server_publickey(session, &srv_pubkey);
312#else
Michal Vaskocc0aa7d2016-05-31 12:48:42 +0200313 ret = ssh_get_publickey(session, &srv_pubkey);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800314#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100315 if (ret < 0) {
roman6e5fd702023-04-27 14:30:27 +0200316 ERR(NULL, "Unable to get server's public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100317 return -1;
318 }
319
romanf6e32012023-04-24 15:51:26 +0200320 *srv_pubkey_type = ssh_key_type(srv_pubkey);
321 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, hash_sha1, &hlen);
Michal Vaskoef112d72016-02-18 13:28:25 +0100322 ssh_key_free(srv_pubkey);
323 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200324 ERR(NULL, "Failed to calculate SHA1 hash of the server public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100325 return -1;
326 }
327
romanf6e32012023-04-24 15:51:26 +0200328 *hexa = ssh_get_hexa(*hash_sha1, hlen);
329 if (!*hexa) {
330 ERR(NULL, "Getting the hostkey's hex string failed.");
331 return -1;
332 }
333
334 return 0;
335}
336
337#ifdef ENABLE_DNSSEC
338static int
339nc_client_ssh_do_dnssec_sshfp_check(ssh_session session, enum ssh_keytypes_e srv_pubkey_type, const char *hostname, unsigned char *hash_sha1)
340{
341 int ret;
342
343 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) && (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
344 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
345 /* TODO else branch? */
346 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
347 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
348 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
349 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
350 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
351 }
352
353 /* DNSSEC SSHFP check successful, that's enough */
354 if (!ret) {
355 VRB(NULL, "DNSSEC SSHFP check successful.");
356#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
357 ssh_session_update_known_hosts(session);
358#else
359 ssh_write_knownhost(session);
360#endif
361 }
362
363 return ret;
364 }
365
366 return 1;
367}
368
369#endif
370
371static int
372nc_client_ssh_auth_hostkey_check(const char *hostname, uint16_t port, ssh_session session)
373{
374 char *hexa = NULL;
375 unsigned char *hash_sha1 = NULL;
376 NC_SSH_KNOWNHOSTS_MODE knownhosts_mode = ssh_opts.knownhosts_mode;
377 enum ssh_keytypes_e srv_pubkey_type;
378 char answer[5];
379 FILE *out = NULL, *in = NULL;
380 int c, state;
381
382#ifdef ENABLE_DNSSEC
383 int dnssec_ret;
384#endif
385
386 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_SKIP) {
387 /* skip all hostkey checks */
388 return 0;
389 }
390
391 if (nc_client_ssh_get_srv_pubkey_data(session, &srv_pubkey_type, &hexa, &hash_sha1)) {
392 goto error;
393 }
394
395#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
396 state = ssh_session_is_known_server(session);
397#else
398 state = ssh_is_server_known(session);
399#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100400
401 switch (state) {
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800402#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
403 case SSH_KNOWN_HOSTS_OK:
404#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100405 case SSH_SERVER_KNOWN_OK:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800406#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100407 break; /* ok */
408
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800409#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
410 case SSH_KNOWN_HOSTS_CHANGED:
411#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100412 case SSH_SERVER_KNOWN_CHANGED:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800413#endif
romanf6e32012023-04-24 15:51:26 +0200414 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT) {
415 /* is the mode is set to accept, then accept any connection even if the remote key changed */
416 WRN(NULL, "Remote host key changed, but you have requested accept mode so the connection will not be terminated.");
417 break;
418 } else {
419 ERR(NULL, "Remote host key changed, the connection will be terminated!");
420 goto error;
421 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100422
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800423#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
424 case SSH_KNOWN_HOSTS_OTHER:
425#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100426 case SSH_SERVER_FOUND_OTHER:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800427#endif
Michal Vasko05532772021-06-03 12:12:38 +0200428 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 +0100429 goto hostkey_not_known;
430
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800431#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
432 case SSH_KNOWN_HOSTS_NOT_FOUND:
433#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100434 case SSH_SERVER_FILE_NOT_FOUND:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800435#endif
Michal Vasko05532772021-06-03 12:12:38 +0200436 WRN(NULL, "Could not find the known hosts file.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100437 goto hostkey_not_known;
438
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800439#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
440 case SSH_KNOWN_HOSTS_UNKNOWN:
441#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100442 case SSH_SERVER_NOT_KNOWN:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800443#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100444hostkey_not_known:
445#ifdef ENABLE_DNSSEC
romanf6e32012023-04-24 15:51:26 +0200446 /* do dnssec check, if it's ok then we're done otherwise continue */
447 dnssec_ret = nc_client_ssh_do_dnssec_sshfp_check(session, srv_pubkey_type, hostname, hash_sha1);
448 if (!dnssec_ret) {
449 ssh_clean_pubkey_hash(&hash_sha1);
450 ssh_string_free_char(hexa);
451 return 0;
Michal Vaskoef112d72016-02-18 13:28:25 +0100452 }
453#endif
454
romanf6e32012023-04-24 15:51:26 +0200455 /* open the files for reading/writing */
Michal Vasko51228ac2018-03-29 14:57:53 +0200456 if (!(in = nc_open_in(1, NULL))) {
457 goto error;
458 }
romanf6e32012023-04-24 15:51:26 +0200459
Michal Vasko51228ac2018-03-29 14:57:53 +0200460 if (!(out = nc_open_out())) {
461 goto error;
462 }
463
romanf6e32012023-04-24 15:51:26 +0200464 if (knownhosts_mode == NC_SSH_KNOWNHOSTS_STRICT) {
465 /* do not connect if the hostkey is not present in known_hosts file in this mode */
466 ERR(NULL, "No %s host key is known for [%s]:%hu and you have requested strict checking.\n", ssh_key_type_to_char(srv_pubkey_type), hostname, port);
467 goto error;
468 } else if ((knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT_NEW) || (knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT)) {
469 /* add a new entry to the known_hosts file without prompting */
470 if (nc_client_ssh_update_known_hosts(session, hostname)) {
471 goto error;
472 }
473
474 VRB(NULL, "Permanently added '[%s]:%hu' (%s) to the list of known hosts.", hostname, port, ssh_key_type_to_char(srv_pubkey_type));
475
476 break;
477 }
478
Michal Vaskoef112d72016-02-18 13:28:25 +0100479 /* try to get result from user */
Michal Vasko51228ac2018-03-29 14:57:53 +0200480 if (fprintf(out, "The authenticity of the host \'%s\' cannot be established.\n", hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200481 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200482 goto error;
483 }
484 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 +0200485 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200486 goto error;
487 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100488
489#ifdef ENABLE_DNSSEC
romanf6e32012023-04-24 15:51:26 +0200490 if (dnssec_ret == 2) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200491 if (fprintf(out, "No matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200492 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200493 goto error;
494 }
romanf6e32012023-04-24 15:51:26 +0200495 } else if (dnssec_ret == 1) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200496 if (fprintf(out, "Matching host key fingerprint found using DNS.\n") < 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;
499 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100500 }
501#endif
502
Michal Vasko51228ac2018-03-29 14:57:53 +0200503 if (fprintf(out, "Are you sure you want to continue connecting (yes/no)? ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200504 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200505 goto error;
506 }
507 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100508
509 do {
Michal Vasko51228ac2018-03-29 14:57:53 +0200510 if (fscanf(in, "%4s", answer) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200511 ERR(NULL, "Reading from input failed (%s).", feof(in) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200512 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100513 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200514 while (((c = getc(in)) != EOF) && (c != '\n')) {}
Michal Vaskoef112d72016-02-18 13:28:25 +0100515
Michal Vasko51228ac2018-03-29 14:57:53 +0200516 fflush(in);
Michal Vaskoef112d72016-02-18 13:28:25 +0100517 if (!strcmp("yes", answer)) {
romanf6e32012023-04-24 15:51:26 +0200518 /* store the key into the known_hosts file */
519 nc_client_ssh_update_known_hosts(session, hostname);
Michal Vaskoef112d72016-02-18 13:28:25 +0100520 } else if (!strcmp("no", answer)) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200521 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100522 } else {
Michal Vasko51228ac2018-03-29 14:57:53 +0200523 if (fprintf(out, "Please type 'yes' or 'no': ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200524 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200525 goto error;
526 }
Michal Vasko5fb5f992020-11-26 15:19:31 +0100527 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100528 }
529 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
530
531 break;
532
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800533#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
534 case SSH_KNOWN_HOSTS_ERROR:
535#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100536 case SSH_SERVER_ERROR:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800537#endif
Michal Vasko05532772021-06-03 12:12:38 +0200538 ERR(NULL, "SSH error: %s", ssh_get_error(session));
Michal Vasko51228ac2018-03-29 14:57:53 +0200539 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100540 }
541
Michal Vasko51228ac2018-03-29 14:57:53 +0200542 nc_close_inout(in, 1, NULL);
543 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100544 ssh_clean_pubkey_hash(&hash_sha1);
545 ssh_string_free_char(hexa);
546 return 0;
547
Michal Vasko51228ac2018-03-29 14:57:53 +0200548error:
549 nc_close_inout(in, 1, NULL);
550 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100551 ssh_clean_pubkey_hash(&hash_sha1);
552 ssh_string_free_char(hexa);
553 return -1;
554}
555
Radek Krejci62aa0642017-05-25 16:33:49 +0200556char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200557sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200558{
Michal Vasko51228ac2018-03-29 14:57:53 +0200559 char *buf = NULL;
560 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200561 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200562 FILE *in = NULL, *out = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200563
Michal Vasko11d142a2016-01-19 15:58:24 +0100564 buf = malloc(buflen * sizeof *buf);
565 if (!buf) {
566 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100567 return NULL;
568 }
569
Michal Vasko51228ac2018-03-29 14:57:53 +0200570 if (!(in = nc_open_in(0, &oldterm))) {
571 goto error;
572 }
573 if (!(out = nc_open_out())) {
574 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200575 }
576
Michal Vasko51228ac2018-03-29 14:57:53 +0200577 if (fprintf(out, "%s@%s password: ", username, hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200578 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200579 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200580 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200581 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200582
583 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200584 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100585 if (len >= buflen - 1) {
586 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100587 buf = nc_realloc(buf, buflen * sizeof *buf);
588 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100589 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200590 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100591 }
592 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100593 buf[len++] = (char)c;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100594 }
595 buf[len++] = 0; /* terminating null byte */
596
Michal Vasko51228ac2018-03-29 14:57:53 +0200597 fprintf(out, "\n");
598
599 nc_close_inout(in, 0, &oldterm);
600 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100601 return buf;
Michal Vasko93f26d92018-02-01 09:08:35 +0100602
Michal Vasko51228ac2018-03-29 14:57:53 +0200603error:
604 nc_close_inout(in, 0, &oldterm);
605 nc_close_inout(out, 1, NULL);
Michal Vasko93f26d92018-02-01 09:08:35 +0100606 free(buf);
607 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100608}
609
Radek Krejci62aa0642017-05-25 16:33:49 +0200610char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200611sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100612{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200613 unsigned int buflen = 64, cur_len;
Michal Vasko51228ac2018-03-29 14:57:53 +0200614 int c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200615 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200616 char *buf = NULL;
617 FILE *in = NULL, *out = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100618
619 buf = malloc(buflen * sizeof *buf);
620 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100621 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100622 return NULL;
623 }
624
Michal Vasko51228ac2018-03-29 14:57:53 +0200625 if (!(in = nc_open_in(echo, &oldterm))) {
626 goto error;
627 }
628 if (!(out = nc_open_out())) {
629 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100630 }
631
Michal Vasko51228ac2018-03-29 14:57:53 +0200632 if (auth_name && (fprintf(out, "%s\n", auth_name) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200633 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200634 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100635 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200636 if (instruction && (fprintf(out, "%s\n", instruction) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200637 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200638 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100639 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200640 if (fputs(prompt, out) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200641 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200642 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200643 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200644 fflush(out);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100645
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200646 cur_len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200647 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200648 if (cur_len >= buflen - 1) {
649 buflen *= 2;
650 buf = nc_realloc(buf, buflen * sizeof *buf);
651 if (!buf) {
652 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200653 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200654 }
655 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100656 buf[cur_len++] = (char)c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200657 }
658 /* terminating null byte */
659 buf[cur_len] = '\0';
660
Michal Vasko51228ac2018-03-29 14:57:53 +0200661 fprintf(out, "\n");
662
663 nc_close_inout(in, echo, &oldterm);
664 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200665 return buf;
666
Michal Vasko51228ac2018-03-29 14:57:53 +0200667error:
668 nc_close_inout(in, echo, &oldterm);
669 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200670 free(buf);
671 return NULL;
672}
673
Radek Krejci62aa0642017-05-25 16:33:49 +0200674char *
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200675sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200676{
Michal Vasko51228ac2018-03-29 14:57:53 +0200677 char *buf = NULL;
678 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200679 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200680 FILE *in = NULL, *out = NULL;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200681
682 buf = malloc(buflen * sizeof *buf);
683 if (!buf) {
684 ERRMEM;
685 return NULL;
686 }
687
Michal Vasko51228ac2018-03-29 14:57:53 +0200688 if (!(in = nc_open_in(0, &oldterm))) {
689 goto error;
690 }
691 if (!(out = nc_open_out())) {
692 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200693 }
694
Michal Vasko51228ac2018-03-29 14:57:53 +0200695 if (fprintf(out, "Enter passphrase for the key '%s': ", privkey_path) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200696 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200697 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200698 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200699 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200700
701 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200702 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100703 if (len >= buflen - 1) {
704 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100705 buf = nc_realloc(buf, buflen * sizeof *buf);
706 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100707 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200708 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100709 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100710 }
711 buf[len++] = (char)c;
712 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200713 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100714
Michal Vasko51228ac2018-03-29 14:57:53 +0200715 fprintf(out, "\n");
716
717 nc_close_inout(in, 0, &oldterm);
718 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100719 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100720
Michal Vasko51228ac2018-03-29 14:57:53 +0200721error:
722 nc_close_inout(in, 0, &oldterm);
723 nc_close_inout(out, 1, NULL);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100724 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100725 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100726}
727
romanf6e32012023-04-24 15:51:26 +0200728API int
729nc_client_ssh_set_knownhosts_path(const char *path)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100730{
romanf6e32012023-04-24 15:51:26 +0200731 free(ssh_opts.knownhosts_path);
732
733 if (!path) {
734 ssh_opts.knownhosts_path = NULL;
735 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100736 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100737
romanf6e32012023-04-24 15:51:26 +0200738 ssh_opts.knownhosts_path = strdup(path);
739 if (!ssh_opts.knownhosts_path) {
740 ERRMEM;
741 return 1;
Radek Krejci90a84a22017-05-25 13:53:00 +0200742 }
romanf6e32012023-04-24 15:51:26 +0200743
744 return 0;
Radek Krejci90a84a22017-05-25 13:53:00 +0200745}
746
Radek Krejci90a84a22017-05-25 13:53:00 +0200747API void
romanf6e32012023-04-24 15:51:26 +0200748nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode)
Radek Krejci90a84a22017-05-25 13:53:00 +0200749{
romanf6e32012023-04-24 15:51:26 +0200750 ssh_opts.knownhosts_mode = mode;
Radek Krejci90a84a22017-05-25 13:53:00 +0200751}
Michal Vaskoef112d72016-02-18 13:28:25 +0100752
Michal Vasko30e2c872016-02-18 10:03:21 +0100753static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200754_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 +0200755 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100756{
757 if (auth_password) {
758 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200759 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100760 } else {
761 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200762 opts->auth_password_priv = NULL;
763 }
764}
765
766static void
767_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 +0200768 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200769{
770 if (auth_password) {
771 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
772 }
773 if (priv) {
774 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100775 }
776}
777
778API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200779nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200780 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100781{
Radek Krejci90a84a22017-05-25 13:53:00 +0200782 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100783}
784
785API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200786nc_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 +0200787 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100788{
Radek Krejci90a84a22017-05-25 13:53:00 +0200789 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
790}
791
792API void
793nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200794 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200795{
796 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
797}
798
799API void
800nc_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 +0200801 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200802{
803 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100804}
805
806static void
807_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200808 const char *prompt, int echo, void *priv),
809 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100810{
811 if (auth_interactive) {
812 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200813 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100814 } else {
815 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200816 opts->auth_interactive_priv = NULL;
817 }
818}
819
820static void
821_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200822 const char *prompt, int echo, void *priv),
823 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200824{
825 if (auth_interactive) {
826 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
827 }
828 if (priv) {
829 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100830 }
831}
832
833API void
834nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200835 const char *prompt, int echo, void *priv),
836 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100837{
Radek Krejci90a84a22017-05-25 13:53:00 +0200838 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100839}
840
841API void
842nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200843 const char *prompt, int echo, void *priv),
844 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100845{
Radek Krejci90a84a22017-05-25 13:53:00 +0200846 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
847}
848
849API void
850nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200851 const char *prompt, int echo, void *priv),
852 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200853{
854 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
855}
856
857API void
858nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200859 const char *prompt, int echo, void *priv),
860 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200861{
862 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100863}
864
865static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200866_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 +0200867 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100868{
869 if (auth_privkey_passphrase) {
870 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200871 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100872 } else {
873 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200874 opts->auth_privkey_passphrase_priv = NULL;
875 }
876}
877
878static void
879_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 +0200880 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200881{
882 if (auth_privkey_passphrase) {
883 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
884 }
885 if (priv) {
886 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100887 }
888}
889
890API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200891nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200892 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100893{
Radek Krejci90a84a22017-05-25 13:53:00 +0200894 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100895}
896
897API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200898nc_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 +0200899 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100900{
Radek Krejci90a84a22017-05-25 13:53:00 +0200901 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
902}
903
904API void
905nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200906 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200907{
908 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
909}
910
911API void
912nc_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 +0200913 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200914{
915 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100916}
917
Michal Vasko3031aae2016-01-27 16:07:18 +0100918static int
919_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 +0100920{
921 int i;
922 FILE *key;
923 char line[128];
924
roman40672412023-05-04 11:10:22 +0200925 NC_CHECK_ARG_RET(NULL, pub_key, priv_key, -1);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100926
Michal Vasko3031aae2016-01-27 16:07:18 +0100927 for (i = 0; i < opts->key_count; ++i) {
928 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
929 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200930 WRN(NULL, "Private key \"%s\" found with another public key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200931 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100932 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100933 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200934 WRN(NULL, "Public key \"%s\" found with another private key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200935 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100936 continue;
937 }
938
Michal Vasko05532772021-06-03 12:12:38 +0200939 ERR(NULL, "SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100940 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100941 }
942 }
943
Michal Vasko3031aae2016-01-27 16:07:18 +0100944 /* add the keys */
945 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100946 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
947 if (!opts->keys) {
948 ERRMEM;
949 return -1;
950 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100951 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
952 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
953 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100954
Michal Vasko4eb3c312016-03-01 14:09:37 +0100955 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
956 ERRMEM;
957 return -1;
958 }
959
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100960 /* check encryption */
961 if ((key = fopen(priv_key, "r"))) {
962 /* 1st line - key type */
963 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100964 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200965 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100966 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100967 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100968 /* 2nd line - encryption information or key */
969 if (!fgets(line, sizeof line, key)) {
970 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200971 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100972 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100973 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100974 fclose(key);
975 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100976 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100977 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100978 }
979
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100980 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100981}
982
983API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100984nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100985{
Michal Vasko3031aae2016-01-27 16:07:18 +0100986 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
987}
988
989API int
990nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
991{
992 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
993}
994
995static int
996_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
997{
998 if (idx >= opts->key_count) {
roman40672412023-05-04 11:10:22 +0200999 ERRARG(NULL, "idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001000 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001001 }
1002
Michal Vasko3031aae2016-01-27 16:07:18 +01001003 free(opts->keys[idx].pubkey_path);
1004 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001005
Michal Vasko3031aae2016-01-27 16:07:18 +01001006 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +01001007 if (idx < opts->key_count) {
1008 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
1009 }
1010 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +01001011 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
1012 if (!opts->keys) {
1013 ERRMEM;
1014 return -1;
1015 }
Michal Vaskoc0256492016-02-02 12:19:06 +01001016 } else {
1017 free(opts->keys);
1018 opts->keys = NULL;
1019 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001020
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001021 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001022}
1023
1024API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001025nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001026{
Michal Vasko3031aae2016-01-27 16:07:18 +01001027 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001028}
1029
1030API int
Michal Vasko3031aae2016-01-27 16:07:18 +01001031nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001032{
Michal Vasko3031aae2016-01-27 16:07:18 +01001033 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
1034}
1035
1036static int
1037_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
1038{
1039 return opts->key_count;
1040}
1041
1042API int
1043nc_client_ssh_get_keypair_count(void)
1044{
1045 return _nc_client_ssh_get_keypair_count(&ssh_opts);
1046}
1047
1048API int
1049nc_client_ssh_ch_get_keypair_count(void)
1050{
1051 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
1052}
1053
1054static int
1055_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
1056{
Michal Vasko45e53ae2016-04-07 11:46:03 +02001057 if (idx >= opts->key_count) {
roman40672412023-05-04 11:10:22 +02001058 ERRARG(NULL, "idx");
Michal Vasko45e53ae2016-04-07 11:46:03 +02001059 return -1;
1060 } else if (!pub_key && !priv_key) {
roman40672412023-05-04 11:10:22 +02001061 ERRARG(NULL, "pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001062 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001063 }
1064
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001065 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001066 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001067 }
1068 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001069 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001070 }
1071
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001072 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001073}
1074
Michal Vasko3031aae2016-01-27 16:07:18 +01001075API int
1076nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
1077{
1078 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
1079}
1080
1081API int
1082nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
1083{
1084 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
1085}
1086
1087static void
1088_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 +01001089{
1090 if (pref < 0) {
1091 pref = -1;
1092 }
1093
1094 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001095 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001096 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001097 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001098 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001099 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001100 }
1101}
1102
Michal Vasko3031aae2016-01-27 16:07:18 +01001103API void
1104nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001105{
Michal Vasko3031aae2016-01-27 16:07:18 +01001106 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
1107}
1108
1109API void
1110nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
1111{
1112 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
1113}
1114
1115static int16_t
1116_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
1117{
1118 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001119
1120 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001121 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001122 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001123 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001124 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001125 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001126 }
1127
1128 return pref;
1129}
1130
Michal Vasko3031aae2016-01-27 16:07:18 +01001131API int16_t
1132nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1133{
1134 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
1135}
1136
1137API int16_t
1138nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1139{
1140 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
1141}
1142
1143static int
1144_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
1145{
1146 if (opts->username) {
1147 free(opts->username);
1148 }
1149 if (username) {
1150 opts->username = strdup(username);
1151 if (!opts->username) {
1152 ERRMEM;
1153 return -1;
1154 }
1155 } else {
1156 opts->username = NULL;
1157 }
1158
1159 return 0;
1160}
1161
1162API int
1163nc_client_ssh_set_username(const char *username)
1164{
1165 return _nc_client_ssh_set_username(username, &ssh_opts);
1166}
1167
1168API int
1169nc_client_ssh_ch_set_username(const char *username)
1170{
1171 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1172}
1173
Michal Vaskoe22c6732016-01-29 11:03:02 +01001174static const char *
1175_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1176{
1177 return opts->username;
1178}
1179
1180API const char *
1181nc_client_ssh_get_username(void)
1182{
1183 return _nc_client_ssh_get_username(&ssh_opts);
1184}
1185
1186API const char *
1187nc_client_ssh_ch_get_username(void)
1188{
1189 return _nc_client_ssh_get_username(&ssh_ch_opts);
1190}
1191
Michal Vasko3031aae2016-01-27 16:07:18 +01001192API int
1193nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1194{
Michal Vasko9d4cca52022-09-07 11:19:57 +02001195 return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +01001196}
1197
1198API int
1199nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1200{
1201 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1202}
1203
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001204/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001205 * Host, port, username, and a connected socket is expected to be set.
Radek Krejciae813f42018-07-02 13:38:30 +02001206 *
1207 * return values
1208 * -1 failure
1209 * 0 try again
1210 * 1 success
Michal Vasko7b62fed2015-10-26 15:39:46 +01001211 */
1212static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001213connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001214{
Radek Krejciae813f42018-07-02 13:38:30 +02001215 int j, ret_auth, userauthlist, ret, attempt = 0;
Michal Vasko235efdc2015-12-17 12:05:04 +01001216 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001217 int16_t pref;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001218 const char *prompt;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001219 char *s, *answer, echo;
1220 ssh_key pubkey, privkey;
1221 ssh_session ssh_sess;
roman6ece9c52022-06-22 09:29:17 +02001222 struct timespec ts_timeout;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001223
1224 ssh_sess = session->ti.libssh.session;
1225
Michal Vaskod8a74192023-02-06 15:51:50 +01001226 nc_timeouttime_get(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001227 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1228 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001229 if (nc_timeouttime_cur_diff(&ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001230 break;
1231 }
1232 }
1233 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001234 ERR(session, "SSH connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001235 return 0;
1236 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001237 ERR(session, "Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
1238 DBG(session, "Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001239 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001240 }
1241
romanf6e32012023-04-24 15:51:26 +02001242 if (nc_client_ssh_auth_hostkey_check(session->host, session->port, ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001243 ERR(session, "Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001244 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001245 }
1246
Michal Vasko36c7be82017-02-22 13:37:59 +01001247 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001248 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001249 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001250 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1251 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001252 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001253 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001254 }
1255 }
1256 if (ret_auth == SSH_AUTH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001257 ERR(session, "Request authentication methods timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001258 return 0;
1259 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001260 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001261 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001262 } else if (ret_auth == SSH_AUTH_SUCCESS) {
Radek Krejciae813f42018-07-02 13:38:30 +02001263 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001264 }
1265
1266 /* check what authentication methods are available */
1267 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001268
1269 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001270 if (opts->auth_pref[0].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001271 VRB(session, "Interactive SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001272 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001273 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001274 if (opts->auth_pref[1].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001275 VRB(session, "Password SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001276 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001277 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001278 if (opts->auth_pref[2].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001279 VRB(session, "Publickey SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001280 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001281 }
1282
Michal Vasko0190bc32016-03-02 15:47:49 +01001283 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001284 auth = 0;
1285 pref = 0;
1286 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1287 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001288 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001289 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001290 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001291 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001292 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001293 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001294 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001295 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001296 }
1297
Michal Vasko235efdc2015-12-17 12:05:04 +01001298 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001299 if (!attempt) {
Michal Vasko05532772021-06-03 12:12:38 +02001300 ERR(session, "Unable to authenticate to the remote server (no supported authentication methods detected).");
Radek Krejciae813f42018-07-02 13:38:30 +02001301 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001302 ERR(session, "Unable to authenticate to the remote server (all attempts via supported authentication "
1303 "methods failed).");
Radek Krejciae813f42018-07-02 13:38:30 +02001304 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001305 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001306 }
1307
1308 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001309 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001310 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001311 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1312
Michal Vasko05532772021-06-03 12:12:38 +02001313 VRB(session, "Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001314 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001315 if (s == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001316 ERR(session, "Unable to get the password.");
Michal Vasko88583042018-03-29 09:18:58 +02001317 return -1;
1318 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001319
Michal Vasko36c7be82017-02-22 13:37:59 +01001320 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001321 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001322 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001323 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1324 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001325 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001326 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001327 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001328 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001329 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001330 free(s);
1331 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001332
Michal Vasko7b62fed2015-10-26 15:39:46 +01001333 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001334 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1335
Michal Vasko05532772021-06-03 12:12:38 +02001336 VRB(session, "Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001337
Michal Vasko36c7be82017-02-22 13:37:59 +01001338 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001339 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001340 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001341 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) ||
1342 (ret_auth == SSH_AUTH_AGAIN)) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001343 if (ret_auth == SSH_AUTH_AGAIN) {
1344 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001345 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001346 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001347 }
1348 continue;
1349 }
1350
Michal Vasko7b62fed2015-10-26 15:39:46 +01001351 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1352 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001353 if (!prompt) {
1354 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001355 break;
1356 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001357
Michal Vasko30e2c872016-02-18 10:03:21 +01001358 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001359 ssh_userauth_kbdint_getinstruction(ssh_sess),
1360 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001361 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1362 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001363 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001364 break;
1365 }
1366 free(answer);
1367 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001368 if (ret_auth == SSH_AUTH_ERROR) {
1369 break;
1370 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001371 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001372 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001373 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001374 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001375 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001376
Michal Vasko206d3b12015-12-04 11:08:42 +01001377 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001378 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1379
Michal Vasko05532772021-06-03 12:12:38 +02001380 VRB(session, "Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001381
1382 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001383 if (!opts->key_count) {
Michal Vasko05532772021-06-03 12:12:38 +02001384 VRB(session, "No key pair specified.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001385 break;
1386 }
1387
Michal Vasko30e2c872016-02-18 10:03:21 +01001388 for (j = 0; j < opts->key_count; j++) {
Michal Vasko05532772021-06-03 12:12:38 +02001389 VRB(session, "Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001390 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1391 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001392
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001393 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1394 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001395 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001396 continue;
1397 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001398 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001399 continue;
1400 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001401
Michal Vasko36c7be82017-02-22 13:37:59 +01001402 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001403 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001404 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001405 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1406 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001407 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001408 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001409 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001410 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001411 ssh_key_free(pubkey);
1412
1413 if (ret_auth == SSH_AUTH_DENIED) {
1414 continue;
1415 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001416 break;
1417 }
1418
Michal Vasko30e2c872016-02-18 10:03:21 +01001419 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001420 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001421 } else {
1422 s = NULL;
1423 }
1424
Michal Vasko0190bc32016-03-02 15:47:49 +01001425 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001426 if (s) {
1427 memset(s, 0, strlen(s));
1428 free(s);
1429 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001430 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001431 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001432 continue;
1433 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001434 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001435 continue;
1436 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001437
Michal Vasko36c7be82017-02-22 13:37:59 +01001438 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001439 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001440 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001441 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1442 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001443 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001444 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001445 }
1446 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001447 ssh_key_free(privkey);
1448
Michal Vasko0190bc32016-03-02 15:47:49 +01001449 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001450 break;
1451 }
1452 }
1453 break;
1454 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001455
Michal Vasko0190bc32016-03-02 15:47:49 +01001456 switch (ret_auth) {
1457 case SSH_AUTH_AGAIN:
Michal Vasko05532772021-06-03 12:12:38 +02001458 ERR(session, "Authentication response timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001459 return 0;
1460 case SSH_AUTH_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001461 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001462 return -1;
1463 case SSH_AUTH_DENIED:
Michal Vasko05532772021-06-03 12:12:38 +02001464 WRN(session, "Authentication denied.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001465 break;
1466 case SSH_AUTH_PARTIAL:
Michal Vasko05532772021-06-03 12:12:38 +02001467 VRB(session, "Partial authentication success.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001468 break;
1469 case SSH_AUTH_SUCCESS:
Michal Vasko05532772021-06-03 12:12:38 +02001470 VRB(session, "Authentication successful.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001471 break;
1472 case SSH_AUTH_INFO:
1473 ERRINT;
1474 return -1;
1475 }
Radek Krejciae813f42018-07-02 13:38:30 +02001476
1477 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001478 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001479
Michal Vasko0190bc32016-03-02 15:47:49 +01001480 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001481}
1482
1483/* Open new SSH channel and request the 'netconf' subsystem.
1484 * SSH connection is expected to be established.
1485 */
1486static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001487open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001488{
1489 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001490 int ret;
roman6ece9c52022-06-22 09:29:17 +02001491 struct timespec ts_timeout;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001492
1493 ssh_sess = session->ti.libssh.session;
1494
1495 if (!ssh_is_connected(ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001496 ERR(session, "SSH session not connected.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001497 return -1;
1498 }
1499
1500 if (session->ti.libssh.channel) {
Michal Vasko05532772021-06-03 12:12:38 +02001501 ERR(session, "SSH channel already created.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001502 return -1;
1503 }
1504
Michal Vasko7b62fed2015-10-26 15:39:46 +01001505 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001506 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001507 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001508 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001509 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001510 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1511 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001512 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001513 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001514 }
1515 }
1516 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001517 ERR(session, "Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001518 ssh_channel_free(session->ti.libssh.channel);
1519 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001520 return 0;
1521 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001522 ERR(session, "Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001523 ssh_channel_free(session->ti.libssh.channel);
1524 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001525 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001526 }
1527
1528 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001529 if (timeout > -1) {
Michal Vaskod8a74192023-02-06 15:51:50 +01001530 nc_timeouttime_get(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001531 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001532 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1533 usleep(NC_TIMEOUT_STEP);
Michal Vaskod8a74192023-02-06 15:51:50 +01001534 if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001535 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001536 }
1537 }
1538 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001539 ERR(session, "Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001540 ssh_channel_free(session->ti.libssh.channel);
1541 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001542 return 0;
1543 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001544 ERR(session, "Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001545 ssh_channel_free(session->ti.libssh.channel);
1546 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001547 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001548 }
1549
Michal Vasko0190bc32016-03-02 15:47:49 +01001550 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001551}
1552
Michal Vasko30e2c872016-02-18 10:03:21 +01001553static struct nc_session *
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001554_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
1555 struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001556{
Michal Vasko66032bc2019-01-22 15:03:12 +01001557 char *host = NULL, *username = NULL, *ip_host;
Dragos Dan8ce3b7c2021-03-09 09:17:22 +02001558 unsigned int port = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001559 int sock;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001560 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001561 struct nc_session *session = NULL;
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001562 char *buf = NULL;
1563 size_t buf_len = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001564
roman40672412023-05-04 11:10:22 +02001565 NC_CHECK_ARG_RET(NULL, ssh_session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001566
1567 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001568 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001569 if (!session) {
1570 ERRMEM;
1571 return NULL;
1572 }
1573 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001574 session->ti_type = NC_TI_LIBSSH;
1575 session->ti.libssh.session = ssh_session;
1576
1577 /* was port set? */
Michal Vasko097fc852021-03-09 08:18:17 +01001578 ssh_options_get_port(ssh_session, &port);
Michal Vasko30e2c872016-02-18 10:03:21 +01001579
1580 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1581 /*
1582 * There is no file descriptor (detected based on the host, there is no way to check
1583 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1584 */
1585
1586 /* remember host */
1587 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001588 if (!host) {
1589 ERRMEM;
1590 goto fail;
1591 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001592 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1593
1594 /* create and connect socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001595 sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001596 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001597 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001598 goto fail;
1599 }
1600 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001601 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko66032bc2019-01-22 15:03:12 +01001602
1603 free(host);
1604 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001605 }
1606
1607 /* was username set? */
1608 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1609
1610 if (!ssh_is_connected(ssh_session)) {
1611 /*
1612 * We are connected, but not SSH authenticated. (Transport layer)
1613 */
1614
1615 /* remember username */
1616 if (!username) {
1617 if (!opts->username) {
romanf6e32012023-04-24 15:51:26 +02001618 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Michal Vasko30e2c872016-02-18 10:03:21 +01001619 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001620 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001621 goto fail;
1622 }
1623 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001624 free(buf);
Michal Vasko30e2c872016-02-18 10:03:21 +01001625 } else {
1626 username = strdup(opts->username);
1627 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001628 if (!username) {
1629 ERRMEM;
1630 goto fail;
1631 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001632 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1633 }
1634
1635 /* connect and authenticate SSH session */
1636 session->host = host;
1637 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001638 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001639 goto fail;
1640 }
1641 }
1642
1643 /*
1644 * Almost done, open a netconf channel. (Transport layer / application layer)
1645 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001646 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001647 goto fail;
1648 }
1649
1650 /*
1651 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1652 */
1653
Michal Vasko78939072022-12-12 07:43:18 +01001654 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001655 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001656 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001657 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001658
1659 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001660 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001661 goto fail;
1662 }
1663 session->status = NC_STATUS_RUNNING;
1664
1665 if (nc_ctx_check_and_fill(session) == -1) {
1666 goto fail;
1667 }
1668
Michal Vasko93224072021-11-09 12:14:28 +01001669 /* store information if not previously */
1670 session->host = host;
1671 session->port = port;
1672 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001673
1674 return session;
1675
1676fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001677 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001678 return NULL;
1679}
1680
Radek Krejciac6d3472015-10-22 15:47:18 +02001681API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001682nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001683{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001684 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001685 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001686 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001687 char *username, *ip_host = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001688 struct passwd *pw, pw_buf;
Radek Krejciac6d3472015-10-22 15:47:18 +02001689 struct nc_session *session = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001690 char *buf = NULL;
1691 size_t buf_len = 0;
romanf6e32012023-04-24 15:51:26 +02001692 char *known_hosts_path = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001693
1694 /* process parameters */
roman3f9b65c2023-06-05 14:26:58 +02001695 if (!host || (host[0] == '\0')) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001696 host = "localhost";
1697 }
1698
1699 if (!port) {
1700 port = NC_PORT_SSH;
1701 }
Michal Vasko55fded62016-02-02 12:19:34 +01001702 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001703
Michal Vasko3031aae2016-01-27 16:07:18 +01001704 if (!ssh_opts.username) {
romanf6e32012023-04-24 15:51:26 +02001705 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Radek Krejciac6d3472015-10-22 15:47:18 +02001706 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001707 ERR(session, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko1d430d92021-10-11 09:30:43 +02001708 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001709 } else {
1710 username = pw->pw_name;
1711 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001712 } else {
1713 username = ssh_opts.username;
romanf6e32012023-04-24 15:51:26 +02001714
1715 pw = nc_getpw(0, username, &pw_buf, &buf, &buf_len);
1716 }
1717
1718 if (ssh_opts.knownhosts_path) {
1719 /* known_hosts file path was set so use it */
1720 known_hosts_path = strdup(ssh_opts.knownhosts_path);
1721 if (!known_hosts_path) {
1722 ERRMEM;
1723 goto fail;
1724 }
1725 } else if (pw) {
1726 /* path not set explicitly, but current user's username found in /etc/passwd, so create the path */
1727 if (asprintf(&known_hosts_path, "%s/.ssh/known_hosts", pw->pw_dir) == -1) {
1728 ERRMEM;
1729 goto fail;
1730 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001731 }
1732
1733 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001734 session = nc_new_session(NC_CLIENT, 0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001735 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001736 ERRMEM;
Michal Vasko1d430d92021-10-11 09:30:43 +02001737 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001738 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001739 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001740
Michal Vasko131120a2018-05-29 15:44:02 +02001741 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001742 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001743 session->ti.libssh.session = ssh_new();
1744 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001745 ERR(session, "Unable to initialize SSH session.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001746 goto fail;
1747 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001748
Michal Vasko7b62fed2015-10-26 15:39:46 +01001749 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001750 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001751 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001752 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001753 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
romanf6e32012023-04-24 15:51:26 +02001754 if (known_hosts_path) {
1755 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_path);
1756 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001757
1758 /* create and assign communication socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001759 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001760 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001761 ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001762 goto fail;
1763 }
1764 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001765 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001766
Michal Vasko93224072021-11-09 12:14:28 +01001767 /* store information for session connection */
1768 session->host = strdup(host);
1769 session->username = strdup(username);
romanf6e32012023-04-24 15:51:26 +02001770 session->port = port;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001771 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1) ||
1772 (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001773 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001774 }
1775
Michal Vasko78939072022-12-12 07:43:18 +01001776 if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001777 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001778 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001779 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001780
Radek Krejciac6d3472015-10-22 15:47:18 +02001781 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001782 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001783 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001784 }
Michal Vaskoad611702015-12-03 13:41:51 +01001785 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001786
Michal Vaskoef578332016-01-25 13:20:09 +01001787 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001788 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001789 }
1790
Michal Vasko93224072021-11-09 12:14:28 +01001791 /* update information */
1792 free(session->host);
1793 session->host = ip_host;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001794 session->port = port;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001795
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001796 free(buf);
romanf6e32012023-04-24 15:51:26 +02001797 free(known_hosts_path);
Radek Krejciac6d3472015-10-22 15:47:18 +02001798 return session;
1799
Michal Vasko7b62fed2015-10-26 15:39:46 +01001800fail:
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001801 free(buf);
romanf6e32012023-04-24 15:51:26 +02001802 free(known_hosts_path);
Michal Vasko66032bc2019-01-22 15:03:12 +01001803 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001804 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001805 return NULL;
1806}
1807
1808API struct nc_session *
1809nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1810{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001811 return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001812}
1813
1814API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001815nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001816{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001817 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001818
roman40672412023-05-04 11:10:22 +02001819 NC_CHECK_ARG_RET(session, session, NULL);
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001820
Michal Vasko7b62fed2015-10-26 15:39:46 +01001821 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001822 new_session = nc_new_session(NC_CLIENT, 1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001823 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001824 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001825 return NULL;
1826 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001827 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001828
Michal Vasko131120a2018-05-29 15:44:02 +02001829 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001830 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001831 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001832 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001833
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001834 /* append to the session ring list */
1835 if (!session->ti.libssh.next) {
1836 session->ti.libssh.next = new_session;
1837 new_session->ti.libssh.next = session;
1838 } else {
1839 ptr = session->ti.libssh.next;
1840 session->ti.libssh.next = new_session;
1841 new_session->ti.libssh.next = ptr;
1842 }
1843
Michal Vasko7b62fed2015-10-26 15:39:46 +01001844 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001845 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001846 goto fail;
1847 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001848 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001849 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001850 }
Michal Vasko131120a2018-05-29 15:44:02 +02001851 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001852
Michal Vasko78939072022-12-12 07:43:18 +01001853 if (nc_client_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001854 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001855 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001856 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001857
Michal Vasko7b62fed2015-10-26 15:39:46 +01001858 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001859 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001860 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001861 }
Michal Vaskoad611702015-12-03 13:41:51 +01001862 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001863
Michal Vaskoef578332016-01-25 13:20:09 +01001864 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001865 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001866 }
1867
Michal Vasko93224072021-11-09 12:14:28 +01001868 /* store information into session */
1869 new_session->host = strdup(session->host);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001870 new_session->port = session->port;
Michal Vasko93224072021-11-09 12:14:28 +01001871 new_session->username = strdup(session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001872
Michal Vasko7b62fed2015-10-26 15:39:46 +01001873 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001874
1875fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001876 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001877 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001878}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001879
Michal Vasko3031aae2016-01-27 16:07:18 +01001880struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001881nc_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 +01001882{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001883 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001884 unsigned int uint_port;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001885 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001886 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001887 ssh_session sess;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001888 char *buf = NULL;
1889 size_t buf_len = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001890
Michal Vasko80cad7f2015-12-08 14:42:27 +01001891 sess = ssh_new();
1892 if (!sess) {
Michal Vasko05532772021-06-03 12:12:38 +02001893 ERR(NULL, "Unable to initialize an SSH session.");
Michal Vasko80cad7f2015-12-08 14:42:27 +01001894 close(sock);
1895 return NULL;
1896 }
1897
1898 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001899 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001900 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001901 uint_port = port;
1902 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001903 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001904 if (!ssh_ch_opts.username) {
romanf6e32012023-04-24 15:51:26 +02001905 pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len);
Michal Vasko3031aae2016-01-27 16:07:18 +01001906 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001907 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko435e5cf2019-04-23 08:48:44 +02001908 ssh_free(sess);
Michal Vasko3031aae2016-01-27 16:07:18 +01001909 return NULL;
1910 }
1911 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001912 free(buf);
Michal Vasko3031aae2016-01-27 16:07:18 +01001913 } else {
1914 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001915 }
Michal Vasko8fd6fca2019-02-04 10:59:49 +01001916 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1917 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1918#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
1919 ssh_options_set(sess, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1920 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1921#endif
Michal Vasko80cad7f2015-12-08 14:42:27 +01001922
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001923 session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
Michal Vasko435e5cf2019-04-23 08:48:44 +02001924 if (!session) {
Michal Vasko457f0532019-08-15 13:59:49 +02001925 /* sess is freed */
Michal Vasko435e5cf2019-04-23 08:48:44 +02001926 return NULL;
Michal Vasko4282fae2016-02-18 10:03:42 +01001927 }
1928
Michal Vasko435e5cf2019-04-23 08:48:44 +02001929 session->flags |= NC_SESSION_CALLHOME;
Michal Vasko30e2c872016-02-18 10:03:21 +01001930 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001931}