blob: 5f6c623c0fd4b62d718e31654283a3814d03a774 [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"))) {
61 ERR("Unable to open the current terminal (%s).", strerror(errno));
62 return NULL;
63 }
64
65 if (tcgetattr(fileno(ret), oldterm)) {
66 ERR("Unable to get terminal settings (%s).", strerror(errno));
67 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)) {
76 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
77 fclose(ret);
78 return NULL;
79 }
80
81 return ret;
82}
83
84static void
85restore_tty_close(FILE *tty, struct termios *oldterm)
86{
87 if (tcsetattr(fileno(tty), TCSANOW, oldterm) != 0) {
88 ERR("Unable to restore terminal settings (%s).", strerror(errno));
89 }
90 fclose(tty);
91}
92
Michal Vaskoe22c6732016-01-29 11:03:02 +010093static void
94_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +010095{
96 int i;
97
Michal Vaskoe22c6732016-01-29 11:03:02 +010098 for (i = 0; i < opts->key_count; ++i) {
99 free(opts->keys[i].pubkey_path);
100 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +0100101 }
Michal Vaskoe22c6732016-01-29 11:03:02 +0100102 free(opts->keys);
103 free(opts->username);
Radek Krejci5cebc6b2017-05-26 13:24:38 +0200104 opts->keys = NULL;
105 opts->username = NULL;
Michal Vaskoe22c6732016-01-29 11:03:02 +0100106}
Michal Vasko990089e2015-10-27 15:05:25 +0100107
Michal Vaskob7558c52016-02-26 15:04:19 +0100108void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100109nc_client_ssh_destroy_opts(void)
110{
111 _nc_client_ssh_destroy_opts(&ssh_opts);
112 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +0100113}
114
Michal Vaskoef112d72016-02-18 13:28:25 +0100115#ifdef ENABLE_DNSSEC
116
117/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
118/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
119static int
Michal Vasko650011a2016-02-25 14:49:29 +0100120sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg) {
Michal Vaskoef112d72016-02-18 13:28:25 +0100121 ns_msg handle;
122 ns_rr rr;
123 val_status_t val_status;
124 const unsigned char* rdata;
125 unsigned char buf[4096];
126 int buf_len = 4096;
127 int ret = 0, i, j, len;
128
129 /* class 1 - internet, type 44 - SSHFP */
130 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
131
132 if ((len < 0) || !val_istrusted(val_status)) {
133 ret = 2;
134 goto finish;
135 }
136
137 if (ns_initparse(buf, len, &handle) < 0) {
138 ERR("Failed to initialize DNSSEC response parser.");
139 ret = 2;
140 goto finish;
141 }
142
143 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
144 ERR("DNSSEC query returned %d.", i);
145 ret = 2;
146 goto finish;
147 }
148
149 if (!libsres_msg_getflag(handle, ns_f_ad)) {
150 /* response not secured by DNSSEC */
151 ret = 1;
152 }
153
154 /* query section */
155 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
Michal Vasko650011a2016-02-25 14:49:29 +0100156 ERR("DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100157 ret = 2;
158 goto finish;
159 }
160
161 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
Michal Vasko650011a2016-02-25 14:49:29 +0100162 ERR("DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100163 ret = 2;
164 goto finish;
165 }
166
167 /* answer section */
168 i = 0;
169 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
170 if (ns_rr_type(rr) != 44) {
171 ++i;
172 continue;
173 }
174
175 rdata = ns_rr_rdata(rr);
176 if (rdata[0] != type) {
177 ++i;
178 continue;
179 }
180 if (rdata[1] != alg) {
181 ++i;
182 continue;
183 }
184
185 /* we found the correct SSHFP entry */
186 rdata += 2;
187 for (j = 0; j < 20; ++j) {
188 if (rdata[j] != (unsigned char)sha1hash[j]) {
189 ret = 2;
190 goto finish;
191 }
192 }
193
194 /* server fingerprint is supported by a DNS entry,
195 * we have already determined if DNSSEC was used or not
196 */
197 goto finish;
198 }
199
200 /* no match */
201 ret = 2;
202
203finish:
204 val_free_validator_state();
205 return ret;
206}
207
208#endif /* ENABLE_DNSSEC */
209
Radek Krejci62aa0642017-05-25 16:33:49 +0200210int
Radek Krejci90a84a22017-05-25 13:53:00 +0200211sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(priv))
Michal Vaskoef112d72016-02-18 13:28:25 +0100212{
213 char *hexa;
214 int c, state, ret;
215 ssh_key srv_pubkey;
216 unsigned char *hash_sha1 = NULL;
217 size_t hlen;
218 enum ssh_keytypes_e srv_pubkey_type;
219 char answer[5];
220
221 state = ssh_is_server_known(session);
222
Michal Vaskocc0aa7d2016-05-31 12:48:42 +0200223 ret = ssh_get_publickey(session, &srv_pubkey);
Michal Vaskoef112d72016-02-18 13:28:25 +0100224 if (ret < 0) {
225 ERR("Unable to get server public key.");
226 return -1;
227 }
228
229 srv_pubkey_type = ssh_key_type(srv_pubkey);
230 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
231 ssh_key_free(srv_pubkey);
232 if (ret < 0) {
233 ERR("Failed to calculate SHA1 hash of the server public key.");
234 return -1;
235 }
236
237 hexa = ssh_get_hexa(hash_sha1, hlen);
238
239 switch (state) {
240 case SSH_SERVER_KNOWN_OK:
241 break; /* ok */
242
243 case SSH_SERVER_KNOWN_CHANGED:
244 ERR("Remote host key changed, the connection will be terminated!");
245 goto fail;
246
247 case SSH_SERVER_FOUND_OTHER:
248 WRN("Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
249 goto hostkey_not_known;
250
251 case SSH_SERVER_FILE_NOT_FOUND:
252 WRN("Could not find the known hosts file.");
253 goto hostkey_not_known;
254
255 case SSH_SERVER_NOT_KNOWN:
256hostkey_not_known:
257#ifdef ENABLE_DNSSEC
258 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
259 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
Michal Vasko650011a2016-02-25 14:49:29 +0100260 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100261 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100262 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100263 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100264 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100265 }
266
267 /* DNSSEC SSHFP check successful, that's enough */
268 if (!ret) {
269 VRB("DNSSEC SSHFP check successful.");
270 ssh_write_knownhost(session);
271 ssh_clean_pubkey_hash(&hash_sha1);
272 ssh_string_free_char(hexa);
273 return 0;
274 }
275 }
276#endif
277
278 /* try to get result from user */
279 fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname);
280 fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa);
281
282#ifdef ENABLE_DNSSEC
283 if (ret == 2) {
284 fprintf(stdout, "No matching host key fingerprint found using DNS.\n");
285 } else if (ret == 1) {
286 fprintf(stdout, "Matching host key fingerprint found using DNS.\n");
287 }
288#endif
289
290 fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? ");
291
292 do {
293 if (fscanf(stdin, "%4s", answer) == EOF) {
294 ERR("fscanf() failed (%s).", strerror(errno));
295 goto fail;
296 }
297 while (((c = getchar()) != EOF) && (c != '\n'));
298
299 fflush(stdin);
300 if (!strcmp("yes", answer)) {
301 /* store the key into the host file */
302 ret = ssh_write_knownhost(session);
303 if (ret != SSH_OK) {
304 WRN("Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
305 }
306 } else if (!strcmp("no", answer)) {
307 goto fail;
308 } else {
309 fprintf(stdout, "Please type 'yes' or 'no': ");
310 }
311 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
312
313 break;
314
315 case SSH_SERVER_ERROR:
316 ssh_clean_pubkey_hash(&hash_sha1);
317 fprintf(stderr,"%s",ssh_get_error(session));
318 return -1;
319 }
320
321 ssh_clean_pubkey_hash(&hash_sha1);
322 ssh_string_free_char(hexa);
323 return 0;
324
325fail:
326 ssh_clean_pubkey_hash(&hash_sha1);
327 ssh_string_free_char(hexa);
328 return -1;
329}
330
Radek Krejci62aa0642017-05-25 16:33:49 +0200331char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200332sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200333{
Michal Vasko4eb3c312016-03-01 14:09:37 +0100334 char *buf;
Michal Vasko93ab6172018-02-16 15:58:12 +0100335 int c, buflen = 1024, len, ret;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200336 struct termios oldterm;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100337 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +0200338
Michal Vasko11d142a2016-01-19 15:58:24 +0100339 buf = malloc(buflen * sizeof *buf);
340 if (!buf) {
341 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100342 return NULL;
343 }
344
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200345 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
346 ERR("ttyname_r failed (%s).", strerror(ret));
347 free(buf);
348 return NULL;
349 }
350
351 if (!(tty = open_tty_noecho(buf, &oldterm))) {
352 free(buf);
353 return NULL;
354 }
355
Michal Vasko93f26d92018-02-01 09:08:35 +0100356 if (fprintf(stdout, "%s@%s password: ", username, hostname) < 1) {
357 goto stdout_fail;
358 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200359 fflush(stdout);
360
361 len = 0;
Michal Vasko93f26d92018-02-01 09:08:35 +0100362 while (((c = fgetc(tty)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100363 if (len >= buflen - 1) {
364 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100365 buf = nc_realloc(buf, buflen * sizeof *buf);
366 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100367 ERRMEM;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200368 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100369 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100370 }
371 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100372 buf[len++] = (char)c;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100373 }
374 buf[len++] = 0; /* terminating null byte */
375
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200376 fprintf(stdout, "\n");
377 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100378 return buf;
Michal Vasko93f26d92018-02-01 09:08:35 +0100379
380stdout_fail:
381 if (feof(stdout)) {
382 ERR("Writing into stdout failed (End of file).");
383 } else {
384 assert(ferror(stdout));
385 ERR("Writing into stdout failed (%s).", strerror(errno));
386 }
387 restore_tty_close(tty, &oldterm);
388 free(buf);
389 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100390}
391
Radek Krejci62aa0642017-05-25 16:33:49 +0200392char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200393sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100394{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200395 unsigned int buflen = 64, cur_len;
Michal Vasko93ab6172018-02-16 15:58:12 +0100396 int ret, c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200397 struct termios oldterm;
398 char *buf;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100399 FILE *tty;
400
401 buf = malloc(buflen * sizeof *buf);
402 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100403 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100404 return NULL;
405 }
406
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200407 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
408 ERR("ttyname_r failed (%s).", strerror(ret));
409 free(buf);
410 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100411 }
412
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200413 if (!echo) {
414 if (!(tty = open_tty_noecho(buf, &oldterm))) {
415 free(buf);
416 return NULL;
417 }
418 } else {
419 tty = stdin;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100420 }
421
Michal Vasko7b62fed2015-10-26 15:39:46 +0100422
Michal Vasko93f26d92018-02-01 09:08:35 +0100423 if (auth_name && (fprintf(stdout, "%s\n", auth_name) < 1)) {
424 goto stdout_fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100425 }
Michal Vasko93f26d92018-02-01 09:08:35 +0100426 if (instruction && (fprintf(stdout, "%s\n", instruction) < 1)) {
427 goto stdout_fail;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200428 }
Michal Vasko93f26d92018-02-01 09:08:35 +0100429 if (fputs(prompt, stdout) == EOF) {
430 goto stdout_fail;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200431 }
432 fflush(stdout);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100433
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200434 cur_len = 0;
Michal Vasko93f26d92018-02-01 09:08:35 +0100435 while (((c = fgetc(tty)) != EOF) && (c != '\n')) {
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200436 if (cur_len >= buflen - 1) {
437 buflen *= 2;
438 buf = nc_realloc(buf, buflen * sizeof *buf);
439 if (!buf) {
440 ERRMEM;
441 goto fail;
442 }
443 }
Michal Vasko93ab6172018-02-16 15:58:12 +0100444 buf[cur_len++] = (char)c;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200445 }
446 /* terminating null byte */
447 buf[cur_len] = '\0';
448
449 fprintf(stdout, "\n");
450 if (!echo) {
451 restore_tty_close(tty, &oldterm);
452 }
453 return buf;
454
Michal Vasko93f26d92018-02-01 09:08:35 +0100455stdout_fail:
456 if (feof(stdout)) {
457 ERR("Writing into stdout failed (End of file).");
458 } else {
459 assert(ferror(stdout));
460 ERR("Writing into stdout failed (%s).", strerror(errno));
461 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200462fail:
463 if (!echo) {
464 restore_tty_close(tty, &oldterm);
465 }
466 free(buf);
467 return NULL;
468}
469
Radek Krejci62aa0642017-05-25 16:33:49 +0200470char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200471sshauth_privkey_passphrase(const char* privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200472{
Michal Vasko93ab6172018-02-16 15:58:12 +0100473 char *buf;
474 int c, buflen = 1024, len, ret;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200475 struct termios oldterm;
476 FILE *tty;
477
478 buf = malloc(buflen * sizeof *buf);
479 if (!buf) {
480 ERRMEM;
481 return NULL;
482 }
483
484 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
485 ERR("ttyname_r failed (%s).", strerror(ret));
486 free(buf);
487 return NULL;
488 }
489
490 if (!(tty = open_tty_noecho(buf, &oldterm))) {
491 free(buf);
492 return NULL;
493 }
494
Michal Vasko93f26d92018-02-01 09:08:35 +0100495 if (fprintf(stdout, "Enter passphrase for the key '%s':", privkey_path) < 1) {
496 goto stdout_fail;
497 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200498 fflush(stdout);
499
500 len = 0;
Michal Vasko93f26d92018-02-01 09:08:35 +0100501 while (((c = fgetc(tty)) != EOF) && (c != '\n')) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100502 if (len >= buflen - 1) {
503 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100504 buf = nc_realloc(buf, buflen * sizeof *buf);
505 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100506 ERRMEM;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100507 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100508 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100509 }
510 buf[len++] = (char)c;
511 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200512 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100513
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200514 fprintf(stdout, "\n");
515 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100516 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100517
Michal Vasko93f26d92018-02-01 09:08:35 +0100518stdout_fail:
519 if (feof(stdout)) {
520 ERR("Writing into stdout failed (End of file).");
521 } else {
522 assert(ferror(stdout));
523 ERR("Writing into stdout failed (%s).", strerror(errno));
524 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100525fail:
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200526 restore_tty_close(tty, &oldterm);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100527 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100528 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100529}
530
Michal Vaskoef112d72016-02-18 13:28:25 +0100531static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200532_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
533 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100534{
Michal Vaskoef112d72016-02-18 13:28:25 +0100535 if (auth_hostkey_check) {
536 opts->auth_hostkey_check = auth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200537 opts->auth_hostkey_check_priv = priv;
Michal Vaskoef112d72016-02-18 13:28:25 +0100538 } else {
539 opts->auth_hostkey_check = sshauth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200540 opts->auth_hostkey_check_priv = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100541 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100542}
543
Radek Krejci90a84a22017-05-25 13:53:00 +0200544static void
545_nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
546 void **priv, struct nc_client_ssh_opts *opts)
Michal Vaskoef112d72016-02-18 13:28:25 +0100547{
Radek Krejci90a84a22017-05-25 13:53:00 +0200548 if (auth_hostkey_check) {
549 (*auth_hostkey_check) = opts->auth_hostkey_check == sshauth_hostkey_check ? NULL : opts->auth_hostkey_check;
550 }
551 if (priv) {
552 (*priv) = opts->auth_hostkey_check_priv;
553 }
554}
555
556
557API void
558nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
559 void *priv)
560{
561 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100562}
563
564API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200565nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
566 void *priv)
Michal Vaskoef112d72016-02-18 13:28:25 +0100567{
Radek Krejci90a84a22017-05-25 13:53:00 +0200568 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100569}
570
Radek Krejci90a84a22017-05-25 13:53:00 +0200571API void
572nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
573 void **priv)
574{
575 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
576}
577
578API void
579nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
580 void **priv)
581{
582 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
583}
Michal Vaskoef112d72016-02-18 13:28:25 +0100584
Michal Vasko30e2c872016-02-18 10:03:21 +0100585static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200586_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
587 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100588{
589 if (auth_password) {
590 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200591 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100592 } else {
593 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200594 opts->auth_password_priv = NULL;
595 }
596}
597
598static void
599_nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
600 void **priv, struct nc_client_ssh_opts *opts)
601{
602 if (auth_password) {
603 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
604 }
605 if (priv) {
606 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100607 }
608}
609
610API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200611nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
612 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100613{
Radek Krejci90a84a22017-05-25 13:53:00 +0200614 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100615}
616
617API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200618nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
619 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100620{
Radek Krejci90a84a22017-05-25 13:53:00 +0200621 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
622}
623
624API void
625nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
626 void **priv)
627{
628 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
629}
630
631API void
632nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
633 void **priv)
634{
635 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100636}
637
638static void
639_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200640 const char *prompt, int echo, void *priv),
641 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100642{
643 if (auth_interactive) {
644 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200645 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100646 } else {
647 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200648 opts->auth_interactive_priv = NULL;
649 }
650}
651
652static void
653_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
654 const char *prompt, int echo, void *priv),
655 void **priv, struct nc_client_ssh_opts *opts)
656{
657 if (auth_interactive) {
658 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
659 }
660 if (priv) {
661 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100662 }
663}
664
665API void
666nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200667 const char *prompt, int echo, void *priv),
668 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100669{
Radek Krejci90a84a22017-05-25 13:53:00 +0200670 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100671}
672
673API void
674nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200675 const char *prompt, int echo, 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_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
679}
680
681API void
682nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
683 const char *prompt, int echo, void *priv),
684 void **priv)
685{
686 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
687}
688
689API void
690nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
691 const char *prompt, int echo, void *priv),
692 void **priv)
693{
694 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100695}
696
697static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200698_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
699 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100700{
701 if (auth_privkey_passphrase) {
702 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200703 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100704 } else {
705 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200706 opts->auth_privkey_passphrase_priv = NULL;
707 }
708}
709
710static void
711_nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
712 void **priv, struct nc_client_ssh_opts *opts)
713{
714 if (auth_privkey_passphrase) {
715 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
716 }
717 if (priv) {
718 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100719 }
720}
721
722API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200723nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
724 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100725{
Radek Krejci90a84a22017-05-25 13:53:00 +0200726 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100727}
728
729API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200730nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
731 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100732{
Radek Krejci90a84a22017-05-25 13:53:00 +0200733 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
734}
735
736API void
737nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
738 void **priv)
739{
740 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
741}
742
743API void
744nc_client_ssh_ch_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
745 void **priv)
746{
747 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100748}
749
Michal Vasko3031aae2016-01-27 16:07:18 +0100750static int
751_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 +0100752{
753 int i;
754 FILE *key;
755 char line[128];
756
Michal Vasko45e53ae2016-04-07 11:46:03 +0200757 if (!pub_key) {
758 ERRARG("pub_key");
759 return -1;
760 } else if (!priv_key) {
761 ERRARG("priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100762 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100763 }
764
Michal Vasko3031aae2016-01-27 16:07:18 +0100765 for (i = 0; i < opts->key_count; ++i) {
766 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
767 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100768 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100769 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100770 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100771 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100772 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100773 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100774 continue;
775 }
776
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100777 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100778 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100779 }
780 }
781
Michal Vasko3031aae2016-01-27 16:07:18 +0100782 /* add the keys */
783 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100784 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
785 if (!opts->keys) {
786 ERRMEM;
787 return -1;
788 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100789 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
790 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
791 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100792
Michal Vasko4eb3c312016-03-01 14:09:37 +0100793 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
794 ERRMEM;
795 return -1;
796 }
797
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100798 /* check encryption */
799 if ((key = fopen(priv_key, "r"))) {
800 /* 1st line - key type */
801 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100802 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100803 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100804 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100805 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100806 /* 2nd line - encryption information or key */
807 if (!fgets(line, sizeof line, key)) {
808 fclose(key);
809 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100810 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100811 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100812 fclose(key);
813 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100814 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100815 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100816 }
817
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100818 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100819}
820
821API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100822nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100823{
Michal Vasko3031aae2016-01-27 16:07:18 +0100824 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
825}
826
827API int
828nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
829{
830 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
831}
832
833static int
834_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
835{
836 if (idx >= opts->key_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200837 ERRARG("idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100838 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100839 }
840
Michal Vasko3031aae2016-01-27 16:07:18 +0100841 free(opts->keys[idx].pubkey_path);
842 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100843
Michal Vasko3031aae2016-01-27 16:07:18 +0100844 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100845 if (idx < opts->key_count) {
846 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
847 }
848 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100849 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
850 if (!opts->keys) {
851 ERRMEM;
852 return -1;
853 }
Michal Vaskoc0256492016-02-02 12:19:06 +0100854 } else {
855 free(opts->keys);
856 opts->keys = NULL;
857 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100858
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100859 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100860}
861
862API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100863nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100864{
Michal Vasko3031aae2016-01-27 16:07:18 +0100865 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100866}
867
868API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100869nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100870{
Michal Vasko3031aae2016-01-27 16:07:18 +0100871 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
872}
873
874static int
875_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
876{
877 return opts->key_count;
878}
879
880API int
881nc_client_ssh_get_keypair_count(void)
882{
883 return _nc_client_ssh_get_keypair_count(&ssh_opts);
884}
885
886API int
887nc_client_ssh_ch_get_keypair_count(void)
888{
889 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
890}
891
892static int
893_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
894{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200895 if (idx >= opts->key_count) {
896 ERRARG("idx");
897 return -1;
898 } else if (!pub_key && !priv_key) {
899 ERRARG("pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100900 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100901 }
902
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100903 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100904 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100905 }
906 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100907 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100908 }
909
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100910 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100911}
912
Michal Vasko3031aae2016-01-27 16:07:18 +0100913API int
914nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
915{
916 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
917}
918
919API int
920nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
921{
922 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
923}
924
925static void
926_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 +0100927{
928 if (pref < 0) {
929 pref = -1;
930 }
931
932 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100933 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100934 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100935 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100936 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100937 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100938 }
939}
940
Michal Vasko3031aae2016-01-27 16:07:18 +0100941API void
942nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100943{
Michal Vasko3031aae2016-01-27 16:07:18 +0100944 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
945}
946
947API void
948nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
949{
950 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
951}
952
953static int16_t
954_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
955{
956 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100957
958 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100959 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100960 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100961 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100962 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100963 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100964 }
965
966 return pref;
967}
968
Michal Vasko3031aae2016-01-27 16:07:18 +0100969API int16_t
970nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
971{
972 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
973}
974
975API int16_t
976nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
977{
978 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
979}
980
981static int
982_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
983{
984 if (opts->username) {
985 free(opts->username);
986 }
987 if (username) {
988 opts->username = strdup(username);
989 if (!opts->username) {
990 ERRMEM;
991 return -1;
992 }
993 } else {
994 opts->username = NULL;
995 }
996
997 return 0;
998}
999
1000API int
1001nc_client_ssh_set_username(const char *username)
1002{
1003 return _nc_client_ssh_set_username(username, &ssh_opts);
1004}
1005
1006API int
1007nc_client_ssh_ch_set_username(const char *username)
1008{
1009 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1010}
1011
Michal Vaskoe22c6732016-01-29 11:03:02 +01001012static const char *
1013_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1014{
1015 return opts->username;
1016}
1017
1018API const char *
1019nc_client_ssh_get_username(void)
1020{
1021 return _nc_client_ssh_get_username(&ssh_opts);
1022}
1023
1024API const char *
1025nc_client_ssh_ch_get_username(void)
1026{
1027 return _nc_client_ssh_get_username(&ssh_ch_opts);
1028}
1029
Michal Vasko3031aae2016-01-27 16:07:18 +01001030API int
1031nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1032{
1033 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
1034}
1035
1036API int
1037nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1038{
1039 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1040}
1041
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001042/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001043 * Host, port, username, and a connected socket is expected to be set.
1044 */
1045static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001046connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001047{
Michal Vasko36c7be82017-02-22 13:37:59 +01001048 int j, ret_auth, userauthlist, ret;
Michal Vasko235efdc2015-12-17 12:05:04 +01001049 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001050 int16_t pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001051 const char* prompt;
1052 char *s, *answer, echo;
1053 ssh_key pubkey, privkey;
1054 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001055 struct timespec ts_timeout, ts_cur;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001056
1057 ssh_sess = session->ti.libssh.session;
1058
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001059 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001060 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001061 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1062 usleep(NC_TIMEOUT_STEP);
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001063 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001064 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001065 break;
1066 }
1067 }
1068 if (ret == SSH_AGAIN) {
1069 ERR("SSH connect timeout.");
1070 return 0;
1071 } else if (ret != SSH_OK) {
1072 ERR("Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001073 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001074 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001075 }
1076
Radek Krejci90a84a22017-05-25 13:53:00 +02001077 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001078 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001079 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001080 }
1081
Michal Vasko36c7be82017-02-22 13:37:59 +01001082 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001083 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001084 nc_addtimespec(&ts_timeout, timeout);
1085 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001086 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1087 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001088 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001089 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001090 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1091 break;
1092 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001093 }
1094 }
1095 if (ret_auth == SSH_AUTH_AGAIN) {
1096 ERR("Request authentication methods timeout.");
1097 return 0;
1098 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001099 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001100 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001101 }
1102
1103 /* check what authentication methods are available */
1104 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001105
1106 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001107 if (opts->auth_pref[0].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001108 VRB("Interactive SSH authentication method was disabled.");
1109 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001110 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001111 if (opts->auth_pref[1].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001112 VRB("Password SSH authentication method was disabled.");
1113 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001114 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001115 if (opts->auth_pref[2].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001116 VRB("Publickey SSH authentication method was disabled.");
1117 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001118 }
1119
Michal Vasko0190bc32016-03-02 15:47:49 +01001120 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001121 auth = 0;
1122 pref = 0;
1123 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1124 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001125 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001126 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001127 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001128 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001129 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001130 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001131 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001132 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001133 }
1134
Michal Vasko235efdc2015-12-17 12:05:04 +01001135 if (!auth) {
Michal Vaskod083db62016-01-19 10:31:29 +01001136 ERR("Unable to authenticate to the remote server (no supported authentication methods left).");
Michal Vasko0190bc32016-03-02 15:47:49 +01001137 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001138 }
1139
1140 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001141 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001142 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001143 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1144
Michal Vaskoef578332016-01-25 13:20:09 +01001145 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001146 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko0190bc32016-03-02 15:47:49 +01001147
Michal Vasko36c7be82017-02-22 13:37:59 +01001148 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001149 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001150 nc_addtimespec(&ts_timeout, timeout);
1151 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001152 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1153 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001154 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001155 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001156 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1157 break;
1158 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001159 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001160 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001161 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001162 free(s);
1163 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001164
Michal Vasko7b62fed2015-10-26 15:39:46 +01001165 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001166 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1167
Michal Vaskod083db62016-01-19 10:31:29 +01001168 VRB("Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001169
Michal Vasko36c7be82017-02-22 13:37:59 +01001170 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001171 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001172 nc_addtimespec(&ts_timeout, timeout);
1173 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001174 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO)
1175 || (ret_auth == SSH_AUTH_AGAIN)) {
1176 if (ret_auth == SSH_AUTH_AGAIN) {
1177 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001178 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001179 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001180 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1181 break;
1182 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001183 }
1184 continue;
1185 }
1186
Michal Vasko7b62fed2015-10-26 15:39:46 +01001187 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1188 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001189 if (!prompt) {
1190 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001191 break;
1192 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001193
1194 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1195 echo = 0;
1196
Michal Vasko30e2c872016-02-18 10:03:21 +01001197 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
1198 ssh_userauth_kbdint_getinstruction(ssh_sess),
Radek Krejci90a84a22017-05-25 13:53:00 +02001199 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001200 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1201 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001202 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001203 break;
1204 }
1205 free(answer);
1206 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001207 if (ret_auth == SSH_AUTH_ERROR) {
1208 break;
1209 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001210 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001211 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001212 nc_addtimespec(&ts_timeout, timeout);
1213 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001214 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001215 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001216
Michal Vasko206d3b12015-12-04 11:08:42 +01001217 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001218 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1219
Michal Vaskod083db62016-01-19 10:31:29 +01001220 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001221
1222 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001223 if (!opts->key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001224 VRB("No key pair specified.");
1225 break;
1226 }
1227
Michal Vasko30e2c872016-02-18 10:03:21 +01001228 for (j = 0; j < opts->key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +01001229 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko30e2c872016-02-18 10:03:21 +01001230 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1231 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001232
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001233 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1234 if (ret == SSH_EOF) {
1235 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
1236 continue;
1237 } else if (ret == SSH_ERROR) {
1238 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001239 continue;
1240 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001241
Michal Vasko36c7be82017-02-22 13:37:59 +01001242 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001243 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001244 nc_addtimespec(&ts_timeout, timeout);
1245 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001246 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1247 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001248 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001249 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001250 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1251 break;
1252 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001253 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001254 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001255 ssh_key_free(pubkey);
1256
1257 if (ret_auth == SSH_AUTH_DENIED) {
1258 continue;
1259 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001260 break;
1261 }
1262
Michal Vasko30e2c872016-02-18 10:03:21 +01001263 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001264 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001265 } else {
1266 s = NULL;
1267 }
1268
Michal Vasko0190bc32016-03-02 15:47:49 +01001269 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001270 if (s) {
1271 memset(s, 0, strlen(s));
1272 free(s);
1273 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001274 if (ret == SSH_EOF) {
1275 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
1276 continue;
1277 } else if (ret == SSH_ERROR) {
1278 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001279 continue;
1280 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001281
Michal Vasko36c7be82017-02-22 13:37:59 +01001282 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001283 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001284 nc_addtimespec(&ts_timeout, timeout);
1285 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001286 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1287 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001288 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001289 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001290 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1291 break;
1292 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001293 }
1294 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001295 ssh_key_free(privkey);
1296
Michal Vasko0190bc32016-03-02 15:47:49 +01001297 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001298 break;
1299 }
1300 }
1301 break;
1302 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001303
Michal Vasko0190bc32016-03-02 15:47:49 +01001304 switch (ret_auth) {
1305 case SSH_AUTH_AGAIN:
1306 ERR("Authentication response timeout.");
1307 return 0;
1308 case SSH_AUTH_ERROR:
1309 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
1310 return -1;
1311 case SSH_AUTH_DENIED:
1312 WRN("Authentication denied.");
1313 break;
1314 case SSH_AUTH_PARTIAL:
1315 VRB("Partial authentication success.");
1316 break;
1317 case SSH_AUTH_SUCCESS:
1318 VRB("Authentication successful.");
1319 break;
1320 case SSH_AUTH_INFO:
1321 ERRINT;
1322 return -1;
1323 }
1324 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001325
Michal Vasko0190bc32016-03-02 15:47:49 +01001326 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001327}
1328
1329/* Open new SSH channel and request the 'netconf' subsystem.
1330 * SSH connection is expected to be established.
1331 */
1332static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001333open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001334{
1335 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001336 int ret;
1337 struct timespec ts_timeout, ts_cur;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001338
1339 ssh_sess = session->ti.libssh.session;
1340
1341 if (!ssh_is_connected(ssh_sess)) {
1342 ERR("SSH session not connected.");
1343 return -1;
1344 }
1345
1346 if (session->ti.libssh.channel) {
1347 ERR("SSH channel already created.");
1348 return -1;
1349 }
1350
Michal Vasko7b62fed2015-10-26 15:39:46 +01001351 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001352 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001353 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001354 nc_addtimespec(&ts_timeout, timeout);
1355 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001356 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001357 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1358 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001359 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001360 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001361 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1362 break;
1363 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001364 }
1365 }
1366 if (ret == SSH_AGAIN) {
1367 ERR("Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001368 ssh_channel_free(session->ti.libssh.channel);
1369 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001370 return 0;
1371 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001372 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001373 ssh_channel_free(session->ti.libssh.channel);
1374 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001375 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001376 }
1377
1378 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001379 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001380 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001381 nc_addtimespec(&ts_timeout, timeout);
1382 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001383 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1384 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001385 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001386 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001387 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1388 break;
1389 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001390 }
1391 }
1392 if (ret == SSH_AGAIN) {
1393 ERR("Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001394 ssh_channel_free(session->ti.libssh.channel);
1395 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001396 return 0;
1397 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001398 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001399 ssh_channel_free(session->ti.libssh.channel);
1400 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001401 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001402 }
1403
Michal Vasko0190bc32016-03-02 15:47:49 +01001404 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001405}
1406
Michal Vasko30e2c872016-02-18 10:03:21 +01001407static struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001408_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 +01001409{
1410 char *host = NULL, *username = NULL;
1411 unsigned short port = 0;
1412 int sock;
1413 struct passwd *pw;
1414 struct nc_session *session = NULL;
1415
1416 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001417 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001418 return NULL;
1419 }
1420
1421 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001422 session = nc_new_session(0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001423 if (!session) {
1424 ERRMEM;
1425 return NULL;
1426 }
1427 session->status = NC_STATUS_STARTING;
1428 session->side = NC_CLIENT;
1429
1430 /* transport lock */
Michal Vasko30e2c872016-02-18 10:03:21 +01001431 pthread_mutex_init(session->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001432 pthread_cond_init(session->ti_cond, NULL);
1433 *session->ti_inuse = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001434
1435 session->ti_type = NC_TI_LIBSSH;
1436 session->ti.libssh.session = ssh_session;
1437
1438 /* was port set? */
1439 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1440
1441 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1442 /*
1443 * There is no file descriptor (detected based on the host, there is no way to check
1444 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1445 */
1446
1447 /* remember host */
1448 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001449 if (!host) {
1450 ERRMEM;
1451 goto fail;
1452 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001453 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1454
1455 /* create and connect socket */
1456 sock = nc_sock_connect(host, port);
1457 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001458 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001459 goto fail;
1460 }
1461 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001462 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001463 }
1464
1465 /* was username set? */
1466 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1467
1468 if (!ssh_is_connected(ssh_session)) {
1469 /*
1470 * We are connected, but not SSH authenticated. (Transport layer)
1471 */
1472
1473 /* remember username */
1474 if (!username) {
1475 if (!opts->username) {
1476 pw = getpwuid(getuid());
1477 if (!pw) {
1478 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1479 goto fail;
1480 }
1481 username = strdup(pw->pw_name);
1482 } else {
1483 username = strdup(opts->username);
1484 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001485 if (!username) {
1486 ERRMEM;
1487 goto fail;
1488 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001489 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1490 }
1491
1492 /* connect and authenticate SSH session */
1493 session->host = host;
1494 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001495 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001496 goto fail;
1497 }
1498 }
1499
1500 /*
1501 * Almost done, open a netconf channel. (Transport layer / application layer)
1502 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001503 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001504 goto fail;
1505 }
1506
1507 /*
1508 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1509 */
1510
Radek Krejcifd5b6682017-06-13 15:52:53 +02001511 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1512 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001513 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001514 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001515
1516 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001517 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001518 goto fail;
1519 }
1520 session->status = NC_STATUS_RUNNING;
1521
1522 if (nc_ctx_check_and_fill(session) == -1) {
1523 goto fail;
1524 }
1525
1526 /* store information into the dictionary */
1527 if (host) {
1528 session->host = lydict_insert_zc(ctx, host);
1529 }
1530 if (port) {
1531 session->port = port;
1532 }
1533 if (username) {
1534 session->username = lydict_insert_zc(ctx, username);
1535 }
1536
1537 return session;
1538
1539fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001540 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001541 return NULL;
1542}
1543
Radek Krejciac6d3472015-10-22 15:47:18 +02001544API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001545nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001546{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001547 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001548 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001549 uint32_t port_uint;
Michal Vasko3031aae2016-01-27 16:07:18 +01001550 char *username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001551 struct passwd *pw;
1552 struct nc_session *session = NULL;
1553
1554 /* process parameters */
1555 if (!host || strisempty(host)) {
1556 host = "localhost";
1557 }
1558
1559 if (!port) {
1560 port = NC_PORT_SSH;
1561 }
Michal Vasko55fded62016-02-02 12:19:34 +01001562 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001563
Michal Vasko3031aae2016-01-27 16:07:18 +01001564 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001565 pw = getpwuid(getuid());
1566 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001567 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1568 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001569 } else {
1570 username = pw->pw_name;
1571 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001572 } else {
1573 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001574 }
1575
1576 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001577 session = nc_new_session(0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001578 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001579 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001580 return NULL;
1581 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001582 session->status = NC_STATUS_STARTING;
1583 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +02001584
Michal Vasko7b62fed2015-10-26 15:39:46 +01001585 /* transport lock */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001586 pthread_mutex_init(session->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001587 pthread_cond_init(session->ti_cond, NULL);
1588 *session->ti_inuse = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001589
1590 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001591 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001592 session->ti.libssh.session = ssh_new();
1593 if (!session->ti.libssh.session) {
1594 ERR("Unable to initialize SSH session.");
1595 goto fail;
1596 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001597
Michal Vasko7b62fed2015-10-26 15:39:46 +01001598 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001599 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001600 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001601 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001602 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko086311b2016-01-08 09:53:11 +01001603 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS,
1604 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1605 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1606 /* ecdsa is probably not supported... */
1607 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1608 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001609
1610 /* create and assign communication socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001611 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001612 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001613 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001614 goto fail;
1615 }
1616 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001617 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001618
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001619 /* temporarily, for session connection */
1620 session->host = host;
1621 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001622 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1)
1623 || (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001624 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001625 }
1626
Radek Krejcifd5b6682017-06-13 15:52:53 +02001627 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1628 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001629 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001630 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001631
Radek Krejciac6d3472015-10-22 15:47:18 +02001632 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001633 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001634 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001635 }
Michal Vaskoad611702015-12-03 13:41:51 +01001636 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001637
Michal Vaskoef578332016-01-25 13:20:09 +01001638 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001639 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001640 }
1641
1642 /* store information into the dictionary */
1643 session->host = lydict_insert(ctx, host, 0);
1644 session->port = port;
1645 session->username = lydict_insert(ctx, username, 0);
1646
Radek Krejciac6d3472015-10-22 15:47:18 +02001647 return session;
1648
Michal Vasko7b62fed2015-10-26 15:39:46 +01001649fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001650 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001651 return NULL;
1652}
1653
1654API struct nc_session *
1655nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1656{
Michal Vasko0190bc32016-03-02 15:47:49 +01001657 return _nc_connect_libssh(ssh_session, ctx, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001658}
1659
1660API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001661nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001662{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001663 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001664
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001665 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001666 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001667 return NULL;
1668 }
1669
Michal Vasko7b62fed2015-10-26 15:39:46 +01001670 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001671 new_session = nc_new_session(1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001672 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001673 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001674 return NULL;
1675 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001676 new_session->status = NC_STATUS_STARTING;
1677 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001678
1679 /* share some parameters including the session lock */
1680 new_session->ti_type = NC_TI_LIBSSH;
1681 new_session->ti_lock = session->ti_lock;
Michal Vaskoade892d2017-02-22 13:40:35 +01001682 new_session->ti_cond = session->ti_cond;
1683 new_session->ti_inuse = session->ti_inuse;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001684 new_session->ti.libssh.session = session->ti.libssh.session;
1685
1686 /* create the channel safely */
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001687 if (nc_session_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001688 goto fail;
1689 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001690
1691 /* open a channel */
Michal Vasko0190bc32016-03-02 15:47:49 +01001692 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001693 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001694 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001695
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001696 if (nc_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001697 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001698 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001699 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001700
Michal Vasko7b62fed2015-10-26 15:39:46 +01001701 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001702 if (nc_handshake(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001703 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001704 }
Michal Vaskoad611702015-12-03 13:41:51 +01001705 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001706
Michal Vaskoade892d2017-02-22 13:40:35 +01001707 nc_session_unlock(new_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001708
Michal Vaskoef578332016-01-25 13:20:09 +01001709 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001710 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001711 }
1712
1713 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001714 new_session->host = lydict_insert(ctx, session->host, 0);
1715 new_session->port = session->port;
1716 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001717
1718 /* append to the session ring list */
1719 if (!session->ti.libssh.next) {
1720 session->ti.libssh.next = new_session;
1721 new_session->ti.libssh.next = session;
1722 } else {
1723 ptr = session->ti.libssh.next;
1724 session->ti.libssh.next = new_session;
1725 new_session->ti.libssh.next = ptr;
1726 }
1727
1728 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001729
1730fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001731 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001732 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001733}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001734
Michal Vasko3031aae2016-01-27 16:07:18 +01001735struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001736nc_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 +01001737{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001738 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001739 unsigned int uint_port;
Michal Vasko3031aae2016-01-27 16:07:18 +01001740 struct passwd *pw;
Michal Vasko30e2c872016-02-18 10:03:21 +01001741 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001742 ssh_session sess;
1743
Michal Vasko80cad7f2015-12-08 14:42:27 +01001744 sess = ssh_new();
1745 if (!sess) {
1746 ERR("Unable to initialize an SSH session.");
1747 close(sock);
1748 return NULL;
1749 }
1750
1751 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001752 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001753 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001754 uint_port = port;
1755 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001756 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001757 if (!ssh_ch_opts.username) {
1758 pw = getpwuid(getuid());
1759 if (!pw) {
1760 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1761 return NULL;
1762 }
1763 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1764 } else {
1765 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001766 }
Michal Vasko086311b2016-01-08 09:53:11 +01001767 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1768 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1769 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1770 /* ecdsa is probably not supported... */
1771 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1772 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001773
Michal Vasko0190bc32016-03-02 15:47:49 +01001774 session = _nc_connect_libssh(sess, ctx, &ssh_ch_opts, timeout);
Michal Vasko4282fae2016-02-18 10:03:42 +01001775 if (session) {
1776 session->flags |= NC_SESSION_CALLHOME;
1777 }
1778
Michal Vasko30e2c872016-02-18 10:03:21 +01001779 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001780}