blob: feb3c3f53b3cd23f1e4e073b7e64ecb55b767e29 [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{
1149 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
1150}
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;
Michal Vasko36c7be82017-02-22 13:37:59 +01001176 struct timespec ts_timeout, ts_cur;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001177
1178 ssh_sess = session->ti.libssh.session;
1179
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001180 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001181 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001182 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1183 usleep(NC_TIMEOUT_STEP);
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001184 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001185 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001186 break;
1187 }
1188 }
1189 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001190 ERR(session, "SSH connect timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001191 return 0;
1192 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001193 ERR(session, "Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
1194 DBG(session, "Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001195 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001196 }
1197
Radek Krejci90a84a22017-05-25 13:53:00 +02001198 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko05532772021-06-03 12:12:38 +02001199 ERR(session, "Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001200 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001201 }
1202
Michal Vasko36c7be82017-02-22 13:37:59 +01001203 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001204 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001205 nc_addtimespec(&ts_timeout, timeout);
1206 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001207 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1208 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001209 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001210 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001211 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1212 break;
1213 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001214 }
1215 }
1216 if (ret_auth == SSH_AUTH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001217 ERR(session, "Request authentication methods timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001218 return 0;
1219 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001220 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001221 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001222 } else if (ret_auth == SSH_AUTH_SUCCESS) {
Michal Vasko05532772021-06-03 12:12:38 +02001223 WRN(session, "Server accepts \"none\" authentication method.")
Radek Krejciae813f42018-07-02 13:38:30 +02001224 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001225 }
1226
1227 /* check what authentication methods are available */
1228 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001229
1230 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001231 if (opts->auth_pref[0].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001232 VRB(session, "Interactive SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001233 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001234 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001235 if (opts->auth_pref[1].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001236 VRB(session, "Password SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001237 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001238 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001239 if (opts->auth_pref[2].value < 0) {
Michal Vasko05532772021-06-03 12:12:38 +02001240 VRB(session, "Publickey SSH authentication method was disabled.");
Michal Vasko235efdc2015-12-17 12:05:04 +01001241 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001242 }
1243
Michal Vasko0190bc32016-03-02 15:47:49 +01001244 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001245 auth = 0;
1246 pref = 0;
1247 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1248 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001249 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001250 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001251 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001252 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001253 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001254 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001255 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001256 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001257 }
1258
Michal Vasko235efdc2015-12-17 12:05:04 +01001259 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001260 if (!attempt) {
Michal Vasko05532772021-06-03 12:12:38 +02001261 ERR(session, "Unable to authenticate to the remote server (no supported authentication methods detected).");
Radek Krejciae813f42018-07-02 13:38:30 +02001262 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001263 ERR(session, "Unable to authenticate to the remote server (all attempts via supported authentication "
1264 "methods failed).");
Radek Krejciae813f42018-07-02 13:38:30 +02001265 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001266 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001267 }
1268
1269 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001270 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001271 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001272 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1273
Michal Vasko05532772021-06-03 12:12:38 +02001274 VRB(session, "Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001275 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001276 if (s == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001277 ERR(session, "Unable to get the password.");
Michal Vasko88583042018-03-29 09:18:58 +02001278 return -1;
1279 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001280
Michal Vasko36c7be82017-02-22 13:37:59 +01001281 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001282 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001283 nc_addtimespec(&ts_timeout, timeout);
1284 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001285 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1286 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001287 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001288 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001289 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1290 break;
1291 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001292 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001293 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001294 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001295 free(s);
1296 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001297
Michal Vasko7b62fed2015-10-26 15:39:46 +01001298 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001299 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1300
Michal Vasko05532772021-06-03 12:12:38 +02001301 VRB(session, "Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001302
Michal Vasko36c7be82017-02-22 13:37:59 +01001303 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001304 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001305 nc_addtimespec(&ts_timeout, timeout);
1306 }
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001307 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) ||
1308 (ret_auth == SSH_AUTH_AGAIN)) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001309 if (ret_auth == SSH_AUTH_AGAIN) {
1310 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001311 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001312 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001313 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1314 break;
1315 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001316 }
1317 continue;
1318 }
1319
Michal Vasko7b62fed2015-10-26 15:39:46 +01001320 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1321 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001322 if (!prompt) {
1323 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001324 break;
1325 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001326
1327 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1328 echo = 0;
1329
Michal Vasko30e2c872016-02-18 10:03:21 +01001330 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001331 ssh_userauth_kbdint_getinstruction(ssh_sess),
1332 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001333 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1334 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001335 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001336 break;
1337 }
1338 free(answer);
1339 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001340 if (ret_auth == SSH_AUTH_ERROR) {
1341 break;
1342 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001343 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001344 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001345 nc_addtimespec(&ts_timeout, timeout);
1346 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001347 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001348 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001349
Michal Vasko206d3b12015-12-04 11:08:42 +01001350 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001351 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1352
Michal Vasko05532772021-06-03 12:12:38 +02001353 VRB(session, "Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001354
1355 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001356 if (!opts->key_count) {
Michal Vasko05532772021-06-03 12:12:38 +02001357 VRB(session, "No key pair specified.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001358 break;
1359 }
1360
Michal Vasko30e2c872016-02-18 10:03:21 +01001361 for (j = 0; j < opts->key_count; j++) {
Michal Vasko05532772021-06-03 12:12:38 +02001362 VRB(session, "Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001363 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1364 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001365
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001366 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1367 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001368 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001369 continue;
1370 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001371 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001372 continue;
1373 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001374
Michal Vasko36c7be82017-02-22 13:37:59 +01001375 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001376 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001377 nc_addtimespec(&ts_timeout, timeout);
1378 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001379 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1380 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001381 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001382 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001383 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1384 break;
1385 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001386 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001387 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001388 ssh_key_free(pubkey);
1389
1390 if (ret_auth == SSH_AUTH_DENIED) {
1391 continue;
1392 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001393 break;
1394 }
1395
Michal Vasko30e2c872016-02-18 10:03:21 +01001396 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001397 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001398 } else {
1399 s = NULL;
1400 }
1401
Michal Vasko0190bc32016-03-02 15:47:49 +01001402 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001403 if (s) {
1404 memset(s, 0, strlen(s));
1405 free(s);
1406 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001407 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +02001408 WRN(session, "Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001409 continue;
1410 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001411 WRN(session, "Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001412 continue;
1413 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001414
Michal Vasko36c7be82017-02-22 13:37:59 +01001415 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001416 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001417 nc_addtimespec(&ts_timeout, timeout);
1418 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001419 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1420 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001421 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001422 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001423 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1424 break;
1425 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001426 }
1427 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001428 ssh_key_free(privkey);
1429
Michal Vasko0190bc32016-03-02 15:47:49 +01001430 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001431 break;
1432 }
1433 }
1434 break;
1435 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001436
Michal Vasko0190bc32016-03-02 15:47:49 +01001437 switch (ret_auth) {
1438 case SSH_AUTH_AGAIN:
Michal Vasko05532772021-06-03 12:12:38 +02001439 ERR(session, "Authentication response timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001440 return 0;
1441 case SSH_AUTH_ERROR:
Michal Vasko05532772021-06-03 12:12:38 +02001442 ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001443 return -1;
1444 case SSH_AUTH_DENIED:
Michal Vasko05532772021-06-03 12:12:38 +02001445 WRN(session, "Authentication denied.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001446 break;
1447 case SSH_AUTH_PARTIAL:
Michal Vasko05532772021-06-03 12:12:38 +02001448 VRB(session, "Partial authentication success.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001449 break;
1450 case SSH_AUTH_SUCCESS:
Michal Vasko05532772021-06-03 12:12:38 +02001451 VRB(session, "Authentication successful.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001452 break;
1453 case SSH_AUTH_INFO:
1454 ERRINT;
1455 return -1;
1456 }
Radek Krejciae813f42018-07-02 13:38:30 +02001457
1458 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001459 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001460
Michal Vasko0190bc32016-03-02 15:47:49 +01001461 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001462}
1463
1464/* Open new SSH channel and request the 'netconf' subsystem.
1465 * SSH connection is expected to be established.
1466 */
1467static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001468open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001469{
1470 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001471 int ret;
1472 struct timespec ts_timeout, ts_cur;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001473
1474 ssh_sess = session->ti.libssh.session;
1475
1476 if (!ssh_is_connected(ssh_sess)) {
Michal Vasko05532772021-06-03 12:12:38 +02001477 ERR(session, "SSH session not connected.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001478 return -1;
1479 }
1480
1481 if (session->ti.libssh.channel) {
Michal Vasko05532772021-06-03 12:12:38 +02001482 ERR(session, "SSH channel already created.");
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001483 return -1;
1484 }
1485
Michal Vasko7b62fed2015-10-26 15:39:46 +01001486 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001487 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001488 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001489 nc_addtimespec(&ts_timeout, timeout);
1490 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001491 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001492 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1493 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001494 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001495 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001496 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1497 break;
1498 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001499 }
1500 }
1501 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001502 ERR(session, "Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001503 ssh_channel_free(session->ti.libssh.channel);
1504 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001505 return 0;
1506 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001507 ERR(session, "Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001508 ssh_channel_free(session->ti.libssh.channel);
1509 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001510 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001511 }
1512
1513 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001514 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001515 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001516 nc_addtimespec(&ts_timeout, timeout);
1517 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001518 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1519 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001520 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001521 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001522 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1523 break;
1524 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001525 }
1526 }
1527 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001528 ERR(session, "Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001529 ssh_channel_free(session->ti.libssh.channel);
1530 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001531 return 0;
1532 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001533 ERR(session, "Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001534 ssh_channel_free(session->ti.libssh.channel);
1535 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001536 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001537 }
1538
Michal Vasko0190bc32016-03-02 15:47:49 +01001539 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001540}
1541
Michal Vasko30e2c872016-02-18 10:03:21 +01001542static struct nc_session *
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001543_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
1544 struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001545{
Michal Vasko66032bc2019-01-22 15:03:12 +01001546 char *host = NULL, *username = NULL, *ip_host;
Dragos Dan8ce3b7c2021-03-09 09:17:22 +02001547 unsigned int port = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001548 int sock;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001549 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001550 struct nc_session *session = NULL;
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001551 char *buf = NULL;
1552 size_t buf_len = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001553
1554 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001555 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001556 return NULL;
1557 }
1558
1559 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001560 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001561 if (!session) {
1562 ERRMEM;
1563 return NULL;
1564 }
1565 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001566 session->ti_type = NC_TI_LIBSSH;
1567 session->ti.libssh.session = ssh_session;
1568
1569 /* was port set? */
Michal Vasko097fc852021-03-09 08:18:17 +01001570 ssh_options_get_port(ssh_session, &port);
Michal Vasko30e2c872016-02-18 10:03:21 +01001571
1572 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1573 /*
1574 * There is no file descriptor (detected based on the host, there is no way to check
1575 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1576 */
1577
1578 /* remember host */
1579 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001580 if (!host) {
1581 ERRMEM;
1582 goto fail;
1583 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001584 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1585
1586 /* create and connect socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001587 sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001588 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001589 ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001590 goto fail;
1591 }
1592 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001593 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko66032bc2019-01-22 15:03:12 +01001594
1595 free(host);
1596 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001597 }
1598
1599 /* was username set? */
1600 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1601
1602 if (!ssh_is_connected(ssh_session)) {
1603 /*
1604 * We are connected, but not SSH authenticated. (Transport layer)
1605 */
1606
1607 /* remember username */
1608 if (!username) {
1609 if (!opts->username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001610 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Michal Vasko30e2c872016-02-18 10:03:21 +01001611 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001612 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001613 goto fail;
1614 }
1615 username = strdup(pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001616 free(buf);
Michal Vasko30e2c872016-02-18 10:03:21 +01001617 } else {
1618 username = strdup(opts->username);
1619 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001620 if (!username) {
1621 ERRMEM;
1622 goto fail;
1623 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001624 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1625 }
1626
1627 /* connect and authenticate SSH session */
1628 session->host = host;
1629 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001630 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001631 goto fail;
1632 }
1633 }
1634
1635 /*
1636 * Almost done, open a netconf channel. (Transport layer / application layer)
1637 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001638 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001639 goto fail;
1640 }
1641
1642 /*
1643 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1644 */
1645
Radek Krejcifd5b6682017-06-13 15:52:53 +02001646 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1647 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001648 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001649 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001650
1651 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001652 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001653 goto fail;
1654 }
1655 session->status = NC_STATUS_RUNNING;
1656
1657 if (nc_ctx_check_and_fill(session) == -1) {
1658 goto fail;
1659 }
1660
Michal Vasko93224072021-11-09 12:14:28 +01001661 /* store information if not previously */
1662 session->host = host;
1663 session->port = port;
1664 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001665
1666 return session;
1667
1668fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001669 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001670 return NULL;
1671}
1672
Radek Krejciac6d3472015-10-22 15:47:18 +02001673API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001674nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001675{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001676 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001677 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001678 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001679 char *username, *ip_host = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001680 struct passwd *pw, pw_buf;
Radek Krejciac6d3472015-10-22 15:47:18 +02001681 struct nc_session *session = NULL;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001682 char *buf = NULL;
1683 size_t buf_len = 0;
Radek Krejciac6d3472015-10-22 15:47:18 +02001684
1685 /* process parameters */
1686 if (!host || strisempty(host)) {
1687 host = "localhost";
1688 }
1689
1690 if (!port) {
1691 port = NC_PORT_SSH;
1692 }
Michal Vasko55fded62016-02-02 12:19:34 +01001693 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001694
Michal Vasko3031aae2016-01-27 16:07:18 +01001695 if (!ssh_opts.username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001696 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Radek Krejciac6d3472015-10-22 15:47:18 +02001697 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001698 ERR(session, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko1d430d92021-10-11 09:30:43 +02001699 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001700 } else {
1701 username = pw->pw_name;
1702 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001703 } else {
1704 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001705 }
1706
1707 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001708 session = nc_new_session(NC_CLIENT, 0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001709 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001710 ERRMEM;
Michal Vasko1d430d92021-10-11 09:30:43 +02001711 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001712 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001713 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001714
Michal Vasko131120a2018-05-29 15:44:02 +02001715 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001716 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001717 session->ti.libssh.session = ssh_new();
1718 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001719 ERR(session, "Unable to initialize SSH session.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001720 goto fail;
1721 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001722
Michal Vasko7b62fed2015-10-26 15:39:46 +01001723 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001724 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001725 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001726 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001727 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
1728
1729 /* create and assign communication socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001730 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001731 if (sock == -1) {
Michal Vasko05532772021-06-03 12:12:38 +02001732 ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001733 goto fail;
1734 }
1735 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001736 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001737
Michal Vasko93224072021-11-09 12:14:28 +01001738 /* store information for session connection */
1739 session->host = strdup(host);
1740 session->username = strdup(username);
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001741 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1) ||
1742 (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001743 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001744 }
1745
Radek Krejcifd5b6682017-06-13 15:52:53 +02001746 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1747 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001748 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001749 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001750
Radek Krejciac6d3472015-10-22 15:47:18 +02001751 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001752 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001753 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001754 }
Michal Vaskoad611702015-12-03 13:41:51 +01001755 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001756
Michal Vaskoef578332016-01-25 13:20:09 +01001757 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001758 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001759 }
1760
Michal Vasko93224072021-11-09 12:14:28 +01001761 /* update information */
1762 free(session->host);
1763 session->host = ip_host;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001764 session->port = port;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001765
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001766 free(buf);
Radek Krejciac6d3472015-10-22 15:47:18 +02001767 return session;
1768
Michal Vasko7b62fed2015-10-26 15:39:46 +01001769fail:
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001770 free(buf);
Michal Vasko66032bc2019-01-22 15:03:12 +01001771 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001772 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001773 return NULL;
1774}
1775
1776API struct nc_session *
1777nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1778{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001779 return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001780}
1781
1782API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001783nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001784{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001785 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001786
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001787 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001788 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001789 return NULL;
1790 }
1791
Michal Vasko7b62fed2015-10-26 15:39:46 +01001792 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001793 new_session = nc_new_session(NC_CLIENT, 1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001794 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001795 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001796 return NULL;
1797 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001798 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001799
Michal Vasko131120a2018-05-29 15:44:02 +02001800 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001801 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001802 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001803 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001804
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001805 /* append to the session ring list */
1806 if (!session->ti.libssh.next) {
1807 session->ti.libssh.next = new_session;
1808 new_session->ti.libssh.next = session;
1809 } else {
1810 ptr = session->ti.libssh.next;
1811 session->ti.libssh.next = new_session;
1812 new_session->ti.libssh.next = ptr;
1813 }
1814
Michal Vasko7b62fed2015-10-26 15:39:46 +01001815 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001816 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001817 goto fail;
1818 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001819 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001820 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001821 }
Michal Vasko131120a2018-05-29 15:44:02 +02001822 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001823
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001824 if (nc_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001825 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001826 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001827 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001828
Michal Vasko7b62fed2015-10-26 15:39:46 +01001829 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001830 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001831 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001832 }
Michal Vaskoad611702015-12-03 13:41:51 +01001833 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001834
Michal Vaskoef578332016-01-25 13:20:09 +01001835 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001836 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001837 }
1838
Michal Vasko93224072021-11-09 12:14:28 +01001839 /* store information into session */
1840 new_session->host = strdup(session->host);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001841 new_session->port = session->port;
Michal Vasko93224072021-11-09 12:14:28 +01001842 new_session->username = strdup(session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001843
Michal Vasko7b62fed2015-10-26 15:39:46 +01001844 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001845
1846fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001847 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001848 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001849}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001850
Michal Vasko3031aae2016-01-27 16:07:18 +01001851struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001852nc_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 +01001853{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001854 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001855 unsigned int uint_port;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001856 struct passwd *pw, pw_buf;
Michal Vasko30e2c872016-02-18 10:03:21 +01001857 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001858 ssh_session sess;
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001859 char *buf = NULL;
1860 size_t buf_len = 0;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001861
Michal Vasko80cad7f2015-12-08 14:42:27 +01001862 sess = ssh_new();
1863 if (!sess) {
Michal Vasko05532772021-06-03 12:12:38 +02001864 ERR(NULL, "Unable to initialize an SSH session.");
Michal Vasko80cad7f2015-12-08 14:42:27 +01001865 close(sock);
1866 return NULL;
1867 }
1868
1869 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001870 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001871 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001872 uint_port = port;
1873 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001874 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001875 if (!ssh_ch_opts.username) {
Jan Kundrát6aa0eeb2021-10-08 21:10:05 +02001876 pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len);
Michal Vasko3031aae2016-01-27 16:07:18 +01001877 if (!pw) {
Michal Vasko05532772021-06-03 12:12:38 +02001878 ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko435e5cf2019-04-23 08:48:44 +02001879 ssh_free(sess);
Michal Vasko3031aae2016-01-27 16:07:18 +01001880 return NULL;
1881 }
1882 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
Michal Vaskoccd2dd02021-10-11 09:13:01 +02001883 free(buf);
Michal Vasko3031aae2016-01-27 16:07:18 +01001884 } else {
1885 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001886 }
Michal Vasko8fd6fca2019-02-04 10:59:49 +01001887 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1888 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1889#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
1890 ssh_options_set(sess, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1891 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1892#endif
Michal Vasko80cad7f2015-12-08 14:42:27 +01001893
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001894 session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
Michal Vasko435e5cf2019-04-23 08:48:44 +02001895 if (!session) {
Michal Vasko457f0532019-08-15 13:59:49 +02001896 /* sess is freed */
Michal Vasko435e5cf2019-04-23 08:48:44 +02001897 return NULL;
Michal Vasko4282fae2016-02-18 10:03:42 +01001898 }
1899
Michal Vasko435e5cf2019-04-23 08:48:44 +02001900 session->flags |= NC_SESSION_CALLHOME;
Michal Vasko30e2c872016-02-18 10:03:21 +01001901 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001902}