blob: d00f1df66584d1e2b5be6b72e14f76d8e0017799 [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 Vaskoa43b8e32017-05-12 11:46:20 +0200335 int buflen = 1024, len, ret;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100336 char c = 0;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200337 struct termios oldterm;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100338 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +0200339
Michal Vasko11d142a2016-01-19 15:58:24 +0100340 buf = malloc(buflen * sizeof *buf);
341 if (!buf) {
342 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100343 return NULL;
344 }
345
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200346 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
347 ERR("ttyname_r failed (%s).", strerror(ret));
348 free(buf);
349 return NULL;
350 }
351
352 if (!(tty = open_tty_noecho(buf, &oldterm))) {
353 free(buf);
354 return NULL;
355 }
356
357 fprintf(stdout, "%s@%s password: ", username, hostname);
358 fflush(stdout);
359
360 len = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100361 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
362 if (len >= buflen - 1) {
363 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100364 buf = nc_realloc(buf, buflen * sizeof *buf);
365 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100366 ERRMEM;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200367 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100368 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100369 }
370 }
371 buf[len++] = c;
372 }
373 buf[len++] = 0; /* terminating null byte */
374
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200375 fprintf(stdout, "\n");
376 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100377 return buf;
378}
379
Radek Krejci62aa0642017-05-25 16:33:49 +0200380char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200381sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv))
Michal Vasko7b62fed2015-10-26 15:39:46 +0100382{
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200383 unsigned int buflen = 64, cur_len;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100384 char c = 0;
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200385 int ret;
386 struct termios oldterm;
387 char *buf;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100388 FILE *tty;
389
390 buf = malloc(buflen * sizeof *buf);
391 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100392 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100393 return NULL;
394 }
395
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200396 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
397 ERR("ttyname_r failed (%s).", strerror(ret));
398 free(buf);
399 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100400 }
401
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200402 if (!echo) {
403 if (!(tty = open_tty_noecho(buf, &oldterm))) {
404 free(buf);
405 return NULL;
406 }
407 } else {
408 tty = stdin;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100409 }
410
Michal Vasko7b62fed2015-10-26 15:39:46 +0100411
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200412 if (auth_name && (!fwrite(auth_name, sizeof *auth_name, strlen(auth_name), stdout)
413 || !fwrite("\n", sizeof(char), 1, stdout))) {
414 ERR("Writing the auth method name into stdout failed.");
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100415 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100416 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200417 if (instruction && (!fwrite(instruction, sizeof *auth_name, strlen(instruction), stdout)
418 || !fwrite("\n", sizeof(char), 1, stdout))) {
419 ERR("Writing the instruction into stdout failed.");
420 goto fail;
421 }
422 if (!fwrite(prompt, sizeof *prompt, strlen(prompt), stdout)) {
423 ERR("Writing the authentication prompt into stdout failed.");
424 goto fail;
425 }
426 fflush(stdout);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100427
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200428 cur_len = 0;
429 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
430 if (cur_len >= buflen - 1) {
431 buflen *= 2;
432 buf = nc_realloc(buf, buflen * sizeof *buf);
433 if (!buf) {
434 ERRMEM;
435 goto fail;
436 }
437 }
438 buf[cur_len++] = c;
439 }
440 /* terminating null byte */
441 buf[cur_len] = '\0';
442
443 fprintf(stdout, "\n");
444 if (!echo) {
445 restore_tty_close(tty, &oldterm);
446 }
447 return buf;
448
449fail:
450 if (!echo) {
451 restore_tty_close(tty, &oldterm);
452 }
453 free(buf);
454 return NULL;
455}
456
Radek Krejci62aa0642017-05-25 16:33:49 +0200457char *
Radek Krejci90a84a22017-05-25 13:53:00 +0200458sshauth_privkey_passphrase(const char* privkey_path, void *UNUSED(priv))
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200459{
460 char c, *buf;
461 int buflen = 1024, len, ret;
462 struct termios oldterm;
463 FILE *tty;
464
465 buf = malloc(buflen * sizeof *buf);
466 if (!buf) {
467 ERRMEM;
468 return NULL;
469 }
470
471 if ((ret = ttyname_r(STDIN_FILENO, buf, buflen))) {
472 ERR("ttyname_r failed (%s).", strerror(ret));
473 free(buf);
474 return NULL;
475 }
476
477 if (!(tty = open_tty_noecho(buf, &oldterm))) {
478 free(buf);
479 return NULL;
480 }
481
482 fprintf(stdout, "Enter passphrase for the key '%s':", privkey_path);
483 fflush(stdout);
484
485 len = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100486 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
487 if (len >= buflen - 1) {
488 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100489 buf = nc_realloc(buf, buflen * sizeof *buf);
490 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100491 ERRMEM;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100492 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100493 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100494 }
495 buf[len++] = (char)c;
496 }
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200497 buf[len] = 0; /* terminating null byte */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100498
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200499 fprintf(stdout, "\n");
500 restore_tty_close(tty, &oldterm);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100501 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100502
503fail:
Michal Vaskoa43b8e32017-05-12 11:46:20 +0200504 restore_tty_close(tty, &oldterm);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100505 free(buf);
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100506 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100507}
508
Michal Vaskoef112d72016-02-18 13:28:25 +0100509static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200510_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
511 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100512{
Michal Vaskoef112d72016-02-18 13:28:25 +0100513 if (auth_hostkey_check) {
514 opts->auth_hostkey_check = auth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200515 opts->auth_hostkey_check_priv = priv;
Michal Vaskoef112d72016-02-18 13:28:25 +0100516 } else {
517 opts->auth_hostkey_check = sshauth_hostkey_check;
Radek Krejci90a84a22017-05-25 13:53:00 +0200518 opts->auth_hostkey_check_priv = NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100519 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100520}
521
Radek Krejci90a84a22017-05-25 13:53:00 +0200522static void
523_nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
524 void **priv, struct nc_client_ssh_opts *opts)
Michal Vaskoef112d72016-02-18 13:28:25 +0100525{
Radek Krejci90a84a22017-05-25 13:53:00 +0200526 if (auth_hostkey_check) {
527 (*auth_hostkey_check) = opts->auth_hostkey_check == sshauth_hostkey_check ? NULL : opts->auth_hostkey_check;
528 }
529 if (priv) {
530 (*priv) = opts->auth_hostkey_check_priv;
531 }
532}
533
534
535API void
536nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
537 void *priv)
538{
539 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100540}
541
542API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200543nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
544 void *priv)
Michal Vaskoef112d72016-02-18 13:28:25 +0100545{
Radek Krejci90a84a22017-05-25 13:53:00 +0200546 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
Michal Vaskoef112d72016-02-18 13:28:25 +0100547}
548
Radek Krejci90a84a22017-05-25 13:53:00 +0200549API void
550nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
551 void **priv)
552{
553 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts);
554}
555
556API void
557nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
558 void **priv)
559{
560 _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts);
561}
Michal Vaskoef112d72016-02-18 13:28:25 +0100562
Michal Vasko30e2c872016-02-18 10:03:21 +0100563static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200564_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
565 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100566{
567 if (auth_password) {
568 opts->auth_password = auth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200569 opts->auth_password_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100570 } else {
571 opts->auth_password = sshauth_password;
Radek Krejci90a84a22017-05-25 13:53:00 +0200572 opts->auth_password_priv = NULL;
573 }
574}
575
576static void
577_nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
578 void **priv, struct nc_client_ssh_opts *opts)
579{
580 if (auth_password) {
581 (*auth_password) = opts->auth_password == sshauth_password ? NULL : opts->auth_password;
582 }
583 if (priv) {
584 (*priv) = opts->auth_password_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100585 }
586}
587
588API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200589nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
590 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100591{
Radek Krejci90a84a22017-05-25 13:53:00 +0200592 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100593}
594
595API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200596nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
597 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100598{
Radek Krejci90a84a22017-05-25 13:53:00 +0200599 _nc_client_ssh_set_auth_password_clb(auth_password, priv, &ssh_ch_opts);
600}
601
602API void
603nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
604 void **priv)
605{
606 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_opts);
607}
608
609API void
610nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
611 void **priv)
612{
613 _nc_client_ssh_get_auth_password_clb(auth_password, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100614}
615
616static void
617_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200618 const char *prompt, int echo, void *priv),
619 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100620{
621 if (auth_interactive) {
622 opts->auth_interactive = auth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200623 opts->auth_interactive_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100624 } else {
625 opts->auth_interactive = sshauth_interactive;
Radek Krejci90a84a22017-05-25 13:53:00 +0200626 opts->auth_interactive_priv = NULL;
627 }
628}
629
630static void
631_nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
632 const char *prompt, int echo, void *priv),
633 void **priv, struct nc_client_ssh_opts *opts)
634{
635 if (auth_interactive) {
636 (*auth_interactive) = opts->auth_interactive == sshauth_interactive ? NULL : opts->auth_interactive;
637 }
638 if (priv) {
639 (*priv) = opts->auth_interactive_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100640 }
641}
642
643API void
644nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200645 const char *prompt, int echo, void *priv),
646 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100647{
Radek Krejci90a84a22017-05-25 13:53:00 +0200648 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100649}
650
651API void
652nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
Radek Krejci90a84a22017-05-25 13:53:00 +0200653 const char *prompt, int echo, void *priv),
654 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100655{
Radek Krejci90a84a22017-05-25 13:53:00 +0200656 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
657}
658
659API void
660nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
661 const char *prompt, int echo, void *priv),
662 void **priv)
663{
664 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_opts);
665}
666
667API void
668nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
669 const char *prompt, int echo, void *priv),
670 void **priv)
671{
672 _nc_client_ssh_get_auth_interactive_clb(auth_interactive, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100673}
674
675static void
Radek Krejci90a84a22017-05-25 13:53:00 +0200676_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
677 void *priv, struct nc_client_ssh_opts *opts)
Michal Vasko30e2c872016-02-18 10:03:21 +0100678{
679 if (auth_privkey_passphrase) {
680 opts->auth_privkey_passphrase = auth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200681 opts->auth_privkey_passphrase_priv = priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100682 } else {
683 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
Radek Krejci90a84a22017-05-25 13:53:00 +0200684 opts->auth_privkey_passphrase_priv = NULL;
685 }
686}
687
688static void
689_nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
690 void **priv, struct nc_client_ssh_opts *opts)
691{
692 if (auth_privkey_passphrase) {
693 (*auth_privkey_passphrase) = opts->auth_privkey_passphrase == sshauth_privkey_passphrase ? NULL : opts->auth_privkey_passphrase;
694 }
695 if (priv) {
696 (*priv) = opts->auth_privkey_passphrase_priv;
Michal Vasko30e2c872016-02-18 10:03:21 +0100697 }
698}
699
700API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200701nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
702 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100703{
Radek Krejci90a84a22017-05-25 13:53:00 +0200704 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100705}
706
707API void
Radek Krejci90a84a22017-05-25 13:53:00 +0200708nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
709 void *priv)
Michal Vasko30e2c872016-02-18 10:03:21 +0100710{
Radek Krejci90a84a22017-05-25 13:53:00 +0200711 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
712}
713
714API void
715nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
716 void **priv)
717{
718 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_opts);
719}
720
721API void
722nc_client_ssh_ch_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
723 void **priv)
724{
725 _nc_client_ssh_get_auth_privkey_passphrase_clb(auth_privkey_passphrase, priv, &ssh_ch_opts);
Michal Vasko30e2c872016-02-18 10:03:21 +0100726}
727
Michal Vasko3031aae2016-01-27 16:07:18 +0100728static int
729_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 +0100730{
731 int i;
732 FILE *key;
733 char line[128];
734
Michal Vasko45e53ae2016-04-07 11:46:03 +0200735 if (!pub_key) {
736 ERRARG("pub_key");
737 return -1;
738 } else if (!priv_key) {
739 ERRARG("priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100740 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100741 }
742
Michal Vasko3031aae2016-01-27 16:07:18 +0100743 for (i = 0; i < opts->key_count; ++i) {
744 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
745 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100746 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100747 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100748 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100749 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100750 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100751 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100752 continue;
753 }
754
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100755 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100756 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100757 }
758 }
759
Michal Vasko3031aae2016-01-27 16:07:18 +0100760 /* add the keys */
761 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100762 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
763 if (!opts->keys) {
764 ERRMEM;
765 return -1;
766 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100767 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
768 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
769 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100770
Michal Vasko4eb3c312016-03-01 14:09:37 +0100771 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
772 ERRMEM;
773 return -1;
774 }
775
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100776 /* check encryption */
777 if ((key = fopen(priv_key, "r"))) {
778 /* 1st line - key type */
779 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100780 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100781 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100782 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100783 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100784 /* 2nd line - encryption information or key */
785 if (!fgets(line, sizeof line, key)) {
786 fclose(key);
787 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100788 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100789 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100790 fclose(key);
791 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100792 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100793 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100794 }
795
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100796 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100797}
798
799API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100800nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100801{
Michal Vasko3031aae2016-01-27 16:07:18 +0100802 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
803}
804
805API int
806nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
807{
808 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
809}
810
811static int
812_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
813{
814 if (idx >= opts->key_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200815 ERRARG("idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100816 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100817 }
818
Michal Vasko3031aae2016-01-27 16:07:18 +0100819 free(opts->keys[idx].pubkey_path);
820 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100821
Michal Vasko3031aae2016-01-27 16:07:18 +0100822 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100823 if (idx < opts->key_count) {
824 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
825 }
826 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100827 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
828 if (!opts->keys) {
829 ERRMEM;
830 return -1;
831 }
Michal Vaskoc0256492016-02-02 12:19:06 +0100832 } else {
833 free(opts->keys);
834 opts->keys = NULL;
835 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100836
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100837 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100838}
839
840API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100841nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100842{
Michal Vasko3031aae2016-01-27 16:07:18 +0100843 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100844}
845
846API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100847nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100848{
Michal Vasko3031aae2016-01-27 16:07:18 +0100849 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
850}
851
852static int
853_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
854{
855 return opts->key_count;
856}
857
858API int
859nc_client_ssh_get_keypair_count(void)
860{
861 return _nc_client_ssh_get_keypair_count(&ssh_opts);
862}
863
864API int
865nc_client_ssh_ch_get_keypair_count(void)
866{
867 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
868}
869
870static int
871_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
872{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200873 if (idx >= opts->key_count) {
874 ERRARG("idx");
875 return -1;
876 } else if (!pub_key && !priv_key) {
877 ERRARG("pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100878 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100879 }
880
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100881 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100882 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100883 }
884 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100885 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100886 }
887
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100888 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100889}
890
Michal Vasko3031aae2016-01-27 16:07:18 +0100891API int
892nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
893{
894 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
895}
896
897API int
898nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
899{
900 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
901}
902
903static void
904_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 +0100905{
906 if (pref < 0) {
907 pref = -1;
908 }
909
910 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100911 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100912 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100913 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100914 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100915 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100916 }
917}
918
Michal Vasko3031aae2016-01-27 16:07:18 +0100919API void
920nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100921{
Michal Vasko3031aae2016-01-27 16:07:18 +0100922 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
923}
924
925API void
926nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
927{
928 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
929}
930
931static int16_t
932_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
933{
934 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100935
936 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100937 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100938 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100939 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100940 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100941 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100942 }
943
944 return pref;
945}
946
Michal Vasko3031aae2016-01-27 16:07:18 +0100947API int16_t
948nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
949{
950 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
951}
952
953API int16_t
954nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
955{
956 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
957}
958
959static int
960_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
961{
962 if (opts->username) {
963 free(opts->username);
964 }
965 if (username) {
966 opts->username = strdup(username);
967 if (!opts->username) {
968 ERRMEM;
969 return -1;
970 }
971 } else {
972 opts->username = NULL;
973 }
974
975 return 0;
976}
977
978API int
979nc_client_ssh_set_username(const char *username)
980{
981 return _nc_client_ssh_set_username(username, &ssh_opts);
982}
983
984API int
985nc_client_ssh_ch_set_username(const char *username)
986{
987 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
988}
989
Michal Vaskoe22c6732016-01-29 11:03:02 +0100990static const char *
991_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
992{
993 return opts->username;
994}
995
996API const char *
997nc_client_ssh_get_username(void)
998{
999 return _nc_client_ssh_get_username(&ssh_opts);
1000}
1001
1002API const char *
1003nc_client_ssh_ch_get_username(void)
1004{
1005 return _nc_client_ssh_get_username(&ssh_ch_opts);
1006}
1007
Michal Vasko3031aae2016-01-27 16:07:18 +01001008API int
1009nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
1010{
1011 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
1012}
1013
1014API int
1015nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
1016{
1017 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
1018}
1019
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001020/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +01001021 * Host, port, username, and a connected socket is expected to be set.
1022 */
1023static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001024connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001025{
Michal Vasko36c7be82017-02-22 13:37:59 +01001026 int j, ret_auth, userauthlist, ret;
Michal Vasko235efdc2015-12-17 12:05:04 +01001027 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +01001028 int16_t pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001029 const char* prompt;
1030 char *s, *answer, echo;
1031 ssh_key pubkey, privkey;
1032 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001033 struct timespec ts_timeout, ts_cur;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001034
1035 ssh_sess = session->ti.libssh.session;
1036
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001037 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001038 nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
Michal Vasko0190bc32016-03-02 15:47:49 +01001039 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
1040 usleep(NC_TIMEOUT_STEP);
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001041 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001042 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
Michal Vasko0190bc32016-03-02 15:47:49 +01001043 break;
1044 }
1045 }
1046 if (ret == SSH_AGAIN) {
1047 ERR("SSH connect timeout.");
1048 return 0;
1049 } else if (ret != SSH_OK) {
1050 ERR("Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001051 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001052 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001053 }
1054
Radek Krejci90a84a22017-05-25 13:53:00 +02001055 if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001056 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +01001057 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001058 }
1059
Michal Vasko36c7be82017-02-22 13:37:59 +01001060 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001061 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001062 nc_addtimespec(&ts_timeout, timeout);
1063 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001064 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
1065 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001066 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001067 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001068 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1069 break;
1070 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001071 }
1072 }
1073 if (ret_auth == SSH_AUTH_AGAIN) {
1074 ERR("Request authentication methods timeout.");
1075 return 0;
1076 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001077 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +01001078 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001079 }
1080
1081 /* check what authentication methods are available */
1082 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +01001083
1084 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +01001085 if (opts->auth_pref[0].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001086 VRB("Interactive SSH authentication method was disabled.");
1087 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001088 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001089 if (opts->auth_pref[1].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001090 VRB("Password SSH authentication method was disabled.");
1091 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001092 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001093 if (opts->auth_pref[2].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001094 VRB("Publickey SSH authentication method was disabled.");
1095 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001096 }
1097
Michal Vasko0190bc32016-03-02 15:47:49 +01001098 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001099 auth = 0;
1100 pref = 0;
1101 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1102 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001103 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001104 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001105 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001106 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001107 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001108 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001109 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001110 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001111 }
1112
Michal Vasko235efdc2015-12-17 12:05:04 +01001113 if (!auth) {
Michal Vaskod083db62016-01-19 10:31:29 +01001114 ERR("Unable to authenticate to the remote server (no supported authentication methods left).");
Michal Vasko0190bc32016-03-02 15:47:49 +01001115 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001116 }
1117
1118 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001119 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001120 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001121 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1122
Michal Vaskoef578332016-01-25 13:20:09 +01001123 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Radek Krejci90a84a22017-05-25 13:53:00 +02001124 s = opts->auth_password(session->username, session->host, opts->auth_password_priv);
Michal Vasko0190bc32016-03-02 15:47:49 +01001125
Michal Vasko36c7be82017-02-22 13:37:59 +01001126 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001127 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001128 nc_addtimespec(&ts_timeout, timeout);
1129 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001130 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1131 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001132 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001133 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001134 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1135 break;
1136 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001137 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001138 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001139 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001140 free(s);
1141 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001142
Michal Vasko7b62fed2015-10-26 15:39:46 +01001143 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001144 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1145
Michal Vaskod083db62016-01-19 10:31:29 +01001146 VRB("Keyboard-interactive authentication.");
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_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO)
1153 || (ret_auth == SSH_AUTH_AGAIN)) {
1154 if (ret_auth == SSH_AUTH_AGAIN) {
1155 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001156 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001157 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001158 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1159 break;
1160 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001161 }
1162 continue;
1163 }
1164
Michal Vasko7b62fed2015-10-26 15:39:46 +01001165 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1166 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001167 if (!prompt) {
1168 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001169 break;
1170 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001171
1172 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1173 echo = 0;
1174
Michal Vasko30e2c872016-02-18 10:03:21 +01001175 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
1176 ssh_userauth_kbdint_getinstruction(ssh_sess),
Radek Krejci90a84a22017-05-25 13:53:00 +02001177 prompt, echo, opts->auth_interactive_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001178 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1179 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001180 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001181 break;
1182 }
1183 free(answer);
1184 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001185 if (ret_auth == SSH_AUTH_ERROR) {
1186 break;
1187 }
Michal Vasko36c7be82017-02-22 13:37:59 +01001188 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001189 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001190 nc_addtimespec(&ts_timeout, timeout);
1191 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001192 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001193 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001194
Michal Vasko206d3b12015-12-04 11:08:42 +01001195 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001196 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1197
Michal Vaskod083db62016-01-19 10:31:29 +01001198 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001199
1200 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001201 if (!opts->key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001202 VRB("No key pair specified.");
1203 break;
1204 }
1205
Michal Vasko30e2c872016-02-18 10:03:21 +01001206 for (j = 0; j < opts->key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +01001207 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko30e2c872016-02-18 10:03:21 +01001208 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1209 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001210
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001211 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1212 if (ret == SSH_EOF) {
1213 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
1214 continue;
1215 } else if (ret == SSH_ERROR) {
1216 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001217 continue;
1218 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001219
Michal Vasko36c7be82017-02-22 13:37:59 +01001220 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001221 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001222 nc_addtimespec(&ts_timeout, timeout);
1223 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001224 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1225 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001226 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001227 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001228 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1229 break;
1230 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001231 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001232 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001233 ssh_key_free(pubkey);
1234
1235 if (ret_auth == SSH_AUTH_DENIED) {
1236 continue;
1237 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001238 break;
1239 }
1240
Michal Vasko30e2c872016-02-18 10:03:21 +01001241 if (opts->keys[j].privkey_crypt) {
Radek Krejci90a84a22017-05-25 13:53:00 +02001242 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path, opts->auth_privkey_passphrase_priv);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001243 } else {
1244 s = NULL;
1245 }
1246
Michal Vasko0190bc32016-03-02 15:47:49 +01001247 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001248 if (s) {
1249 memset(s, 0, strlen(s));
1250 free(s);
1251 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001252 if (ret == SSH_EOF) {
1253 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
1254 continue;
1255 } else if (ret == SSH_ERROR) {
1256 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001257 continue;
1258 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001259
Michal Vasko36c7be82017-02-22 13:37:59 +01001260 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001261 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001262 nc_addtimespec(&ts_timeout, timeout);
1263 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001264 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1265 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001266 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001267 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001268 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1269 break;
1270 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001271 }
1272 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001273 ssh_key_free(privkey);
1274
Michal Vasko0190bc32016-03-02 15:47:49 +01001275 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001276 break;
1277 }
1278 }
1279 break;
1280 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001281
Michal Vasko0190bc32016-03-02 15:47:49 +01001282 switch (ret_auth) {
1283 case SSH_AUTH_AGAIN:
1284 ERR("Authentication response timeout.");
1285 return 0;
1286 case SSH_AUTH_ERROR:
1287 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
1288 return -1;
1289 case SSH_AUTH_DENIED:
1290 WRN("Authentication denied.");
1291 break;
1292 case SSH_AUTH_PARTIAL:
1293 VRB("Partial authentication success.");
1294 break;
1295 case SSH_AUTH_SUCCESS:
1296 VRB("Authentication successful.");
1297 break;
1298 case SSH_AUTH_INFO:
1299 ERRINT;
1300 return -1;
1301 }
1302 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001303
Michal Vasko0190bc32016-03-02 15:47:49 +01001304 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001305}
1306
1307/* Open new SSH channel and request the 'netconf' subsystem.
1308 * SSH connection is expected to be established.
1309 */
1310static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001311open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001312{
1313 ssh_session ssh_sess;
Michal Vasko36c7be82017-02-22 13:37:59 +01001314 int ret;
1315 struct timespec ts_timeout, ts_cur;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001316
1317 ssh_sess = session->ti.libssh.session;
1318
1319 if (!ssh_is_connected(ssh_sess)) {
1320 ERR("SSH session not connected.");
1321 return -1;
1322 }
1323
1324 if (session->ti.libssh.channel) {
1325 ERR("SSH channel already created.");
1326 return -1;
1327 }
1328
Michal Vasko7b62fed2015-10-26 15:39:46 +01001329 /* open a channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001330 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001331 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001332 nc_addtimespec(&ts_timeout, timeout);
1333 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001334 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001335 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1336 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001337 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001338 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001339 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1340 break;
1341 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001342 }
1343 }
1344 if (ret == SSH_AGAIN) {
1345 ERR("Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001346 ssh_channel_free(session->ti.libssh.channel);
1347 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001348 return 0;
1349 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001350 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001351 ssh_channel_free(session->ti.libssh.channel);
1352 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001353 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001354 }
1355
1356 /* execute the NETCONF subsystem on the channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001357 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001358 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001359 nc_addtimespec(&ts_timeout, timeout);
1360 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001361 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1362 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001363 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001364 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001365 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1366 break;
1367 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001368 }
1369 }
1370 if (ret == SSH_AGAIN) {
1371 ERR("Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001372 ssh_channel_free(session->ti.libssh.channel);
1373 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001374 return 0;
1375 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001376 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001377 ssh_channel_free(session->ti.libssh.channel);
1378 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001379 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001380 }
1381
Michal Vasko0190bc32016-03-02 15:47:49 +01001382 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001383}
1384
Michal Vasko30e2c872016-02-18 10:03:21 +01001385static struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001386_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 +01001387{
1388 char *host = NULL, *username = NULL;
1389 unsigned short port = 0;
1390 int sock;
1391 struct passwd *pw;
1392 struct nc_session *session = NULL;
1393
1394 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001395 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001396 return NULL;
1397 }
1398
1399 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001400 session = nc_new_session(0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001401 if (!session) {
1402 ERRMEM;
1403 return NULL;
1404 }
1405 session->status = NC_STATUS_STARTING;
1406 session->side = NC_CLIENT;
1407
1408 /* transport lock */
Michal Vasko30e2c872016-02-18 10:03:21 +01001409 pthread_mutex_init(session->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001410 pthread_cond_init(session->ti_cond, NULL);
1411 *session->ti_inuse = 0;
Michal Vasko30e2c872016-02-18 10:03:21 +01001412
1413 session->ti_type = NC_TI_LIBSSH;
1414 session->ti.libssh.session = ssh_session;
1415
1416 /* was port set? */
1417 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1418
1419 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1420 /*
1421 * There is no file descriptor (detected based on the host, there is no way to check
1422 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1423 */
1424
1425 /* remember host */
1426 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001427 if (!host) {
1428 ERRMEM;
1429 goto fail;
1430 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001431 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1432
1433 /* create and connect socket */
1434 sock = nc_sock_connect(host, port);
1435 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001436 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko30e2c872016-02-18 10:03:21 +01001437 goto fail;
1438 }
1439 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001440 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001441 }
1442
1443 /* was username set? */
1444 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1445
1446 if (!ssh_is_connected(ssh_session)) {
1447 /*
1448 * We are connected, but not SSH authenticated. (Transport layer)
1449 */
1450
1451 /* remember username */
1452 if (!username) {
1453 if (!opts->username) {
1454 pw = getpwuid(getuid());
1455 if (!pw) {
1456 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1457 goto fail;
1458 }
1459 username = strdup(pw->pw_name);
1460 } else {
1461 username = strdup(opts->username);
1462 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001463 if (!username) {
1464 ERRMEM;
1465 goto fail;
1466 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001467 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1468 }
1469
1470 /* connect and authenticate SSH session */
1471 session->host = host;
1472 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001473 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001474 goto fail;
1475 }
1476 }
1477
1478 /*
1479 * Almost done, open a netconf channel. (Transport layer / application layer)
1480 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001481 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001482 goto fail;
1483 }
1484
1485 /*
1486 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1487 */
1488
Radek Krejcifd5b6682017-06-13 15:52:53 +02001489 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1490 goto fail;
Michal Vasko30e2c872016-02-18 10:03:21 +01001491 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001492 ctx = session->ctx;
Michal Vasko30e2c872016-02-18 10:03:21 +01001493
1494 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001495 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001496 goto fail;
1497 }
1498 session->status = NC_STATUS_RUNNING;
1499
1500 if (nc_ctx_check_and_fill(session) == -1) {
1501 goto fail;
1502 }
1503
1504 /* store information into the dictionary */
1505 if (host) {
1506 session->host = lydict_insert_zc(ctx, host);
1507 }
1508 if (port) {
1509 session->port = port;
1510 }
1511 if (username) {
1512 session->username = lydict_insert_zc(ctx, username);
1513 }
1514
1515 return session;
1516
1517fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001518 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001519 return NULL;
1520}
1521
Radek Krejciac6d3472015-10-22 15:47:18 +02001522API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001523nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001524{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001525 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001526 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001527 uint32_t port_uint;
Michal Vasko3031aae2016-01-27 16:07:18 +01001528 char *username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001529 struct passwd *pw;
1530 struct nc_session *session = NULL;
1531
1532 /* process parameters */
1533 if (!host || strisempty(host)) {
1534 host = "localhost";
1535 }
1536
1537 if (!port) {
1538 port = NC_PORT_SSH;
1539 }
Michal Vasko55fded62016-02-02 12:19:34 +01001540 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001541
Michal Vasko3031aae2016-01-27 16:07:18 +01001542 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001543 pw = getpwuid(getuid());
1544 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001545 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1546 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001547 } else {
1548 username = pw->pw_name;
1549 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001550 } else {
1551 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001552 }
1553
1554 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001555 session = nc_new_session(0);
Radek Krejciac6d3472015-10-22 15:47:18 +02001556 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001557 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001558 return NULL;
1559 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001560 session->status = NC_STATUS_STARTING;
1561 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +02001562
Michal Vasko7b62fed2015-10-26 15:39:46 +01001563 /* transport lock */
Michal Vasko7b62fed2015-10-26 15:39:46 +01001564 pthread_mutex_init(session->ti_lock, NULL);
Michal Vaskoade892d2017-02-22 13:40:35 +01001565 pthread_cond_init(session->ti_cond, NULL);
1566 *session->ti_inuse = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001567
1568 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001569 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001570 session->ti.libssh.session = ssh_new();
1571 if (!session->ti.libssh.session) {
1572 ERR("Unable to initialize SSH session.");
1573 goto fail;
1574 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001575
Michal Vasko7b62fed2015-10-26 15:39:46 +01001576 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001577 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001578 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001579 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001580 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko086311b2016-01-08 09:53:11 +01001581 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS,
1582 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1583 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1584 /* ecdsa is probably not supported... */
1585 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1586 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001587
1588 /* create and assign communication socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001589 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001590 if (sock == -1) {
Michal Vasko29af44b2016-10-13 10:59:55 +02001591 ERR("Unable to connect to %s:%u (%s).", host, port, strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001592 goto fail;
1593 }
1594 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001595 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001596
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001597 /* temporarily, for session connection */
1598 session->host = host;
1599 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001600 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1)
1601 || (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001602 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001603 }
1604
Radek Krejcifd5b6682017-06-13 15:52:53 +02001605 if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
1606 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001607 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001608 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001609
Radek Krejciac6d3472015-10-22 15:47:18 +02001610 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001611 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001612 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001613 }
Michal Vaskoad611702015-12-03 13:41:51 +01001614 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001615
Michal Vaskoef578332016-01-25 13:20:09 +01001616 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001617 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001618 }
1619
1620 /* store information into the dictionary */
1621 session->host = lydict_insert(ctx, host, 0);
1622 session->port = port;
1623 session->username = lydict_insert(ctx, username, 0);
1624
Radek Krejciac6d3472015-10-22 15:47:18 +02001625 return session;
1626
Michal Vasko7b62fed2015-10-26 15:39:46 +01001627fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001628 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001629 return NULL;
1630}
1631
1632API struct nc_session *
1633nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1634{
Michal Vasko0190bc32016-03-02 15:47:49 +01001635 return _nc_connect_libssh(ssh_session, ctx, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001636}
1637
1638API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001639nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001640{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001641 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001642
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001643 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001644 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001645 return NULL;
1646 }
1647
Michal Vasko7b62fed2015-10-26 15:39:46 +01001648 /* prepare session structure */
Michal Vaskoade892d2017-02-22 13:40:35 +01001649 new_session = nc_new_session(1);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001650 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001651 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001652 return NULL;
1653 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001654 new_session->status = NC_STATUS_STARTING;
1655 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001656
1657 /* share some parameters including the session lock */
1658 new_session->ti_type = NC_TI_LIBSSH;
1659 new_session->ti_lock = session->ti_lock;
Michal Vaskoade892d2017-02-22 13:40:35 +01001660 new_session->ti_cond = session->ti_cond;
1661 new_session->ti_inuse = session->ti_inuse;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001662 new_session->ti.libssh.session = session->ti.libssh.session;
1663
1664 /* create the channel safely */
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001665 if (nc_session_lock(new_session, -1, __func__) != 1) {
Michal Vaskoade892d2017-02-22 13:40:35 +01001666 goto fail;
1667 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001668
1669 /* open a channel */
Michal Vasko0190bc32016-03-02 15:47:49 +01001670 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001671 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001672 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001673
Michal Vaskoedcf1f72017-10-19 11:30:46 +02001674 if (nc_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) {
Radek Krejcifd5b6682017-06-13 15:52:53 +02001675 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001676 }
Radek Krejcifd5b6682017-06-13 15:52:53 +02001677 ctx = session->ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001678
Michal Vasko7b62fed2015-10-26 15:39:46 +01001679 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001680 if (nc_handshake(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001681 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001682 }
Michal Vaskoad611702015-12-03 13:41:51 +01001683 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001684
Michal Vaskoade892d2017-02-22 13:40:35 +01001685 nc_session_unlock(new_session, NC_SESSION_LOCK_TIMEOUT, __func__);
Michal Vasko56b5bf72016-01-19 11:20:35 +01001686
Michal Vaskoef578332016-01-25 13:20:09 +01001687 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001688 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001689 }
1690
1691 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001692 new_session->host = lydict_insert(ctx, session->host, 0);
1693 new_session->port = session->port;
1694 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001695
1696 /* append to the session ring list */
1697 if (!session->ti.libssh.next) {
1698 session->ti.libssh.next = new_session;
1699 new_session->ti.libssh.next = session;
1700 } else {
1701 ptr = session->ti.libssh.next;
1702 session->ti.libssh.next = new_session;
1703 new_session->ti.libssh.next = ptr;
1704 }
1705
1706 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001707
1708fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001709 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001710 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001711}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001712
Michal Vasko3031aae2016-01-27 16:07:18 +01001713struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001714nc_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 +01001715{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001716 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001717 unsigned int uint_port;
Michal Vasko3031aae2016-01-27 16:07:18 +01001718 struct passwd *pw;
Michal Vasko30e2c872016-02-18 10:03:21 +01001719 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001720 ssh_session sess;
1721
Michal Vasko80cad7f2015-12-08 14:42:27 +01001722 sess = ssh_new();
1723 if (!sess) {
1724 ERR("Unable to initialize an SSH session.");
1725 close(sock);
1726 return NULL;
1727 }
1728
1729 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001730 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001731 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko0cfa90c2016-10-13 10:34:46 +02001732 uint_port = port;
1733 ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001734 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001735 if (!ssh_ch_opts.username) {
1736 pw = getpwuid(getuid());
1737 if (!pw) {
1738 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1739 return NULL;
1740 }
1741 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1742 } else {
1743 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001744 }
Michal Vasko086311b2016-01-08 09:53:11 +01001745 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1746 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1747 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1748 /* ecdsa is probably not supported... */
1749 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1750 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001751
Michal Vasko0190bc32016-03-02 15:47:49 +01001752 session = _nc_connect_libssh(sess, ctx, &ssh_ch_opts, timeout);
Michal Vasko4282fae2016-02-18 10:03:42 +01001753 if (session) {
1754 session->flags |= NC_SESSION_CALLHOME;
1755 }
1756
Michal Vasko30e2c872016-02-18 10:03:21 +01001757 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001758}