blob: d75d07b7d2b06be84907645fb824b95839429b7b [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
14 *
15 * 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>
31
Michal Vasko7b62fed2015-10-26 15:39:46 +010032#ifdef ENABLE_DNSSEC
33# include <validator/validator.h>
34# include <validator/resolver.h>
35# include <validator/validator-compat.h>
36#endif
37
Michal Vasko745ff832015-12-08 14:40:29 +010038#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020039#include <libyang/libyang.h>
40
Michal Vaskoe22c6732016-01-29 11:03:02 +010041#include "session_client.h"
42#include "session_client_ch.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020043#include "libnetconf.h"
44
Michal Vaskoef112d72016-02-18 13:28:25 +010045static int sshauth_hostkey_check(const char *hostname, ssh_session session);
Michal Vasko30e2c872016-02-18 10:03:21 +010046static char *sshauth_password(const char *username, const char *hostname);
47static char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo);
48static char *sshauth_privkey_passphrase(const char* privkey_path);
49
Michal Vaskodaf9a092016-02-09 10:42:05 +010050extern struct nc_client_opts client_opts;
51
Michal Vasko3031aae2016-01-27 16:07:18 +010052static struct nc_client_ssh_opts ssh_opts = {
Michal Vasko30e2c872016-02-18 10:03:21 +010053 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
Michal Vaskoef112d72016-02-18 13:28:25 +010054 .auth_hostkey_check = sshauth_hostkey_check,
Michal Vasko30e2c872016-02-18 10:03:21 +010055 .auth_password = sshauth_password,
56 .auth_interactive = sshauth_interactive,
57 .auth_privkey_passphrase = sshauth_privkey_passphrase
Michal Vasko7b62fed2015-10-26 15:39:46 +010058};
Radek Krejciac6d3472015-10-22 15:47:18 +020059
Michal Vasko3031aae2016-01-27 16:07:18 +010060static struct nc_client_ssh_opts ssh_ch_opts = {
Michal Vasko30e2c872016-02-18 10:03:21 +010061 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Michal Vaskoef112d72016-02-18 13:28:25 +010062 .auth_hostkey_check = sshauth_hostkey_check,
Michal Vasko30e2c872016-02-18 10:03:21 +010063 .auth_password = sshauth_password,
64 .auth_interactive = sshauth_interactive,
65 .auth_privkey_passphrase = sshauth_privkey_passphrase
Michal Vasko3031aae2016-01-27 16:07:18 +010066};
67
Michal Vaskoe22c6732016-01-29 11:03:02 +010068static void
69_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +010070{
71 int i;
72
Michal Vaskoe22c6732016-01-29 11:03:02 +010073 for (i = 0; i < opts->key_count; ++i) {
74 free(opts->keys[i].pubkey_path);
75 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +010076 }
Michal Vaskoe22c6732016-01-29 11:03:02 +010077 free(opts->keys);
78 free(opts->username);
79}
Michal Vasko990089e2015-10-27 15:05:25 +010080
Michal Vaskoe22c6732016-01-29 11:03:02 +010081API void
82nc_client_ssh_destroy_opts(void)
83{
84 _nc_client_ssh_destroy_opts(&ssh_opts);
85 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +010086}
87
Michal Vaskoef112d72016-02-18 13:28:25 +010088#ifdef ENABLE_DNSSEC
89
90/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
91/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
92static int
93sshauth_hostkey_hash_dnssec_check(const char *hostname, const char *sha1hash, int type, int alg) {
94 ns_msg handle;
95 ns_rr rr;
96 val_status_t val_status;
97 const unsigned char* rdata;
98 unsigned char buf[4096];
99 int buf_len = 4096;
100 int ret = 0, i, j, len;
101
102 /* class 1 - internet, type 44 - SSHFP */
103 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
104
105 if ((len < 0) || !val_istrusted(val_status)) {
106 ret = 2;
107 goto finish;
108 }
109
110 if (ns_initparse(buf, len, &handle) < 0) {
111 ERR("Failed to initialize DNSSEC response parser.");
112 ret = 2;
113 goto finish;
114 }
115
116 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
117 ERR("DNSSEC query returned %d.", i);
118 ret = 2;
119 goto finish;
120 }
121
122 if (!libsres_msg_getflag(handle, ns_f_ad)) {
123 /* response not secured by DNSSEC */
124 ret = 1;
125 }
126
127 /* query section */
128 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
129 ERROR("DNSSEC query section parser fail.");
130 ret = 2;
131 goto finish;
132 }
133
134 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
135 ERROR("DNSSEC query in the answer does not match the original query.");
136 ret = 2;
137 goto finish;
138 }
139
140 /* answer section */
141 i = 0;
142 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
143 if (ns_rr_type(rr) != 44) {
144 ++i;
145 continue;
146 }
147
148 rdata = ns_rr_rdata(rr);
149 if (rdata[0] != type) {
150 ++i;
151 continue;
152 }
153 if (rdata[1] != alg) {
154 ++i;
155 continue;
156 }
157
158 /* we found the correct SSHFP entry */
159 rdata += 2;
160 for (j = 0; j < 20; ++j) {
161 if (rdata[j] != (unsigned char)sha1hash[j]) {
162 ret = 2;
163 goto finish;
164 }
165 }
166
167 /* server fingerprint is supported by a DNS entry,
168 * we have already determined if DNSSEC was used or not
169 */
170 goto finish;
171 }
172
173 /* no match */
174 ret = 2;
175
176finish:
177 val_free_validator_state();
178 return ret;
179}
180
181#endif /* ENABLE_DNSSEC */
182
183static int
184sshauth_hostkey_check(const char *hostname, ssh_session session)
185{
186 char *hexa;
187 int c, state, ret;
188 ssh_key srv_pubkey;
189 unsigned char *hash_sha1 = NULL;
190 size_t hlen;
191 enum ssh_keytypes_e srv_pubkey_type;
192 char answer[5];
193
194 state = ssh_is_server_known(session);
195
196 ret = ssh_get_publickey(session, &srv_pubkey);
197 if (ret < 0) {
198 ERR("Unable to get server public key.");
199 return -1;
200 }
201
202 srv_pubkey_type = ssh_key_type(srv_pubkey);
203 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
204 ssh_key_free(srv_pubkey);
205 if (ret < 0) {
206 ERR("Failed to calculate SHA1 hash of the server public key.");
207 return -1;
208 }
209
210 hexa = ssh_get_hexa(hash_sha1, hlen);
211
212 switch (state) {
213 case SSH_SERVER_KNOWN_OK:
214 break; /* ok */
215
216 case SSH_SERVER_KNOWN_CHANGED:
217 ERR("Remote host key changed, the connection will be terminated!");
218 goto fail;
219
220 case SSH_SERVER_FOUND_OTHER:
221 WRN("Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
222 goto hostkey_not_known;
223
224 case SSH_SERVER_FILE_NOT_FOUND:
225 WRN("Could not find the known hosts file.");
226 goto hostkey_not_known;
227
228 case SSH_SERVER_NOT_KNOWN:
229hostkey_not_known:
230#ifdef ENABLE_DNSSEC
231 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
232 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
233 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
234 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
235 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
236 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
237 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
238 }
239
240 /* DNSSEC SSHFP check successful, that's enough */
241 if (!ret) {
242 VRB("DNSSEC SSHFP check successful.");
243 ssh_write_knownhost(session);
244 ssh_clean_pubkey_hash(&hash_sha1);
245 ssh_string_free_char(hexa);
246 return 0;
247 }
248 }
249#endif
250
251 /* try to get result from user */
252 fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname);
253 fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa);
254
255#ifdef ENABLE_DNSSEC
256 if (ret == 2) {
257 fprintf(stdout, "No matching host key fingerprint found using DNS.\n");
258 } else if (ret == 1) {
259 fprintf(stdout, "Matching host key fingerprint found using DNS.\n");
260 }
261#endif
262
263 fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? ");
264
265 do {
266 if (fscanf(stdin, "%4s", answer) == EOF) {
267 ERR("fscanf() failed (%s).", strerror(errno));
268 goto fail;
269 }
270 while (((c = getchar()) != EOF) && (c != '\n'));
271
272 fflush(stdin);
273 if (!strcmp("yes", answer)) {
274 /* store the key into the host file */
275 ret = ssh_write_knownhost(session);
276 if (ret != SSH_OK) {
277 WRN("Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
278 }
279 } else if (!strcmp("no", answer)) {
280 goto fail;
281 } else {
282 fprintf(stdout, "Please type 'yes' or 'no': ");
283 }
284 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
285
286 break;
287
288 case SSH_SERVER_ERROR:
289 ssh_clean_pubkey_hash(&hash_sha1);
290 fprintf(stderr,"%s",ssh_get_error(session));
291 return -1;
292 }
293
294 ssh_clean_pubkey_hash(&hash_sha1);
295 ssh_string_free_char(hexa);
296 return 0;
297
298fail:
299 ssh_clean_pubkey_hash(&hash_sha1);
300 ssh_string_free_char(hexa);
301 return -1;
302}
303
Michal Vasko7b62fed2015-10-26 15:39:46 +0100304static char *
305sshauth_password(const char *username, const char *hostname)
Radek Krejciac6d3472015-10-22 15:47:18 +0200306{
Michal Vasko7b62fed2015-10-26 15:39:46 +0100307 char *buf, *newbuf;
308 int buflen = 1024, len = 0;
309 char c = 0;
310 struct termios newterm, oldterm;
311 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +0200312
Michal Vasko7b62fed2015-10-26 15:39:46 +0100313 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100314 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100315 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200316 }
317
Michal Vasko7b62fed2015-10-26 15:39:46 +0100318 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100319 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100320 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100321 return NULL;
322 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200323
Michal Vasko7b62fed2015-10-26 15:39:46 +0100324 fprintf(tty, "%s@%s password: ", username, hostname);
325 fflush(tty);
Radek Krejciac6d3472015-10-22 15:47:18 +0200326
Michal Vasko7b62fed2015-10-26 15:39:46 +0100327 /* system("stty -echo"); */
328 newterm = oldterm;
329 newterm.c_lflag &= ~ECHO;
330 newterm.c_lflag &= ~ICANON;
331 tcflush(fileno(tty), TCIFLUSH);
332 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100333 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100334 fclose(tty);
335 return NULL;
336 }
337
338 buf = malloc(buflen * sizeof *buf);
339 if (!buf) {
340 ERRMEM;
341 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100342 return NULL;
343 }
344
345 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
346 if (len >= buflen - 1) {
347 buflen *= 2;
348 newbuf = realloc(buf, buflen * sizeof *newbuf);
349 if (!newbuf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100350 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100351
352 /* remove content of the buffer */
353 memset(buf, 0, len);
354 free(buf);
355
356 /* restore terminal settings */
357 if (tcsetattr(fileno(tty), TCSANOW, &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100358 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100359 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100360 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100361 return NULL;
362 } else {
363 buf = newbuf;
364 }
365 }
366 buf[len++] = c;
367 }
368 buf[len++] = 0; /* terminating null byte */
369
370 /* system ("stty echo"); */
371 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100372 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100373 /*
374 * terminal probably still hides input characters, but we have password
375 * and anyway we are unable to set terminal to the previous state, so
376 * just continue
377 */
378 }
379 fprintf(tty, "\n");
380
381 fclose(tty);
382 return buf;
383}
384
385static char *
386sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo)
387{
388 unsigned int buflen = 8, response_len;
389 char c = 0;
390 struct termios newterm, oldterm;
391 char *newtext, *response;
392 FILE *tty;
393
394 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100395 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100396 return NULL;
397 }
398
399 if (tcgetattr(fileno(tty), &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100400 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100401 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100402 return NULL;
403 }
404
405 if (auth_name && (!fwrite(auth_name, sizeof(char), strlen(auth_name), tty)
406 || !fwrite("\n", sizeof(char), 1, tty))) {
407 ERR("Writing the auth method name into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100408 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100409 return NULL;
410 }
411
412 if (instruction && (!fwrite(instruction, sizeof(char), strlen(instruction), tty)
413 || !fwrite("\n", sizeof(char), 1, tty))) {
414 ERR("Writing the instruction into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100415 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100416 return NULL;
417 }
418
419 if (!fwrite(prompt, sizeof(char), strlen(prompt), tty)) {
420 ERR("Writing the authentication prompt into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100421 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100422 return NULL;
423 }
424 fflush(tty);
425 if (!echo) {
426 /* system("stty -echo"); */
427 newterm = oldterm;
428 newterm.c_lflag &= ~ECHO;
429 tcflush(fileno(tty), TCIFLUSH);
430 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100431 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100432 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100433 return NULL;
434 }
435 }
436
437 response = malloc(buflen * sizeof *response);
438 response_len = 0;
439 if (!response) {
440 ERRMEM;
441 /* restore terminal settings */
442 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100443 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100444 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100445 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100446 return NULL;
447 }
448
449 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
450 if (response_len >= buflen - 1) {
451 buflen *= 2;
452 newtext = realloc(response, buflen * sizeof *newtext);
453 if (!newtext) {
Michal Vaskod083db62016-01-19 10:31:29 +0100454 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100455 free(response);
456
457 /* restore terminal settings */
458 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100459 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100460 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100461 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100462 return NULL;
463 } else {
464 response = newtext;
465 }
466 }
467 response[response_len++] = c;
468 }
469 /* terminating null byte */
470 response[response_len++] = '\0';
471
472 /* system ("stty echo"); */
473 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100474 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100475 /*
476 * terminal probably still hides input characters, but we have password
477 * and anyway we are unable to set terminal to the previous state, so
478 * just continue
479 */
480 }
481
482 fprintf(tty, "\n");
483 fclose(tty);
484 return response;
485}
486
487static char *
Michal Vasko30e2c872016-02-18 10:03:21 +0100488sshauth_privkey_passphrase(const char* privkey_path)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100489{
490 char c, *buf, *newbuf;
491 int buflen = 1024, len = 0;
492 struct termios newterm, oldterm;
493 FILE *tty;
494
495 buf = malloc(buflen * sizeof *buf);
496 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100497 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100498 return NULL;
499 }
500
501 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100502 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100503 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100504 }
505
506 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100507 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100508 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100509 }
510
511 fprintf(tty, "Enter passphrase for the key '%s':", privkey_path);
512 fflush(tty);
513
514 /* system("stty -echo"); */
515 newterm = oldterm;
516 newterm.c_lflag &= ~ECHO;
517 newterm.c_lflag &= ~ICANON;
518 tcflush(fileno(tty), TCIFLUSH);
519 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100520 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100521 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100522 }
523
524 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
525 if (len >= buflen - 1) {
526 buflen *= 2;
527 newbuf = realloc(buf, buflen * sizeof *newbuf);
528 if (!newbuf) {
529 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100530 /* restore terminal settings */
531 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100532 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100533 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100534 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100535 }
536 buf = newbuf;
537 }
538 buf[len++] = (char)c;
539 }
540 buf[len++] = 0; /* terminating null byte */
541
542 /* system ("stty echo"); */
543 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100544 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100545 /*
546 * terminal probably still hides input characters, but we have password
547 * and anyway we are unable to set terminal to the previous state, so
548 * just continue
549 */
550 }
551 fprintf(tty, "\n");
552
553 fclose(tty);
554 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100555
556fail:
557 free(buf);
Michal Vaskode581a82016-01-22 13:15:35 +0100558 if (tty) {
559 fclose(tty);
560 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100561 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100562}
563
Michal Vaskoef112d72016-02-18 13:28:25 +0100564static void
565_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session),
566 struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100567{
Michal Vaskoef112d72016-02-18 13:28:25 +0100568 if (auth_hostkey_check) {
569 opts->auth_hostkey_check = auth_hostkey_check;
570 } else {
571 opts->auth_hostkey_check = sshauth_hostkey_check;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100572 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100573}
574
Michal Vaskoef112d72016-02-18 13:28:25 +0100575API void
576nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session))
577{
578 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, &ssh_opts);
579}
580
581API void
582nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session))
583{
584 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, &ssh_ch_opts);
585}
586
587
Michal Vasko30e2c872016-02-18 10:03:21 +0100588static void
589_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname),
590 struct nc_client_ssh_opts *opts)
591{
592 if (auth_password) {
593 opts->auth_password = auth_password;
594 } else {
595 opts->auth_password = sshauth_password;
596 }
597}
598
599API void
600nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname))
601{
602 _nc_client_ssh_set_auth_password_clb(auth_password, &ssh_opts);
603}
604
605API void
606nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname))
607{
608 _nc_client_ssh_set_auth_password_clb(auth_password, &ssh_ch_opts);
609}
610
611static void
612_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
613 const char *prompt, int echo),
614 struct nc_client_ssh_opts *opts)
615{
616 if (auth_interactive) {
617 opts->auth_interactive = auth_interactive;
618 } else {
619 opts->auth_interactive = sshauth_interactive;
620 }
621}
622
623API void
624nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
625 const char *prompt, int echo))
626{
627 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, &ssh_opts);
628}
629
630API void
631nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
632 const char *prompt, int echo))
633{
634 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, &ssh_ch_opts);
635}
636
637static void
638_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path),
639 struct nc_client_ssh_opts *opts)
640{
641 if (auth_privkey_passphrase) {
642 opts->auth_privkey_passphrase = auth_privkey_passphrase;
643 } else {
644 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
645 }
646}
647
648API void
649nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path))
650{
651 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, &ssh_opts);
652}
653
654API void
655nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path))
656{
657 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, &ssh_ch_opts);
658}
659
Michal Vasko3031aae2016-01-27 16:07:18 +0100660static int
661_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 +0100662{
663 int i;
664 FILE *key;
665 char line[128];
666
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100667 if (!pub_key || !priv_key) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100668 ERRARG;
669 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100670 }
671
Michal Vasko3031aae2016-01-27 16:07:18 +0100672 for (i = 0; i < opts->key_count; ++i) {
673 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
674 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100675 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100676 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100677 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100678 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100679 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100680 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100681 continue;
682 }
683
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100684 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100685 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100686 }
687 }
688
Michal Vasko3031aae2016-01-27 16:07:18 +0100689 /* add the keys */
690 ++opts->key_count;
691 opts->keys = realloc(opts->keys, opts->key_count * sizeof *opts->keys);
692 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
693 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
694 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100695
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100696 /* check encryption */
697 if ((key = fopen(priv_key, "r"))) {
698 /* 1st line - key type */
699 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100700 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100701 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100702 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100703 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100704 /* 2nd line - encryption information or key */
705 if (!fgets(line, sizeof line, key)) {
706 fclose(key);
707 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100708 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100709 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100710 fclose(key);
711 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100712 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100713 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100714 }
715
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100716 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100717}
718
719API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100720nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100721{
Michal Vasko3031aae2016-01-27 16:07:18 +0100722 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
723}
724
725API int
726nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
727{
728 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
729}
730
731static int
732_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
733{
734 if (idx >= opts->key_count) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100735 ERRARG;
736 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100737 }
738
Michal Vasko3031aae2016-01-27 16:07:18 +0100739 free(opts->keys[idx].pubkey_path);
740 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100741
Michal Vasko3031aae2016-01-27 16:07:18 +0100742 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100743 if (idx < opts->key_count) {
744 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
745 }
746 if (opts->key_count) {
747 opts->keys = realloc(opts->keys, opts->key_count * sizeof *opts->keys);
748 } else {
749 free(opts->keys);
750 opts->keys = NULL;
751 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100752
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100753 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100754}
755
756API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100757nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100758{
Michal Vasko3031aae2016-01-27 16:07:18 +0100759 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100760}
761
762API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100763nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100764{
Michal Vasko3031aae2016-01-27 16:07:18 +0100765 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
766}
767
768static int
769_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
770{
771 return opts->key_count;
772}
773
774API int
775nc_client_ssh_get_keypair_count(void)
776{
777 return _nc_client_ssh_get_keypair_count(&ssh_opts);
778}
779
780API int
781nc_client_ssh_ch_get_keypair_count(void)
782{
783 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
784}
785
786static int
787_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
788{
789 if ((idx >= opts->key_count) || (!pub_key && !priv_key)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100790 ERRARG;
791 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100792 }
793
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100794 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100795 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100796 }
797 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100798 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100799 }
800
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100801 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100802}
803
Michal Vasko3031aae2016-01-27 16:07:18 +0100804API int
805nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
806{
807 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
808}
809
810API int
811nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
812{
813 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
814}
815
816static void
817_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 +0100818{
819 if (pref < 0) {
820 pref = -1;
821 }
822
823 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100824 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100825 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100826 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100827 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100828 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100829 }
830}
831
Michal Vasko3031aae2016-01-27 16:07:18 +0100832API void
833nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100834{
Michal Vasko3031aae2016-01-27 16:07:18 +0100835 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
836}
837
838API void
839nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
840{
841 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
842}
843
844static int16_t
845_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
846{
847 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100848
849 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100850 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100851 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100852 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100853 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100854 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100855 }
856
857 return pref;
858}
859
Michal Vasko3031aae2016-01-27 16:07:18 +0100860API int16_t
861nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
862{
863 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
864}
865
866API int16_t
867nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
868{
869 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
870}
871
872static int
873_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
874{
875 if (opts->username) {
876 free(opts->username);
877 }
878 if (username) {
879 opts->username = strdup(username);
880 if (!opts->username) {
881 ERRMEM;
882 return -1;
883 }
884 } else {
885 opts->username = NULL;
886 }
887
888 return 0;
889}
890
891API int
892nc_client_ssh_set_username(const char *username)
893{
894 return _nc_client_ssh_set_username(username, &ssh_opts);
895}
896
897API int
898nc_client_ssh_ch_set_username(const char *username)
899{
900 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
901}
902
Michal Vaskoe22c6732016-01-29 11:03:02 +0100903static const char *
904_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
905{
906 return opts->username;
907}
908
909API const char *
910nc_client_ssh_get_username(void)
911{
912 return _nc_client_ssh_get_username(&ssh_opts);
913}
914
915API const char *
916nc_client_ssh_ch_get_username(void)
917{
918 return _nc_client_ssh_get_username(&ssh_ch_opts);
919}
920
Michal Vasko3031aae2016-01-27 16:07:18 +0100921API int
922nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
923{
924 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
925}
926
927API int
928nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
929{
930 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
931}
932
Michal Vasko8e2f4a62016-02-01 15:59:48 +0100933/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +0100934 * Host, port, username, and a connected socket is expected to be set.
935 */
936static int
Michal Vasko30e2c872016-02-18 10:03:21 +0100937connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100938{
Michal Vasko235efdc2015-12-17 12:05:04 +0100939 int j, ret_auth, userauthlist;
940 NC_SSH_AUTH_TYPE auth;
941 short int pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100942 const char* prompt;
943 char *s, *answer, echo;
944 ssh_key pubkey, privkey;
945 ssh_session ssh_sess;
946
947 ssh_sess = session->ti.libssh.session;
948
949 if (ssh_connect(ssh_sess) != SSH_OK) {
950 ERR("Starting the SSH session failed (%s)", ssh_get_error(ssh_sess));
951 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100952 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100953 }
954
Michal Vaskoef112d72016-02-18 13:28:25 +0100955 if (opts->auth_hostkey_check(session->host, ssh_sess)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100956 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +0100957 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100958 }
959
960 if ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_ERROR) {
961 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100962 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100963 }
964
965 /* check what authentication methods are available */
966 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +0100967
968 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +0100969 if (opts->auth_pref[0].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100970 VRB("Interactive SSH authentication method was disabled.");
971 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100972 }
Michal Vasko30e2c872016-02-18 10:03:21 +0100973 if (opts->auth_pref[1].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100974 VRB("Password SSH authentication method was disabled.");
975 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100976 }
Michal Vasko30e2c872016-02-18 10:03:21 +0100977 if (opts->auth_pref[2].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100978 VRB("Publickey SSH authentication method was disabled.");
979 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100980 }
981
Michal Vasko235efdc2015-12-17 12:05:04 +0100982 while (ret_auth != SSH_AUTH_SUCCESS) {
983 auth = 0;
984 pref = 0;
985 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
986 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +0100987 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +0100988 }
Michal Vasko30e2c872016-02-18 10:03:21 +0100989 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100990 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +0100991 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +0100992 }
Michal Vasko30e2c872016-02-18 10:03:21 +0100993 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100994 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100995 }
996
Michal Vasko235efdc2015-12-17 12:05:04 +0100997 if (!auth) {
Michal Vaskod083db62016-01-19 10:31:29 +0100998 ERR("Unable to authenticate to the remote server (no supported authentication methods left).");
Michal Vasko235efdc2015-12-17 12:05:04 +0100999 break;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001000 }
1001
1002 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001003 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001004 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001005 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1006
Michal Vaskoef578332016-01-25 13:20:09 +01001007 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Michal Vasko30e2c872016-02-18 10:03:21 +01001008 s = opts->auth_password(session->username, session->host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001009 if ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) != SSH_AUTH_SUCCESS) {
1010 memset(s, 0, strlen(s));
Michal Vaskod083db62016-01-19 10:31:29 +01001011 VRB("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001012 }
1013 free(s);
1014 break;
1015 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001016 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1017
Michal Vaskod083db62016-01-19 10:31:29 +01001018 VRB("Keyboard-interactive authentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001019 while ((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) {
1020 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1021 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
1022 if (prompt == NULL) {
1023 break;
1024 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001025
1026 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1027 echo = 0;
1028
Michal Vasko30e2c872016-02-18 10:03:21 +01001029 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
1030 ssh_userauth_kbdint_getinstruction(ssh_sess),
1031 prompt, echo);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001032 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1033 free(answer);
1034 break;
1035 }
1036 free(answer);
1037 }
1038 }
1039
1040 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001041 VRB("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001042 }
1043
1044 break;
Michal Vasko206d3b12015-12-04 11:08:42 +01001045 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001046 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1047
Michal Vaskod083db62016-01-19 10:31:29 +01001048 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001049
1050 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001051 if (!opts->key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001052 VRB("No key pair specified.");
1053 break;
1054 }
1055
Michal Vasko30e2c872016-02-18 10:03:21 +01001056 for (j = 0; j < opts->key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +01001057 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko30e2c872016-02-18 10:03:21 +01001058 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1059 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001060
Michal Vasko30e2c872016-02-18 10:03:21 +01001061 if (ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey) != SSH_OK) {
1062 WRN("Failed to import the key \"%s\".", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001063 continue;
1064 }
1065 ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey);
1066 if ((ret_auth == SSH_AUTH_DENIED) || (ret_auth == SSH_AUTH_PARTIAL)) {
1067 ssh_key_free(pubkey);
1068 continue;
1069 }
1070 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001071 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001072 ssh_key_free(pubkey);
1073 break;
1074 }
1075
Michal Vasko30e2c872016-02-18 10:03:21 +01001076 if (opts->keys[j].privkey_crypt) {
1077 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001078 } else {
1079 s = NULL;
1080 }
1081
Michal Vasko30e2c872016-02-18 10:03:21 +01001082 if (ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey) != SSH_OK) {
1083 WRN("Failed to import the key \"%s\".", opts->keys[j].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001084 if (s) {
1085 memset(s, 0, strlen(s));
1086 free(s);
1087 }
1088 ssh_key_free(pubkey);
1089 continue;
1090 }
1091
1092 if (s) {
1093 memset(s, 0, strlen(s));
1094 free(s);
1095 }
1096
1097 ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey);
1098 ssh_key_free(pubkey);
1099 ssh_key_free(privkey);
1100
1101 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001102 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001103 }
1104 if (ret_auth == SSH_AUTH_SUCCESS) {
1105 break;
1106 }
1107 }
1108 break;
1109 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001110 }
1111
1112 /* check a state of authentication */
1113 if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vaskod083db62016-01-19 10:31:29 +01001114 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001115 }
1116
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001117 return 0;
1118}
1119
1120/* Open new SSH channel and request the 'netconf' subsystem.
1121 * SSH connection is expected to be established.
1122 */
1123static int
1124open_netconf_channel(struct nc_session *session)
1125{
1126 ssh_session ssh_sess;
1127
1128 ssh_sess = session->ti.libssh.session;
1129
1130 if (!ssh_is_connected(ssh_sess)) {
1131 ERR("SSH session not connected.");
1132 return -1;
1133 }
1134
1135 if (session->ti.libssh.channel) {
1136 ERR("SSH channel already created.");
1137 return -1;
1138 }
1139
Michal Vasko7b62fed2015-10-26 15:39:46 +01001140 /* open a channel */
1141 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
1142 if (ssh_channel_open_session(session->ti.libssh.channel) != SSH_OK) {
1143 ssh_channel_free(session->ti.libssh.channel);
1144 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001145 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
1146 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001147 }
1148
1149 /* execute the NETCONF subsystem on the channel */
1150 if (ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf") != SSH_OK) {
1151 ssh_channel_free(session->ti.libssh.channel);
1152 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001153 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
1154 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001155 }
1156
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001157 return 0;
Radek Krejciac6d3472015-10-22 15:47:18 +02001158}
1159
Michal Vasko30e2c872016-02-18 10:03:21 +01001160static struct nc_session *
1161_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_client_ssh_opts *opts)
1162{
1163 char *host = NULL, *username = NULL;
1164 unsigned short port = 0;
1165 int sock;
1166 struct passwd *pw;
1167 struct nc_session *session = NULL;
1168
1169 if (!ssh_session) {
1170 ERRARG;
1171 return NULL;
1172 }
1173
1174 /* prepare session structure */
1175 session = calloc(1, sizeof *session);
1176 if (!session) {
1177 ERRMEM;
1178 return NULL;
1179 }
1180 session->status = NC_STATUS_STARTING;
1181 session->side = NC_CLIENT;
1182
1183 /* transport lock */
1184 session->ti_lock = malloc(sizeof *session->ti_lock);
1185 if (!session->ti_lock) {
1186 ERRMEM;
1187 goto fail;
1188 }
1189 pthread_mutex_init(session->ti_lock, NULL);
1190
1191 session->ti_type = NC_TI_LIBSSH;
1192 session->ti.libssh.session = ssh_session;
1193
1194 /* was port set? */
1195 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1196
1197 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1198 /*
1199 * There is no file descriptor (detected based on the host, there is no way to check
1200 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1201 */
1202
1203 /* remember host */
1204 host = strdup("localhost");
1205 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1206
1207 /* create and connect socket */
1208 sock = nc_sock_connect(host, port);
1209 if (sock == -1) {
1210 goto fail;
1211 }
1212 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1213 }
1214
1215 /* was username set? */
1216 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1217
1218 if (!ssh_is_connected(ssh_session)) {
1219 /*
1220 * We are connected, but not SSH authenticated. (Transport layer)
1221 */
1222
1223 /* remember username */
1224 if (!username) {
1225 if (!opts->username) {
1226 pw = getpwuid(getuid());
1227 if (!pw) {
1228 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1229 goto fail;
1230 }
1231 username = strdup(pw->pw_name);
1232 } else {
1233 username = strdup(opts->username);
1234 }
1235 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1236 }
1237
1238 /* connect and authenticate SSH session */
1239 session->host = host;
1240 session->username = username;
1241 if (connect_ssh_session(session, opts)) {
1242 goto fail;
1243 }
1244 }
1245
1246 /*
1247 * Almost done, open a netconf channel. (Transport layer / application layer)
1248 */
1249 if (open_netconf_channel(session)) {
1250 goto fail;
1251 }
1252
1253 /*
1254 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1255 */
1256
1257 /* assign context (dicionary needed for handshake) */
1258 if (!ctx) {
1259 if (client_opts.schema_searchpath) {
1260 ctx = ly_ctx_new(client_opts.schema_searchpath);
1261 } else {
1262 ctx = ly_ctx_new(SCHEMAS_DIR);
1263 }
1264 } else {
1265 session->flags |= NC_SESSION_SHAREDCTX;
1266 }
1267 session->ctx = ctx;
1268
1269 /* NETCONF handshake */
1270 if (nc_handshake(session)) {
1271 goto fail;
1272 }
1273 session->status = NC_STATUS_RUNNING;
1274
1275 if (nc_ctx_check_and_fill(session) == -1) {
1276 goto fail;
1277 }
1278
1279 /* store information into the dictionary */
1280 if (host) {
1281 session->host = lydict_insert_zc(ctx, host);
1282 }
1283 if (port) {
1284 session->port = port;
1285 }
1286 if (username) {
1287 session->username = lydict_insert_zc(ctx, username);
1288 }
1289
1290 return session;
1291
1292fail:
1293 nc_session_free(session);
1294 return NULL;
1295}
1296
Radek Krejciac6d3472015-10-22 15:47:18 +02001297API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001298nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001299{
Michal Vaskoc111ac52015-12-08 14:36:36 +01001300 const int timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001301 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001302 uint32_t port_uint;
Michal Vasko3031aae2016-01-27 16:07:18 +01001303 char *username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001304 struct passwd *pw;
1305 struct nc_session *session = NULL;
1306
1307 /* process parameters */
1308 if (!host || strisempty(host)) {
1309 host = "localhost";
1310 }
1311
1312 if (!port) {
1313 port = NC_PORT_SSH;
1314 }
Michal Vasko55fded62016-02-02 12:19:34 +01001315 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001316
Michal Vasko3031aae2016-01-27 16:07:18 +01001317 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001318 pw = getpwuid(getuid());
1319 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001320 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1321 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001322 } else {
1323 username = pw->pw_name;
1324 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001325 } else {
1326 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001327 }
1328
1329 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001330 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001331 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001332 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001333 return NULL;
1334 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001335 session->status = NC_STATUS_STARTING;
1336 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +02001337
Michal Vasko7b62fed2015-10-26 15:39:46 +01001338 /* transport lock */
1339 session->ti_lock = malloc(sizeof *session->ti_lock);
1340 if (!session->ti_lock) {
1341 ERRMEM;
1342 goto fail;
1343 }
1344 pthread_mutex_init(session->ti_lock, NULL);
1345
1346 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001347 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001348 session->ti.libssh.session = ssh_new();
1349 if (!session->ti.libssh.session) {
1350 ERR("Unable to initialize SSH session.");
1351 goto fail;
1352 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001353
Michal Vasko7b62fed2015-10-26 15:39:46 +01001354 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001355 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001356 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001357 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001358 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko086311b2016-01-08 09:53:11 +01001359 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS,
1360 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1361 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1362 /* ecdsa is probably not supported... */
1363 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1364 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001365
1366 /* create and assign communication socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001367 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001368 if (sock == -1) {
1369 goto fail;
1370 }
1371 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1372
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001373 /* temporarily, for session connection */
1374 session->host = host;
1375 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001376 if (connect_ssh_session(session, &ssh_opts) || open_netconf_channel(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001377 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001378 }
1379
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001380 /* assign context (dicionary needed for handshake) */
1381 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001382 if (client_opts.schema_searchpath) {
1383 ctx = ly_ctx_new(client_opts.schema_searchpath);
1384 } else {
1385 ctx = ly_ctx_new(SCHEMAS_DIR);
1386 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001387 } else {
1388 session->flags |= NC_SESSION_SHAREDCTX;
1389 }
1390 session->ctx = ctx;
1391
Radek Krejciac6d3472015-10-22 15:47:18 +02001392 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001393 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001394 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001395 }
Michal Vaskoad611702015-12-03 13:41:51 +01001396 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001397
Michal Vaskoef578332016-01-25 13:20:09 +01001398 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001399 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001400 }
1401
1402 /* store information into the dictionary */
1403 session->host = lydict_insert(ctx, host, 0);
1404 session->port = port;
1405 session->username = lydict_insert(ctx, username, 0);
1406
Radek Krejciac6d3472015-10-22 15:47:18 +02001407 return session;
1408
Michal Vasko7b62fed2015-10-26 15:39:46 +01001409fail:
Radek Krejciac6d3472015-10-22 15:47:18 +02001410 nc_session_free(session);
1411 return NULL;
1412}
1413
1414API struct nc_session *
1415nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1416{
Michal Vasko30e2c872016-02-18 10:03:21 +01001417 return _nc_connect_libssh(ssh_session, ctx, &ssh_opts);
Radek Krejciac6d3472015-10-22 15:47:18 +02001418}
1419
1420API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001421nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001422{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001423 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001424
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001425 if (!session) {
1426 ERRARG;
1427 return NULL;
1428 }
1429
Michal Vasko7b62fed2015-10-26 15:39:46 +01001430 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001431 new_session = calloc(1, sizeof *new_session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001432 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001433 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001434 return NULL;
1435 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001436 new_session->status = NC_STATUS_STARTING;
1437 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001438
1439 /* share some parameters including the session lock */
1440 new_session->ti_type = NC_TI_LIBSSH;
1441 new_session->ti_lock = session->ti_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001442 new_session->ti.libssh.session = session->ti.libssh.session;
1443
1444 /* create the channel safely */
1445 pthread_mutex_lock(new_session->ti_lock);
1446
1447 /* open a channel */
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001448 if (open_netconf_channel(new_session)) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001449 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001450 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001451
1452 /* assign context (dicionary needed for handshake) */
1453 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001454 if (client_opts.schema_searchpath) {
1455 ctx = ly_ctx_new(client_opts.schema_searchpath);
1456 } else {
1457 ctx = ly_ctx_new(SCHEMAS_DIR);
1458 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001459 } else {
Michal Vasko56b5bf72016-01-19 11:20:35 +01001460 new_session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001461 }
Michal Vasko56b5bf72016-01-19 11:20:35 +01001462 new_session->ctx = ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001463
Michal Vasko7b62fed2015-10-26 15:39:46 +01001464 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001465 if (nc_handshake(new_session)) {
1466 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001467 }
Michal Vaskoad611702015-12-03 13:41:51 +01001468 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001469
Michal Vasko56b5bf72016-01-19 11:20:35 +01001470 pthread_mutex_unlock(new_session->ti_lock);
1471
Michal Vaskoef578332016-01-25 13:20:09 +01001472 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001473 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001474 }
1475
1476 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001477 new_session->host = lydict_insert(ctx, session->host, 0);
1478 new_session->port = session->port;
1479 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001480
1481 /* append to the session ring list */
1482 if (!session->ti.libssh.next) {
1483 session->ti.libssh.next = new_session;
1484 new_session->ti.libssh.next = session;
1485 } else {
1486 ptr = session->ti.libssh.next;
1487 session->ti.libssh.next = new_session;
1488 new_session->ti.libssh.next = ptr;
1489 }
1490
1491 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001492
1493fail:
1494 nc_session_free(new_session);
1495 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001496}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001497
Michal Vasko3031aae2016-01-27 16:07:18 +01001498struct nc_session *
Michal Vaskoaf58cd92016-01-28 11:56:02 +01001499nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001500{
1501 const int ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko3031aae2016-01-27 16:07:18 +01001502 struct passwd *pw;
Michal Vasko30e2c872016-02-18 10:03:21 +01001503 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001504 ssh_session sess;
1505
Michal Vasko80cad7f2015-12-08 14:42:27 +01001506 sess = ssh_new();
1507 if (!sess) {
1508 ERR("Unable to initialize an SSH session.");
1509 close(sock);
1510 return NULL;
1511 }
1512
1513 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001514 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001515 ssh_options_set(sess, SSH_OPTIONS_PORT, &port);
1516 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001517 if (!ssh_ch_opts.username) {
1518 pw = getpwuid(getuid());
1519 if (!pw) {
1520 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1521 return NULL;
1522 }
1523 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1524 } else {
1525 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001526 }
Michal Vasko086311b2016-01-08 09:53:11 +01001527 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1528 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1529 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1530 /* ecdsa is probably not supported... */
1531 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1532 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001533
Michal Vasko30e2c872016-02-18 10:03:21 +01001534 session = _nc_connect_libssh(sess, ctx, &ssh_ch_opts);
Michal Vasko4282fae2016-02-18 10:03:42 +01001535 if (session) {
1536 session->flags |= NC_SESSION_CALLHOME;
1537 }
1538
Michal Vasko30e2c872016-02-18 10:03:21 +01001539 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001540}