blob: a36ea6f7289fc73992b411ebd5804d986af85121 [file] [log] [blame]
Radek Krejciac6d3472015-10-22 15:47:18 +02001/**
Michal Vasko086311b2016-01-08 09:53:11 +01002 * \file session_client_ssh.c
Radek Krejciac6d3472015-10-22 15:47:18 +02003 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko086311b2016-01-08 09:53:11 +01004 * \author Michal Vasko <mvasko@cesnet.cz>
5 * \brief libnetconf2 - SSH specific client session transport functions
Radek Krejciac6d3472015-10-22 15:47:18 +02006 *
7 * This source is compiled only with libssh.
8 *
9 * Copyright (c) 2015 CESNET, z.s.p.o.
10 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010011 * This source code is licensed under BSD 3-Clause License (the "License").
12 * You may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010014 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010015 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejciac6d3472015-10-22 15:47:18 +020016 */
17
Michal Vasko7b62fed2015-10-26 15:39:46 +010018#define _GNU_SOURCE
Radek Krejciac6d3472015-10-22 15:47:18 +020019#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010020#include <stdlib.h>
21#include <stddef.h>
22#include <stdio.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020023#include <string.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010024#include <errno.h>
25#include <fcntl.h>
26#include <termios.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <pwd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020030#include <unistd.h>
Michal Vasko36c7be82017-02-22 13:37:59 +010031#include <pthread.h>
32#include <time.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020033
Michal Vasko7b62fed2015-10-26 15:39:46 +010034#ifdef ENABLE_DNSSEC
35# include <validator/validator.h>
36# include <validator/resolver.h>
37# include <validator/validator-compat.h>
38#endif
39
Michal Vasko745ff832015-12-08 14:40:29 +010040#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020041#include <libyang/libyang.h>
42
Michal Vaskoe22c6732016-01-29 11:03:02 +010043#include "session_client.h"
44#include "session_client_ch.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020045#include "libnetconf.h"
46
Radek Krejci62aa0642017-05-25 16:33:49 +020047struct nc_client_context *nc_client_context_location(void);
Radek Krejcifd5b6682017-06-13 15:52:53 +020048int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx);
Michal Vasko30e2c872016-02-18 10:03:21 +010049
Radek Krejci62aa0642017-05-25 16:33:49 +020050#define client_opts nc_client_context_location()->opts
51#define ssh_opts nc_client_context_location()->ssh_opts
52#define ssh_ch_opts nc_client_context_location()->ssh_ch_opts
Michal Vasko3031aae2016-01-27 16:07:18 +010053
Michal Vaskoa43b8e32017-05-12 11:46:20 +020054static FILE *
55open_tty_noecho(const char *path, struct termios *oldterm)
56{
57 struct termios newterm;
58 FILE *ret;
59
60 if (!(ret = fopen(path, "r"))) {
Michal Vasko51228ac2018-03-29 14:57:53 +020061 ERR("Unable to open terminal \"%s\" for reading (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020062 return NULL;
63 }
64
65 if (tcgetattr(fileno(ret), oldterm)) {
Michal Vasko88583042018-03-29 09:18:58 +020066 ERR("Unable to get terminal \"%s\" settings (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020067 fclose(ret);
68 return NULL;
69 }
70
71 newterm = *oldterm;
72 newterm.c_lflag &= ~ECHO;
73 newterm.c_lflag &= ~ICANON;
74 tcflush(fileno(ret), TCIFLUSH);
75 if (tcsetattr(fileno(ret), TCSANOW, &newterm)) {
Michal Vasko88583042018-03-29 09:18:58 +020076 ERR("Unable to change terminal \"%s\" settings for hiding password (%s).", path, strerror(errno));
Michal Vaskoa43b8e32017-05-12 11:46:20 +020077 fclose(ret);
78 return NULL;
79 }
80
81 return ret;
82}
83
Michal Vasko51228ac2018-03-29 14:57:53 +020084static FILE *
85nc_open_in(int echo, struct termios *oldterm)
Michal Vaskoa43b8e32017-05-12 11:46:20 +020086{
Michal Vasko51228ac2018-03-29 14:57:53 +020087 char buf[512];
88 int buflen = 512, ret;
89 FILE *in;
90
91 if (!echo) {
92 in = open_tty_noecho("/dev/tty", oldterm);
93 } else {
94 in = fopen("/dev/tty", "r");
95 if (!in) {
96 ERR("Unable to open terminal \"/dev/tty\" for reading (%s).", strerror(errno));
97 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +020098 }
Michal Vasko51228ac2018-03-29 14:57:53 +020099
100 if (!in) {
101 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
102 ERR("ttyname_r failed (%s).", strerror(ret));
103 return NULL;
104 }
105
106 if (!echo) {
107 in = open_tty_noecho(buf, oldterm);
108 } else {
109 in = fopen(buf, "r");
110 if (!in) {
111 ERR("Unable to open terminal \"%s\" for reading (%s).", buf, strerror(errno));
112 }
113 }
114 }
115
116 return in;
117}
118
119static FILE *
120nc_open_out(void)
121{
122 char buf[512];
123 int buflen = 512, ret;
124 FILE *out;
125
126 out = fopen("/dev/tty", "w");
127 if (!out) {
128 ERR("Unable to open terminal \"/dev/tty\" for writing (%s).", strerror(errno));
129
130 if ((ret = ttyname_r(STDOUT_FILENO, buf, buflen))) {
131 ERR("ttyname_r failed (%s).", strerror(ret));
132 return NULL;
133 }
134
135 out = fopen(buf, "w");
136 if (!out) {
137 ERR("Unable to open terminal \"%s\" for writing (%s).", buf, strerror(errno));
138 }
139 }
140
141 return out;
142}
143
144static void
145nc_close_inout(FILE *inout, int echo, struct termios *oldterm)
146{
147 if (inout) {
148 if (!echo && (tcsetattr(fileno(inout), TCSANOW, oldterm) != 0)) {
149 ERR("Unable to restore terminal settings (%s).", strerror(errno));
150 }
151 fclose(inout);
152 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200153}
154
Kevin Barrettd37d2fb2019-08-28 14:25:34 -0400155void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100156_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +0100157{
158 int i;
159
Michal Vaskoe22c6732016-01-29 11:03:02 +0100160 for (i = 0; i < opts->key_count; ++i) {
161 free(opts->keys[i].pubkey_path);
162 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +0100163 }
Michal Vaskoe22c6732016-01-29 11:03:02 +0100164 free(opts->keys);
165 free(opts->username);
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200166 opts->keys = NULL;
167 opts->username = NULL;
Michal Vaskoe22c6732016-01-29 11:03:02 +0100168}
Michal Vasko990089e2015-10-27 15:05:25 +0100169
Michal Vaskob7558c52016-02-26 15:04:19 +0100170void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100171nc_client_ssh_destroy_opts(void)
172{
173 _nc_client_ssh_destroy_opts(&ssh_opts);
174 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +0100175}
176
Michal Vaskoef112d72016-02-18 13:28:25 +0100177#ifdef ENABLE_DNSSEC
178
179/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
180/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
181static int
Michal Vasko650011a2016-02-25 14:49:29 +0100182sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg) {
Michal Vaskoef112d72016-02-18 13:28:25 +0100183 ns_msg handle;
184 ns_rr rr;
185 val_status_t val_status;
186 const unsigned char* rdata;
187 unsigned char buf[4096];
188 int buf_len = 4096;
189 int ret = 0, i, j, len;
190
191 /* class 1 - internet, type 44 - SSHFP */
192 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
193
194 if ((len < 0) || !val_istrusted(val_status)) {
195 ret = 2;
196 goto finish;
197 }
198
199 if (ns_initparse(buf, len, &handle) < 0) {
200 ERR("Failed to initialize DNSSEC response parser.");
201 ret = 2;
202 goto finish;
203 }
204
205 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
206 ERR("DNSSEC query returned %d.", i);
207 ret = 2;
208 goto finish;
209 }
210
211 if (!libsres_msg_getflag(handle, ns_f_ad)) {
212 /* response not secured by DNSSEC */
213 ret = 1;
214 }
215
216 /* query section */
217 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
Michal Vasko650011a2016-02-25 14:49:29 +0100218 ERR("DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100219 ret = 2;
220 goto finish;
221 }
222
223 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
Michal Vasko650011a2016-02-25 14:49:29 +0100224 ERR("DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100225 ret = 2;
226 goto finish;
227 }
228
229 /* answer section */
230 i = 0;
231 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
232 if (ns_rr_type(rr) != 44) {
233 ++i;
234 continue;
235 }
236
237 rdata = ns_rr_rdata(rr);
238 if (rdata[0] != type) {
239 ++i;
240 continue;
241 }
242 if (rdata[1] != alg) {
243 ++i;
244 continue;
245 }
246
247 /* we found the correct SSHFP entry */
248 rdata += 2;
249 for (j = 0; j < 20; ++j) {
250 if (rdata[j] != (unsigned char)sha1hash[j]) {
251 ret = 2;
252 goto finish;
253 }
254 }
255
256 /* server fingerprint is supported by a DNS entry,
257 * we have already determined if DNSSEC was used or not
258 */
259 goto finish;
260 }
261
262 /* no match */
263 ret = 2;
264
265finish:
266 val_free_validator_state();
267 return ret;
268}
269
270#endif /* ENABLE_DNSSEC */
271
Radek Krejci62aa0642017-05-25 16:33:49 +0200272int
Radek Krejci90a84a22017-05-25 13:53:00 +0200273sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(priv))
Michal Vaskoef112d72016-02-18 13:28:25 +0100274{
Michal Vasko51228ac2018-03-29 14:57:53 +0200275 char *hexa = NULL;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800276#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
277 int c, ret;
278 enum ssh_known_hosts_e state;
279#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100280 int c, state, ret;
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800281#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100282 ssh_key srv_pubkey;
283 unsigned char *hash_sha1 = NULL;
284 size_t hlen;
285 enum ssh_keytypes_e srv_pubkey_type;
286 char answer[5];
Michal Vasko51228ac2018-03-29 14:57:53 +0200287 FILE *out = NULL, *in = NULL;
Michal Vaskoef112d72016-02-18 13:28:25 +0100288
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800289#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
290 state = ssh_session_is_known_server(session);
291#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100292 state = ssh_is_server_known(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800293#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100294
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800295#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 0))
296 ret = ssh_get_server_publickey(session, &srv_pubkey);
297#else
Michal Vaskocc0aa7d2016-05-31 12:48:42 +0200298 ret = ssh_get_publickey(session, &srv_pubkey);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800299#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100300 if (ret < 0) {
301 ERR("Unable to get server public key.");
302 return -1;
303 }
304
305 srv_pubkey_type = ssh_key_type(srv_pubkey);
306 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
307 ssh_key_free(srv_pubkey);
308 if (ret < 0) {
309 ERR("Failed to calculate SHA1 hash of the server public key.");
310 return -1;
311 }
312
313 hexa = ssh_get_hexa(hash_sha1, hlen);
314
315 switch (state) {
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800316#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
317 case SSH_KNOWN_HOSTS_OK:
318#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100319 case SSH_SERVER_KNOWN_OK:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800320#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100321 break; /* ok */
322
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800323#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
324 case SSH_KNOWN_HOSTS_CHANGED:
325#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100326 case SSH_SERVER_KNOWN_CHANGED:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800327#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100328 ERR("Remote host key changed, the connection will be terminated!");
Michal Vasko51228ac2018-03-29 14:57:53 +0200329 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100330
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800331#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
332 case SSH_KNOWN_HOSTS_OTHER:
333#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100334 case SSH_SERVER_FOUND_OTHER:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800335#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100336 WRN("Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
337 goto hostkey_not_known;
338
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800339#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
340 case SSH_KNOWN_HOSTS_NOT_FOUND:
341#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100342 case SSH_SERVER_FILE_NOT_FOUND:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800343#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100344 WRN("Could not find the known hosts file.");
345 goto hostkey_not_known;
346
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800347#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
348 case SSH_KNOWN_HOSTS_UNKNOWN:
349#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100350 case SSH_SERVER_NOT_KNOWN:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800351#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100352hostkey_not_known:
353#ifdef ENABLE_DNSSEC
354 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
355 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
Michal Vasko650011a2016-02-25 14:49:29 +0100356 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100357 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100358 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100359 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100360 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100361 }
362
363 /* DNSSEC SSHFP check successful, that's enough */
364 if (!ret) {
365 VRB("DNSSEC SSHFP check successful.");
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800366#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
367 ssh_session_update_known_hosts(session);
368#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100369 ssh_write_knownhost(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800370#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100371 ssh_clean_pubkey_hash(&hash_sha1);
372 ssh_string_free_char(hexa);
373 return 0;
374 }
375 }
376#endif
377
Michal Vasko51228ac2018-03-29 14:57:53 +0200378 if (!(in = nc_open_in(1, NULL))) {
379 goto error;
380 }
381 if (!(out = nc_open_out())) {
382 goto error;
383 }
384
Michal Vaskoef112d72016-02-18 13:28:25 +0100385 /* try to get result from user */
Michal Vasko51228ac2018-03-29 14:57:53 +0200386 if (fprintf(out, "The authenticity of the host \'%s\' cannot be established.\n", hostname) < 1) {
387 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
388 goto error;
389 }
390 if (fprintf(out, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa) < 1) {
391 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
392 goto error;
393 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100394
395#ifdef ENABLE_DNSSEC
396 if (ret == 2) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200397 if (fprintf(out, "No matching host key fingerprint found using DNS.\n") < 1) {
398 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
399 goto error;
400 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100401 } else if (ret == 1) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200402 if (fprintf(out, "Matching host key fingerprint found using DNS.\n") < 1) {
403 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
404 goto error;
405 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100406 }
407#endif
408
Michal Vasko51228ac2018-03-29 14:57:53 +0200409 if (fprintf(out, "Are you sure you want to continue connecting (yes/no)? ") < 1) {
410 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
411 goto error;
412 }
413 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100414
415 do {
Michal Vasko51228ac2018-03-29 14:57:53 +0200416 if (fscanf(in, "%4s", answer) == EOF) {
417 ERR("Reading from input failed (%s).", feof(in) ? "EOF" : strerror(errno));
418 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100419 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200420 while (((c = getc(in)) != EOF) && (c != '\n'));
Michal Vaskoef112d72016-02-18 13:28:25 +0100421
Michal Vasko51228ac2018-03-29 14:57:53 +0200422 fflush(in);
Michal Vaskoef112d72016-02-18 13:28:25 +0100423 if (!strcmp("yes", answer)) {
424 /* store the key into the host file */
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800425#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
426 ret = ssh_session_update_known_hosts(session);
427#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100428 ret = ssh_write_knownhost(session);
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800429#endif
Michal Vaskoef112d72016-02-18 13:28:25 +0100430 if (ret != SSH_OK) {
431 WRN("Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
432 }
433 } else if (!strcmp("no", answer)) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200434 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100435 } else {
Michal Vasko51228ac2018-03-29 14:57:53 +0200436 if (fprintf(out, "Please type 'yes' or 'no': ") < 1) {
437 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
438 goto error;
439 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100440 }
441 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
442
443 break;
444
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800445#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0))
446 case SSH_KNOWN_HOSTS_ERROR:
447#else
Michal Vaskoef112d72016-02-18 13:28:25 +0100448 case SSH_SERVER_ERROR:
Bi-Ruei, Chiu947d8122019-09-08 19:28:05 +0800449#endif
Michal Vasko3b49eb22018-05-24 09:17:49 +0200450 ERR("SSH error: %s", ssh_get_error(session));
Michal Vasko51228ac2018-03-29 14:57:53 +0200451 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100452 }
453
Michal Vasko51228ac2018-03-29 14:57:53 +0200454 nc_close_inout(in, 1, NULL);
455 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100456 ssh_clean_pubkey_hash(&hash_sha1);
457 ssh_string_free_char(hexa);
458 return 0;
459
Michal Vasko51228ac2018-03-29 14:57:53 +0200460error:
461 nc_close_inout(in, 1, NULL);
462 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100463 ssh_clean_pubkey_hash(&hash_sha1);
464 ssh_string_free_char(hexa);
465 return -1;
466}
467
Radek Krejci62aa0642017-05-25 16:33:49 +0200468char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200469sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200470{
Michal Vasko51228ac2018-03-29 14:57:53 +0200471 char *buf = NULL;
472 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200473 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200474 FILE *in = NULL, *out = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200475
Michal Vasko11d142a2016-01-19 15:58:24 +0100476 buf = malloc(buflen * sizeof *buf);
477 if (!buf) {
478 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100479 return NULL;
480 }
481
Michal Vasko51228ac2018-03-29 14:57:53 +0200482 if (!(in = nc_open_in(0, &oldterm))) {
483 goto error;
484 }
485 if (!(out = nc_open_out())) {
486 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200487 }
488
Michal Vasko51228ac2018-03-29 14:57:53 +0200489 if (fprintf(out, "%s@%s password: ", username, hostname) < 1) {
490 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
491 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200492 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200493 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200494
495 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200496 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100497 if (len >= buflen - 1) {
498 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100499 buf = nc_realloc(buf, buflen * sizeof *buf);
500 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100501 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200502 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100503 }
504 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100505 buf[len++] = (char)c;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100506 }
507 buf[len++] = 0; /* terminating null byte */
508
Michal Vasko51228ac2018-03-29 14:57:53 +0200509 fprintf(out, "\n");
510
511 nc_close_inout(in, 0, &oldterm);
512 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100513 return buf;
Michal Vasko93f26d92018-02-01 09:08:35 +0100514
Michal Vasko51228ac2018-03-29 14:57:53 +0200515error:
516 nc_close_inout(in, 0, &oldterm);
517 nc_close_inout(out, 1, NULL);
Michal Vasko93f26d92018-02-01 09:08:35 +0100518 free(buf);
519 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100520}
521
Radek Krejci62aa0642017-05-25 16:33:49 +0200522char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200523sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100524{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200525 unsigned int buflen = 64, cur_len;
Michal Vasko51228ac2018-03-29 14:57:53 +0200526 int c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200527 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200528 char *buf = NULL;
529 FILE *in = NULL, *out = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100530
531 buf = malloc(buflen * sizeof *buf);
532 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100533 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100534 return NULL;
535 }
536
Michal Vasko51228ac2018-03-29 14:57:53 +0200537 if (!(in = nc_open_in(echo, &oldterm))) {
538 goto error;
539 }
540 if (!(out = nc_open_out())) {
541 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100542 }
543
Michal Vasko51228ac2018-03-29 14:57:53 +0200544 if (auth_name && (fprintf(out, "%s\n", auth_name) < 1)) {
545 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
546 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100547 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200548 if (instruction && (fprintf(out, "%s\n", instruction) < 1)) {
549 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
550 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100551 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200552 if (fputs(prompt, out) == EOF) {
553 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
554 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200555 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200556 fflush(out);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100557
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200558 cur_len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200559 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200560 if (cur_len >= buflen - 1) {
561 buflen *= 2;
562 buf = nc_realloc(buf, buflen * sizeof *buf);
563 if (!buf) {
564 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200565 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200566 }
567 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100568 buf[cur_len++] = (char)c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200569 }
570 /* terminating null byte */
571 buf[cur_len] = '\0';
572
Michal Vasko51228ac2018-03-29 14:57:53 +0200573 fprintf(out, "\n");
574
575 nc_close_inout(in, echo, &oldterm);
576 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200577 return buf;
578
Michal Vasko51228ac2018-03-29 14:57:53 +0200579error:
580 nc_close_inout(in, echo, &oldterm);
581 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200582 free(buf);
583 return NULL;
584}
585
Radek Krejci62aa0642017-05-25 16:33:49 +0200586char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200587sshauth_privkey_passphrase(const char* privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200588{
Michal Vasko51228ac2018-03-29 14:57:53 +0200589 char *buf = NULL;
590 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200591 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200592 FILE *in = NULL, *out = NULL;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200593
594 buf = malloc(buflen * sizeof *buf);
595 if (!buf) {
596 ERRMEM;
597 return NULL;
598 }
599
Michal Vasko51228ac2018-03-29 14:57:53 +0200600 if (!(in = nc_open_in(0, &oldterm))) {
601 goto error;
602 }
603 if (!(out = nc_open_out())) {
604 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200605 }
606
Michal Vasko51228ac2018-03-29 14:57:53 +0200607 if (fprintf(out, "Enter passphrase for the key '%s': ", privkey_path) < 1) {
608 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
609 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200610 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200611 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200612
613 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200614 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100615 if (len >= buflen - 1) {
616 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100617 buf = nc_realloc(buf, buflen * sizeof *buf);
618 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100619 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200620 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100621 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100622 }
623 buf[len++] = (char)c;
624 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200625 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100626
Michal Vasko51228ac2018-03-29 14:57:53 +0200627 fprintf(out, "\n");
628
629 nc_close_inout(in, 0, &oldterm);
630 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100631 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100632
Michal Vasko51228ac2018-03-29 14:57:53 +0200633error:
634 nc_close_inout(in, 0, &oldterm);
635 nc_close_inout(out, 1, NULL);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100636 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100637 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100638}
639
Michal Vaskoef112d72016-02-18 13:28:25 +0100640static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200641_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
642 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100643{
Michal Vaskoef112d72016-02-18 13:28:25 +0100644 if (auth_hostkey_check) {
645 opts->auth_hostkey_check = auth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200646 opts->auth_hostkey_check_priv = priv;
Michal Vaskoef112d72016-02-18 13:28:25 +0100647 } else {
648 opts->auth_hostkey_check = sshauth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200649 opts->auth_hostkey_check_priv = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100650 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100651}
652
Radek Krejci90a84a22017-05-25 13:53:00 +0200653static void
654_nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
655 void **priv, struct nc_client_ssh_opts *opts)
Michal Vaskoef112d72016-02-18 13:28:25 +0100656{
Radek Krejci90a84a22017-05-25 13:53:00 +0200657 if (auth_hostkey_check) {
658 (*auth_hostkey_check) = opts->auth_hostkey_check == sshauth_hostkey_check ? NULL : opts->auth_hostkey_check;
659 }
660 if (priv) {
661 (*priv) = opts->auth_hostkey_check_priv;
662 }
663}
664
665
666API void
667nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
668 void *priv)
669{
670 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100671}
672
673API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200674nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
675 void *priv)
Michal Vaskoef112d72016-02-18 13:28:25 +0100676{
Radek Krejci90a84a22017-05-25 13:53:00 +0200677 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100678}
679
Radek Krejci90a84a22017-05-25 13:53:00 +0200680API void
681nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
682 void **priv)
683{
684 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
685}
686
687API void
688nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
689 void **priv)
690{
691 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
692}
Michal Vaskoef112d72016-02-18 13:28:25 +0100693
Michal Vasko30e2c872016-02-18 10:03:21 +0100694static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200695_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
696 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100697{
698 if (auth_password) {
699 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200700 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100701 } else {
702 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200703 opts->auth_password_priv = NULL;
704 }
705}
706
707static void
708_nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
709 void **priv, struct nc_client_ssh_opts *opts)
710{
711 if (auth_password) {
712 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
713 }
714 if (priv) {
715 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100716 }
717}
718
719API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200720nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
721 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100722{
Radek Krejci90a84a22017-05-25 13:53:00 +0200723 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100724}
725
726API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200727nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
728 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_ch_opts);
731}
732
733API void
734nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
735 void **priv)
736{
737 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
738}
739
740API void
741nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
742 void **priv)
743{
744 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100745}
746
747static void
748_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200749 const char *prompt, int echo, void *priv),
750 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100751{
752 if (auth_interactive) {
753 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200754 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100755 } else {
756 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200757 opts->auth_interactive_priv = NULL;
758 }
759}
760
761static void
762_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
763 const char *prompt, int echo, void *priv),
764 void **priv, struct nc_client_ssh_opts *opts)
765{
766 if (auth_interactive) {
767 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
768 }
769 if (priv) {
770 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100771 }
772}
773
774API void
775nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200776 const char *prompt, int echo, void *priv),
777 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100778{
Radek Krejci90a84a22017-05-25 13:53:00 +0200779 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100780}
781
782API void
783nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200784 const char *prompt, int echo, void *priv),
785 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100786{
Radek Krejci90a84a22017-05-25 13:53:00 +0200787 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
788}
789
790API void
791nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
792 const char *prompt, int echo, void *priv),
793 void **priv)
794{
795 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
796}
797
798API void
799nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
800 const char *prompt, int echo, void *priv),
801 void **priv)
802{
803 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100804}
805
806static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200807_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
808 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100809{
810 if (auth_privkey_passphrase) {
811 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200812 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100813 } else {
814 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200815 opts->auth_privkey_passphrase_priv = NULL;
816 }
817}
818
819static void
820_nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
821 void **priv, struct nc_client_ssh_opts *opts)
822{
823 if (auth_privkey_passphrase) {
824 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
825 }
826 if (priv) {
827 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100828 }
829}
830
831API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200832nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
833 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100834{
Radek Krejci90a84a22017-05-25 13:53:00 +0200835 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100836}
837
838API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200839nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
840 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_ch_opts);
843}
844
845API void
846nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
847 void **priv)
848{
849 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
850}
851
852API void
853nc_client_ssh_ch_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
854 void **priv)
855{
856 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100857}
858
Michal Vasko3031aae2016-01-27 16:07:18 +0100859static int
860_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 +0100861{
862 int i;
863 FILE *key;
864 char line[128];
865
Michal Vasko45e53ae2016-04-07 11:46:03 +0200866 if (!pub_key) {
867 ERRARG("pub_key");
868 return -1;
869 } else if (!priv_key) {
870 ERRARG("priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100871 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100872 }
873
Michal Vasko3031aae2016-01-27 16:07:18 +0100874 for (i = 0; i < opts->key_count; ++i) {
875 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
876 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100877 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100878 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100879 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100880 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100881 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100882 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100883 continue;
884 }
885
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100886 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100887 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100888 }
889 }
890
Michal Vasko3031aae2016-01-27 16:07:18 +0100891 /* add the keys */
892 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100893 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
894 if (!opts->keys) {
895 ERRMEM;
896 return -1;
897 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100898 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
899 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
900 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100901
Michal Vasko4eb3c312016-03-01 14:09:37 +0100902 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
903 ERRMEM;
904 return -1;
905 }
906
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100907 /* check encryption */
908 if ((key = fopen(priv_key, "r"))) {
909 /* 1st line - key type */
910 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100911 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100912 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100913 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100914 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100915 /* 2nd line - encryption information or key */
916 if (!fgets(line, sizeof line, key)) {
917 fclose(key);
918 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100919 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100920 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100921 fclose(key);
922 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100923 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100924 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100925 }
926
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100927 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100928}
929
930API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100931nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100932{
Michal Vasko3031aae2016-01-27 16:07:18 +0100933 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
934}
935
936API int
937nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
938{
939 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
940}
941
942static int
943_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
944{
945 if (idx >= opts->key_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200946 ERRARG("idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100947 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100948 }
949
Michal Vasko3031aae2016-01-27 16:07:18 +0100950 free(opts->keys[idx].pubkey_path);
951 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100952
Michal Vasko3031aae2016-01-27 16:07:18 +0100953 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100954 if (idx < opts->key_count) {
955 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
956 }
957 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100958 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
959 if (!opts->keys) {
960 ERRMEM;
961 return -1;
962 }
Michal Vaskoc0256492016-02-02 12:19:06 +0100963 } else {
964 free(opts->keys);
965 opts->keys = NULL;
966 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100967
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100968 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100969}
970
971API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100972nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100973{
Michal Vasko3031aae2016-01-27 16:07:18 +0100974 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100975}
976
977API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100978nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100979{
Michal Vasko3031aae2016-01-27 16:07:18 +0100980 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
981}
982
983static int
984_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
985{
986 return opts->key_count;
987}
988
989API int
990nc_client_ssh_get_keypair_count(void)
991{
992 return _nc_client_ssh_get_keypair_count(&ssh_opts);
993}
994
995API int
996nc_client_ssh_ch_get_keypair_count(void)
997{
998 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
999}
1000
1001static int
1002_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
1003{
Michal Vasko45e53ae2016-04-07 11:46:03 +02001004 if (idx >= opts->key_count) {
1005 ERRARG("idx");
1006 return -1;
1007 } else if (!pub_key && !priv_key) {
1008 ERRARG("pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001009 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001010 }
1011
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001012 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001013 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001014 }
1015 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001016 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001017 }
1018
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001019 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001020}
1021
Michal Vasko3031aae2016-01-27 16:07:18 +01001022API int
1023nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
1024{
1025 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
1026}
1027
1028API int
1029nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
1030{
1031 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
1032}
1033
1034static void
1035_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 +01001036{
1037 if (pref < 0) {
1038 pref = -1;
1039 }
1040
1041 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001042 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001043 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001044 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001045 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001046 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001047 }
1048}
1049
Michal Vasko3031aae2016-01-27 16:07:18 +01001050API void
1051nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001052{
Michal Vasko3031aae2016-01-27 16:07:18 +01001053 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
1054}
1055
1056API void
1057nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
1058{
1059 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
1060}
1061
1062static int16_t
1063_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
1064{
1065 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001066
1067 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001068 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001069 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001070 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001071 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001072 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001073 }
1074
1075 return pref;
1076}
1077
Michal Vasko3031aae2016-01-27 16:07:18 +01001078API int16_t
1079nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1080{
1081 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
1082}
1083
1084API int16_t
1085nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1086{
1087 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
1088}
1089
1090static int
1091_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
1092{
1093 if (opts->username) {
1094 free(opts->username);
1095 }
1096 if (username) {
1097 opts->username = strdup(username);
1098 if (!opts->username) {
1099 ERRMEM;
1100 return -1;
1101 }
1102 } else {
1103 opts->username = NULL;
1104 }
1105
1106 return 0;
1107}
1108
1109API int
1110nc_client_ssh_set_username(const char *username)
1111{
1112 return _nc_client_ssh_set_username(username, &ssh_opts);
1113}
1114
1115API int
1116nc_client_ssh_ch_set_username(const char *username)
1117{
1118 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1119}
1120
Michal Vaskoe22c6732016-01-29 11:03:02 +01001121static const char *
1122_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1123{
1124 return opts->username;
1125}
1126
1127API const char *
1128nc_client_ssh_get_username(void)
1129{
1130 return _nc_client_ssh_get_username(&ssh_opts);
1131}
1132
1133API const char *
1134nc_client_ssh_ch_get_username(void)
1135{
1136 return _nc_client_ssh_get_username(&ssh_ch_opts);
1137}
1138
Michal Vasko3031aae2016-01-27 16:07:18 +01001139API int
1140nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1141{
1142 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
1143}
1144
1145API int
1146nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1147{
1148 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1149}
1150
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001151/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001152 * Host, port, username, and a connected socket is expected to be set.
Radek Krejciae813f42018-07-02 13:38:30 +02001153 *
1154 * return values
1155 * -1 failure
1156 * 0 try again
1157 * 1 success
Michal Vasko7b62fed2015-10-26 15:39:46 +01001158 */
1159static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001160connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001161{
Radek Krejciae813f42018-07-02 13:38:30 +02001162 int j, ret_auth, userauthlist, ret, attempt = 0;
Michal Vasko235efdc2015-12-17 12:05:04 +01001163 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001164 int16_t pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001165 const char* prompt;
1166 char *s, *answer, echo;
1167 ssh_key pubkey, privkey;
1168 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001169 struct timespec ts_timeout, ts_cur;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001170
1171 ssh_sess = session->ti.libssh.session;
1172
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001173 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001174 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001175 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1176 usleep(NC_TIMEOUT_STEP);
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001177 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001178 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001179 break;
1180 }
1181 }
1182 if (ret == SSH_AGAIN) {
1183 ERR("SSH connect timeout.");
1184 return 0;
1185 } else if (ret != SSH_OK) {
1186 ERR("Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001187 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001188 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001189 }
1190
Radek Krejci90a84a22017-05-25 13:53:00 +02001191 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001192 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001193 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001194 }
1195
Michal Vasko36c7be82017-02-22 13:37:59 +01001196 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001197 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001198 nc_addtimespec(&ts_timeout, timeout);
1199 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001200 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1201 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001202 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001203 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001204 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1205 break;
1206 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001207 }
1208 }
1209 if (ret_auth == SSH_AUTH_AGAIN) {
1210 ERR("Request authentication methods timeout.");
1211 return 0;
1212 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001213 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001214 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001215 } else if (ret_auth == SSH_AUTH_SUCCESS) {
1216 WRN("Server accepts \"none\" authentication method.")
1217 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001218 }
1219
1220 /* check what authentication methods are available */
1221 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001222
1223 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001224 if (opts->auth_pref[0].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001225 VRB("Interactive SSH authentication method was disabled.");
1226 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001227 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001228 if (opts->auth_pref[1].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001229 VRB("Password SSH authentication method was disabled.");
1230 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001231 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001232 if (opts->auth_pref[2].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001233 VRB("Publickey SSH authentication method was disabled.");
1234 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001235 }
1236
Michal Vasko0190bc32016-03-02 15:47:49 +01001237 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001238 auth = 0;
1239 pref = 0;
1240 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1241 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001242 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001243 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001244 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001245 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001246 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001247 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001248 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001249 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001250 }
1251
Michal Vasko235efdc2015-12-17 12:05:04 +01001252 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001253 if (!attempt) {
1254 ERR("Unable to authenticate to the remote server (no supported authentication methods detected).");
1255 } else {
1256 ERR("Unable to authenticate to the remote server (all attempts via supported authentication methods failed).");
1257 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001258 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001259 }
1260
1261 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001262 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001263 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001264 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1265
Michal Vaskoef578332016-01-25 13:20:09 +01001266 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001267 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001268 if (s == NULL) {
1269 ERR("Unable to get the password.");
1270 return -1;
1271 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001272
Michal Vasko36c7be82017-02-22 13:37:59 +01001273 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001274 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001275 nc_addtimespec(&ts_timeout, timeout);
1276 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001277 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1278 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001279 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001280 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001281 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1282 break;
1283 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001284 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001285 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001286 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001287 free(s);
1288 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001289
Michal Vasko7b62fed2015-10-26 15:39:46 +01001290 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001291 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1292
Michal Vaskod083db62016-01-19 10:31:29 +01001293 VRB("Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001294
Michal Vasko36c7be82017-02-22 13:37:59 +01001295 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001296 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001297 nc_addtimespec(&ts_timeout, timeout);
1298 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001299 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO)
1300 || (ret_auth == SSH_AUTH_AGAIN)) {
1301 if (ret_auth == SSH_AUTH_AGAIN) {
1302 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001303 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001304 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001305 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1306 break;
1307 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001308 }
1309 continue;
1310 }
1311
Michal Vasko7b62fed2015-10-26 15:39:46 +01001312 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1313 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001314 if (!prompt) {
1315 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001316 break;
1317 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001318
1319 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1320 echo = 0;
1321
Michal Vasko30e2c872016-02-18 10:03:21 +01001322 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
1323 ssh_userauth_kbdint_getinstruction(ssh_sess),
Radek Krejci90a84a22017-05-25 13:53:00 +02001324 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001325 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1326 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001327 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001328 break;
1329 }
1330 free(answer);
1331 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001332 if (ret_auth == SSH_AUTH_ERROR) {
1333 break;
1334 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001335 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001336 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001337 nc_addtimespec(&ts_timeout, timeout);
1338 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001339 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001340 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001341
Michal Vasko206d3b12015-12-04 11:08:42 +01001342 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001343 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1344
Michal Vaskod083db62016-01-19 10:31:29 +01001345 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001346
1347 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001348 if (!opts->key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001349 VRB("No key pair specified.");
1350 break;
1351 }
1352
Michal Vasko30e2c872016-02-18 10:03:21 +01001353 for (j = 0; j < opts->key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +01001354 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko30e2c872016-02-18 10:03:21 +01001355 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1356 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001357
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001358 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1359 if (ret == SSH_EOF) {
1360 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
1361 continue;
1362 } else if (ret == SSH_ERROR) {
1363 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001364 continue;
1365 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001366
Michal Vasko36c7be82017-02-22 13:37:59 +01001367 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001368 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001369 nc_addtimespec(&ts_timeout, timeout);
1370 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001371 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1372 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001373 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001374 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001375 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1376 break;
1377 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001378 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001379 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001380 ssh_key_free(pubkey);
1381
1382 if (ret_auth == SSH_AUTH_DENIED) {
1383 continue;
1384 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001385 break;
1386 }
1387
Michal Vasko30e2c872016-02-18 10:03:21 +01001388 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001389 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001390 } else {
1391 s = NULL;
1392 }
1393
Michal Vasko0190bc32016-03-02 15:47:49 +01001394 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001395 if (s) {
1396 memset(s, 0, strlen(s));
1397 free(s);
1398 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001399 if (ret == SSH_EOF) {
1400 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
1401 continue;
1402 } else if (ret == SSH_ERROR) {
1403 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001404 continue;
1405 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001406
Michal Vasko36c7be82017-02-22 13:37:59 +01001407 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001408 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001409 nc_addtimespec(&ts_timeout, timeout);
1410 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001411 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1412 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001413 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001414 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001415 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1416 break;
1417 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001418 }
1419 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001420 ssh_key_free(privkey);
1421
Michal Vasko0190bc32016-03-02 15:47:49 +01001422 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001423 break;
1424 }
1425 }
1426 break;
1427 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001428
Michal Vasko0190bc32016-03-02 15:47:49 +01001429 switch (ret_auth) {
1430 case SSH_AUTH_AGAIN:
1431 ERR("Authentication response timeout.");
1432 return 0;
1433 case SSH_AUTH_ERROR:
1434 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
1435 return -1;
1436 case SSH_AUTH_DENIED:
1437 WRN("Authentication denied.");
1438 break;
1439 case SSH_AUTH_PARTIAL:
1440 VRB("Partial authentication success.");
1441 break;
1442 case SSH_AUTH_SUCCESS:
1443 VRB("Authentication successful.");
1444 break;
1445 case SSH_AUTH_INFO:
1446 ERRINT;
1447 return -1;
1448 }
Radek Krejciae813f42018-07-02 13:38:30 +02001449
1450 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001451 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001452
Michal Vasko0190bc32016-03-02 15:47:49 +01001453 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001454}
1455
1456/* Open new SSH channel and request the 'netconf' subsystem.
1457 * SSH connection is expected to be established.
1458 */
1459static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001460open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001461{
1462 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001463 int ret;
1464 struct timespec ts_timeout, ts_cur;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001465
1466 ssh_sess = session->ti.libssh.session;
1467
1468 if (!ssh_is_connected(ssh_sess)) {
1469 ERR("SSH session not connected.");
1470 return -1;
1471 }
1472
1473 if (session->ti.libssh.channel) {
1474 ERR("SSH channel already created.");
1475 return -1;
1476 }
1477
Michal Vasko7b62fed2015-10-26 15:39:46 +01001478 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001479 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001480 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001481 nc_addtimespec(&ts_timeout, timeout);
1482 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001483 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001484 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1485 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001486 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001487 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001488 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1489 break;
1490 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001491 }
1492 }
1493 if (ret == SSH_AGAIN) {
1494 ERR("Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001495 ssh_channel_free(session->ti.libssh.channel);
1496 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001497 return 0;
1498 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001499 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001500 ssh_channel_free(session->ti.libssh.channel);
1501 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001502 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001503 }
1504
1505 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001506 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001507 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001508 nc_addtimespec(&ts_timeout, timeout);
1509 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001510 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1511 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001512 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001513 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001514 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1515 break;
1516 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001517 }
1518 }
1519 if (ret == SSH_AGAIN) {
1520 ERR("Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001521 ssh_channel_free(session->ti.libssh.channel);
1522 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001523 return 0;
1524 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001525 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001526 ssh_channel_free(session->ti.libssh.channel);
1527 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001528 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001529 }
1530
Michal Vasko0190bc32016-03-02 15:47:49 +01001531 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001532}
1533
Michal Vasko30e2c872016-02-18 10:03:21 +01001534static struct nc_session *
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001535_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepalives *ka,
1536 struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001537{
Michal Vasko66032bc2019-01-22 15:03:12 +01001538 char *host = NULL, *username = NULL, *ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001539 unsigned short port = 0;
1540 int sock;
1541 struct passwd *pw;
1542 struct nc_session *session = NULL;
1543
1544 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001545 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001546 return NULL;
1547 }
1548
1549 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001550 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001551 if (!session) {
1552 ERRMEM;
1553 return NULL;
1554 }
1555 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001556 session->ti_type = NC_TI_LIBSSH;
1557 session->ti.libssh.session = ssh_session;
1558
1559 /* was port set? */
1560 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1561
1562 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1563 /*
1564 * There is no file descriptor (detected based on the host, there is no way to check
1565 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1566 */
1567
1568 /* remember host */
1569 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001570 if (!host) {
1571 ERRMEM;
1572 goto fail;
1573 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001574 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1575
1576 /* create and connect socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001577 sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001578 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001579 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001580 goto fail;
1581 }
1582 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001583 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko66032bc2019-01-22 15:03:12 +01001584
1585 free(host);
1586 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001587 }
1588
1589 /* was username set? */
1590 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1591
1592 if (!ssh_is_connected(ssh_session)) {
1593 /*
1594 * We are connected, but not SSH authenticated. (Transport layer)
1595 */
1596
1597 /* remember username */
1598 if (!username) {
1599 if (!opts->username) {
1600 pw = getpwuid(getuid());
1601 if (!pw) {
1602 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1603 goto fail;
1604 }
1605 username = strdup(pw->pw_name);
1606 } else {
1607 username = strdup(opts->username);
1608 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001609 if (!username) {
1610 ERRMEM;
1611 goto fail;
1612 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001613 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1614 }
1615
1616 /* connect and authenticate SSH session */
1617 session->host = host;
1618 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001619 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001620 goto fail;
1621 }
1622 }
1623
1624 /*
1625 * Almost done, open a netconf channel. (Transport layer / application layer)
1626 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001627 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001628 goto fail;
1629 }
1630
1631 /*
1632 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1633 */
1634
Radek Krejcifd5b6682017-06-13 15:52:53 +02001635 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1636 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001637 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001638 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001639
1640 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001641 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001642 goto fail;
1643 }
1644 session->status = NC_STATUS_RUNNING;
1645
1646 if (nc_ctx_check_and_fill(session) == -1) {
1647 goto fail;
1648 }
1649
1650 /* store information into the dictionary */
1651 if (host) {
1652 session->host = lydict_insert_zc(ctx, host);
1653 }
1654 if (port) {
1655 session->port = port;
1656 }
1657 if (username) {
1658 session->username = lydict_insert_zc(ctx, username);
1659 }
1660
1661 return session;
1662
1663fail:
Michal Vasko3df5b252019-04-23 08:48:17 +02001664 free(host);
1665 session->host = NULL;
1666 free(username);
1667 session->username = NULL;
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001668 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001669 return NULL;
1670}
1671
Radek Krejciac6d3472015-10-22 15:47:18 +02001672API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001673nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001674{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001675 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001676 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001677 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001678 char *username, *ip_host = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001679 struct passwd *pw;
1680 struct nc_session *session = NULL;
1681
1682 /* process parameters */
1683 if (!host || strisempty(host)) {
1684 host = "localhost";
1685 }
1686
1687 if (!port) {
1688 port = NC_PORT_SSH;
1689 }
Michal Vasko55fded62016-02-02 12:19:34 +01001690 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001691
Michal Vasko3031aae2016-01-27 16:07:18 +01001692 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001693 pw = getpwuid(getuid());
1694 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001695 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1696 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001697 } else {
1698 username = pw->pw_name;
1699 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001700 } else {
1701 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001702 }
1703
1704 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001705 session = nc_new_session(NC_CLIENT, 0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001706 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001707 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001708 return NULL;
1709 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001710 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001711
Michal Vasko131120a2018-05-29 15:44:02 +02001712 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001713 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001714 session->ti.libssh.session = ssh_new();
1715 if (!session->ti.libssh.session) {
1716 ERR("Unable to initialize SSH session.");
1717 goto fail;
1718 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001719
Michal Vasko7b62fed2015-10-26 15:39:46 +01001720 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001721 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001722 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001723 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001724 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko52006472019-01-17 13:39:11 +01001725 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1726 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
Michal Vasko3cd67472019-12-10 09:43:05 +01001727 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_KEY_EXCHANGE, "curve25519-sha256,ecdh-sha2-nistp256,"
1728 "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,diffie-hellman-group-exchange-sha256,"
1729 "diffie-hellman-group14-sha1,diffie-hellman-group1-sha1,diffie-hellman-group-exchange-sha1");
Michal Vasko32af6142019-01-23 09:30:42 +01001730#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
Michal Vasko52006472019-01-17 13:39:11 +01001731 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1732 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
Michal Vasko32af6142019-01-23 09:30:42 +01001733#endif
Michal Vasko7b62fed2015-10-26 15:39:46 +01001734
1735 /* create and assign communication socket */
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001736 sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001737 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001738 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001739 goto fail;
1740 }
1741 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001742 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001743
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001744 /* temporarily, for session connection */
1745 session->host = host;
1746 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001747 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1)
1748 || (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001749 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001750 }
1751
Radek Krejcifd5b6682017-06-13 15:52:53 +02001752 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1753 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001754 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001755 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001756
Radek Krejciac6d3472015-10-22 15:47:18 +02001757 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001758 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001759 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001760 }
Michal Vaskoad611702015-12-03 13:41:51 +01001761 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001762
Michal Vaskoef578332016-01-25 13:20:09 +01001763 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001764 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001765 }
1766
1767 /* store information into the dictionary */
Michal Vasko66032bc2019-01-22 15:03:12 +01001768 session->host = lydict_insert_zc(ctx, ip_host);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001769 session->port = port;
1770 session->username = lydict_insert(ctx, username, 0);
1771
Radek Krejciac6d3472015-10-22 15:47:18 +02001772 return session;
1773
Michal Vasko7b62fed2015-10-26 15:39:46 +01001774fail:
Michal Vasko66032bc2019-01-22 15:03:12 +01001775 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001776 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001777 return NULL;
1778}
1779
1780API struct nc_session *
1781nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1782{
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001783 return _nc_connect_libssh(ssh_session, ctx, &client_opts.ka, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001784}
1785
1786API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001787nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001788{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001789 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001790
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001791 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001792 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001793 return NULL;
1794 }
1795
Michal Vasko7b62fed2015-10-26 15:39:46 +01001796 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001797 new_session = nc_new_session(NC_CLIENT, 1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001798 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001799 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001800 return NULL;
1801 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001802 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001803
Michal Vasko131120a2018-05-29 15:44:02 +02001804 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001805 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001806 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001807 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001808
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001809 /* append to the session ring list */
1810 if (!session->ti.libssh.next) {
1811 session->ti.libssh.next = new_session;
1812 new_session->ti.libssh.next = session;
1813 } else {
1814 ptr = session->ti.libssh.next;
1815 session->ti.libssh.next = new_session;
1816 new_session->ti.libssh.next = ptr;
1817 }
1818
Michal Vasko7b62fed2015-10-26 15:39:46 +01001819 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001820 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001821 goto fail;
1822 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001823 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001824 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001825 }
Michal Vasko131120a2018-05-29 15:44:02 +02001826 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001827
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001828 if (nc_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001829 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001830 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001831 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001832
Michal Vasko7b62fed2015-10-26 15:39:46 +01001833 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001834 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001835 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001836 }
Michal Vaskoad611702015-12-03 13:41:51 +01001837 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001838
Michal Vaskoef578332016-01-25 13:20:09 +01001839 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001840 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001841 }
1842
1843 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001844 new_session->host = lydict_insert(ctx, session->host, 0);
1845 new_session->port = session->port;
1846 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001847
Michal Vasko7b62fed2015-10-26 15:39:46 +01001848 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001849
1850fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001851 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001852 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001853}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001854
Michal Vasko3031aae2016-01-27 16:07:18 +01001855struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001856nc_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 +01001857{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001858 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001859 unsigned int uint_port;
Michal Vasko3031aae2016-01-27 16:07:18 +01001860 struct passwd *pw;
Michal Vasko30e2c872016-02-18 10:03:21 +01001861 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001862 ssh_session sess;
1863
Michal Vasko80cad7f2015-12-08 14:42:27 +01001864 sess = ssh_new();
1865 if (!sess) {
1866 ERR("Unable to initialize an SSH session.");
1867 close(sock);
1868 return NULL;
1869 }
1870
1871 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001872 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001873 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001874 uint_port = port;
1875 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001876 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001877 if (!ssh_ch_opts.username) {
1878 pw = getpwuid(getuid());
1879 if (!pw) {
1880 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
Michal Vasko435e5cf2019-04-23 08:48:44 +02001881 ssh_free(sess);
Michal Vasko3031aae2016-01-27 16:07:18 +01001882 return NULL;
1883 }
1884 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1885 } else {
1886 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001887 }
Michal Vasko8fd6fca2019-02-04 10:59:49 +01001888 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1889 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1890#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
1891 ssh_options_set(sess, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1892 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
1893#endif
Michal Vasko80cad7f2015-12-08 14:42:27 +01001894
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001895 session = _nc_connect_libssh(sess, ctx, &client_opts.ka, &ssh_ch_opts, timeout);
Michal Vasko435e5cf2019-04-23 08:48:44 +02001896 if (!session) {
Michal Vasko457f0532019-08-15 13:59:49 +02001897 /* sess is freed */
Michal Vasko435e5cf2019-04-23 08:48:44 +02001898 return NULL;
Michal Vasko4282fae2016-02-18 10:03:42 +01001899 }
1900
Michal Vasko435e5cf2019-04-23 08:48:44 +02001901 session->flags |= NC_SESSION_CALLHOME;
Michal Vasko30e2c872016-02-18 10:03:21 +01001902 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001903}