blob: 27e7e47a4e0bf439929991d785041631d5cd91cb [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
Michal Vaskoe22c6732016-01-29 11:03:02 +0100155static void
156_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;
Michal Vaskoef112d72016-02-18 13:28:25 +0100276 int c, state, ret;
277 ssh_key srv_pubkey;
278 unsigned char *hash_sha1 = NULL;
279 size_t hlen;
280 enum ssh_keytypes_e srv_pubkey_type;
281 char answer[5];
Michal Vasko51228ac2018-03-29 14:57:53 +0200282 FILE *out = NULL, *in = NULL;
Michal Vaskoef112d72016-02-18 13:28:25 +0100283
284 state = ssh_is_server_known(session);
285
Michal Vaskocc0aa7d2016-05-31 12:48:42 +0200286 ret = ssh_get_publickey(session, &srv_pubkey);
Michal Vaskoef112d72016-02-18 13:28:25 +0100287 if (ret < 0) {
288 ERR("Unable to get server public key.");
289 return -1;
290 }
291
292 srv_pubkey_type = ssh_key_type(srv_pubkey);
293 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
294 ssh_key_free(srv_pubkey);
295 if (ret < 0) {
296 ERR("Failed to calculate SHA1 hash of the server public key.");
297 return -1;
298 }
299
300 hexa = ssh_get_hexa(hash_sha1, hlen);
301
302 switch (state) {
303 case SSH_SERVER_KNOWN_OK:
304 break; /* ok */
305
306 case SSH_SERVER_KNOWN_CHANGED:
307 ERR("Remote host key changed, the connection will be terminated!");
Michal Vasko51228ac2018-03-29 14:57:53 +0200308 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100309
310 case SSH_SERVER_FOUND_OTHER:
311 WRN("Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
312 goto hostkey_not_known;
313
314 case SSH_SERVER_FILE_NOT_FOUND:
315 WRN("Could not find the known hosts file.");
316 goto hostkey_not_known;
317
318 case SSH_SERVER_NOT_KNOWN:
319hostkey_not_known:
320#ifdef ENABLE_DNSSEC
321 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
322 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
Michal Vasko650011a2016-02-25 14:49:29 +0100323 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100324 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100325 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100326 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100327 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100328 }
329
330 /* DNSSEC SSHFP check successful, that's enough */
331 if (!ret) {
332 VRB("DNSSEC SSHFP check successful.");
333 ssh_write_knownhost(session);
334 ssh_clean_pubkey_hash(&hash_sha1);
335 ssh_string_free_char(hexa);
336 return 0;
337 }
338 }
339#endif
340
Michal Vasko51228ac2018-03-29 14:57:53 +0200341 if (!(in = nc_open_in(1, NULL))) {
342 goto error;
343 }
344 if (!(out = nc_open_out())) {
345 goto error;
346 }
347
Michal Vaskoef112d72016-02-18 13:28:25 +0100348 /* try to get result from user */
Michal Vasko51228ac2018-03-29 14:57:53 +0200349 if (fprintf(out, "The authenticity of the host \'%s\' cannot be established.\n", hostname) < 1) {
350 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
351 goto error;
352 }
353 if (fprintf(out, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa) < 1) {
354 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
355 goto error;
356 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100357
358#ifdef ENABLE_DNSSEC
359 if (ret == 2) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200360 if (fprintf(out, "No matching host key fingerprint found using DNS.\n") < 1) {
361 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
362 goto error;
363 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100364 } else if (ret == 1) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200365 if (fprintf(out, "Matching host key fingerprint found using DNS.\n") < 1) {
366 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
367 goto error;
368 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100369 }
370#endif
371
Michal Vasko51228ac2018-03-29 14:57:53 +0200372 if (fprintf(out, "Are you sure you want to continue connecting (yes/no)? ") < 1) {
373 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
374 goto error;
375 }
376 fflush(out);
Michal Vaskoef112d72016-02-18 13:28:25 +0100377
378 do {
Michal Vasko51228ac2018-03-29 14:57:53 +0200379 if (fscanf(in, "%4s", answer) == EOF) {
380 ERR("Reading from input failed (%s).", feof(in) ? "EOF" : strerror(errno));
381 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100382 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200383 while (((c = getc(in)) != EOF) && (c != '\n'));
Michal Vaskoef112d72016-02-18 13:28:25 +0100384
Michal Vasko51228ac2018-03-29 14:57:53 +0200385 fflush(in);
Michal Vaskoef112d72016-02-18 13:28:25 +0100386 if (!strcmp("yes", answer)) {
387 /* store the key into the host file */
388 ret = ssh_write_knownhost(session);
389 if (ret != SSH_OK) {
390 WRN("Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
391 }
392 } else if (!strcmp("no", answer)) {
Michal Vasko51228ac2018-03-29 14:57:53 +0200393 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100394 } else {
Michal Vasko51228ac2018-03-29 14:57:53 +0200395 if (fprintf(out, "Please type 'yes' or 'no': ") < 1) {
396 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
397 goto error;
398 }
Michal Vaskoef112d72016-02-18 13:28:25 +0100399 }
400 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
401
402 break;
403
404 case SSH_SERVER_ERROR:
Michal Vasko3b49eb22018-05-24 09:17:49 +0200405 ERR("SSH error: %s", ssh_get_error(session));
Michal Vasko51228ac2018-03-29 14:57:53 +0200406 goto error;
Michal Vaskoef112d72016-02-18 13:28:25 +0100407 }
408
Michal Vasko51228ac2018-03-29 14:57:53 +0200409 nc_close_inout(in, 1, NULL);
410 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100411 ssh_clean_pubkey_hash(&hash_sha1);
412 ssh_string_free_char(hexa);
413 return 0;
414
Michal Vasko51228ac2018-03-29 14:57:53 +0200415error:
416 nc_close_inout(in, 1, NULL);
417 nc_close_inout(out, 1, NULL);
Michal Vaskoef112d72016-02-18 13:28:25 +0100418 ssh_clean_pubkey_hash(&hash_sha1);
419 ssh_string_free_char(hexa);
420 return -1;
421}
422
Radek Krejci62aa0642017-05-25 16:33:49 +0200423char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200424sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200425{
Michal Vasko51228ac2018-03-29 14:57:53 +0200426 char *buf = NULL;
427 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200428 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200429 FILE *in = NULL, *out = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200430
Michal Vasko11d142a2016-01-19 15:58:24 +0100431 buf = malloc(buflen * sizeof *buf);
432 if (!buf) {
433 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100434 return NULL;
435 }
436
Michal Vasko51228ac2018-03-29 14:57:53 +0200437 if (!(in = nc_open_in(0, &oldterm))) {
438 goto error;
439 }
440 if (!(out = nc_open_out())) {
441 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200442 }
443
Michal Vasko51228ac2018-03-29 14:57:53 +0200444 if (fprintf(out, "%s@%s password: ", username, hostname) < 1) {
445 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
446 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200447 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200448 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200449
450 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200451 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100452 if (len >= buflen - 1) {
453 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100454 buf = nc_realloc(buf, buflen * sizeof *buf);
455 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100456 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200457 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100458 }
459 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100460 buf[len++] = (char)c;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100461 }
462 buf[len++] = 0; /* terminating null byte */
463
Michal Vasko51228ac2018-03-29 14:57:53 +0200464 fprintf(out, "\n");
465
466 nc_close_inout(in, 0, &oldterm);
467 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100468 return buf;
Michal Vasko93f26d92018-02-01 09:08:35 +0100469
Michal Vasko51228ac2018-03-29 14:57:53 +0200470error:
471 nc_close_inout(in, 0, &oldterm);
472 nc_close_inout(out, 1, NULL);
Michal Vasko93f26d92018-02-01 09:08:35 +0100473 free(buf);
474 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100475}
476
Radek Krejci62aa0642017-05-25 16:33:49 +0200477char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200478sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100479{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200480 unsigned int buflen = 64, cur_len;
Michal Vasko51228ac2018-03-29 14:57:53 +0200481 int c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200482 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200483 char *buf = NULL;
484 FILE *in = NULL, *out = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100485
486 buf = malloc(buflen * sizeof *buf);
487 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100488 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100489 return NULL;
490 }
491
Michal Vasko51228ac2018-03-29 14:57:53 +0200492 if (!(in = nc_open_in(echo, &oldterm))) {
493 goto error;
494 }
495 if (!(out = nc_open_out())) {
496 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100497 }
498
Michal Vasko51228ac2018-03-29 14:57:53 +0200499 if (auth_name && (fprintf(out, "%s\n", auth_name) < 1)) {
500 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
501 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100502 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200503 if (instruction && (fprintf(out, "%s\n", instruction) < 1)) {
504 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
505 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100506 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200507 if (fputs(prompt, out) == EOF) {
508 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
509 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200510 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200511 fflush(out);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100512
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200513 cur_len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200514 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200515 if (cur_len >= buflen - 1) {
516 buflen *= 2;
517 buf = nc_realloc(buf, buflen * sizeof *buf);
518 if (!buf) {
519 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200520 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200521 }
522 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100523 buf[cur_len++] = (char)c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200524 }
525 /* terminating null byte */
526 buf[cur_len] = '\0';
527
Michal Vasko51228ac2018-03-29 14:57:53 +0200528 fprintf(out, "\n");
529
530 nc_close_inout(in, echo, &oldterm);
531 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200532 return buf;
533
Michal Vasko51228ac2018-03-29 14:57:53 +0200534error:
535 nc_close_inout(in, echo, &oldterm);
536 nc_close_inout(out, 1, NULL);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200537 free(buf);
538 return NULL;
539}
540
Radek Krejci62aa0642017-05-25 16:33:49 +0200541char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200542sshauth_privkey_passphrase(const char* privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200543{
Michal Vasko51228ac2018-03-29 14:57:53 +0200544 char *buf = NULL;
545 int c, buflen = 1024, len;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200546 struct termios oldterm;
Michal Vasko51228ac2018-03-29 14:57:53 +0200547 FILE *in = NULL, *out = NULL;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200548
549 buf = malloc(buflen * sizeof *buf);
550 if (!buf) {
551 ERRMEM;
552 return NULL;
553 }
554
Michal Vasko51228ac2018-03-29 14:57:53 +0200555 if (!(in = nc_open_in(0, &oldterm))) {
556 goto error;
557 }
558 if (!(out = nc_open_out())) {
559 goto error;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200560 }
561
Michal Vasko51228ac2018-03-29 14:57:53 +0200562 if (fprintf(out, "Enter passphrase for the key '%s': ", privkey_path) < 1) {
563 ERR("Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno));
564 goto error;
Michal Vasko88583042018-03-29 09:18:58 +0200565 }
Michal Vasko51228ac2018-03-29 14:57:53 +0200566 fflush(out);
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200567
568 len = 0;
Michal Vasko51228ac2018-03-29 14:57:53 +0200569 while (((c = fgetc(in)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100570 if (len >= buflen - 1) {
571 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100572 buf = nc_realloc(buf, buflen * sizeof *buf);
573 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100574 ERRMEM;
Michal Vasko51228ac2018-03-29 14:57:53 +0200575 goto error;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100576 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100577 }
578 buf[len++] = (char)c;
579 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200580 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100581
Michal Vasko51228ac2018-03-29 14:57:53 +0200582 fprintf(out, "\n");
583
584 nc_close_inout(in, 0, &oldterm);
585 nc_close_inout(out, 1, NULL);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100586 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100587
Michal Vasko51228ac2018-03-29 14:57:53 +0200588error:
589 nc_close_inout(in, 0, &oldterm);
590 nc_close_inout(out, 1, NULL);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100591 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100592 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100593}
594
Michal Vaskoef112d72016-02-18 13:28:25 +0100595static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200596_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
597 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100598{
Michal Vaskoef112d72016-02-18 13:28:25 +0100599 if (auth_hostkey_check) {
600 opts->auth_hostkey_check = auth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200601 opts->auth_hostkey_check_priv = priv;
Michal Vaskoef112d72016-02-18 13:28:25 +0100602 } else {
603 opts->auth_hostkey_check = sshauth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200604 opts->auth_hostkey_check_priv = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100605 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100606}
607
Radek Krejci90a84a22017-05-25 13:53:00 +0200608static void
609_nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
610 void **priv, struct nc_client_ssh_opts *opts)
Michal Vaskoef112d72016-02-18 13:28:25 +0100611{
Radek Krejci90a84a22017-05-25 13:53:00 +0200612 if (auth_hostkey_check) {
613 (*auth_hostkey_check) = opts->auth_hostkey_check == sshauth_hostkey_check ? NULL : opts->auth_hostkey_check;
614 }
615 if (priv) {
616 (*priv) = opts->auth_hostkey_check_priv;
617 }
618}
619
620
621API void
622nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
623 void *priv)
624{
625 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100626}
627
628API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200629nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
630 void *priv)
Michal Vaskoef112d72016-02-18 13:28:25 +0100631{
Radek Krejci90a84a22017-05-25 13:53:00 +0200632 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100633}
634
Radek Krejci90a84a22017-05-25 13:53:00 +0200635API void
636nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
637 void **priv)
638{
639 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
640}
641
642API void
643nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
644 void **priv)
645{
646 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
647}
Michal Vaskoef112d72016-02-18 13:28:25 +0100648
Michal Vasko30e2c872016-02-18 10:03:21 +0100649static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200650_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
651 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100652{
653 if (auth_password) {
654 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200655 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100656 } else {
657 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200658 opts->auth_password_priv = NULL;
659 }
660}
661
662static void
663_nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
664 void **priv, struct nc_client_ssh_opts *opts)
665{
666 if (auth_password) {
667 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
668 }
669 if (priv) {
670 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100671 }
672}
673
674API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200675nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
676 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100677{
Radek Krejci90a84a22017-05-25 13:53:00 +0200678 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100679}
680
681API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200682nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
683 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100684{
Radek Krejci90a84a22017-05-25 13:53:00 +0200685 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
686}
687
688API void
689nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
690 void **priv)
691{
692 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
693}
694
695API void
696nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
697 void **priv)
698{
699 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100700}
701
702static void
703_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200704 const char *prompt, int echo, void *priv),
705 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100706{
707 if (auth_interactive) {
708 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200709 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100710 } else {
711 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200712 opts->auth_interactive_priv = NULL;
713 }
714}
715
716static void
717_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
718 const char *prompt, int echo, void *priv),
719 void **priv, struct nc_client_ssh_opts *opts)
720{
721 if (auth_interactive) {
722 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
723 }
724 if (priv) {
725 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100726 }
727}
728
729API void
730nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200731 const char *prompt, int echo, void *priv),
732 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100733{
Radek Krejci90a84a22017-05-25 13:53:00 +0200734 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100735}
736
737API void
738nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200739 const char *prompt, int echo, void *priv),
740 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100741{
Radek Krejci90a84a22017-05-25 13:53:00 +0200742 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
743}
744
745API void
746nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
747 const char *prompt, int echo, void *priv),
748 void **priv)
749{
750 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
751}
752
753API void
754nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
755 const char *prompt, int echo, void *priv),
756 void **priv)
757{
758 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100759}
760
761static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200762_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
763 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100764{
765 if (auth_privkey_passphrase) {
766 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200767 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100768 } else {
769 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200770 opts->auth_privkey_passphrase_priv = NULL;
771 }
772}
773
774static void
775_nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
776 void **priv, struct nc_client_ssh_opts *opts)
777{
778 if (auth_privkey_passphrase) {
779 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
780 }
781 if (priv) {
782 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100783 }
784}
785
786API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200787nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
788 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100789{
Radek Krejci90a84a22017-05-25 13:53:00 +0200790 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100791}
792
793API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200794nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
795 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100796{
Radek Krejci90a84a22017-05-25 13:53:00 +0200797 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
798}
799
800API void
801nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
802 void **priv)
803{
804 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
805}
806
807API void
808nc_client_ssh_ch_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
809 void **priv)
810{
811 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100812}
813
Michal Vasko3031aae2016-01-27 16:07:18 +0100814static int
815_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 +0100816{
817 int i;
818 FILE *key;
819 char line[128];
820
Michal Vasko45e53ae2016-04-07 11:46:03 +0200821 if (!pub_key) {
822 ERRARG("pub_key");
823 return -1;
824 } else if (!priv_key) {
825 ERRARG("priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100826 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100827 }
828
Michal Vasko3031aae2016-01-27 16:07:18 +0100829 for (i = 0; i < opts->key_count; ++i) {
830 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
831 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100832 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100833 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100834 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100835 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100836 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100837 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100838 continue;
839 }
840
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100841 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100842 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100843 }
844 }
845
Michal Vasko3031aae2016-01-27 16:07:18 +0100846 /* add the keys */
847 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100848 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
849 if (!opts->keys) {
850 ERRMEM;
851 return -1;
852 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100853 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
854 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
855 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100856
Michal Vasko4eb3c312016-03-01 14:09:37 +0100857 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
858 ERRMEM;
859 return -1;
860 }
861
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100862 /* check encryption */
863 if ((key = fopen(priv_key, "r"))) {
864 /* 1st line - key type */
865 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100866 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100867 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100868 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100869 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100870 /* 2nd line - encryption information or key */
871 if (!fgets(line, sizeof line, key)) {
872 fclose(key);
873 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100874 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100875 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100876 fclose(key);
877 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100878 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100879 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100880 }
881
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100882 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100883}
884
885API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100886nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100887{
Michal Vasko3031aae2016-01-27 16:07:18 +0100888 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
889}
890
891API int
892nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
893{
894 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
895}
896
897static int
898_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
899{
900 if (idx >= opts->key_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200901 ERRARG("idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100902 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100903 }
904
Michal Vasko3031aae2016-01-27 16:07:18 +0100905 free(opts->keys[idx].pubkey_path);
906 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100907
Michal Vasko3031aae2016-01-27 16:07:18 +0100908 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100909 if (idx < opts->key_count) {
910 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
911 }
912 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100913 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
914 if (!opts->keys) {
915 ERRMEM;
916 return -1;
917 }
Michal Vaskoc0256492016-02-02 12:19:06 +0100918 } else {
919 free(opts->keys);
920 opts->keys = NULL;
921 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100922
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100923 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100924}
925
926API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100927nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100928{
Michal Vasko3031aae2016-01-27 16:07:18 +0100929 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100930}
931
932API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100933nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100934{
Michal Vasko3031aae2016-01-27 16:07:18 +0100935 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
936}
937
938static int
939_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
940{
941 return opts->key_count;
942}
943
944API int
945nc_client_ssh_get_keypair_count(void)
946{
947 return _nc_client_ssh_get_keypair_count(&ssh_opts);
948}
949
950API int
951nc_client_ssh_ch_get_keypair_count(void)
952{
953 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
954}
955
956static int
957_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
958{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200959 if (idx >= opts->key_count) {
960 ERRARG("idx");
961 return -1;
962 } else if (!pub_key && !priv_key) {
963 ERRARG("pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100964 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100965 }
966
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100967 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100968 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100969 }
970 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100971 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100972 }
973
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100974 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100975}
976
Michal Vasko3031aae2016-01-27 16:07:18 +0100977API int
978nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
979{
980 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
981}
982
983API int
984nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
985{
986 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
987}
988
989static void
990_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 +0100991{
992 if (pref < 0) {
993 pref = -1;
994 }
995
996 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100997 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100998 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100999 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001000 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001001 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001002 }
1003}
1004
Michal Vasko3031aae2016-01-27 16:07:18 +01001005API void
1006nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001007{
Michal Vasko3031aae2016-01-27 16:07:18 +01001008 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
1009}
1010
1011API void
1012nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
1013{
1014 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
1015}
1016
1017static int16_t
1018_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
1019{
1020 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001021
1022 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001023 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001024 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001025 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001026 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001027 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +01001028 }
1029
1030 return pref;
1031}
1032
Michal Vasko3031aae2016-01-27 16:07:18 +01001033API int16_t
1034nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1035{
1036 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
1037}
1038
1039API int16_t
1040nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
1041{
1042 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
1043}
1044
1045static int
1046_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
1047{
1048 if (opts->username) {
1049 free(opts->username);
1050 }
1051 if (username) {
1052 opts->username = strdup(username);
1053 if (!opts->username) {
1054 ERRMEM;
1055 return -1;
1056 }
1057 } else {
1058 opts->username = NULL;
1059 }
1060
1061 return 0;
1062}
1063
1064API int
1065nc_client_ssh_set_username(const char *username)
1066{
1067 return _nc_client_ssh_set_username(username, &ssh_opts);
1068}
1069
1070API int
1071nc_client_ssh_ch_set_username(const char *username)
1072{
1073 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1074}
1075
Michal Vaskoe22c6732016-01-29 11:03:02 +01001076static const char *
1077_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1078{
1079 return opts->username;
1080}
1081
1082API const char *
1083nc_client_ssh_get_username(void)
1084{
1085 return _nc_client_ssh_get_username(&ssh_opts);
1086}
1087
1088API const char *
1089nc_client_ssh_ch_get_username(void)
1090{
1091 return _nc_client_ssh_get_username(&ssh_ch_opts);
1092}
1093
Michal Vasko3031aae2016-01-27 16:07:18 +01001094API int
1095nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1096{
1097 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
1098}
1099
1100API int
1101nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1102{
1103 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1104}
1105
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001106/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001107 * Host, port, username, and a connected socket is expected to be set.
Radek Krejciae813f42018-07-02 13:38:30 +02001108 *
1109 * return values
1110 * -1 failure
1111 * 0 try again
1112 * 1 success
Michal Vasko7b62fed2015-10-26 15:39:46 +01001113 */
1114static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001115connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001116{
Radek Krejciae813f42018-07-02 13:38:30 +02001117 int j, ret_auth, userauthlist, ret, attempt = 0;
Michal Vasko235efdc2015-12-17 12:05:04 +01001118 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001119 int16_t pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001120 const char* prompt;
1121 char *s, *answer, echo;
1122 ssh_key pubkey, privkey;
1123 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001124 struct timespec ts_timeout, ts_cur;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001125
1126 ssh_sess = session->ti.libssh.session;
1127
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001128 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001129 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001130 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1131 usleep(NC_TIMEOUT_STEP);
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001132 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001133 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001134 break;
1135 }
1136 }
1137 if (ret == SSH_AGAIN) {
1138 ERR("SSH connect timeout.");
1139 return 0;
1140 } else if (ret != SSH_OK) {
1141 ERR("Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001142 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001143 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001144 }
1145
Radek Krejci90a84a22017-05-25 13:53:00 +02001146 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001147 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001148 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001149 }
1150
Michal Vasko36c7be82017-02-22 13:37:59 +01001151 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001152 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001153 nc_addtimespec(&ts_timeout, timeout);
1154 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001155 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1156 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001157 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001158 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001159 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1160 break;
1161 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001162 }
1163 }
1164 if (ret_auth == SSH_AUTH_AGAIN) {
1165 ERR("Request authentication methods timeout.");
1166 return 0;
1167 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001168 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001169 return -1;
Radek Krejciae813f42018-07-02 13:38:30 +02001170 } else if (ret_auth == SSH_AUTH_SUCCESS) {
1171 WRN("Server accepts \"none\" authentication method.")
1172 return 1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001173 }
1174
1175 /* check what authentication methods are available */
1176 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001177
1178 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001179 if (opts->auth_pref[0].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001180 VRB("Interactive SSH authentication method was disabled.");
1181 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001182 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001183 if (opts->auth_pref[1].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001184 VRB("Password SSH authentication method was disabled.");
1185 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001186 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001187 if (opts->auth_pref[2].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001188 VRB("Publickey SSH authentication method was disabled.");
1189 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001190 }
1191
Michal Vasko0190bc32016-03-02 15:47:49 +01001192 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001193 auth = 0;
1194 pref = 0;
1195 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1196 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001197 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001198 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001199 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001200 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001201 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001202 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001203 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001204 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001205 }
1206
Michal Vasko235efdc2015-12-17 12:05:04 +01001207 if (!auth) {
Radek Krejciae813f42018-07-02 13:38:30 +02001208 if (!attempt) {
1209 ERR("Unable to authenticate to the remote server (no supported authentication methods detected).");
1210 } else {
1211 ERR("Unable to authenticate to the remote server (all attempts via supported authentication methods failed).");
1212 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001213 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001214 }
1215
1216 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001217 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001218 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001219 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1220
Michal Vaskoef578332016-01-25 13:20:09 +01001221 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001222 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko88583042018-03-29 09:18:58 +02001223 if (s == NULL) {
1224 ERR("Unable to get the password.");
1225 return -1;
1226 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001227
Michal Vasko36c7be82017-02-22 13:37:59 +01001228 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001229 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001230 nc_addtimespec(&ts_timeout, timeout);
1231 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001232 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1233 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001234 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001235 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001236 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1237 break;
1238 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001239 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001240 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001241 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001242 free(s);
1243 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001244
Michal Vasko7b62fed2015-10-26 15:39:46 +01001245 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001246 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1247
Michal Vaskod083db62016-01-19 10:31:29 +01001248 VRB("Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001249
Michal Vasko36c7be82017-02-22 13:37:59 +01001250 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001251 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001252 nc_addtimespec(&ts_timeout, timeout);
1253 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001254 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO)
1255 || (ret_auth == SSH_AUTH_AGAIN)) {
1256 if (ret_auth == SSH_AUTH_AGAIN) {
1257 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001258 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001259 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001260 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1261 break;
1262 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001263 }
1264 continue;
1265 }
1266
Michal Vasko7b62fed2015-10-26 15:39:46 +01001267 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1268 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001269 if (!prompt) {
1270 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001271 break;
1272 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001273
1274 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1275 echo = 0;
1276
Michal Vasko30e2c872016-02-18 10:03:21 +01001277 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
1278 ssh_userauth_kbdint_getinstruction(ssh_sess),
Radek Krejci90a84a22017-05-25 13:53:00 +02001279 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001280 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1281 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001282 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001283 break;
1284 }
1285 free(answer);
1286 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001287 if (ret_auth == SSH_AUTH_ERROR) {
1288 break;
1289 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001290 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001291 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001292 nc_addtimespec(&ts_timeout, timeout);
1293 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001294 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001295 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001296
Michal Vasko206d3b12015-12-04 11:08:42 +01001297 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001298 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1299
Michal Vaskod083db62016-01-19 10:31:29 +01001300 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001301
1302 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001303 if (!opts->key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001304 VRB("No key pair specified.");
1305 break;
1306 }
1307
Michal Vasko30e2c872016-02-18 10:03:21 +01001308 for (j = 0; j < opts->key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +01001309 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko30e2c872016-02-18 10:03:21 +01001310 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1311 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001312
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001313 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1314 if (ret == SSH_EOF) {
1315 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
1316 continue;
1317 } else if (ret == SSH_ERROR) {
1318 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001319 continue;
1320 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001321
Michal Vasko36c7be82017-02-22 13:37:59 +01001322 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001323 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001324 nc_addtimespec(&ts_timeout, timeout);
1325 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001326 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1327 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001328 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001329 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001330 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1331 break;
1332 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001333 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001334 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001335 ssh_key_free(pubkey);
1336
1337 if (ret_auth == SSH_AUTH_DENIED) {
1338 continue;
1339 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001340 break;
1341 }
1342
Michal Vasko30e2c872016-02-18 10:03:21 +01001343 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001344 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001345 } else {
1346 s = NULL;
1347 }
1348
Michal Vasko0190bc32016-03-02 15:47:49 +01001349 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001350 if (s) {
1351 memset(s, 0, strlen(s));
1352 free(s);
1353 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001354 if (ret == SSH_EOF) {
1355 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
1356 continue;
1357 } else if (ret == SSH_ERROR) {
1358 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001359 continue;
1360 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001361
Michal Vasko36c7be82017-02-22 13:37:59 +01001362 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001363 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001364 nc_addtimespec(&ts_timeout, timeout);
1365 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001366 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1367 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001368 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001369 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001370 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1371 break;
1372 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001373 }
1374 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001375 ssh_key_free(privkey);
1376
Michal Vasko0190bc32016-03-02 15:47:49 +01001377 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001378 break;
1379 }
1380 }
1381 break;
1382 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001383
Michal Vasko0190bc32016-03-02 15:47:49 +01001384 switch (ret_auth) {
1385 case SSH_AUTH_AGAIN:
1386 ERR("Authentication response timeout.");
1387 return 0;
1388 case SSH_AUTH_ERROR:
1389 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
1390 return -1;
1391 case SSH_AUTH_DENIED:
1392 WRN("Authentication denied.");
1393 break;
1394 case SSH_AUTH_PARTIAL:
1395 VRB("Partial authentication success.");
1396 break;
1397 case SSH_AUTH_SUCCESS:
1398 VRB("Authentication successful.");
1399 break;
1400 case SSH_AUTH_INFO:
1401 ERRINT;
1402 return -1;
1403 }
Radek Krejciae813f42018-07-02 13:38:30 +02001404
1405 attempt++;
Michal Vasko0190bc32016-03-02 15:47:49 +01001406 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001407
Michal Vasko0190bc32016-03-02 15:47:49 +01001408 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001409}
1410
1411/* Open new SSH channel and request the 'netconf' subsystem.
1412 * SSH connection is expected to be established.
1413 */
1414static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001415open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001416{
1417 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001418 int ret;
1419 struct timespec ts_timeout, ts_cur;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001420
1421 ssh_sess = session->ti.libssh.session;
1422
1423 if (!ssh_is_connected(ssh_sess)) {
1424 ERR("SSH session not connected.");
1425 return -1;
1426 }
1427
1428 if (session->ti.libssh.channel) {
1429 ERR("SSH channel already created.");
1430 return -1;
1431 }
1432
Michal Vasko7b62fed2015-10-26 15:39:46 +01001433 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001434 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001435 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001436 nc_addtimespec(&ts_timeout, timeout);
1437 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001438 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001439 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1440 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001441 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001442 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001443 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1444 break;
1445 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001446 }
1447 }
1448 if (ret == SSH_AGAIN) {
1449 ERR("Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001450 ssh_channel_free(session->ti.libssh.channel);
1451 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001452 return 0;
1453 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001454 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001455 ssh_channel_free(session->ti.libssh.channel);
1456 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001457 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001458 }
1459
1460 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001461 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001462 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001463 nc_addtimespec(&ts_timeout, timeout);
1464 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001465 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1466 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001467 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001468 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001469 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1470 break;
1471 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001472 }
1473 }
1474 if (ret == SSH_AGAIN) {
1475 ERR("Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001476 ssh_channel_free(session->ti.libssh.channel);
1477 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001478 return 0;
1479 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001480 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001481 ssh_channel_free(session->ti.libssh.channel);
1482 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001483 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001484 }
1485
Michal Vasko0190bc32016-03-02 15:47:49 +01001486 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001487}
1488
Michal Vasko30e2c872016-02-18 10:03:21 +01001489static struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001490_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001491{
Michal Vasko66032bc2019-01-22 15:03:12 +01001492 char *host = NULL, *username = NULL, *ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001493 unsigned short port = 0;
1494 int sock;
1495 struct passwd *pw;
1496 struct nc_session *session = NULL;
1497
1498 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001499 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001500 return NULL;
1501 }
1502
1503 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001504 session = nc_new_session(NC_CLIENT, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001505 if (!session) {
1506 ERRMEM;
1507 return NULL;
1508 }
1509 session->status = NC_STATUS_STARTING;
Michal Vasko30e2c872016-02-18 10:03:21 +01001510 session->ti_type = NC_TI_LIBSSH;
1511 session->ti.libssh.session = ssh_session;
1512
1513 /* was port set? */
1514 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1515
1516 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1517 /*
1518 * There is no file descriptor (detected based on the host, there is no way to check
1519 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1520 */
1521
1522 /* remember host */
1523 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001524 if (!host) {
1525 ERRMEM;
1526 goto fail;
1527 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001528 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1529
1530 /* create and connect socket */
Michal Vasko66032bc2019-01-22 15:03:12 +01001531 sock = nc_sock_connect(host, port, -1, NULL, &ip_host);
Michal Vasko30e2c872016-02-18 10:03:21 +01001532 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001533 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001534 goto fail;
1535 }
1536 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001537 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko66032bc2019-01-22 15:03:12 +01001538
1539 free(host);
1540 host = ip_host;
Michal Vasko30e2c872016-02-18 10:03:21 +01001541 }
1542
1543 /* was username set? */
1544 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1545
1546 if (!ssh_is_connected(ssh_session)) {
1547 /*
1548 * We are connected, but not SSH authenticated. (Transport layer)
1549 */
1550
1551 /* remember username */
1552 if (!username) {
1553 if (!opts->username) {
1554 pw = getpwuid(getuid());
1555 if (!pw) {
1556 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1557 goto fail;
1558 }
1559 username = strdup(pw->pw_name);
1560 } else {
1561 username = strdup(opts->username);
1562 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001563 if (!username) {
1564 ERRMEM;
1565 goto fail;
1566 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001567 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1568 }
1569
1570 /* connect and authenticate SSH session */
1571 session->host = host;
1572 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001573 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001574 goto fail;
1575 }
1576 }
1577
1578 /*
1579 * Almost done, open a netconf channel. (Transport layer / application layer)
1580 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001581 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001582 goto fail;
1583 }
1584
1585 /*
1586 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1587 */
1588
Radek Krejcifd5b6682017-06-13 15:52:53 +02001589 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1590 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001591 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001592 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001593
1594 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001595 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001596 goto fail;
1597 }
1598 session->status = NC_STATUS_RUNNING;
1599
1600 if (nc_ctx_check_and_fill(session) == -1) {
1601 goto fail;
1602 }
1603
1604 /* store information into the dictionary */
1605 if (host) {
1606 session->host = lydict_insert_zc(ctx, host);
1607 }
1608 if (port) {
1609 session->port = port;
1610 }
1611 if (username) {
1612 session->username = lydict_insert_zc(ctx, username);
1613 }
1614
1615 return session;
1616
1617fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001618 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001619 return NULL;
1620}
1621
Radek Krejciac6d3472015-10-22 15:47:18 +02001622API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001623nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001624{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001625 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001626 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001627 uint32_t port_uint;
Michal Vasko66032bc2019-01-22 15:03:12 +01001628 char *username, *ip_host = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001629 struct passwd *pw;
1630 struct nc_session *session = NULL;
1631
1632 /* process parameters */
1633 if (!host || strisempty(host)) {
1634 host = "localhost";
1635 }
1636
1637 if (!port) {
1638 port = NC_PORT_SSH;
1639 }
Michal Vasko55fded62016-02-02 12:19:34 +01001640 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001641
Michal Vasko3031aae2016-01-27 16:07:18 +01001642 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001643 pw = getpwuid(getuid());
1644 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001645 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1646 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001647 } else {
1648 username = pw->pw_name;
1649 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001650 } else {
1651 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001652 }
1653
1654 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001655 session = nc_new_session(NC_CLIENT, 0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001656 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001657 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001658 return NULL;
1659 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001660 session->status = NC_STATUS_STARTING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001661
Michal Vasko131120a2018-05-29 15:44:02 +02001662 /* transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001663 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001664 session->ti.libssh.session = ssh_new();
1665 if (!session->ti.libssh.session) {
1666 ERR("Unable to initialize SSH session.");
1667 goto fail;
1668 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001669
Michal Vasko7b62fed2015-10-26 15:39:46 +01001670 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001671 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001672 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001673 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001674 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko52006472019-01-17 13:39:11 +01001675 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256,"
1676 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
Michal Vasko32af6142019-01-23 09:30:42 +01001677#ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES
Michal Vasko52006472019-01-17 13:39:11 +01001678 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES, "ssh-ed25519,ecdsa-sha2-nistp256,"
1679 "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss");
Michal Vasko32af6142019-01-23 09:30:42 +01001680#endif
Michal Vasko7b62fed2015-10-26 15:39:46 +01001681
1682 /* create and assign communication socket */
Michal Vasko66032bc2019-01-22 15:03:12 +01001683 sock = nc_sock_connect(host, port, -1, NULL, &ip_host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001684 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001685 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001686 goto fail;
1687 }
1688 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001689 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001690
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001691 /* temporarily, for session connection */
1692 session->host = host;
1693 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001694 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1)
1695 || (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001696 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001697 }
1698
Radek Krejcifd5b6682017-06-13 15:52:53 +02001699 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1700 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001701 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001702 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001703
Radek Krejciac6d3472015-10-22 15:47:18 +02001704 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001705 if (nc_handshake_io(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001706 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001707 }
Michal Vaskoad611702015-12-03 13:41:51 +01001708 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001709
Michal Vaskoef578332016-01-25 13:20:09 +01001710 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001711 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001712 }
1713
1714 /* store information into the dictionary */
Michal Vasko66032bc2019-01-22 15:03:12 +01001715 session->host = lydict_insert_zc(ctx, ip_host);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001716 session->port = port;
1717 session->username = lydict_insert(ctx, username, 0);
1718
Radek Krejciac6d3472015-10-22 15:47:18 +02001719 return session;
1720
Michal Vasko7b62fed2015-10-26 15:39:46 +01001721fail:
Michal Vasko66032bc2019-01-22 15:03:12 +01001722 free(ip_host);
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001723 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001724 return NULL;
1725}
1726
1727API struct nc_session *
1728nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1729{
Michal Vasko0190bc32016-03-02 15:47:49 +01001730 return _nc_connect_libssh(ssh_session, ctx, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001731}
1732
1733API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001734nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001735{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001736 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001737
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001738 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001739 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001740 return NULL;
1741 }
1742
Michal Vasko7b62fed2015-10-26 15:39:46 +01001743 /* prepare session structure */
Michal Vasko131120a2018-05-29 15:44:02 +02001744 new_session = nc_new_session(NC_CLIENT, 1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001745 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001746 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001747 return NULL;
1748 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001749 new_session->status = NC_STATUS_STARTING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001750
Michal Vasko131120a2018-05-29 15:44:02 +02001751 /* share some parameters including the IO lock (we are using one socket for both sessions) */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001752 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001753 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko131120a2018-05-29 15:44:02 +02001754 new_session->io_lock = session->io_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001755
Michal Vasko1e7f9e72019-01-28 08:55:47 +01001756 /* append to the session ring list */
1757 if (!session->ti.libssh.next) {
1758 session->ti.libssh.next = new_session;
1759 new_session->ti.libssh.next = session;
1760 } else {
1761 ptr = session->ti.libssh.next;
1762 session->ti.libssh.next = new_session;
1763 new_session->ti.libssh.next = ptr;
1764 }
1765
Michal Vasko7b62fed2015-10-26 15:39:46 +01001766 /* create the channel safely */
Michal Vasko131120a2018-05-29 15:44:02 +02001767 if (nc_session_io_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001768 goto fail;
1769 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001770 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001771 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001772 }
Michal Vasko131120a2018-05-29 15:44:02 +02001773 nc_session_io_unlock(new_session, __func__);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001774
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001775 if (nc_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001776 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001777 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001778 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001779
Michal Vasko7b62fed2015-10-26 15:39:46 +01001780 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001781 if (nc_handshake_io(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001782 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001783 }
Michal Vaskoad611702015-12-03 13:41:51 +01001784 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001785
Michal Vaskoef578332016-01-25 13:20:09 +01001786 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001787 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001788 }
1789
1790 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001791 new_session->host = lydict_insert(ctx, session->host, 0);
1792 new_session->port = session->port;
1793 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001794
Michal Vasko7b62fed2015-10-26 15:39:46 +01001795 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001796
1797fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001798 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001799 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001800}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001801
Michal Vasko3031aae2016-01-27 16:07:18 +01001802struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001803nc_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 +01001804{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001805 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001806 unsigned int uint_port;
Michal Vasko3031aae2016-01-27 16:07:18 +01001807 struct passwd *pw;
Michal Vasko30e2c872016-02-18 10:03:21 +01001808 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001809 ssh_session sess;
1810
Michal Vasko80cad7f2015-12-08 14:42:27 +01001811 sess = ssh_new();
1812 if (!sess) {
1813 ERR("Unable to initialize an SSH session.");
1814 close(sock);
1815 return NULL;
1816 }
1817
1818 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001819 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001820 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001821 uint_port = port;
1822 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001823 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001824 if (!ssh_ch_opts.username) {
1825 pw = getpwuid(getuid());
1826 if (!pw) {
1827 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1828 return NULL;
1829 }
1830 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1831 } else {
1832 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001833 }
Michal Vasko086311b2016-01-08 09:53:11 +01001834 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1835 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1836 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1837 /* ecdsa is probably not supported... */
1838 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1839 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001840
Michal Vasko0190bc32016-03-02 15:47:49 +01001841 session = _nc_connect_libssh(sess, ctx, &ssh_ch_opts, timeout);
Michal Vasko4282fae2016-02-18 10:03:42 +01001842 if (session) {
1843 session->flags |= NC_SESSION_CALLHOME;
1844 }
1845
Michal Vasko30e2c872016-02-18 10:03:21 +01001846 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001847}