blob: cf4ad959b5f824225837d7f40068c994155cbf01 [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 Krejci90a84a22017-05-25 13:53:00 +020047static int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv);
48static char *sshauth_password(const char *username, const char *hostname, void *priv);
49static char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
50static char *sshauth_privkey_passphrase(const char* privkey_path, void *priv);
Michal Vasko30e2c872016-02-18 10:03:21 +010051
Michal Vaskodaf9a092016-02-09 10:42:05 +010052extern struct nc_client_opts client_opts;
53
Michal Vasko3031aae2016-01-27 16:07:18 +010054static struct nc_client_ssh_opts ssh_opts = {
Michal Vasko30e2c872016-02-18 10:03:21 +010055 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
Michal Vaskoef112d72016-02-18 13:28:25 +010056 .auth_hostkey_check = sshauth_hostkey_check,
Michal Vasko30e2c872016-02-18 10:03:21 +010057 .auth_password = sshauth_password,
58 .auth_interactive = sshauth_interactive,
59 .auth_privkey_passphrase = sshauth_privkey_passphrase
Michal Vasko7b62fed2015-10-26 15:39:46 +010060};
Radek Krejciac6d3472015-10-22 15:47:18 +020061
Michal Vasko3031aae2016-01-27 16:07:18 +010062static struct nc_client_ssh_opts ssh_ch_opts = {
Michal Vasko30e2c872016-02-18 10:03:21 +010063 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Michal Vaskoef112d72016-02-18 13:28:25 +010064 .auth_hostkey_check = sshauth_hostkey_check,
Michal Vasko30e2c872016-02-18 10:03:21 +010065 .auth_password = sshauth_password,
66 .auth_interactive = sshauth_interactive,
67 .auth_privkey_passphrase = sshauth_privkey_passphrase
Michal Vasko3031aae2016-01-27 16:07:18 +010068};
69
Michal Vaskoa43b8e32017-05-12 11:46:20 +020070static FILE *
71open_tty_noecho(const char *path, struct termios *oldterm)
72{
73 struct termios newterm;
74 FILE *ret;
75
76 if (!(ret = fopen(path, "r"))) {
77 ERR("Unable to open the current terminal (%s).", strerror(errno));
78 return NULL;
79 }
80
81 if (tcgetattr(fileno(ret), oldterm)) {
82 ERR("Unable to get terminal settings (%s).", strerror(errno));
83 fclose(ret);
84 return NULL;
85 }
86
87 newterm = *oldterm;
88 newterm.c_lflag &= ~ECHO;
89 newterm.c_lflag &= ~ICANON;
90 tcflush(fileno(ret), TCIFLUSH);
91 if (tcsetattr(fileno(ret), TCSANOW, &newterm)) {
92 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
93 fclose(ret);
94 return NULL;
95 }
96
97 return ret;
98}
99
100static void
101restore_tty_close(FILE *tty, struct termios *oldterm)
102{
103 if (tcsetattr(fileno(tty), TCSANOW, oldterm) != 0) {
104 ERR("Unable to restore terminal settings (%s).", strerror(errno));
105 }
106 fclose(tty);
107}
108
Michal Vaskoe22c6732016-01-29 11:03:02 +0100109static void
110_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +0100111{
112 int i;
113
Michal Vaskoe22c6732016-01-29 11:03:02 +0100114 for (i = 0; i < opts->key_count; ++i) {
115 free(opts->keys[i].pubkey_path);
116 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +0100117 }
Michal Vaskoe22c6732016-01-29 11:03:02 +0100118 free(opts->keys);
119 free(opts->username);
120}
Michal Vasko990089e2015-10-27 15:05:25 +0100121
Michal Vaskob7558c52016-02-26 15:04:19 +0100122void
Michal Vaskoe22c6732016-01-29 11:03:02 +0100123nc_client_ssh_destroy_opts(void)
124{
125 _nc_client_ssh_destroy_opts(&ssh_opts);
126 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +0100127}
128
Michal Vaskoef112d72016-02-18 13:28:25 +0100129#ifdef ENABLE_DNSSEC
130
131/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
132/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
133static int
Michal Vasko650011a2016-02-25 14:49:29 +0100134sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg) {
Michal Vaskoef112d72016-02-18 13:28:25 +0100135 ns_msg handle;
136 ns_rr rr;
137 val_status_t val_status;
138 const unsigned char* rdata;
139 unsigned char buf[4096];
140 int buf_len = 4096;
141 int ret = 0, i, j, len;
142
143 /* class 1 - internet, type 44 - SSHFP */
144 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
145
146 if ((len < 0) || !val_istrusted(val_status)) {
147 ret = 2;
148 goto finish;
149 }
150
151 if (ns_initparse(buf, len, &handle) < 0) {
152 ERR("Failed to initialize DNSSEC response parser.");
153 ret = 2;
154 goto finish;
155 }
156
157 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
158 ERR("DNSSEC query returned %d.", i);
159 ret = 2;
160 goto finish;
161 }
162
163 if (!libsres_msg_getflag(handle, ns_f_ad)) {
164 /* response not secured by DNSSEC */
165 ret = 1;
166 }
167
168 /* query section */
169 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
Michal Vasko650011a2016-02-25 14:49:29 +0100170 ERR("DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100171 ret = 2;
172 goto finish;
173 }
174
175 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
Michal Vasko650011a2016-02-25 14:49:29 +0100176 ERR("DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100177 ret = 2;
178 goto finish;
179 }
180
181 /* answer section */
182 i = 0;
183 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
184 if (ns_rr_type(rr) != 44) {
185 ++i;
186 continue;
187 }
188
189 rdata = ns_rr_rdata(rr);
190 if (rdata[0] != type) {
191 ++i;
192 continue;
193 }
194 if (rdata[1] != alg) {
195 ++i;
196 continue;
197 }
198
199 /* we found the correct SSHFP entry */
200 rdata += 2;
201 for (j = 0; j < 20; ++j) {
202 if (rdata[j] != (unsigned char)sha1hash[j]) {
203 ret = 2;
204 goto finish;
205 }
206 }
207
208 /* server fingerprint is supported by a DNS entry,
209 * we have already determined if DNSSEC was used or not
210 */
211 goto finish;
212 }
213
214 /* no match */
215 ret = 2;
216
217finish:
218 val_free_validator_state();
219 return ret;
220}
221
222#endif /* ENABLE_DNSSEC */
223
224static int
Radek Krejci90a84a22017-05-25 13:53:00 +0200225sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(priv))
Michal Vaskoef112d72016-02-18 13:28:25 +0100226{
227 char *hexa;
228 int c, state, ret;
229 ssh_key srv_pubkey;
230 unsigned char *hash_sha1 = NULL;
231 size_t hlen;
232 enum ssh_keytypes_e srv_pubkey_type;
233 char answer[5];
234
235 state = ssh_is_server_known(session);
236
Michal Vaskocc0aa7d2016-05-31 12:48:42 +0200237 ret = ssh_get_publickey(session, &srv_pubkey);
Michal Vaskoef112d72016-02-18 13:28:25 +0100238 if (ret < 0) {
239 ERR("Unable to get server public key.");
240 return -1;
241 }
242
243 srv_pubkey_type = ssh_key_type(srv_pubkey);
244 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
245 ssh_key_free(srv_pubkey);
246 if (ret < 0) {
247 ERR("Failed to calculate SHA1 hash of the server public key.");
248 return -1;
249 }
250
251 hexa = ssh_get_hexa(hash_sha1, hlen);
252
253 switch (state) {
254 case SSH_SERVER_KNOWN_OK:
255 break; /* ok */
256
257 case SSH_SERVER_KNOWN_CHANGED:
258 ERR("Remote host key changed, the connection will be terminated!");
259 goto fail;
260
261 case SSH_SERVER_FOUND_OTHER:
262 WRN("Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
263 goto hostkey_not_known;
264
265 case SSH_SERVER_FILE_NOT_FOUND:
266 WRN("Could not find the known hosts file.");
267 goto hostkey_not_known;
268
269 case SSH_SERVER_NOT_KNOWN:
270hostkey_not_known:
271#ifdef ENABLE_DNSSEC
272 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
273 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
Michal Vasko650011a2016-02-25 14:49:29 +0100274 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100275 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100276 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100277 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100278 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100279 }
280
281 /* DNSSEC SSHFP check successful, that's enough */
282 if (!ret) {
283 VRB("DNSSEC SSHFP check successful.");
284 ssh_write_knownhost(session);
285 ssh_clean_pubkey_hash(&hash_sha1);
286 ssh_string_free_char(hexa);
287 return 0;
288 }
289 }
290#endif
291
292 /* try to get result from user */
293 fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname);
294 fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa);
295
296#ifdef ENABLE_DNSSEC
297 if (ret == 2) {
298 fprintf(stdout, "No matching host key fingerprint found using DNS.\n");
299 } else if (ret == 1) {
300 fprintf(stdout, "Matching host key fingerprint found using DNS.\n");
301 }
302#endif
303
304 fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? ");
305
306 do {
307 if (fscanf(stdin, "%4s", answer) == EOF) {
308 ERR("fscanf() failed (%s).", strerror(errno));
309 goto fail;
310 }
311 while (((c = getchar()) != EOF) && (c != '\n'));
312
313 fflush(stdin);
314 if (!strcmp("yes", answer)) {
315 /* store the key into the host file */
316 ret = ssh_write_knownhost(session);
317 if (ret != SSH_OK) {
318 WRN("Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
319 }
320 } else if (!strcmp("no", answer)) {
321 goto fail;
322 } else {
323 fprintf(stdout, "Please type 'yes' or 'no': ");
324 }
325 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
326
327 break;
328
329 case SSH_SERVER_ERROR:
330 ssh_clean_pubkey_hash(&hash_sha1);
331 fprintf(stderr,"%s",ssh_get_error(session));
332 return -1;
333 }
334
335 ssh_clean_pubkey_hash(&hash_sha1);
336 ssh_string_free_char(hexa);
337 return 0;
338
339fail:
340 ssh_clean_pubkey_hash(&hash_sha1);
341 ssh_string_free_char(hexa);
342 return -1;
343}
344
Michal Vasko7b62fed2015-10-26 15:39:46 +0100345static char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200346sshauth_password(const char *username, const char *hostname, void *UNUSED(priv))
Radek Krejciac6d3472015-10-22 15:47:18 +0200347{
Michal Vasko4eb3c312016-03-01 14:09:37 +0100348 char *buf;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200349 int buflen = 1024, len, ret;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100350 char c = 0;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200351 struct termios oldterm;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100352 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +0200353
Michal Vasko11d142a2016-01-19 15:58:24 +0100354 buf = malloc(buflen * sizeof *buf);
355 if (!buf) {
356 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100357 return NULL;
358 }
359
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200360 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
361 ERR("ttyname_r failed (%s).", strerror(ret));
362 free(buf);
363 return NULL;
364 }
365
366 if (!(tty = open_tty_noecho(buf, &oldterm))) {
367 free(buf);
368 return NULL;
369 }
370
371 fprintf(stdout, "%s@%s password: ", username, hostname);
372 fflush(stdout);
373
374 len = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100375 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
376 if (len >= buflen - 1) {
377 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100378 buf = nc_realloc(buf, buflen * sizeof *buf);
379 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100380 ERRMEM;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200381 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100382 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100383 }
384 }
385 buf[len++] = c;
386 }
387 buf[len++] = 0; /* terminating null byte */
388
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200389 fprintf(stdout, "\n");
390 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100391 return buf;
392}
393
394static char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200395sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100396{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200397 unsigned int buflen = 64, cur_len;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100398 char c = 0;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200399 int ret;
400 struct termios oldterm;
401 char *buf;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100402 FILE *tty;
403
404 buf = malloc(buflen * sizeof *buf);
405 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100406 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100407 return NULL;
408 }
409
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200410 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
411 ERR("ttyname_r failed (%s).", strerror(ret));
412 free(buf);
413 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100414 }
415
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200416 if (!echo) {
417 if (!(tty = open_tty_noecho(buf, &oldterm))) {
418 free(buf);
419 return NULL;
420 }
421 } else {
422 tty = stdin;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100423 }
424
Michal Vasko7b62fed2015-10-26 15:39:46 +0100425
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200426 if (auth_name && (!fwrite(auth_name, sizeof *auth_name, strlen(auth_name), stdout)
427 || !fwrite("\n", sizeof(char), 1, stdout))) {
428 ERR("Writing the auth method name into stdout failed.");
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100429 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100430 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200431 if (instruction && (!fwrite(instruction, sizeof *auth_name, strlen(instruction), stdout)
432 || !fwrite("\n", sizeof(char), 1, stdout))) {
433 ERR("Writing the instruction into stdout failed.");
434 goto fail;
435 }
436 if (!fwrite(prompt, sizeof *prompt, strlen(prompt), stdout)) {
437 ERR("Writing the authentication prompt into stdout failed.");
438 goto fail;
439 }
440 fflush(stdout);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100441
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200442 cur_len = 0;
443 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
444 if (cur_len >= buflen - 1) {
445 buflen *= 2;
446 buf = nc_realloc(buf, buflen * sizeof *buf);
447 if (!buf) {
448 ERRMEM;
449 goto fail;
450 }
451 }
452 buf[cur_len++] = c;
453 }
454 /* terminating null byte */
455 buf[cur_len] = '\0';
456
457 fprintf(stdout, "\n");
458 if (!echo) {
459 restore_tty_close(tty, &oldterm);
460 }
461 return buf;
462
463fail:
464 if (!echo) {
465 restore_tty_close(tty, &oldterm);
466 }
467 free(buf);
468 return NULL;
469}
470
471static char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200472sshauth_privkey_passphrase(const char* privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200473{
474 char c, *buf;
475 int buflen = 1024, len, ret;
476 struct termios oldterm;
477 FILE *tty;
478
479 buf = malloc(buflen * sizeof *buf);
480 if (!buf) {
481 ERRMEM;
482 return NULL;
483 }
484
485 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
486 ERR("ttyname_r failed (%s).", strerror(ret));
487 free(buf);
488 return NULL;
489 }
490
491 if (!(tty = open_tty_noecho(buf, &oldterm))) {
492 free(buf);
493 return NULL;
494 }
495
496 fprintf(stdout, "Enter passphrase for the key '%s':", privkey_path);
497 fflush(stdout);
498
499 len = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100500 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
501 if (len >= buflen - 1) {
502 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100503 buf = nc_realloc(buf, buflen * sizeof *buf);
504 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100505 ERRMEM;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100506 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100507 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100508 }
509 buf[len++] = (char)c;
510 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200511 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100512
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200513 fprintf(stdout, "\n");
514 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100515 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100516
517fail:
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200518 restore_tty_close(tty, &oldterm);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100519 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100520 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100521}
522
Michal Vaskoef112d72016-02-18 13:28:25 +0100523static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200524_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
525 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100526{
Michal Vaskoef112d72016-02-18 13:28:25 +0100527 if (auth_hostkey_check) {
528 opts->auth_hostkey_check = auth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200529 opts->auth_hostkey_check_priv = priv;
Michal Vaskoef112d72016-02-18 13:28:25 +0100530 } else {
531 opts->auth_hostkey_check = sshauth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200532 opts->auth_hostkey_check_priv = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100533 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100534}
535
Radek Krejci90a84a22017-05-25 13:53:00 +0200536static void
537_nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
538 void **priv, struct nc_client_ssh_opts *opts)
Michal Vaskoef112d72016-02-18 13:28:25 +0100539{
Radek Krejci90a84a22017-05-25 13:53:00 +0200540 if (auth_hostkey_check) {
541 (*auth_hostkey_check) = opts->auth_hostkey_check == sshauth_hostkey_check ? NULL : opts->auth_hostkey_check;
542 }
543 if (priv) {
544 (*priv) = opts->auth_hostkey_check_priv;
545 }
546}
547
548
549API void
550nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
551 void *priv)
552{
553 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100554}
555
556API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200557nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
558 void *priv)
Michal Vaskoef112d72016-02-18 13:28:25 +0100559{
Radek Krejci90a84a22017-05-25 13:53:00 +0200560 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100561}
562
Radek Krejci90a84a22017-05-25 13:53:00 +0200563API void
564nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
565 void **priv)
566{
567 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
568}
569
570API void
571nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
572 void **priv)
573{
574 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
575}
Michal Vaskoef112d72016-02-18 13:28:25 +0100576
Michal Vasko30e2c872016-02-18 10:03:21 +0100577static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200578_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
579 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100580{
581 if (auth_password) {
582 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200583 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100584 } else {
585 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200586 opts->auth_password_priv = NULL;
587 }
588}
589
590static void
591_nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
592 void **priv, struct nc_client_ssh_opts *opts)
593{
594 if (auth_password) {
595 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
596 }
597 if (priv) {
598 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100599 }
600}
601
602API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200603nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
604 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100605{
Radek Krejci90a84a22017-05-25 13:53:00 +0200606 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100607}
608
609API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200610nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
611 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100612{
Radek Krejci90a84a22017-05-25 13:53:00 +0200613 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
614}
615
616API void
617nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
618 void **priv)
619{
620 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
621}
622
623API void
624nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
625 void **priv)
626{
627 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100628}
629
630static void
631_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200632 const char *prompt, int echo, void *priv),
633 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100634{
635 if (auth_interactive) {
636 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200637 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100638 } else {
639 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200640 opts->auth_interactive_priv = NULL;
641 }
642}
643
644static void
645_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
646 const char *prompt, int echo, void *priv),
647 void **priv, struct nc_client_ssh_opts *opts)
648{
649 if (auth_interactive) {
650 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
651 }
652 if (priv) {
653 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100654 }
655}
656
657API void
658nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200659 const char *prompt, int echo, void *priv),
660 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100661{
Radek Krejci90a84a22017-05-25 13:53:00 +0200662 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100663}
664
665API void
666nc_client_ssh_ch_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_ch_opts);
671}
672
673API void
674nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
675 const char *prompt, int echo, void *priv),
676 void **priv)
677{
678 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
679}
680
681API void
682nc_client_ssh_ch_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_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100687}
688
689static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200690_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
691 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100692{
693 if (auth_privkey_passphrase) {
694 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200695 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100696 } else {
697 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200698 opts->auth_privkey_passphrase_priv = NULL;
699 }
700}
701
702static void
703_nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
704 void **priv, struct nc_client_ssh_opts *opts)
705{
706 if (auth_privkey_passphrase) {
707 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
708 }
709 if (priv) {
710 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100711 }
712}
713
714API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200715nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
716 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100717{
Radek Krejci90a84a22017-05-25 13:53:00 +0200718 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100719}
720
721API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200722nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
723 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100724{
Radek Krejci90a84a22017-05-25 13:53:00 +0200725 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
726}
727
728API void
729nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
730 void **priv)
731{
732 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
733}
734
735API void
736nc_client_ssh_ch_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
737 void **priv)
738{
739 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100740}
741
Michal Vasko3031aae2016-01-27 16:07:18 +0100742static int
743_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 +0100744{
745 int i;
746 FILE *key;
747 char line[128];
748
Michal Vasko45e53ae2016-04-07 11:46:03 +0200749 if (!pub_key) {
750 ERRARG("pub_key");
751 return -1;
752 } else if (!priv_key) {
753 ERRARG("priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100754 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100755 }
756
Michal Vasko3031aae2016-01-27 16:07:18 +0100757 for (i = 0; i < opts->key_count; ++i) {
758 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
759 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100760 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100761 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100762 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100763 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100764 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100765 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100766 continue;
767 }
768
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100769 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100770 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100771 }
772 }
773
Michal Vasko3031aae2016-01-27 16:07:18 +0100774 /* add the keys */
775 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100776 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
777 if (!opts->keys) {
778 ERRMEM;
779 return -1;
780 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100781 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
782 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
783 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100784
Michal Vasko4eb3c312016-03-01 14:09:37 +0100785 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
786 ERRMEM;
787 return -1;
788 }
789
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100790 /* check encryption */
791 if ((key = fopen(priv_key, "r"))) {
792 /* 1st line - key type */
793 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100794 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100795 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100796 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100797 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100798 /* 2nd line - encryption information or key */
799 if (!fgets(line, sizeof line, key)) {
800 fclose(key);
801 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100802 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100803 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100804 fclose(key);
805 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100806 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100807 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100808 }
809
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100810 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100811}
812
813API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100814nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100815{
Michal Vasko3031aae2016-01-27 16:07:18 +0100816 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
817}
818
819API int
820nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
821{
822 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
823}
824
825static int
826_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
827{
828 if (idx >= opts->key_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200829 ERRARG("idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100830 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100831 }
832
Michal Vasko3031aae2016-01-27 16:07:18 +0100833 free(opts->keys[idx].pubkey_path);
834 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100835
Michal Vasko3031aae2016-01-27 16:07:18 +0100836 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100837 if (idx < opts->key_count) {
838 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
839 }
840 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100841 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
842 if (!opts->keys) {
843 ERRMEM;
844 return -1;
845 }
Michal Vaskoc0256492016-02-02 12:19:06 +0100846 } else {
847 free(opts->keys);
848 opts->keys = NULL;
849 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100850
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100851 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100852}
853
854API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100855nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100856{
Michal Vasko3031aae2016-01-27 16:07:18 +0100857 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100858}
859
860API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100861nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100862{
Michal Vasko3031aae2016-01-27 16:07:18 +0100863 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
864}
865
866static int
867_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
868{
869 return opts->key_count;
870}
871
872API int
873nc_client_ssh_get_keypair_count(void)
874{
875 return _nc_client_ssh_get_keypair_count(&ssh_opts);
876}
877
878API int
879nc_client_ssh_ch_get_keypair_count(void)
880{
881 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
882}
883
884static int
885_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
886{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200887 if (idx >= opts->key_count) {
888 ERRARG("idx");
889 return -1;
890 } else if (!pub_key && !priv_key) {
891 ERRARG("pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100892 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100893 }
894
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100895 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100896 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100897 }
898 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100899 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100900 }
901
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100902 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100903}
904
Michal Vasko3031aae2016-01-27 16:07:18 +0100905API int
906nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
907{
908 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
909}
910
911API int
912nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
913{
914 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
915}
916
917static void
918_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 +0100919{
920 if (pref < 0) {
921 pref = -1;
922 }
923
924 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100925 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100926 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100927 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100928 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100929 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100930 }
931}
932
Michal Vasko3031aae2016-01-27 16:07:18 +0100933API void
934nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100935{
Michal Vasko3031aae2016-01-27 16:07:18 +0100936 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
937}
938
939API void
940nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
941{
942 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
943}
944
945static int16_t
946_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
947{
948 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100949
950 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100951 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100952 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100953 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100954 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100955 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100956 }
957
958 return pref;
959}
960
Michal Vasko3031aae2016-01-27 16:07:18 +0100961API int16_t
962nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
963{
964 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
965}
966
967API int16_t
968nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
969{
970 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
971}
972
973static int
974_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
975{
976 if (opts->username) {
977 free(opts->username);
978 }
979 if (username) {
980 opts->username = strdup(username);
981 if (!opts->username) {
982 ERRMEM;
983 return -1;
984 }
985 } else {
986 opts->username = NULL;
987 }
988
989 return 0;
990}
991
992API int
993nc_client_ssh_set_username(const char *username)
994{
995 return _nc_client_ssh_set_username(username, &ssh_opts);
996}
997
998API int
999nc_client_ssh_ch_set_username(const char *username)
1000{
1001 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
1002}
1003
Michal Vaskoe22c6732016-01-29 11:03:02 +01001004static const char *
1005_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
1006{
1007 return opts->username;
1008}
1009
1010API const char *
1011nc_client_ssh_get_username(void)
1012{
1013 return _nc_client_ssh_get_username(&ssh_opts);
1014}
1015
1016API const char *
1017nc_client_ssh_ch_get_username(void)
1018{
1019 return _nc_client_ssh_get_username(&ssh_ch_opts);
1020}
1021
Michal Vasko3031aae2016-01-27 16:07:18 +01001022API int
1023nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1024{
1025 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
1026}
1027
1028API int
1029nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1030{
1031 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1032}
1033
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001034/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001035 * Host, port, username, and a connected socket is expected to be set.
1036 */
1037static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001038connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001039{
Michal Vasko36c7be82017-02-22 13:37:59 +01001040 int j, ret_auth, userauthlist, ret;
Michal Vasko235efdc2015-12-17 12:05:04 +01001041 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001042 int16_t pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001043 const char* prompt;
1044 char *s, *answer, echo;
1045 ssh_key pubkey, privkey;
1046 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001047 struct timespec ts_timeout, ts_cur;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001048
1049 ssh_sess = session->ti.libssh.session;
1050
Michal Vasko36c7be82017-02-22 13:37:59 +01001051 nc_gettimespec(&ts_timeout);
1052 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001053 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1054 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001055 nc_gettimespec(&ts_cur);
1056 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001057 break;
1058 }
1059 }
1060 if (ret == SSH_AGAIN) {
1061 ERR("SSH connect timeout.");
1062 return 0;
1063 } else if (ret != SSH_OK) {
1064 ERR("Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001065 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001066 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001067 }
1068
Radek Krejci90a84a22017-05-25 13:53:00 +02001069 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001070 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001071 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001072 }
1073
Michal Vasko36c7be82017-02-22 13:37:59 +01001074 if (timeout > -1) {
1075 nc_gettimespec(&ts_timeout);
1076 nc_addtimespec(&ts_timeout, timeout);
1077 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001078 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1079 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001080 if (timeout > -1) {
1081 nc_gettimespec(&ts_cur);
1082 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1083 break;
1084 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001085 }
1086 }
1087 if (ret_auth == SSH_AUTH_AGAIN) {
1088 ERR("Request authentication methods timeout.");
1089 return 0;
1090 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001091 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001092 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001093 }
1094
1095 /* check what authentication methods are available */
1096 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001097
1098 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001099 if (opts->auth_pref[0].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001100 VRB("Interactive SSH authentication method was disabled.");
1101 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001102 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001103 if (opts->auth_pref[1].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001104 VRB("Password SSH authentication method was disabled.");
1105 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001106 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001107 if (opts->auth_pref[2].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001108 VRB("Publickey SSH authentication method was disabled.");
1109 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001110 }
1111
Michal Vasko0190bc32016-03-02 15:47:49 +01001112 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001113 auth = 0;
1114 pref = 0;
1115 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1116 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001117 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001118 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001119 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001120 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001121 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001122 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001123 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001124 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001125 }
1126
Michal Vasko235efdc2015-12-17 12:05:04 +01001127 if (!auth) {
Michal Vaskod083db62016-01-19 10:31:29 +01001128 ERR("Unable to authenticate to the remote server (no supported authentication methods left).");
Michal Vasko0190bc32016-03-02 15:47:49 +01001129 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001130 }
1131
1132 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001133 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001134 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001135 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1136
Michal Vaskoef578332016-01-25 13:20:09 +01001137 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001138 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko0190bc32016-03-02 15:47:49 +01001139
Michal Vasko36c7be82017-02-22 13:37:59 +01001140 if (timeout > -1) {
1141 nc_gettimespec(&ts_timeout);
1142 nc_addtimespec(&ts_timeout, timeout);
1143 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001144 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1145 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001146 if (timeout > -1) {
1147 nc_gettimespec(&ts_cur);
1148 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1149 break;
1150 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001151 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001152 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001153 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001154 free(s);
1155 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001156
Michal Vasko7b62fed2015-10-26 15:39:46 +01001157 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001158 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1159
Michal Vaskod083db62016-01-19 10:31:29 +01001160 VRB("Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001161
Michal Vasko36c7be82017-02-22 13:37:59 +01001162 if (timeout > -1) {
1163 nc_gettimespec(&ts_timeout);
1164 nc_addtimespec(&ts_timeout, timeout);
1165 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001166 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO)
1167 || (ret_auth == SSH_AUTH_AGAIN)) {
1168 if (ret_auth == SSH_AUTH_AGAIN) {
1169 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001170 if (timeout > -1) {
1171 nc_gettimespec(&ts_cur);
1172 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1173 break;
1174 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001175 }
1176 continue;
1177 }
1178
Michal Vasko7b62fed2015-10-26 15:39:46 +01001179 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1180 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001181 if (!prompt) {
1182 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001183 break;
1184 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001185
1186 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1187 echo = 0;
1188
Michal Vasko30e2c872016-02-18 10:03:21 +01001189 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
1190 ssh_userauth_kbdint_getinstruction(ssh_sess),
Radek Krejci90a84a22017-05-25 13:53:00 +02001191 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001192 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1193 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001194 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001195 break;
1196 }
1197 free(answer);
1198 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001199 if (ret_auth == SSH_AUTH_ERROR) {
1200 break;
1201 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001202 if (timeout > -1) {
1203 nc_gettimespec(&ts_timeout);
1204 nc_addtimespec(&ts_timeout, timeout);
1205 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001206 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001207 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001208
Michal Vasko206d3b12015-12-04 11:08:42 +01001209 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001210 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1211
Michal Vaskod083db62016-01-19 10:31:29 +01001212 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001213
1214 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001215 if (!opts->key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001216 VRB("No key pair specified.");
1217 break;
1218 }
1219
Michal Vasko30e2c872016-02-18 10:03:21 +01001220 for (j = 0; j < opts->key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +01001221 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko30e2c872016-02-18 10:03:21 +01001222 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1223 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001224
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001225 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1226 if (ret == SSH_EOF) {
1227 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
1228 continue;
1229 } else if (ret == SSH_ERROR) {
1230 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001231 continue;
1232 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001233
Michal Vasko36c7be82017-02-22 13:37:59 +01001234 if (timeout > -1) {
1235 nc_gettimespec(&ts_timeout);
1236 nc_addtimespec(&ts_timeout, timeout);
1237 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001238 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1239 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001240 if (timeout > -1) {
1241 nc_gettimespec(&ts_cur);
1242 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1243 break;
1244 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001245 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001246 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001247 ssh_key_free(pubkey);
1248
1249 if (ret_auth == SSH_AUTH_DENIED) {
1250 continue;
1251 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001252 break;
1253 }
1254
Michal Vasko30e2c872016-02-18 10:03:21 +01001255 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001256 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001257 } else {
1258 s = NULL;
1259 }
1260
Michal Vasko0190bc32016-03-02 15:47:49 +01001261 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001262 if (s) {
1263 memset(s, 0, strlen(s));
1264 free(s);
1265 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001266 if (ret == SSH_EOF) {
1267 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
1268 continue;
1269 } else if (ret == SSH_ERROR) {
1270 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001271 continue;
1272 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001273
Michal Vasko36c7be82017-02-22 13:37:59 +01001274 if (timeout > -1) {
1275 nc_gettimespec(&ts_timeout);
1276 nc_addtimespec(&ts_timeout, timeout);
1277 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001278 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1279 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001280 if (timeout > -1) {
1281 nc_gettimespec(&ts_cur);
1282 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1283 break;
1284 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001285 }
1286 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001287 ssh_key_free(privkey);
1288
Michal Vasko0190bc32016-03-02 15:47:49 +01001289 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001290 break;
1291 }
1292 }
1293 break;
1294 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001295
Michal Vasko0190bc32016-03-02 15:47:49 +01001296 switch (ret_auth) {
1297 case SSH_AUTH_AGAIN:
1298 ERR("Authentication response timeout.");
1299 return 0;
1300 case SSH_AUTH_ERROR:
1301 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
1302 return -1;
1303 case SSH_AUTH_DENIED:
1304 WRN("Authentication denied.");
1305 break;
1306 case SSH_AUTH_PARTIAL:
1307 VRB("Partial authentication success.");
1308 break;
1309 case SSH_AUTH_SUCCESS:
1310 VRB("Authentication successful.");
1311 break;
1312 case SSH_AUTH_INFO:
1313 ERRINT;
1314 return -1;
1315 }
1316 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001317
Michal Vasko0190bc32016-03-02 15:47:49 +01001318 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001319}
1320
1321/* Open new SSH channel and request the 'netconf' subsystem.
1322 * SSH connection is expected to be established.
1323 */
1324static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001325open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001326{
1327 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001328 int ret;
1329 struct timespec ts_timeout, ts_cur;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001330
1331 ssh_sess = session->ti.libssh.session;
1332
1333 if (!ssh_is_connected(ssh_sess)) {
1334 ERR("SSH session not connected.");
1335 return -1;
1336 }
1337
1338 if (session->ti.libssh.channel) {
1339 ERR("SSH channel already created.");
1340 return -1;
1341 }
1342
Michal Vasko7b62fed2015-10-26 15:39:46 +01001343 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001344 if (timeout > -1) {
1345 nc_gettimespec(&ts_timeout);
1346 nc_addtimespec(&ts_timeout, timeout);
1347 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001348 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001349 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1350 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001351 if (timeout > -1) {
1352 nc_gettimespec(&ts_cur);
1353 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1354 break;
1355 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001356 }
1357 }
1358 if (ret == SSH_AGAIN) {
1359 ERR("Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001360 ssh_channel_free(session->ti.libssh.channel);
1361 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001362 return 0;
1363 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001364 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001365 ssh_channel_free(session->ti.libssh.channel);
1366 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001367 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001368 }
1369
1370 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001371 if (timeout > -1) {
1372 nc_gettimespec(&ts_timeout);
1373 nc_addtimespec(&ts_timeout, timeout);
1374 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001375 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1376 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001377 if (timeout > -1) {
1378 nc_gettimespec(&ts_cur);
1379 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1380 break;
1381 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001382 }
1383 }
1384 if (ret == SSH_AGAIN) {
1385 ERR("Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001386 ssh_channel_free(session->ti.libssh.channel);
1387 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001388 return 0;
1389 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001390 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001391 ssh_channel_free(session->ti.libssh.channel);
1392 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001393 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001394 }
1395
Michal Vasko0190bc32016-03-02 15:47:49 +01001396 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001397}
1398
Michal Vasko30e2c872016-02-18 10:03:21 +01001399static struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001400_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 +01001401{
1402 char *host = NULL, *username = NULL;
1403 unsigned short port = 0;
1404 int sock;
1405 struct passwd *pw;
1406 struct nc_session *session = NULL;
1407
1408 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001409 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001410 return NULL;
1411 }
1412
1413 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001414 session = nc_new_session(0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001415 if (!session) {
1416 ERRMEM;
1417 return NULL;
1418 }
1419 session->status = NC_STATUS_STARTING;
1420 session->side = NC_CLIENT;
1421
1422 /* transport lock */
Michal Vasko30e2c872016-02-18 10:03:21 +01001423 pthread_mutex_init(session->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001424 pthread_cond_init(session->ti_cond, NULL);
1425 *session->ti_inuse = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001426
1427 session->ti_type = NC_TI_LIBSSH;
1428 session->ti.libssh.session = ssh_session;
1429
1430 /* was port set? */
1431 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1432
1433 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1434 /*
1435 * There is no file descriptor (detected based on the host, there is no way to check
1436 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1437 */
1438
1439 /* remember host */
1440 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001441 if (!host) {
1442 ERRMEM;
1443 goto fail;
1444 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001445 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1446
1447 /* create and connect socket */
1448 sock = nc_sock_connect(host, port);
1449 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001450 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001451 goto fail;
1452 }
1453 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001454 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001455 }
1456
1457 /* was username set? */
1458 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1459
1460 if (!ssh_is_connected(ssh_session)) {
1461 /*
1462 * We are connected, but not SSH authenticated. (Transport layer)
1463 */
1464
1465 /* remember username */
1466 if (!username) {
1467 if (!opts->username) {
1468 pw = getpwuid(getuid());
1469 if (!pw) {
1470 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1471 goto fail;
1472 }
1473 username = strdup(pw->pw_name);
1474 } else {
1475 username = strdup(opts->username);
1476 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001477 if (!username) {
1478 ERRMEM;
1479 goto fail;
1480 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001481 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1482 }
1483
1484 /* connect and authenticate SSH session */
1485 session->host = host;
1486 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001487 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001488 goto fail;
1489 }
1490 }
1491
1492 /*
1493 * Almost done, open a netconf channel. (Transport layer / application layer)
1494 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001495 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001496 goto fail;
1497 }
1498
1499 /*
1500 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1501 */
1502
1503 /* assign context (dicionary needed for handshake) */
1504 if (!ctx) {
1505 if (client_opts.schema_searchpath) {
1506 ctx = ly_ctx_new(client_opts.schema_searchpath);
1507 } else {
1508 ctx = ly_ctx_new(SCHEMAS_DIR);
1509 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +01001510 /* definitely should not happen, but be ready */
1511 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
1512 /* that's just it */
1513 goto fail;
1514 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001515 } else {
1516 session->flags |= NC_SESSION_SHAREDCTX;
1517 }
1518 session->ctx = ctx;
1519
1520 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001521 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001522 goto fail;
1523 }
1524 session->status = NC_STATUS_RUNNING;
1525
1526 if (nc_ctx_check_and_fill(session) == -1) {
1527 goto fail;
1528 }
1529
1530 /* store information into the dictionary */
1531 if (host) {
1532 session->host = lydict_insert_zc(ctx, host);
1533 }
1534 if (port) {
1535 session->port = port;
1536 }
1537 if (username) {
1538 session->username = lydict_insert_zc(ctx, username);
1539 }
1540
1541 return session;
1542
1543fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001544 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001545 return NULL;
1546}
1547
Radek Krejciac6d3472015-10-22 15:47:18 +02001548API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001549nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001550{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001551 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001552 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001553 uint32_t port_uint;
Michal Vasko3031aae2016-01-27 16:07:18 +01001554 char *username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001555 struct passwd *pw;
1556 struct nc_session *session = NULL;
1557
1558 /* process parameters */
1559 if (!host || strisempty(host)) {
1560 host = "localhost";
1561 }
1562
1563 if (!port) {
1564 port = NC_PORT_SSH;
1565 }
Michal Vasko55fded62016-02-02 12:19:34 +01001566 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001567
Michal Vasko3031aae2016-01-27 16:07:18 +01001568 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001569 pw = getpwuid(getuid());
1570 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001571 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1572 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001573 } else {
1574 username = pw->pw_name;
1575 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001576 } else {
1577 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001578 }
1579
1580 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001581 session = nc_new_session(0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001582 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001583 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001584 return NULL;
1585 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001586 session->status = NC_STATUS_STARTING;
1587 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +02001588
Michal Vasko7b62fed2015-10-26 15:39:46 +01001589 /* transport lock */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001590 pthread_mutex_init(session->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001591 pthread_cond_init(session->ti_cond, NULL);
1592 *session->ti_inuse = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001593
1594 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001595 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001596 session->ti.libssh.session = ssh_new();
1597 if (!session->ti.libssh.session) {
1598 ERR("Unable to initialize SSH session.");
1599 goto fail;
1600 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001601
Michal Vasko7b62fed2015-10-26 15:39:46 +01001602 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001603 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001604 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001605 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001606 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko086311b2016-01-08 09:53:11 +01001607 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS,
1608 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1609 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1610 /* ecdsa is probably not supported... */
1611 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1612 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001613
1614 /* create and assign communication socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001615 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001616 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001617 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001618 goto fail;
1619 }
1620 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001621 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001622
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001623 /* temporarily, for session connection */
1624 session->host = host;
1625 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001626 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1)
1627 || (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001628 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001629 }
1630
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001631 /* assign context (dicionary needed for handshake) */
1632 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001633 if (client_opts.schema_searchpath) {
1634 ctx = ly_ctx_new(client_opts.schema_searchpath);
1635 } else {
1636 ctx = ly_ctx_new(SCHEMAS_DIR);
1637 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +01001638 /* definitely should not happen, but be ready */
1639 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
1640 /* that's just it */
1641 goto fail;
1642 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001643 } else {
1644 session->flags |= NC_SESSION_SHAREDCTX;
1645 }
1646 session->ctx = ctx;
1647
Radek Krejciac6d3472015-10-22 15:47:18 +02001648 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001649 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001650 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001651 }
Michal Vaskoad611702015-12-03 13:41:51 +01001652 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001653
Michal Vaskoef578332016-01-25 13:20:09 +01001654 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001655 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001656 }
1657
1658 /* store information into the dictionary */
1659 session->host = lydict_insert(ctx, host, 0);
1660 session->port = port;
1661 session->username = lydict_insert(ctx, username, 0);
1662
Radek Krejciac6d3472015-10-22 15:47:18 +02001663 return session;
1664
Michal Vasko7b62fed2015-10-26 15:39:46 +01001665fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001666 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001667 return NULL;
1668}
1669
1670API struct nc_session *
1671nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1672{
Michal Vasko0190bc32016-03-02 15:47:49 +01001673 return _nc_connect_libssh(ssh_session, ctx, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001674}
1675
1676API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001677nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001678{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001679 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001680
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001681 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001682 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001683 return NULL;
1684 }
1685
Michal Vasko7b62fed2015-10-26 15:39:46 +01001686 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001687 new_session = nc_new_session(1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001688 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001689 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001690 return NULL;
1691 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001692 new_session->status = NC_STATUS_STARTING;
1693 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001694
1695 /* share some parameters including the session lock */
1696 new_session->ti_type = NC_TI_LIBSSH;
1697 new_session->ti_lock = session->ti_lock;
Michal Vaskoade892d2017-02-22 13:40:35 +01001698 new_session->ti_cond = session->ti_cond;
1699 new_session->ti_inuse = session->ti_inuse;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001700 new_session->ti.libssh.session = session->ti.libssh.session;
1701
1702 /* create the channel safely */
Michal Vaskoade892d2017-02-22 13:40:35 +01001703 if (nc_session_lock(new_session, -1, __func__)) {
1704 goto fail;
1705 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001706
1707 /* open a channel */
Michal Vasko0190bc32016-03-02 15:47:49 +01001708 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001709 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001710 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001711
1712 /* assign context (dicionary needed for handshake) */
1713 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001714 if (client_opts.schema_searchpath) {
1715 ctx = ly_ctx_new(client_opts.schema_searchpath);
1716 } else {
1717 ctx = ly_ctx_new(SCHEMAS_DIR);
1718 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001719 } else {
Michal Vasko56b5bf72016-01-19 11:20:35 +01001720 new_session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001721 }
Michal Vasko56b5bf72016-01-19 11:20:35 +01001722 new_session->ctx = ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001723
Michal Vasko7b62fed2015-10-26 15:39:46 +01001724 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001725 if (nc_handshake(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001726 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001727 }
Michal Vaskoad611702015-12-03 13:41:51 +01001728 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001729
Michal Vaskoade892d2017-02-22 13:40:35 +01001730 nc_session_unlock(new_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001731
Michal Vaskoef578332016-01-25 13:20:09 +01001732 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001733 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001734 }
1735
1736 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001737 new_session->host = lydict_insert(ctx, session->host, 0);
1738 new_session->port = session->port;
1739 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001740
1741 /* append to the session ring list */
1742 if (!session->ti.libssh.next) {
1743 session->ti.libssh.next = new_session;
1744 new_session->ti.libssh.next = session;
1745 } else {
1746 ptr = session->ti.libssh.next;
1747 session->ti.libssh.next = new_session;
1748 new_session->ti.libssh.next = ptr;
1749 }
1750
1751 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001752
1753fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001754 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001755 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001756}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001757
Michal Vasko3031aae2016-01-27 16:07:18 +01001758struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001759nc_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 +01001760{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001761 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001762 unsigned int uint_port;
Michal Vasko3031aae2016-01-27 16:07:18 +01001763 struct passwd *pw;
Michal Vasko30e2c872016-02-18 10:03:21 +01001764 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001765 ssh_session sess;
1766
Michal Vasko80cad7f2015-12-08 14:42:27 +01001767 sess = ssh_new();
1768 if (!sess) {
1769 ERR("Unable to initialize an SSH session.");
1770 close(sock);
1771 return NULL;
1772 }
1773
1774 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001775 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001776 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001777 uint_port = port;
1778 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001779 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001780 if (!ssh_ch_opts.username) {
1781 pw = getpwuid(getuid());
1782 if (!pw) {
1783 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1784 return NULL;
1785 }
1786 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1787 } else {
1788 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001789 }
Michal Vasko086311b2016-01-08 09:53:11 +01001790 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1791 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1792 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1793 /* ecdsa is probably not supported... */
1794 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1795 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001796
Michal Vasko0190bc32016-03-02 15:47:49 +01001797 session = _nc_connect_libssh(sess, ctx, &ssh_ch_opts, timeout);
Michal Vasko4282fae2016-02-18 10:03:42 +01001798 if (session) {
1799 session->flags |= NC_SESSION_CALLHOME;
1800 }
1801
Michal Vasko30e2c872016-02-18 10:03:21 +01001802 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001803}