blob: de0c1dfeb1409093ff85e1ceb83633067069d801 [file] [log] [blame]
Radek Krejciac6d3472015-10-22 15:47:18 +02001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_client_ssh.c
3 * @author Radek Krejci <rkrejci@cesnet.cz>
4 * @author Michal Vasko <mvasko@cesnet.cz>
5 * @brief libnetconf2 - SSH specific client session transport functions
Radek Krejciac6d3472015-10-22 15:47:18 +02006 *
7 * This source is compiled only with libssh.
8 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01009 * @copyright
Michal Vasko40899d82021-05-26 12:04:49 +020010 * Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
Radek Krejciac6d3472015-10-22 15:47:18 +020011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * This source code is licensed under BSD 3-Clause License (the "License").
13 * You may not use this file except in compliance with the License.
14 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010015 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010016 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejciac6d3472015-10-22 15:47:18 +020017 */
18
Michal Vasko7b62fed2015-10-26 15:39:46 +010019#define _GNU_SOURCE
Michal Vasko7a20d2e2021-05-19 16:40:23 +020020
Radek Krejciac6d3472015-10-22 15:47:18 +020021#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010022#include <errno.h>
23#include <fcntl.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010024#include <pthread.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020025#include <pwd.h>
26#include <stddef.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/stat.h>
31#include <sys/types.h>
32#include <termios.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010033#include <time.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020034#include <unistd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020035
Michal Vasko7b62fed2015-10-26 15:39:46 +010036#ifdef ENABLE_DNSSEC
Michal Vasko7b62fed2015-10-26 15:39:46 +010037# include <validator/resolver.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020038# include <validator/validator.h>
Michal Vasko40899d82021-05-26 12:04:49 +020039# include <validator/validator-config.h>
40
Michal Vasko7b62fed2015-10-26 15:39:46 +010041# include <validator/validator-compat.h>
42#endif
43
Michal Vasko745ff832015-12-08 14:40:29 +010044#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020045#include <libyang/libyang.h>
46
Michal Vasko7a20d2e2021-05-19 16:40:23 +020047#include "compat.h"
Michal Vaskob83a3fa2021-05-26 09:53:42 +020048#include "libnetconf.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010049#include "session_client.h"
50#include "session_client_ch.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020051
Radek Krejci62aa0642017-05-25 16:33:49 +020052struct nc_client_context *nc_client_context_location(void);
Radek Krejcifd5b6682017-06-13 15:52:53 +020053int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx);
Michal Vasko30e2c872016-02-18 10:03:21 +010054
Radek Krejci62aa0642017-05-25 16:33:49 +020055#define client_opts nc_client_context_location()->opts
56#define ssh_opts nc_client_context_location()->ssh_opts
57#define ssh_ch_opts nc_client_context_location()->ssh_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010058
Michal Vaskoa43b8e32017-05-12 11:46:20 +020059static FILE *
60open_tty_noecho(const char *path, struct termios *oldterm)
61{
62 struct termios newterm;
63 FILE *ret;
64
65 if (!(ret = fopen(path, "r"))) {
Michal Vasko05532772021-06-03 12:12:38 +020066 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020067 return NULL;
68 }
69
70 if (tcgetattr(fileno(ret), oldterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020071 ERR(NULL, "Unable to get terminal \"%s\" settings (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020072 fclose(ret);
73 return NULL;
74 }
75
76 newterm = *oldterm;
77 newterm.c_lflag &= ~ECHO;
78 newterm.c_lflag &= ~ICANON;
79 tcflush(fileno(ret), TCIFLUSH);
80 if (tcsetattr(fileno(ret), TCSANOW, &newterm)) {
Michal Vasko05532772021-06-03 12:12:38 +020081 ERR(NULL, "Unable to change terminal \"%s\" settings for hiding password (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020082 fclose(ret);
83 return NULL;
84 }
85
86 return ret;
87}
88
Michal Vasko51228ac2018-03-29 14:57:53 +020089static FILE *
90nc_open_in(int echo, struct termios *oldterm)
Michal Vaskoa43b8e32017-05-12 11:46:20 +020091{
Michal Vasko51228ac2018-03-29 14:57:53 +020092 char buf[512];
93 int buflen = 512, ret;
94 FILE *in;
95
96 if (!echo) {
97 in = open_tty_noecho("/dev/tty", oldterm);
98 } else {
99 in = fopen("/dev/tty", "r");
100 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200101 ERR(NULL, "Unable to open terminal \"/dev/tty\" for reading (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200102 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200103 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200104
105 if (!in) {
106 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200107 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200108 return NULL;
109 }
110
111 if (!echo) {
112 in = open_tty_noecho(buf, oldterm);
113 } else {
114 in = fopen(buf, "r");
115 if (!in) {
Michal Vasko05532772021-06-03 12:12:38 +0200116 ERR(NULL, "Unable to open terminal \"%s\" for reading (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200117 }
118 }
119 }
120
121 return in;
122}
123
124static FILE *
125nc_open_out(void)
126{
127 char buf[512];
128 int buflen = 512, ret;
129 FILE *out;
130
131 out = fopen("/dev/tty", "w");
132 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200133 ERR(NULL, "Unable to open terminal \"/dev/tty\" for writing (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200134
135 if ((ret = ttyname_r(STDOUT_FILENO, buf, buflen))) {
Michal Vasko05532772021-06-03 12:12:38 +0200136 ERR(NULL, "ttyname_r failed (%s).", strerror(ret));
Michal Vasko51228ac2018-03-29 14:57:53 +0200137 return NULL;
138 }
139
140 out = fopen(buf, "w");
141 if (!out) {
Michal Vasko05532772021-06-03 12:12:38 +0200142 ERR(NULL, "Unable to open terminal \"%s\" for writing (%s).", buf, strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200143 }
144 }
145
146 return out;
147}
148
149static void
150nc_close_inout(FILE *inout, int echo, struct termios *oldterm)
151{
152 if (inout) {
153 if (!echo && (tcsetattr(fileno(inout), TCSANOW, oldterm) != 0)) {
Michal Vasko05532772021-06-03 12:12:38 +0200154 ERR(NULL, "Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200155 }
156 fclose(inout);
157 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200158}
159
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400160void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100161_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +0100162{
163 int i;
164
Michal Vaskoe22c6732016-01-29 11:03:02 +0100165 for (i = 0; i < opts->key_count; ++i) {
166 free(opts->keys[i].pubkey_path);
167 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +0100168 }
Michal Vaskoe22c6732016-01-29 11:03:02 +0100169 free(opts->keys);
170 free(opts->username);
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200171 opts->keys = NULL;
172 opts->username = NULL;
Michal Vaskoe22c6732016-01-29 11:03:02 +0100173}
Michal Vasko990089e2015-10-27 15:05:25 +0100174
Michal Vaskob7558c52016-02-26 15:04:19 +0100175void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100176nc_client_ssh_destroy_opts(void)
177{
178 _nc_client_ssh_destroy_opts(&ssh_opts);
179 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +0100180}
181
Michal Vaskoef112d72016-02-18 13:28:25 +0100182#ifdef ENABLE_DNSSEC
183
184/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
185/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
186static int
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200187sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg)
188{
Michal Vaskoef112d72016-02-18 13:28:25 +0100189 ns_msg handle;
190 ns_rr rr;
191 val_status_t val_status;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200192 const unsigned char *rdata;
Michal Vaskoef112d72016-02-18 13:28:25 +0100193 unsigned char buf[4096];
194 int buf_len = 4096;
195 int ret = 0, i, j, len;
196
197 /* class 1 - internet, type 44 - SSHFP */
198 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
199
200 if ((len < 0) || !val_istrusted(val_status)) {
201 ret = 2;
202 goto finish;
203 }
204
205 if (ns_initparse(buf, len, &handle) < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200206 ERR(NULL, "Failed to initialize DNSSEC response parser.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100207 ret = 2;
208 goto finish;
209 }
210
211 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
Michal Vasko05532772021-06-03 12:12:38 +0200212 ERR(NULL, "DNSSEC query returned %d.", i);
Michal Vaskoef112d72016-02-18 13:28:25 +0100213 ret = 2;
214 goto finish;
215 }
216
217 if (!libsres_msg_getflag(handle, ns_f_ad)) {
218 /* response not secured by DNSSEC */
219 ret = 1;
220 }
221
222 /* query section */
223 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
Michal Vasko05532772021-06-03 12:12:38 +0200224 ERR(NULL, "DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100225 ret = 2;
226 goto finish;
227 }
228
229 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200230 ERR(NULL, "DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100231 ret = 2;
232 goto finish;
233 }
234
235 /* answer section */
236 i = 0;
237 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
238 if (ns_rr_type(rr) != 44) {
239 ++i;
240 continue;
241 }
242
243 rdata = ns_rr_rdata(rr);
244 if (rdata[0] != type) {
245 ++i;
246 continue;
247 }
248 if (rdata[1] != alg) {
249 ++i;
250 continue;
251 }
252
253 /* we found the correct SSHFP entry */
254 rdata += 2;
255 for (j = 0; j < 20; ++j) {
256 if (rdata[j] != (unsigned char)sha1hash[j]) {
257 ret = 2;
258 goto finish;
259 }
260 }
261
262 /* server fingerprint is supported by a DNS entry,
263 * we have already determined if DNSSEC was used or not
264 */
265 goto finish;
266 }
267
268 /* no match */
269 ret = 2;
270
271finish:
272 val_free_validator_state();
273 return ret;
274}
275
276#endif /* ENABLE_DNSSEC */
277
Radek Krejci62aa0642017-05-25 16:33:49 +0200278int
Radek Krejci90a84a22017-05-25 13:53:00 +0200279sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(priv))
Michal Vaskoef112d72016-02-18 13:28:25 +0100280{
Michal Vasko51228ac2018-03-29 14:57:53 +0200281 char *hexa = NULL;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200282
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800283#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
284 int c, ret;
285 enum ssh_known_hosts_e state;
286#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100287 int c, state, ret;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800288#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100289 ssh_key srv_pubkey;
290 unsigned char *hash_sha1 = NULL;
291 size_t hlen;
292 enum ssh_keytypes_e srv_pubkey_type;
293 char answer[5];
Michal Vasko51228ac2018-03-29 14:57:53 +0200294 FILE *out = NULL, *in = NULL;
Michal Vaskoef112d72016-02-18 13:28:25 +0100295
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800296#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
297 state = ssh_session_is_known_server(session);
298#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100299 state = ssh_is_server_known(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800300#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100301
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800302#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 0))
303 ret = ssh_get_server_publickey(session, &srv_pubkey);
304#else
Michal Vaskocc0aa7d2016-05-31 12:48:42 +0200305 ret = ssh_get_publickey(session, &srv_pubkey);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800306#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100307 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200308 ERR(NULL, "Unable to get server public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100309 return -1;
310 }
311
312 srv_pubkey_type = ssh_key_type(srv_pubkey);
313 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
314 ssh_key_free(srv_pubkey);
315 if (ret < 0) {
Michal Vasko05532772021-06-03 12:12:38 +0200316 ERR(NULL, "Failed to calculate SHA1 hash of the server public key.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100317 return -1;
318 }
319
320 hexa = ssh_get_hexa(hash_sha1, hlen);
321
322 switch (state) {
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800323#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
324 case SSH_KNOWN_HOSTS_OK:
325#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100326 case SSH_SERVER_KNOWN_OK:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800327#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100328 break; /* ok */
329
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800330#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
331 case SSH_KNOWN_HOSTS_CHANGED:
332#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100333 case SSH_SERVER_KNOWN_CHANGED:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800334#endif
Michal Vasko05532772021-06-03 12:12:38 +0200335 ERR(NULL, "Remote host key changed, the connection will be terminated!");
Michal Vasko51228ac2018-03-29 14:57:53 +0200336 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100337
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800338#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
339 case SSH_KNOWN_HOSTS_OTHER:
340#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100341 case SSH_SERVER_FOUND_OTHER:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800342#endif
Michal Vasko05532772021-06-03 12:12:38 +0200343 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 +0100344 goto hostkey_not_known;
345
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800346#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
347 case SSH_KNOWN_HOSTS_NOT_FOUND:
348#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100349 case SSH_SERVER_FILE_NOT_FOUND:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800350#endif
Michal Vasko05532772021-06-03 12:12:38 +0200351 WRN(NULL, "Could not find the known hosts file.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100352 goto hostkey_not_known;
353
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800354#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
355 case SSH_KNOWN_HOSTS_UNKNOWN:
356#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100357 case SSH_SERVER_NOT_KNOWN:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800358#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100359hostkey_not_known:
360#ifdef ENABLE_DNSSEC
Michal Vaskod1792612021-05-28 11:33:47 +0200361 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) && (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
Michal Vaskoef112d72016-02-18 13:28:25 +0100362 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
Michal Vasko650011a2016-02-25 14:49:29 +0100363 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100364 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100365 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100366 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100367 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100368 }
369
370 /* DNSSEC SSHFP check successful, that's enough */
371 if (!ret) {
Michal Vasko05532772021-06-03 12:12:38 +0200372 VRB(NULL, "DNSSEC SSHFP check successful.");
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800373#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
374 ssh_session_update_known_hosts(session);
375#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100376 ssh_write_knownhost(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800377#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100378 ssh_clean_pubkey_hash(&hash_sha1);
379 ssh_string_free_char(hexa);
380 return 0;
381 }
382 }
383#endif
384
Michal Vasko51228ac2018-03-29 14:57:53 +0200385 if (!(in = nc_open_in(1, NULL))) {
386 goto error;
387 }
388 if (!(out = nc_open_out())) {
389 goto error;
390 }
391
Michal Vaskoef112d72016-02-18 13:28:25 +0100392 /* try to get result from user */
Michal Vasko51228ac2018-03-29 14:57:53 +0200393 if (fprintf(out, "The authenticity of the host \'%s\' cannot be established.\n", hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200394 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200395 goto error;
396 }
397 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 +0200398 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200399 goto error;
400 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100401
402#ifdef ENABLE_DNSSEC
403 if (ret == 2) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200404 if (fprintf(out, "No matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200405 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200406 goto error;
407 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100408 } else if (ret == 1) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200409 if (fprintf(out, "Matching host key fingerprint found using DNS.\n") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200410 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200411 goto error;
412 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100413 }
414#endif
415
Michal Vasko51228ac2018-03-29 14:57:53 +0200416 if (fprintf(out, "Are you sure you want to continue connecting (yes/no)? ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200417 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200418 goto error;
419 }
420 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100421
422 do {
Michal Vasko51228ac2018-03-29 14:57:53 +0200423 if (fscanf(in, "%4s", answer) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200424 ERR(NULL, "Reading from input failed (%s).", feof(in) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200425 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100426 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200427 while (((c = getc(in)) != EOF) && (c != '\n')) {}
Michal Vaskoef112d72016-02-18 13:28:25 +0100428
Michal Vasko51228ac2018-03-29 14:57:53 +0200429 fflush(in);
Michal Vaskoef112d72016-02-18 13:28:25 +0100430 if (!strcmp("yes", answer)) {
431 /* store the key into the host file */
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800432#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
433 ret = ssh_session_update_known_hosts(session);
434#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100435 ret = ssh_write_knownhost(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800436#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100437 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +0200438 WRN(NULL, "Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
Michal Vaskoef112d72016-02-18 13:28:25 +0100439 }
440 } else if (!strcmp("no", answer)) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200441 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100442 } else {
Michal Vasko51228ac2018-03-29 14:57:53 +0200443 if (fprintf(out, "Please type 'yes' or 'no': ") < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200444 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200445 goto error;
446 }
Michal Vasko5fb5f992020-11-26 15:19:31 +0100447 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100448 }
449 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
450
451 break;
452
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800453#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
454 case SSH_KNOWN_HOSTS_ERROR:
455#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100456 case SSH_SERVER_ERROR:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800457#endif
Michal Vasko05532772021-06-03 12:12:38 +0200458 ERR(NULL, "SSH error: %s", ssh_get_error(session));
Michal Vasko51228ac2018-03-29 14:57:53 +0200459 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100460 }
461
Michal Vasko51228ac2018-03-29 14:57:53 +0200462 nc_close_inout(in, 1, NULL);
463 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100464 ssh_clean_pubkey_hash(&hash_sha1);
465 ssh_string_free_char(hexa);
466 return 0;
467
Michal Vasko51228ac2018-03-29 14:57:53 +0200468error:
469 nc_close_inout(in, 1, NULL);
470 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100471 ssh_clean_pubkey_hash(&hash_sha1);
472 ssh_string_free_char(hexa);
473 return -1;
474}
475
Radek Krejci62aa0642017-05-25 16:33:49 +0200476char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200477sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200478{
Michal Vasko51228ac2018-03-29 14:57:53 +0200479 char *buf = NULL;
480 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200481 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200482 FILE *in = NULL, *out = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200483
Michal Vasko11d142a2016-01-19 15:58:24 +0100484 buf = malloc(buflen * sizeof *buf);
485 if (!buf) {
486 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100487 return NULL;
488 }
489
Michal Vasko51228ac2018-03-29 14:57:53 +0200490 if (!(in = nc_open_in(0, &oldterm))) {
491 goto error;
492 }
493 if (!(out = nc_open_out())) {
494 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200495 }
496
Michal Vasko51228ac2018-03-29 14:57:53 +0200497 if (fprintf(out, "%s@%s password: ", username, hostname) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200498 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200499 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200500 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200501 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200502
503 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200504 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100505 if (len >= buflen - 1) {
506 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100507 buf = nc_realloc(buf, buflen * sizeof *buf);
508 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100509 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200510 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100511 }
512 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100513 buf[len++] = (char)c;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100514 }
515 buf[len++] = 0; /* terminating null byte */
516
Michal Vasko51228ac2018-03-29 14:57:53 +0200517 fprintf(out, "\n");
518
519 nc_close_inout(in, 0, &oldterm);
520 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100521 return buf;
Michal Vasko93f26d92018-02-01 09:08:35 +0100522
Michal Vasko51228ac2018-03-29 14:57:53 +0200523error:
524 nc_close_inout(in, 0, &oldterm);
525 nc_close_inout(out, 1, NULL);
Michal Vasko93f26d92018-02-01 09:08:35 +0100526 free(buf);
527 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100528}
529
Radek Krejci62aa0642017-05-25 16:33:49 +0200530char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200531sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100532{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200533 unsigned int buflen = 64, cur_len;
Michal Vasko51228ac2018-03-29 14:57:53 +0200534 int c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200535 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200536 char *buf = NULL;
537 FILE *in = NULL, *out = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100538
539 buf = malloc(buflen * sizeof *buf);
540 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100541 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100542 return NULL;
543 }
544
Michal Vasko51228ac2018-03-29 14:57:53 +0200545 if (!(in = nc_open_in(echo, &oldterm))) {
546 goto error;
547 }
548 if (!(out = nc_open_out())) {
549 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100550 }
551
Michal Vasko51228ac2018-03-29 14:57:53 +0200552 if (auth_name && (fprintf(out, "%s\n", auth_name) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200553 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200554 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100555 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200556 if (instruction && (fprintf(out, "%s\n", instruction) < 1)) {
Michal Vasko05532772021-06-03 12:12:38 +0200557 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200558 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100559 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200560 if (fputs(prompt, out) == EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200561 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200562 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200563 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200564 fflush(out);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100565
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200566 cur_len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200567 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200568 if (cur_len >= buflen - 1) {
569 buflen *= 2;
570 buf = nc_realloc(buf, buflen * sizeof *buf);
571 if (!buf) {
572 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200573 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200574 }
575 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100576 buf[cur_len++] = (char)c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200577 }
578 /* terminating null byte */
579 buf[cur_len] = '\0';
580
Michal Vasko51228ac2018-03-29 14:57:53 +0200581 fprintf(out, "\n");
582
583 nc_close_inout(in, echo, &oldterm);
584 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200585 return buf;
586
Michal Vasko51228ac2018-03-29 14:57:53 +0200587error:
588 nc_close_inout(in, echo, &oldterm);
589 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200590 free(buf);
591 return NULL;
592}
593
Radek Krejci62aa0642017-05-25 16:33:49 +0200594char *
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200595sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200596{
Michal Vasko51228ac2018-03-29 14:57:53 +0200597 char *buf = NULL;
598 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200599 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200600 FILE *in = NULL, *out = NULL;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200601
602 buf = malloc(buflen * sizeof *buf);
603 if (!buf) {
604 ERRMEM;
605 return NULL;
606 }
607
Michal Vasko51228ac2018-03-29 14:57:53 +0200608 if (!(in = nc_open_in(0, &oldterm))) {
609 goto error;
610 }
611 if (!(out = nc_open_out())) {
612 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200613 }
614
Michal Vasko51228ac2018-03-29 14:57:53 +0200615 if (fprintf(out, "Enter passphrase for the key '%s': ", privkey_path) < 1) {
Michal Vasko05532772021-06-03 12:12:38 +0200616 ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
Michal Vasko51228ac2018-03-29 14:57:53 +0200617 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200618 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200619 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200620
621 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200622 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100623 if (len >= buflen - 1) {
624 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100625 buf = nc_realloc(buf, buflen * sizeof *buf);
626 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100627 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200628 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100629 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100630 }
631 buf[len++] = (char)c;
632 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200633 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100634
Michal Vasko51228ac2018-03-29 14:57:53 +0200635 fprintf(out, "\n");
636
637 nc_close_inout(in, 0, &oldterm);
638 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100639 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100640
Michal Vasko51228ac2018-03-29 14:57:53 +0200641error:
642 nc_close_inout(in, 0, &oldterm);
643 nc_close_inout(out, 1, NULL);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100644 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100645 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100646}
647
Michal Vaskoef112d72016-02-18 13:28:25 +0100648static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200649_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200650 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100651{
Michal Vaskoef112d72016-02-18 13:28:25 +0100652 if (auth_hostkey_check) {
653 opts->auth_hostkey_check = auth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200654 opts->auth_hostkey_check_priv = priv;
Michal Vaskoef112d72016-02-18 13:28:25 +0100655 } else {
656 opts->auth_hostkey_check = sshauth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200657 opts->auth_hostkey_check_priv = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100658 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100659}
660
Radek Krejci90a84a22017-05-25 13:53:00 +0200661static void
662_nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200663 void **priv, struct nc_client_ssh_opts *opts)
Michal Vaskoef112d72016-02-18 13:28:25 +0100664{
Radek Krejci90a84a22017-05-25 13:53:00 +0200665 if (auth_hostkey_check) {
666 (*auth_hostkey_check) = opts->auth_hostkey_check == sshauth_hostkey_check ? NULL : opts->auth_hostkey_check;
667 }
668 if (priv) {
669 (*priv) = opts->auth_hostkey_check_priv;
670 }
671}
672
Radek Krejci90a84a22017-05-25 13:53:00 +0200673API void
674nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200675 void *priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200676{
677 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100678}
679
680API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200681nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200682 void *priv)
Michal Vaskoef112d72016-02-18 13:28:25 +0100683{
Radek Krejci90a84a22017-05-25 13:53:00 +0200684 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100685}
686
Radek Krejci90a84a22017-05-25 13:53:00 +0200687API void
688nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200689 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200690{
691 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
692}
693
694API void
695nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200696 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200697{
698 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
699}
Michal Vaskoef112d72016-02-18 13:28:25 +0100700
Michal Vasko30e2c872016-02-18 10:03:21 +0100701static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200702_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 +0200703 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100704{
705 if (auth_password) {
706 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200707 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100708 } else {
709 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200710 opts->auth_password_priv = NULL;
711 }
712}
713
714static void
715_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 +0200716 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200717{
718 if (auth_password) {
719 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
720 }
721 if (priv) {
722 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100723 }
724}
725
726API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200727nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200728 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100729{
Radek Krejci90a84a22017-05-25 13:53:00 +0200730 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100731}
732
733API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200734nc_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 +0200735 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100736{
Radek Krejci90a84a22017-05-25 13:53:00 +0200737 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
738}
739
740API void
741nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200742 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200743{
744 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
745}
746
747API void
748nc_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 +0200749 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200750{
751 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100752}
753
754static void
755_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200756 const char *prompt, int echo, void *priv),
757 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100758{
759 if (auth_interactive) {
760 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200761 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100762 } else {
763 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200764 opts->auth_interactive_priv = NULL;
765 }
766}
767
768static void
769_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200770 const char *prompt, int echo, void *priv),
771 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200772{
773 if (auth_interactive) {
774 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
775 }
776 if (priv) {
777 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100778 }
779}
780
781API void
782nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200783 const char *prompt, int echo, void *priv),
784 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100785{
Radek Krejci90a84a22017-05-25 13:53:00 +0200786 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100787}
788
789API void
790nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200791 const char *prompt, int echo, void *priv),
792 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100793{
Radek Krejci90a84a22017-05-25 13:53:00 +0200794 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
795}
796
797API void
798nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200799 const char *prompt, int echo, void *priv),
800 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200801{
802 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
803}
804
805API void
806nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200807 const char *prompt, int echo, void *priv),
808 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200809{
810 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100811}
812
813static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200814_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 +0200815 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100816{
817 if (auth_privkey_passphrase) {
818 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200819 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100820 } else {
821 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200822 opts->auth_privkey_passphrase_priv = NULL;
823 }
824}
825
826static void
827_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 +0200828 void **priv, struct nc_client_ssh_opts *opts)
Radek Krejci90a84a22017-05-25 13:53:00 +0200829{
830 if (auth_privkey_passphrase) {
831 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
832 }
833 if (priv) {
834 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100835 }
836}
837
838API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200839nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200840 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100841{
Radek Krejci90a84a22017-05-25 13:53:00 +0200842 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100843}
844
845API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200846nc_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 +0200847 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100848{
Radek Krejci90a84a22017-05-25 13:53:00 +0200849 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
850}
851
852API void
853nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200854 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200855{
856 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
857}
858
859API void
860nc_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 +0200861 void **priv)
Radek Krejci90a84a22017-05-25 13:53:00 +0200862{
863 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100864}
865
Michal Vasko3031aae2016-01-27 16:07:18 +0100866static int
867_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 +0100868{
869 int i;
870 FILE *key;
871 char line[128];
872
Michal Vasko45e53ae2016-04-07 11:46:03 +0200873 if (!pub_key) {
874 ERRARG("pub_key");
875 return -1;
876 } else if (!priv_key) {
877 ERRARG("priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100878 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100879 }
880
Michal Vasko3031aae2016-01-27 16:07:18 +0100881 for (i = 0; i < opts->key_count; ++i) {
882 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
883 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200884 WRN(NULL, "Private key \"%s\" found with another public key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200885 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100886 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100887 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko05532772021-06-03 12:12:38 +0200888 WRN(NULL, "Public key \"%s\" found with another private key \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200889 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100890 continue;
891 }
892
Michal Vasko05532772021-06-03 12:12:38 +0200893 ERR(NULL, "SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100894 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100895 }
896 }
897
Michal Vasko3031aae2016-01-27 16:07:18 +0100898 /* add the keys */
899 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100900 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
901 if (!opts->keys) {
902 ERRMEM;
903 return -1;
904 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100905 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
906 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
907 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100908
Michal Vasko4eb3c312016-03-01 14:09:37 +0100909 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
910 ERRMEM;
911 return -1;
912 }
913
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100914 /* check encryption */
915 if ((key = fopen(priv_key, "r"))) {
916 /* 1st line - key type */
917 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100918 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200919 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100920 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100921 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100922 /* 2nd line - encryption information or key */
923 if (!fgets(line, sizeof line, key)) {
924 fclose(key);
Michal Vasko05532772021-06-03 12:12:38 +0200925 ERR(NULL, "fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100926 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100927 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100928 fclose(key);
929 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100930 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100931 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100932 }
933
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100934 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100935}
936
937API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100938nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100939{
Michal Vasko3031aae2016-01-27 16:07:18 +0100940 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
941}
942
943API int
944nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
945{
946 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
947}
948
949static int
950_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
951{
952 if (idx >= opts->key_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200953 ERRARG("idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100954 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100955 }
956
Michal Vasko3031aae2016-01-27 16:07:18 +0100957 free(opts->keys[idx].pubkey_path);
958 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100959
Michal Vasko3031aae2016-01-27 16:07:18 +0100960 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100961 if (idx < opts->key_count) {
962 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
963 }
964 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100965 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
966 if (!opts->keys) {
967 ERRMEM;
968 return -1;
969 }
Michal Vaskoc0256492016-02-02 12:19:06 +0100970 } else {
971 free(opts->keys);
972 opts->keys = NULL;
973 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100974
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100975 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100976}
977
978API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100979nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100980{
Michal Vasko3031aae2016-01-27 16:07:18 +0100981 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100982}
983
984API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100985nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100986{
Michal Vasko3031aae2016-01-27 16:07:18 +0100987 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
988}
989
990static int
991_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
992{
993 return opts->key_count;
994}
995
996API int
997nc_client_ssh_get_keypair_count(void)
998{
999 return _nc_client_ssh_get_keypair_count(&ssh_opts);
1000}
1001
1002API int
1003nc_client_ssh_ch_get_keypair_count(void)
1004{
1005 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
1006}
1007
1008static int
1009_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
1010{
Michal Vasko45e53ae2016-04-07 11:46:03 +02001011 if (idx >= opts->key_count) {
1012 ERRARG("idx");
1013 return -1;
1014 } else if (!pub_key && !priv_key) {
1015 ERRARG("pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001016 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001017 }
1018
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001019 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001020 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001021 }
1022 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001023 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001024 }
1025
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001026 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001027}
1028
Michal Vasko3031aae2016-01-27 16:07:18 +01001029API int
1030nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
1031{
1032 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
1033}
1034
1035API int
1036nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
1037{
1038 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
1039}
1040
1041static void
1042_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 +01001043{
1044 if (pref < 0) {
1045 pref = -1;
1046 }
1047
1048 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001049 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001050 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001051 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001052 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001053 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001054 }
1055}
1056
Michal Vasko3031aae2016-01-27 16:07:18 +01001057API void
1058nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001059{
Michal Vasko3031aae2016-01-27 16:07:18 +01001060 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
1061}
1062
1063API void
1064nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
1065{
1066 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
1067}
1068
1069static int16_t
1070_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
1071{
1072 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001073
1074 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001075 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001076 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001077 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001078 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001079 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001080 }
1081
1082 return pref;
1083}
1084
Michal Vasko3031aae2016-01-27 16:07:18 +01001085API int16_t
1086nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1087{
1088 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
1089}
1090
1091API int16_t
1092nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1093{
1094 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
1095}
1096
1097static int
1098_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
1099{
1100 if (opts->username) {
1101 free(opts->username);
1102 }
1103 if (username) {
1104 opts->username = strdup(username);
1105 if (!opts->username) {
1106 ERRMEM;
1107 return -1;
1108 }
1109 } else {
1110 opts->username = NULL;
1111 }
1112
1113 return 0;
1114}
1115
1116API int
1117nc_client_ssh_set_username(const char *username)
1118{
1119 return _nc_client_ssh_set_username(username, &ssh_opts);
1120}
1121
1122API int
1123nc_client_ssh_ch_set_username(const char *username)
1124{
1125 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1126}
1127
Michal Vaskoe22c6732016-01-29 11:03:02 +01001128static const char *
1129_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1130{
1131 return opts->username;
1132}
1133
1134API const char *
1135nc_client_ssh_get_username(void)
1136{
1137 return _nc_client_ssh_get_username(&ssh_opts);
1138}
1139
1140API const char *
1141nc_client_ssh_ch_get_username(void)
1142{
1143 return _nc_client_ssh_get_username(&ssh_ch_opts);
1144}
1145
Michal Vasko3031aae2016-01-27 16:07:18 +01001146API int
1147nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1148{
Michal Vasko9d4cca52022-09-07 11:19:57 +02001149 return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_LIBSSH);
Michal Vasko3031aae2016-01-27 16:07:18 +01001150}
1151
1152API int
1153nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1154{
1155 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1156}
1157
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001158/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001159 * Host, port, username, and a connected socket is expected to be set.
Radek Krejciae813f42018-07-02 13:38:30 +02001160 *
1161 * return values
1162 * -1 failure
1163 * 0 try again
1164 * 1 success
Michal Vasko7b62fed2015-10-26 15:39:46 +01001165 */
1166static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001167connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001168{
Radek Krejciae813f42018-07-02 13:38:30 +02001169 int j, ret_auth, userauthlist, ret, attempt = 0;
Michal Vasko235efdc2015-12-17 12:05:04 +01001170 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001171 int16_t pref;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001172 const char *prompt;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001173 char *s, *answer, echo;
1174 ssh_key pubkey, privkey;
1175 ssh_session ssh_sess;
roman6ece9c52022-06-22 09:29:17 +02001176 struct timespec ts_timeout;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001177
1178 ssh_sess = session->ti.libssh.session;
1179
roman6ece9c52022-06-22 09:29:17 +02001180 nc_gettimespec_mono_add(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001181 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1182 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001183 if (nc_difftimespec_mono_cur(&ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001184 break;
1185 }
1186 }
1187 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001188 ERR(session, "SSH connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001189 return 0;
1190 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001191 ERR(session, "Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
1192 DBG(session, "Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001193 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001194 }
1195
Radek Krejci90a84a22017-05-25 13:53:00 +02001196 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko05532772021-06-03 12:12:38 +02001197 ERR(session, "Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001198 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001199 }
1200
Michal Vasko36c7be82017-02-22 13:37:59 +01001201 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001202 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001203 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001204 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1205 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001206 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001207 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001208 }
1209 }
1210 if (ret_auth == SSH_AUTH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001211 ERR(session, "Request authentication methods timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001212 return 0;
1213 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001214 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001215 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001216 } else if (ret_auth == SSH_AUTH_SUCCESS) {
Michal Vasko05532772021-06-03 12:12:38 +02001217 WRN(session, "Server accepts \"none\" authentication method.")
Radek Krejciae813f42018-07-02 13:38:30 +02001218 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001219 }
1220
1221 /* check what authentication methods are available */
1222 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001223
1224 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001225 if (opts->auth_pref[0].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001226 VRB(session, "Interactive SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001227 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001228 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001229 if (opts->auth_pref[1].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001230 VRB(session, "Password SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001231 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001232 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001233 if (opts->auth_pref[2].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001234 VRB(session, "Publickey SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001235 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001236 }
1237
Michal Vasko0190bc32016-03-02 15:47:49 +01001238 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001239 auth = 0;
1240 pref = 0;
1241 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1242 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001243 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001244 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001245 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001246 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001247 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001248 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001249 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001250 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001251 }
1252
Michal Vasko235efdc2015-12-17 12:05:04 +01001253 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001254 if (!attempt) {
Michal Vasko05532772021-06-03 12:12:38 +02001255 ERR(session, "Unable to authenticate to the remote server (no supported authentication methods detected).");
Radek Krejciae813f42018-07-02 13:38:30 +02001256 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001257 ERR(session, "Unable to authenticate to the remote server (all attempts via supported authentication "
1258 "methods failed).");
Radek Krejciae813f42018-07-02 13:38:30 +02001259 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001260 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001261 }
1262
1263 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001264 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001265 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001266 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1267
Michal Vasko05532772021-06-03 12:12:38 +02001268 VRB(session, "Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001269 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001270 if (s == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001271 ERR(session, "Unable to get the password.");
Michal Vasko88583042018-03-29 09:18:58 +02001272 return -1;
1273 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001274
Michal Vasko36c7be82017-02-22 13:37:59 +01001275 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001276 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001277 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001278 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1279 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001280 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001281 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001282 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001283 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001284 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001285 free(s);
1286 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001287
Michal Vasko7b62fed2015-10-26 15:39:46 +01001288 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001289 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1290
Michal Vasko05532772021-06-03 12:12:38 +02001291 VRB(session, "Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001292
Michal Vasko36c7be82017-02-22 13:37:59 +01001293 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001294 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001295 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001296 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) ||
1297 (ret_auth == SSH_AUTH_AGAIN)) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001298 if (ret_auth == SSH_AUTH_AGAIN) {
1299 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001300 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001301 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001302 }
1303 continue;
1304 }
1305
Michal Vasko7b62fed2015-10-26 15:39:46 +01001306 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1307 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001308 if (!prompt) {
1309 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001310 break;
1311 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001312
Michal Vasko30e2c872016-02-18 10:03:21 +01001313 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001314 ssh_userauth_kbdint_getinstruction(ssh_sess),
1315 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001316 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1317 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001318 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001319 break;
1320 }
1321 free(answer);
1322 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001323 if (ret_auth == SSH_AUTH_ERROR) {
1324 break;
1325 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001326 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001327 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001328 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001329 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001330 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001331
Michal Vasko206d3b12015-12-04 11:08:42 +01001332 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001333 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1334
Michal Vasko05532772021-06-03 12:12:38 +02001335 VRB(session, "Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001336
1337 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001338 if (!opts->key_count) {
Michal Vasko05532772021-06-03 12:12:38 +02001339 VRB(session, "No key pair specified.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001340 break;
1341 }
1342
Michal Vasko30e2c872016-02-18 10:03:21 +01001343 for (j = 0; j < opts->key_count; j++) {
Michal Vasko05532772021-06-03 12:12:38 +02001344 VRB(session, "Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001345 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1346 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001347
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001348 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1349 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001350 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001351 continue;
1352 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001353 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001354 continue;
1355 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001356
Michal Vasko36c7be82017-02-22 13:37:59 +01001357 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001358 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001359 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001360 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1361 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001362 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001363 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001364 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001365 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001366 ssh_key_free(pubkey);
1367
1368 if (ret_auth == SSH_AUTH_DENIED) {
1369 continue;
1370 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001371 break;
1372 }
1373
Michal Vasko30e2c872016-02-18 10:03:21 +01001374 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001375 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001376 } else {
1377 s = NULL;
1378 }
1379
Michal Vasko0190bc32016-03-02 15:47:49 +01001380 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001381 if (s) {
1382 memset(s, 0, strlen(s));
1383 free(s);
1384 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001385 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001386 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001387 continue;
1388 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001389 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001390 continue;
1391 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001392
Michal Vasko36c7be82017-02-22 13:37:59 +01001393 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001394 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001395 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001396 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1397 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001398 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001399 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001400 }
1401 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001402 ssh_key_free(privkey);
1403
Michal Vasko0190bc32016-03-02 15:47:49 +01001404 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001405 break;
1406 }
1407 }
1408 break;
1409 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001410
Michal Vasko0190bc32016-03-02 15:47:49 +01001411 switch (ret_auth) {
1412 case SSH_AUTH_AGAIN:
Michal Vasko05532772021-06-03 12:12:38 +02001413 ERR(session, "Authentication response timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001414 return 0;
1415 case SSH_AUTH_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001416 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001417 return -1;
1418 case SSH_AUTH_DENIED:
Michal Vasko05532772021-06-03 12:12:38 +02001419 WRN(session, "Authentication denied.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001420 break;
1421 case SSH_AUTH_PARTIAL:
Michal Vasko05532772021-06-03 12:12:38 +02001422 VRB(session, "Partial authentication success.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001423 break;
1424 case SSH_AUTH_SUCCESS:
Michal Vasko05532772021-06-03 12:12:38 +02001425 VRB(session, "Authentication successful.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001426 break;
1427 case SSH_AUTH_INFO:
1428 ERRINT;
1429 return -1;
1430 }
Radek Krejciae813f42018-07-02 13:38:30 +02001431
1432 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001433 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001434
Michal Vasko0190bc32016-03-02 15:47:49 +01001435 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001436}
1437
1438/* Open new SSH channel and request the 'netconf' subsystem.
1439 * SSH connection is expected to be established.
1440 */
1441static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001442open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001443{
1444 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001445 int ret;
roman6ece9c52022-06-22 09:29:17 +02001446 struct timespec ts_timeout;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001447
1448 ssh_sess = session->ti.libssh.session;
1449
1450 if (!ssh_is_connected(ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001451 ERR(session, "SSH session not connected.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001452 return -1;
1453 }
1454
1455 if (session->ti.libssh.channel) {
Michal Vasko05532772021-06-03 12:12:38 +02001456 ERR(session, "SSH channel already created.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001457 return -1;
1458 }
1459
Michal Vasko7b62fed2015-10-26 15:39:46 +01001460 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001461 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001462 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001463 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001464 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001465 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1466 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001467 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001468 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001469 }
1470 }
1471 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001472 ERR(session, "Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001473 ssh_channel_free(session->ti.libssh.channel);
1474 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001475 return 0;
1476 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001477 ERR(session, "Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001478 ssh_channel_free(session->ti.libssh.channel);
1479 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001480 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001481 }
1482
1483 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001484 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001485 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001486 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001487 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1488 usleep(NC_TIMEOUT_STEP);
Michal Vasko60d8ffb2022-07-21 11:08:34 +02001489 if ((timeout > -1) && (nc_difftimespec_mono_cur(&ts_timeout) < 1)) {
roman6ece9c52022-06-22 09:29:17 +02001490 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001491 }
1492 }
1493 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001494 ERR(session, "Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001495 ssh_channel_free(session->ti.libssh.channel);
1496 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001497 return 0;
1498 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001499 ERR(session, "Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001500 ssh_channel_free(session->ti.libssh.channel);
1501 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001502 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001503 }
1504
Michal Vasko0190bc32016-03-02 15:47:49 +01001505 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001506}
1507
Michal Vasko30e2c872016-02-18 10:03:21 +01001508static struct nc_session *
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001509_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
1510 struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001511{
Michal Vasko66032bc2019-01-22 15:03:12 +01001512 char *host = NULL, *username = NULL, *ip_host;
Dragos Dan8ce3b7c2021-03-09 09:17:22 +02001513 unsigned int port = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001514 int sock;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001515 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001516 struct nc_session *session = NULL;
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001517 char *buf = NULL;
1518 size_t buf_len = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001519
1520 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001521 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001522 return NULL;
1523 }
1524
1525 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001526 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001527 if (!session) {
1528 ERRMEM;
1529 return NULL;
1530 }
1531 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001532 session->ti_type = NC_TI_LIBSSH;
1533 session->ti.libssh.session = ssh_session;
1534
1535 /* was port set? */
Michal Vasko097fc852021-03-09 08:18:17 +01001536 ssh_options_get_port(ssh_session, &port);
Michal Vasko30e2c872016-02-18 10:03:21 +01001537
1538 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1539 /*
1540 * There is no file descriptor (detected based on the host, there is no way to check
1541 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1542 */
1543
1544 /* remember host */
1545 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001546 if (!host) {
1547 ERRMEM;
1548 goto fail;
1549 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001550 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1551
1552 /* create and connect socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001553 sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001554 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001555 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001556 goto fail;
1557 }
1558 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001559 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko66032bc2019-01-22 15:03:12 +01001560
1561 free(host);
1562 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001563 }
1564
1565 /* was username set? */
1566 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1567
1568 if (!ssh_is_connected(ssh_session)) {
1569 /*
1570 * We are connected, but not SSH authenticated. (Transport layer)
1571 */
1572
1573 /* remember username */
1574 if (!username) {
1575 if (!opts->username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001576 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Michal Vasko30e2c872016-02-18 10:03:21 +01001577 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001578 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001579 goto fail;
1580 }
1581 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001582 free(buf);
Michal Vasko30e2c872016-02-18 10:03:21 +01001583 } else {
1584 username = strdup(opts->username);
1585 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001586 if (!username) {
1587 ERRMEM;
1588 goto fail;
1589 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001590 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1591 }
1592
1593 /* connect and authenticate SSH session */
1594 session->host = host;
1595 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001596 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001597 goto fail;
1598 }
1599 }
1600
1601 /*
1602 * Almost done, open a netconf channel. (Transport layer / application layer)
1603 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001604 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001605 goto fail;
1606 }
1607
1608 /*
1609 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1610 */
1611
Radek Krejcifd5b6682017-06-13 15:52:53 +02001612 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1613 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001614 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001615 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001616
1617 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001618 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001619 goto fail;
1620 }
1621 session->status = NC_STATUS_RUNNING;
1622
1623 if (nc_ctx_check_and_fill(session) == -1) {
1624 goto fail;
1625 }
1626
Michal Vasko93224072021-11-09 12:14:28 +01001627 /* store information if not previously */
1628 session->host = host;
1629 session->port = port;
1630 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001631
1632 return session;
1633
1634fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001635 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001636 return NULL;
1637}
1638
Radek Krejciac6d3472015-10-22 15:47:18 +02001639API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001640nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001641{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001642 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001643 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001644 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001645 char *username, *ip_host = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001646 struct passwd *pw, pw_buf;
Radek Krejciac6d3472015-10-22 15:47:18 +02001647 struct nc_session *session = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001648 char *buf = NULL;
1649 size_t buf_len = 0;
Radek Krejciac6d3472015-10-22 15:47:18 +02001650
1651 /* process parameters */
1652 if (!host || strisempty(host)) {
1653 host = "localhost";
1654 }
1655
1656 if (!port) {
1657 port = NC_PORT_SSH;
1658 }
Michal Vasko55fded62016-02-02 12:19:34 +01001659 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001660
Michal Vasko3031aae2016-01-27 16:07:18 +01001661 if (!ssh_opts.username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001662 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Radek Krejciac6d3472015-10-22 15:47:18 +02001663 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001664 ERR(session, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko1d430d92021-10-11 09:30:43 +02001665 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001666 } else {
1667 username = pw->pw_name;
1668 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001669 } else {
1670 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001671 }
1672
1673 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001674 session = nc_new_session(NC_CLIENT, 0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001675 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001676 ERRMEM;
Michal Vasko1d430d92021-10-11 09:30:43 +02001677 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001678 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001679 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001680
Michal Vasko131120a2018-05-29 15:44:02 +02001681 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001682 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001683 session->ti.libssh.session = ssh_new();
1684 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001685 ERR(session, "Unable to initialize SSH session.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001686 goto fail;
1687 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001688
Michal Vasko7b62fed2015-10-26 15:39:46 +01001689 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001690 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001691 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001692 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001693 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
1694
1695 /* create and assign communication socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001696 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001697 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001698 ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001699 goto fail;
1700 }
1701 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001702 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001703
Michal Vasko93224072021-11-09 12:14:28 +01001704 /* store information for session connection */
1705 session->host = strdup(host);
1706 session->username = strdup(username);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001707 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1) ||
1708 (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001709 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001710 }
1711
Radek Krejcifd5b6682017-06-13 15:52:53 +02001712 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1713 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001714 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001715 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001716
Radek Krejciac6d3472015-10-22 15:47:18 +02001717 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001718 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001719 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001720 }
Michal Vaskoad611702015-12-03 13:41:51 +01001721 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001722
Michal Vaskoef578332016-01-25 13:20:09 +01001723 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001724 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001725 }
1726
Michal Vasko93224072021-11-09 12:14:28 +01001727 /* update information */
1728 free(session->host);
1729 session->host = ip_host;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001730 session->port = port;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001731
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001732 free(buf);
Radek Krejciac6d3472015-10-22 15:47:18 +02001733 return session;
1734
Michal Vasko7b62fed2015-10-26 15:39:46 +01001735fail:
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001736 free(buf);
Michal Vasko66032bc2019-01-22 15:03:12 +01001737 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001738 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001739 return NULL;
1740}
1741
1742API struct nc_session *
1743nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1744{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001745 return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001746}
1747
1748API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001749nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001750{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001751 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001752
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001753 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001754 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001755 return NULL;
1756 }
1757
Michal Vasko7b62fed2015-10-26 15:39:46 +01001758 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001759 new_session = nc_new_session(NC_CLIENT, 1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001760 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001761 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001762 return NULL;
1763 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001764 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001765
Michal Vasko131120a2018-05-29 15:44:02 +02001766 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001767 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001768 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001769 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001770
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001771 /* append to the session ring list */
1772 if (!session->ti.libssh.next) {
1773 session->ti.libssh.next = new_session;
1774 new_session->ti.libssh.next = session;
1775 } else {
1776 ptr = session->ti.libssh.next;
1777 session->ti.libssh.next = new_session;
1778 new_session->ti.libssh.next = ptr;
1779 }
1780
Michal Vasko7b62fed2015-10-26 15:39:46 +01001781 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001782 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001783 goto fail;
1784 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001785 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001786 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001787 }
Michal Vasko131120a2018-05-29 15:44:02 +02001788 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001789
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001790 if (nc_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001791 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001792 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001793 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001794
Michal Vasko7b62fed2015-10-26 15:39:46 +01001795 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001796 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001797 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001798 }
Michal Vaskoad611702015-12-03 13:41:51 +01001799 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001800
Michal Vaskoef578332016-01-25 13:20:09 +01001801 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001802 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001803 }
1804
Michal Vasko93224072021-11-09 12:14:28 +01001805 /* store information into session */
1806 new_session->host = strdup(session->host);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001807 new_session->port = session->port;
Michal Vasko93224072021-11-09 12:14:28 +01001808 new_session->username = strdup(session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001809
Michal Vasko7b62fed2015-10-26 15:39:46 +01001810 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001811
1812fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001813 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001814 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001815}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001816
Michal Vasko3031aae2016-01-27 16:07:18 +01001817struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001818nc_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 +01001819{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001820 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001821 unsigned int uint_port;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001822 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001823 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001824 ssh_session sess;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001825 char *buf = NULL;
1826 size_t buf_len = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001827
Michal Vasko80cad7f2015-12-08 14:42:27 +01001828 sess = ssh_new();
1829 if (!sess) {
Michal Vasko05532772021-06-03 12:12:38 +02001830 ERR(NULL, "Unable to initialize an SSH session.");
Michal Vasko80cad7f2015-12-08 14:42:27 +01001831 close(sock);
1832 return NULL;
1833 }
1834
1835 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001836 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001837 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001838 uint_port = port;
1839 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001840 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001841 if (!ssh_ch_opts.username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001842 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Michal Vasko3031aae2016-01-27 16:07:18 +01001843 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001844 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko435e5cf2019-04-23 08:48:44 +02001845 ssh_free(sess);
Michal Vasko3031aae2016-01-27 16:07:18 +01001846 return NULL;
1847 }
1848 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001849 free(buf);
Michal Vasko3031aae2016-01-27 16:07:18 +01001850 } else {
1851 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001852 }
Michal Vasko8fd6fca2019-02-04 10:59:49 +01001853 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1854 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1855#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
1856 ssh_options_set(sess, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1857 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1858#endif
Michal Vasko80cad7f2015-12-08 14:42:27 +01001859
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001860 session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
Michal Vasko435e5cf2019-04-23 08:48:44 +02001861 if (!session) {
Michal Vasko457f0532019-08-15 13:59:49 +02001862 /* sess is freed */
Michal Vasko435e5cf2019-04-23 08:48:44 +02001863 return NULL;
Michal Vasko4282fae2016-02-18 10:03:42 +01001864 }
1865
Michal Vasko435e5cf2019-04-23 08:48:44 +02001866 session->flags |= NC_SESSION_CALLHOME;
Michal Vasko30e2c872016-02-18 10:03:21 +01001867 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001868}