blob: 43912116426a31b4c1d6ea5206999bc7bf4130ea [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 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * 3. Neither the name of the Company nor the names of its contributors
21 * may be used to endorse or promote products derived from this
22 * software without specific prior written permission.
23 *
24 */
25
Michal Vasko7b62fed2015-10-26 15:39:46 +010026#define _GNU_SOURCE
Radek Krejciac6d3472015-10-22 15:47:18 +020027#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010028#include <stdlib.h>
29#include <stddef.h>
30#include <stdio.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020031#include <string.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010032#include <errno.h>
33#include <fcntl.h>
34#include <termios.h>
35#include <sys/types.h>
36#include <sys/stat.h>
37#include <pwd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020038#include <unistd.h>
39
Michal Vasko7b62fed2015-10-26 15:39:46 +010040#ifdef ENABLE_DNSSEC
41# include <validator/validator.h>
42# include <validator/resolver.h>
43# include <validator/validator-compat.h>
44#endif
45
Michal Vasko745ff832015-12-08 14:40:29 +010046#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020047#include <libyang/libyang.h>
48
Michal Vaskoe22c6732016-01-29 11:03:02 +010049#include "session_client.h"
50#include "session_client_ch.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020051#include "libnetconf.h"
52
Michal Vaskoef112d72016-02-18 13:28:25 +010053static int sshauth_hostkey_check(const char *hostname, ssh_session session);
Michal Vasko30e2c872016-02-18 10:03:21 +010054static char *sshauth_password(const char *username, const char *hostname);
55static char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo);
56static char *sshauth_privkey_passphrase(const char* privkey_path);
57
Michal Vaskodaf9a092016-02-09 10:42:05 +010058extern struct nc_client_opts client_opts;
59
Michal Vasko3031aae2016-01-27 16:07:18 +010060static struct nc_client_ssh_opts ssh_opts = {
Michal Vasko30e2c872016-02-18 10:03:21 +010061 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}},
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 Vasko7b62fed2015-10-26 15:39:46 +010066};
Radek Krejciac6d3472015-10-22 15:47:18 +020067
Michal Vasko3031aae2016-01-27 16:07:18 +010068static struct nc_client_ssh_opts ssh_ch_opts = {
Michal Vasko30e2c872016-02-18 10:03:21 +010069 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}},
Michal Vaskoef112d72016-02-18 13:28:25 +010070 .auth_hostkey_check = sshauth_hostkey_check,
Michal Vasko30e2c872016-02-18 10:03:21 +010071 .auth_password = sshauth_password,
72 .auth_interactive = sshauth_interactive,
73 .auth_privkey_passphrase = sshauth_privkey_passphrase
Michal Vasko3031aae2016-01-27 16:07:18 +010074};
75
Michal Vaskoe22c6732016-01-29 11:03:02 +010076static void
77_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +010078{
79 int i;
80
Michal Vaskoe22c6732016-01-29 11:03:02 +010081 for (i = 0; i < opts->key_count; ++i) {
82 free(opts->keys[i].pubkey_path);
83 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +010084 }
Michal Vaskoe22c6732016-01-29 11:03:02 +010085 free(opts->keys);
86 free(opts->username);
87}
Michal Vasko990089e2015-10-27 15:05:25 +010088
Michal Vaskoe22c6732016-01-29 11:03:02 +010089API void
90nc_client_ssh_destroy_opts(void)
91{
92 _nc_client_ssh_destroy_opts(&ssh_opts);
93 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +010094}
95
Michal Vaskoef112d72016-02-18 13:28:25 +010096#ifdef ENABLE_DNSSEC
97
98/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
99/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
100static int
101sshauth_hostkey_hash_dnssec_check(const char *hostname, const char *sha1hash, int type, int alg) {
102 ns_msg handle;
103 ns_rr rr;
104 val_status_t val_status;
105 const unsigned char* rdata;
106 unsigned char buf[4096];
107 int buf_len = 4096;
108 int ret = 0, i, j, len;
109
110 /* class 1 - internet, type 44 - SSHFP */
111 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
112
113 if ((len < 0) || !val_istrusted(val_status)) {
114 ret = 2;
115 goto finish;
116 }
117
118 if (ns_initparse(buf, len, &handle) < 0) {
119 ERR("Failed to initialize DNSSEC response parser.");
120 ret = 2;
121 goto finish;
122 }
123
124 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
125 ERR("DNSSEC query returned %d.", i);
126 ret = 2;
127 goto finish;
128 }
129
130 if (!libsres_msg_getflag(handle, ns_f_ad)) {
131 /* response not secured by DNSSEC */
132 ret = 1;
133 }
134
135 /* query section */
136 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
137 ERROR("DNSSEC query section parser fail.");
138 ret = 2;
139 goto finish;
140 }
141
142 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
143 ERROR("DNSSEC query in the answer does not match the original query.");
144 ret = 2;
145 goto finish;
146 }
147
148 /* answer section */
149 i = 0;
150 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
151 if (ns_rr_type(rr) != 44) {
152 ++i;
153 continue;
154 }
155
156 rdata = ns_rr_rdata(rr);
157 if (rdata[0] != type) {
158 ++i;
159 continue;
160 }
161 if (rdata[1] != alg) {
162 ++i;
163 continue;
164 }
165
166 /* we found the correct SSHFP entry */
167 rdata += 2;
168 for (j = 0; j < 20; ++j) {
169 if (rdata[j] != (unsigned char)sha1hash[j]) {
170 ret = 2;
171 goto finish;
172 }
173 }
174
175 /* server fingerprint is supported by a DNS entry,
176 * we have already determined if DNSSEC was used or not
177 */
178 goto finish;
179 }
180
181 /* no match */
182 ret = 2;
183
184finish:
185 val_free_validator_state();
186 return ret;
187}
188
189#endif /* ENABLE_DNSSEC */
190
191static int
192sshauth_hostkey_check(const char *hostname, ssh_session session)
193{
194 char *hexa;
195 int c, state, ret;
196 ssh_key srv_pubkey;
197 unsigned char *hash_sha1 = NULL;
198 size_t hlen;
199 enum ssh_keytypes_e srv_pubkey_type;
200 char answer[5];
201
202 state = ssh_is_server_known(session);
203
204 ret = ssh_get_publickey(session, &srv_pubkey);
205 if (ret < 0) {
206 ERR("Unable to get server public key.");
207 return -1;
208 }
209
210 srv_pubkey_type = ssh_key_type(srv_pubkey);
211 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
212 ssh_key_free(srv_pubkey);
213 if (ret < 0) {
214 ERR("Failed to calculate SHA1 hash of the server public key.");
215 return -1;
216 }
217
218 hexa = ssh_get_hexa(hash_sha1, hlen);
219
220 switch (state) {
221 case SSH_SERVER_KNOWN_OK:
222 break; /* ok */
223
224 case SSH_SERVER_KNOWN_CHANGED:
225 ERR("Remote host key changed, the connection will be terminated!");
226 goto fail;
227
228 case SSH_SERVER_FOUND_OTHER:
229 WRN("Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
230 goto hostkey_not_known;
231
232 case SSH_SERVER_FILE_NOT_FOUND:
233 WRN("Could not find the known hosts file.");
234 goto hostkey_not_known;
235
236 case SSH_SERVER_NOT_KNOWN:
237hostkey_not_known:
238#ifdef ENABLE_DNSSEC
239 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
240 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
241 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
242 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
243 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
244 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
245 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
246 }
247
248 /* DNSSEC SSHFP check successful, that's enough */
249 if (!ret) {
250 VRB("DNSSEC SSHFP check successful.");
251 ssh_write_knownhost(session);
252 ssh_clean_pubkey_hash(&hash_sha1);
253 ssh_string_free_char(hexa);
254 return 0;
255 }
256 }
257#endif
258
259 /* try to get result from user */
260 fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname);
261 fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa);
262
263#ifdef ENABLE_DNSSEC
264 if (ret == 2) {
265 fprintf(stdout, "No matching host key fingerprint found using DNS.\n");
266 } else if (ret == 1) {
267 fprintf(stdout, "Matching host key fingerprint found using DNS.\n");
268 }
269#endif
270
271 fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? ");
272
273 do {
274 if (fscanf(stdin, "%4s", answer) == EOF) {
275 ERR("fscanf() failed (%s).", strerror(errno));
276 goto fail;
277 }
278 while (((c = getchar()) != EOF) && (c != '\n'));
279
280 fflush(stdin);
281 if (!strcmp("yes", answer)) {
282 /* store the key into the host file */
283 ret = ssh_write_knownhost(session);
284 if (ret != SSH_OK) {
285 WRN("Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
286 }
287 } else if (!strcmp("no", answer)) {
288 goto fail;
289 } else {
290 fprintf(stdout, "Please type 'yes' or 'no': ");
291 }
292 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
293
294 break;
295
296 case SSH_SERVER_ERROR:
297 ssh_clean_pubkey_hash(&hash_sha1);
298 fprintf(stderr,"%s",ssh_get_error(session));
299 return -1;
300 }
301
302 ssh_clean_pubkey_hash(&hash_sha1);
303 ssh_string_free_char(hexa);
304 return 0;
305
306fail:
307 ssh_clean_pubkey_hash(&hash_sha1);
308 ssh_string_free_char(hexa);
309 return -1;
310}
311
Michal Vasko7b62fed2015-10-26 15:39:46 +0100312static char *
313sshauth_password(const char *username, const char *hostname)
Radek Krejciac6d3472015-10-22 15:47:18 +0200314{
Michal Vasko7b62fed2015-10-26 15:39:46 +0100315 char *buf, *newbuf;
316 int buflen = 1024, len = 0;
317 char c = 0;
318 struct termios newterm, oldterm;
319 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +0200320
Michal Vasko7b62fed2015-10-26 15:39:46 +0100321 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100322 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100323 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200324 }
325
Michal Vasko7b62fed2015-10-26 15:39:46 +0100326 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100327 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100328 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100329 return NULL;
330 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200331
Michal Vasko7b62fed2015-10-26 15:39:46 +0100332 fprintf(tty, "%s@%s password: ", username, hostname);
333 fflush(tty);
Radek Krejciac6d3472015-10-22 15:47:18 +0200334
Michal Vasko7b62fed2015-10-26 15:39:46 +0100335 /* system("stty -echo"); */
336 newterm = oldterm;
337 newterm.c_lflag &= ~ECHO;
338 newterm.c_lflag &= ~ICANON;
339 tcflush(fileno(tty), TCIFLUSH);
340 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100341 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100342 fclose(tty);
343 return NULL;
344 }
345
346 buf = malloc(buflen * sizeof *buf);
347 if (!buf) {
348 ERRMEM;
349 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100350 return NULL;
351 }
352
353 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
354 if (len >= buflen - 1) {
355 buflen *= 2;
356 newbuf = realloc(buf, buflen * sizeof *newbuf);
357 if (!newbuf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100358 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100359
360 /* remove content of the buffer */
361 memset(buf, 0, len);
362 free(buf);
363
364 /* restore terminal settings */
365 if (tcsetattr(fileno(tty), TCSANOW, &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100366 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100367 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100368 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100369 return NULL;
370 } else {
371 buf = newbuf;
372 }
373 }
374 buf[len++] = c;
375 }
376 buf[len++] = 0; /* terminating null byte */
377
378 /* system ("stty echo"); */
379 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100380 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100381 /*
382 * terminal probably still hides input characters, but we have password
383 * and anyway we are unable to set terminal to the previous state, so
384 * just continue
385 */
386 }
387 fprintf(tty, "\n");
388
389 fclose(tty);
390 return buf;
391}
392
393static char *
394sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo)
395{
396 unsigned int buflen = 8, response_len;
397 char c = 0;
398 struct termios newterm, oldterm;
399 char *newtext, *response;
400 FILE *tty;
401
402 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100403 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100404 return NULL;
405 }
406
407 if (tcgetattr(fileno(tty), &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100408 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100409 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100410 return NULL;
411 }
412
413 if (auth_name && (!fwrite(auth_name, sizeof(char), strlen(auth_name), tty)
414 || !fwrite("\n", sizeof(char), 1, tty))) {
415 ERR("Writing the auth method name into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100416 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100417 return NULL;
418 }
419
420 if (instruction && (!fwrite(instruction, sizeof(char), strlen(instruction), tty)
421 || !fwrite("\n", sizeof(char), 1, tty))) {
422 ERR("Writing the instruction into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100423 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100424 return NULL;
425 }
426
427 if (!fwrite(prompt, sizeof(char), strlen(prompt), tty)) {
428 ERR("Writing the authentication prompt into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100429 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100430 return NULL;
431 }
432 fflush(tty);
433 if (!echo) {
434 /* system("stty -echo"); */
435 newterm = oldterm;
436 newterm.c_lflag &= ~ECHO;
437 tcflush(fileno(tty), TCIFLUSH);
438 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100439 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100440 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100441 return NULL;
442 }
443 }
444
445 response = malloc(buflen * sizeof *response);
446 response_len = 0;
447 if (!response) {
448 ERRMEM;
449 /* restore terminal settings */
450 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100451 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100452 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100453 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100454 return NULL;
455 }
456
457 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
458 if (response_len >= buflen - 1) {
459 buflen *= 2;
460 newtext = realloc(response, buflen * sizeof *newtext);
461 if (!newtext) {
Michal Vaskod083db62016-01-19 10:31:29 +0100462 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100463 free(response);
464
465 /* restore terminal settings */
466 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100467 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100468 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100469 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100470 return NULL;
471 } else {
472 response = newtext;
473 }
474 }
475 response[response_len++] = c;
476 }
477 /* terminating null byte */
478 response[response_len++] = '\0';
479
480 /* system ("stty echo"); */
481 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100482 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100483 /*
484 * terminal probably still hides input characters, but we have password
485 * and anyway we are unable to set terminal to the previous state, so
486 * just continue
487 */
488 }
489
490 fprintf(tty, "\n");
491 fclose(tty);
492 return response;
493}
494
495static char *
Michal Vasko30e2c872016-02-18 10:03:21 +0100496sshauth_privkey_passphrase(const char* privkey_path)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100497{
498 char c, *buf, *newbuf;
499 int buflen = 1024, len = 0;
500 struct termios newterm, oldterm;
501 FILE *tty;
502
503 buf = malloc(buflen * sizeof *buf);
504 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100505 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100506 return NULL;
507 }
508
509 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100510 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100511 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100512 }
513
514 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100515 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100516 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100517 }
518
519 fprintf(tty, "Enter passphrase for the key '%s':", privkey_path);
520 fflush(tty);
521
522 /* system("stty -echo"); */
523 newterm = oldterm;
524 newterm.c_lflag &= ~ECHO;
525 newterm.c_lflag &= ~ICANON;
526 tcflush(fileno(tty), TCIFLUSH);
527 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100528 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100529 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100530 }
531
532 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
533 if (len >= buflen - 1) {
534 buflen *= 2;
535 newbuf = realloc(buf, buflen * sizeof *newbuf);
536 if (!newbuf) {
537 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100538 /* restore terminal settings */
539 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100540 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100541 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100542 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100543 }
544 buf = newbuf;
545 }
546 buf[len++] = (char)c;
547 }
548 buf[len++] = 0; /* terminating null byte */
549
550 /* system ("stty echo"); */
551 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100552 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100553 /*
554 * terminal probably still hides input characters, but we have password
555 * and anyway we are unable to set terminal to the previous state, so
556 * just continue
557 */
558 }
559 fprintf(tty, "\n");
560
561 fclose(tty);
562 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100563
564fail:
565 free(buf);
Michal Vaskode581a82016-01-22 13:15:35 +0100566 if (tty) {
567 fclose(tty);
568 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100569 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100570}
571
Michal Vaskoef112d72016-02-18 13:28:25 +0100572static void
573_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session),
574 struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100575{
Michal Vaskoef112d72016-02-18 13:28:25 +0100576 if (auth_hostkey_check) {
577 opts->auth_hostkey_check = auth_hostkey_check;
578 } else {
579 opts->auth_hostkey_check = sshauth_hostkey_check;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100580 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100581}
582
Michal Vaskoef112d72016-02-18 13:28:25 +0100583API void
584nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session))
585{
586 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, &ssh_opts);
587}
588
589API void
590nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session))
591{
592 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, &ssh_ch_opts);
593}
594
595
Michal Vasko30e2c872016-02-18 10:03:21 +0100596static void
597_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname),
598 struct nc_client_ssh_opts *opts)
599{
600 if (auth_password) {
601 opts->auth_password = auth_password;
602 } else {
603 opts->auth_password = sshauth_password;
604 }
605}
606
607API void
608nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname))
609{
610 _nc_client_ssh_set_auth_password_clb(auth_password, &ssh_opts);
611}
612
613API void
614nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname))
615{
616 _nc_client_ssh_set_auth_password_clb(auth_password, &ssh_ch_opts);
617}
618
619static void
620_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
621 const char *prompt, int echo),
622 struct nc_client_ssh_opts *opts)
623{
624 if (auth_interactive) {
625 opts->auth_interactive = auth_interactive;
626 } else {
627 opts->auth_interactive = sshauth_interactive;
628 }
629}
630
631API void
632nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
633 const char *prompt, int echo))
634{
635 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, &ssh_opts);
636}
637
638API void
639nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
640 const char *prompt, int echo))
641{
642 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, &ssh_ch_opts);
643}
644
645static void
646_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path),
647 struct nc_client_ssh_opts *opts)
648{
649 if (auth_privkey_passphrase) {
650 opts->auth_privkey_passphrase = auth_privkey_passphrase;
651 } else {
652 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
653 }
654}
655
656API void
657nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path))
658{
659 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, &ssh_opts);
660}
661
662API void
663nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path))
664{
665 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, &ssh_ch_opts);
666}
667
Michal Vasko3031aae2016-01-27 16:07:18 +0100668static int
669_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 +0100670{
671 int i;
672 FILE *key;
673 char line[128];
674
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100675 if (!pub_key || !priv_key) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100676 ERRARG;
677 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100678 }
679
Michal Vasko3031aae2016-01-27 16:07:18 +0100680 for (i = 0; i < opts->key_count; ++i) {
681 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
682 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100683 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100684 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100685 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100686 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100687 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100688 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100689 continue;
690 }
691
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100692 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100693 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100694 }
695 }
696
Michal Vasko3031aae2016-01-27 16:07:18 +0100697 /* add the keys */
698 ++opts->key_count;
699 opts->keys = realloc(opts->keys, opts->key_count * sizeof *opts->keys);
700 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
701 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
702 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100703
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100704 /* check encryption */
705 if ((key = fopen(priv_key, "r"))) {
706 /* 1st line - key type */
707 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100708 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100709 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100710 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100711 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100712 /* 2nd line - encryption information or key */
713 if (!fgets(line, sizeof line, key)) {
714 fclose(key);
715 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100716 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100717 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100718 fclose(key);
719 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100720 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100721 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100722 }
723
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100724 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100725}
726
727API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100728nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100729{
Michal Vasko3031aae2016-01-27 16:07:18 +0100730 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
731}
732
733API int
734nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
735{
736 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
737}
738
739static int
740_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
741{
742 if (idx >= opts->key_count) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100743 ERRARG;
744 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100745 }
746
Michal Vasko3031aae2016-01-27 16:07:18 +0100747 free(opts->keys[idx].pubkey_path);
748 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100749
Michal Vasko3031aae2016-01-27 16:07:18 +0100750 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100751 if (idx < opts->key_count) {
752 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
753 }
754 if (opts->key_count) {
755 opts->keys = realloc(opts->keys, opts->key_count * sizeof *opts->keys);
756 } else {
757 free(opts->keys);
758 opts->keys = NULL;
759 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100760
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100761 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100762}
763
764API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100765nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100766{
Michal Vasko3031aae2016-01-27 16:07:18 +0100767 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100768}
769
770API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100771nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100772{
Michal Vasko3031aae2016-01-27 16:07:18 +0100773 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
774}
775
776static int
777_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
778{
779 return opts->key_count;
780}
781
782API int
783nc_client_ssh_get_keypair_count(void)
784{
785 return _nc_client_ssh_get_keypair_count(&ssh_opts);
786}
787
788API int
789nc_client_ssh_ch_get_keypair_count(void)
790{
791 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
792}
793
794static int
795_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
796{
797 if ((idx >= opts->key_count) || (!pub_key && !priv_key)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100798 ERRARG;
799 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100800 }
801
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100802 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100803 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100804 }
805 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100806 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100807 }
808
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100809 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100810}
811
Michal Vasko3031aae2016-01-27 16:07:18 +0100812API int
813nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
814{
815 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
816}
817
818API int
819nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
820{
821 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
822}
823
824static void
825_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 +0100826{
827 if (pref < 0) {
828 pref = -1;
829 }
830
831 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100832 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100833 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100834 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100835 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100836 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100837 }
838}
839
Michal Vasko3031aae2016-01-27 16:07:18 +0100840API void
841nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100842{
Michal Vasko3031aae2016-01-27 16:07:18 +0100843 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
844}
845
846API void
847nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
848{
849 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
850}
851
852static int16_t
853_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
854{
855 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100856
857 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100858 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100859 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100860 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100861 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100862 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100863 }
864
865 return pref;
866}
867
Michal Vasko3031aae2016-01-27 16:07:18 +0100868API int16_t
869nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
870{
871 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
872}
873
874API int16_t
875nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
876{
877 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
878}
879
880static int
881_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
882{
883 if (opts->username) {
884 free(opts->username);
885 }
886 if (username) {
887 opts->username = strdup(username);
888 if (!opts->username) {
889 ERRMEM;
890 return -1;
891 }
892 } else {
893 opts->username = NULL;
894 }
895
896 return 0;
897}
898
899API int
900nc_client_ssh_set_username(const char *username)
901{
902 return _nc_client_ssh_set_username(username, &ssh_opts);
903}
904
905API int
906nc_client_ssh_ch_set_username(const char *username)
907{
908 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
909}
910
Michal Vaskoe22c6732016-01-29 11:03:02 +0100911static const char *
912_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
913{
914 return opts->username;
915}
916
917API const char *
918nc_client_ssh_get_username(void)
919{
920 return _nc_client_ssh_get_username(&ssh_opts);
921}
922
923API const char *
924nc_client_ssh_ch_get_username(void)
925{
926 return _nc_client_ssh_get_username(&ssh_ch_opts);
927}
928
Michal Vasko3031aae2016-01-27 16:07:18 +0100929API int
930nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
931{
932 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
933}
934
935API int
936nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
937{
938 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
939}
940
Michal Vasko8e2f4a62016-02-01 15:59:48 +0100941/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +0100942 * Host, port, username, and a connected socket is expected to be set.
943 */
944static int
Michal Vasko30e2c872016-02-18 10:03:21 +0100945connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100946{
Michal Vasko235efdc2015-12-17 12:05:04 +0100947 int j, ret_auth, userauthlist;
948 NC_SSH_AUTH_TYPE auth;
949 short int pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100950 const char* prompt;
951 char *s, *answer, echo;
952 ssh_key pubkey, privkey;
953 ssh_session ssh_sess;
954
955 ssh_sess = session->ti.libssh.session;
956
957 if (ssh_connect(ssh_sess) != SSH_OK) {
958 ERR("Starting the SSH session failed (%s)", ssh_get_error(ssh_sess));
959 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100960 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100961 }
962
Michal Vaskoef112d72016-02-18 13:28:25 +0100963 if (opts->auth_hostkey_check(session->host, ssh_sess)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100964 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +0100965 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100966 }
967
968 if ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_ERROR) {
969 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100970 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100971 }
972
973 /* check what authentication methods are available */
974 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +0100975
976 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +0100977 if (opts->auth_pref[0].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100978 VRB("Interactive SSH authentication method was disabled.");
979 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100980 }
Michal Vasko30e2c872016-02-18 10:03:21 +0100981 if (opts->auth_pref[1].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100982 VRB("Password SSH authentication method was disabled.");
983 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100984 }
Michal Vasko30e2c872016-02-18 10:03:21 +0100985 if (opts->auth_pref[2].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100986 VRB("Publickey SSH authentication method was disabled.");
987 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100988 }
989
Michal Vasko235efdc2015-12-17 12:05:04 +0100990 while (ret_auth != SSH_AUTH_SUCCESS) {
991 auth = 0;
992 pref = 0;
993 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
994 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +0100995 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +0100996 }
Michal Vasko30e2c872016-02-18 10:03:21 +0100997 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +0100998 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +0100999 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001000 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001001 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001002 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001003 }
1004
Michal Vasko235efdc2015-12-17 12:05:04 +01001005 if (!auth) {
Michal Vaskod083db62016-01-19 10:31:29 +01001006 ERR("Unable to authenticate to the remote server (no supported authentication methods left).");
Michal Vasko235efdc2015-12-17 12:05:04 +01001007 break;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001008 }
1009
1010 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001011 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001012 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001013 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1014
Michal Vaskoef578332016-01-25 13:20:09 +01001015 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Michal Vasko30e2c872016-02-18 10:03:21 +01001016 s = opts->auth_password(session->username, session->host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001017 if ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) != SSH_AUTH_SUCCESS) {
1018 memset(s, 0, strlen(s));
Michal Vaskod083db62016-01-19 10:31:29 +01001019 VRB("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001020 }
1021 free(s);
1022 break;
1023 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001024 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1025
Michal Vaskod083db62016-01-19 10:31:29 +01001026 VRB("Keyboard-interactive authentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001027 while ((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) {
1028 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1029 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
1030 if (prompt == NULL) {
1031 break;
1032 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001033 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
1034 ssh_userauth_kbdint_getinstruction(ssh_sess),
1035 prompt, echo);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001036 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1037 free(answer);
1038 break;
1039 }
1040 free(answer);
1041 }
1042 }
1043
1044 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001045 VRB("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001046 }
1047
1048 break;
Michal Vasko206d3b12015-12-04 11:08:42 +01001049 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001050 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1051
Michal Vaskod083db62016-01-19 10:31:29 +01001052 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001053
1054 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001055 if (!opts->key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001056 VRB("No key pair specified.");
1057 break;
1058 }
1059
Michal Vasko30e2c872016-02-18 10:03:21 +01001060 for (j = 0; j < opts->key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +01001061 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko30e2c872016-02-18 10:03:21 +01001062 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1063 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001064
Michal Vasko30e2c872016-02-18 10:03:21 +01001065 if (ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey) != SSH_OK) {
1066 WRN("Failed to import the key \"%s\".", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001067 continue;
1068 }
1069 ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey);
1070 if ((ret_auth == SSH_AUTH_DENIED) || (ret_auth == SSH_AUTH_PARTIAL)) {
1071 ssh_key_free(pubkey);
1072 continue;
1073 }
1074 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001075 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001076 ssh_key_free(pubkey);
1077 break;
1078 }
1079
Michal Vasko30e2c872016-02-18 10:03:21 +01001080 if (opts->keys[j].privkey_crypt) {
1081 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001082 } else {
1083 s = NULL;
1084 }
1085
Michal Vasko30e2c872016-02-18 10:03:21 +01001086 if (ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey) != SSH_OK) {
1087 WRN("Failed to import the key \"%s\".", opts->keys[j].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001088 if (s) {
1089 memset(s, 0, strlen(s));
1090 free(s);
1091 }
1092 ssh_key_free(pubkey);
1093 continue;
1094 }
1095
1096 if (s) {
1097 memset(s, 0, strlen(s));
1098 free(s);
1099 }
1100
1101 ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey);
1102 ssh_key_free(pubkey);
1103 ssh_key_free(privkey);
1104
1105 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001106 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001107 }
1108 if (ret_auth == SSH_AUTH_SUCCESS) {
1109 break;
1110 }
1111 }
1112 break;
1113 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001114 }
1115
1116 /* check a state of authentication */
1117 if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vaskod083db62016-01-19 10:31:29 +01001118 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001119 }
1120
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001121 return 0;
1122}
1123
1124/* Open new SSH channel and request the 'netconf' subsystem.
1125 * SSH connection is expected to be established.
1126 */
1127static int
1128open_netconf_channel(struct nc_session *session)
1129{
1130 ssh_session ssh_sess;
1131
1132 ssh_sess = session->ti.libssh.session;
1133
1134 if (!ssh_is_connected(ssh_sess)) {
1135 ERR("SSH session not connected.");
1136 return -1;
1137 }
1138
1139 if (session->ti.libssh.channel) {
1140 ERR("SSH channel already created.");
1141 return -1;
1142 }
1143
Michal Vasko7b62fed2015-10-26 15:39:46 +01001144 /* open a channel */
1145 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
1146 if (ssh_channel_open_session(session->ti.libssh.channel) != SSH_OK) {
1147 ssh_channel_free(session->ti.libssh.channel);
1148 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001149 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
1150 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001151 }
1152
1153 /* execute the NETCONF subsystem on the channel */
1154 if (ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf") != SSH_OK) {
1155 ssh_channel_free(session->ti.libssh.channel);
1156 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001157 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
1158 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001159 }
1160
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001161 return 0;
Radek Krejciac6d3472015-10-22 15:47:18 +02001162}
1163
Michal Vasko30e2c872016-02-18 10:03:21 +01001164static struct nc_session *
1165_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_client_ssh_opts *opts)
1166{
1167 char *host = NULL, *username = NULL;
1168 unsigned short port = 0;
1169 int sock;
1170 struct passwd *pw;
1171 struct nc_session *session = NULL;
1172
1173 if (!ssh_session) {
1174 ERRARG;
1175 return NULL;
1176 }
1177
1178 /* prepare session structure */
1179 session = calloc(1, sizeof *session);
1180 if (!session) {
1181 ERRMEM;
1182 return NULL;
1183 }
1184 session->status = NC_STATUS_STARTING;
1185 session->side = NC_CLIENT;
1186
1187 /* transport lock */
1188 session->ti_lock = malloc(sizeof *session->ti_lock);
1189 if (!session->ti_lock) {
1190 ERRMEM;
1191 goto fail;
1192 }
1193 pthread_mutex_init(session->ti_lock, NULL);
1194
1195 session->ti_type = NC_TI_LIBSSH;
1196 session->ti.libssh.session = ssh_session;
1197
1198 /* was port set? */
1199 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1200
1201 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1202 /*
1203 * There is no file descriptor (detected based on the host, there is no way to check
1204 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1205 */
1206
1207 /* remember host */
1208 host = strdup("localhost");
1209 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1210
1211 /* create and connect socket */
1212 sock = nc_sock_connect(host, port);
1213 if (sock == -1) {
1214 goto fail;
1215 }
1216 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1217 }
1218
1219 /* was username set? */
1220 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1221
1222 if (!ssh_is_connected(ssh_session)) {
1223 /*
1224 * We are connected, but not SSH authenticated. (Transport layer)
1225 */
1226
1227 /* remember username */
1228 if (!username) {
1229 if (!opts->username) {
1230 pw = getpwuid(getuid());
1231 if (!pw) {
1232 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1233 goto fail;
1234 }
1235 username = strdup(pw->pw_name);
1236 } else {
1237 username = strdup(opts->username);
1238 }
1239 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1240 }
1241
1242 /* connect and authenticate SSH session */
1243 session->host = host;
1244 session->username = username;
1245 if (connect_ssh_session(session, opts)) {
1246 goto fail;
1247 }
1248 }
1249
1250 /*
1251 * Almost done, open a netconf channel. (Transport layer / application layer)
1252 */
1253 if (open_netconf_channel(session)) {
1254 goto fail;
1255 }
1256
1257 /*
1258 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1259 */
1260
1261 /* assign context (dicionary needed for handshake) */
1262 if (!ctx) {
1263 if (client_opts.schema_searchpath) {
1264 ctx = ly_ctx_new(client_opts.schema_searchpath);
1265 } else {
1266 ctx = ly_ctx_new(SCHEMAS_DIR);
1267 }
1268 } else {
1269 session->flags |= NC_SESSION_SHAREDCTX;
1270 }
1271 session->ctx = ctx;
1272
1273 /* NETCONF handshake */
1274 if (nc_handshake(session)) {
1275 goto fail;
1276 }
1277 session->status = NC_STATUS_RUNNING;
1278
1279 if (nc_ctx_check_and_fill(session) == -1) {
1280 goto fail;
1281 }
1282
1283 /* store information into the dictionary */
1284 if (host) {
1285 session->host = lydict_insert_zc(ctx, host);
1286 }
1287 if (port) {
1288 session->port = port;
1289 }
1290 if (username) {
1291 session->username = lydict_insert_zc(ctx, username);
1292 }
1293
1294 return session;
1295
1296fail:
1297 nc_session_free(session);
1298 return NULL;
1299}
1300
Radek Krejciac6d3472015-10-22 15:47:18 +02001301API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001302nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001303{
Michal Vaskoc111ac52015-12-08 14:36:36 +01001304 const int timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001305 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001306 uint32_t port_uint;
Michal Vasko3031aae2016-01-27 16:07:18 +01001307 char *username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001308 struct passwd *pw;
1309 struct nc_session *session = NULL;
1310
1311 /* process parameters */
1312 if (!host || strisempty(host)) {
1313 host = "localhost";
1314 }
1315
1316 if (!port) {
1317 port = NC_PORT_SSH;
1318 }
Michal Vasko55fded62016-02-02 12:19:34 +01001319 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001320
Michal Vasko3031aae2016-01-27 16:07:18 +01001321 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001322 pw = getpwuid(getuid());
1323 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001324 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1325 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001326 } else {
1327 username = pw->pw_name;
1328 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001329 } else {
1330 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001331 }
1332
1333 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001334 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001335 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001336 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001337 return NULL;
1338 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001339 session->status = NC_STATUS_STARTING;
1340 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +02001341
Michal Vasko7b62fed2015-10-26 15:39:46 +01001342 /* transport lock */
1343 session->ti_lock = malloc(sizeof *session->ti_lock);
1344 if (!session->ti_lock) {
1345 ERRMEM;
1346 goto fail;
1347 }
1348 pthread_mutex_init(session->ti_lock, NULL);
1349
1350 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001351 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001352 session->ti.libssh.session = ssh_new();
1353 if (!session->ti.libssh.session) {
1354 ERR("Unable to initialize SSH session.");
1355 goto fail;
1356 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001357
Michal Vasko7b62fed2015-10-26 15:39:46 +01001358 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001359 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001360 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001361 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001362 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko086311b2016-01-08 09:53:11 +01001363 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS,
1364 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1365 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1366 /* ecdsa is probably not supported... */
1367 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1368 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001369
1370 /* create and assign communication socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001371 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001372 if (sock == -1) {
1373 goto fail;
1374 }
1375 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1376
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001377 /* temporarily, for session connection */
1378 session->host = host;
1379 session->username = username;
Michal Vasko30e2c872016-02-18 10:03:21 +01001380 if (connect_ssh_session(session, &ssh_opts) || open_netconf_channel(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001381 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001382 }
1383
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001384 /* assign context (dicionary needed for handshake) */
1385 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001386 if (client_opts.schema_searchpath) {
1387 ctx = ly_ctx_new(client_opts.schema_searchpath);
1388 } else {
1389 ctx = ly_ctx_new(SCHEMAS_DIR);
1390 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001391 } else {
1392 session->flags |= NC_SESSION_SHAREDCTX;
1393 }
1394 session->ctx = ctx;
1395
Radek Krejciac6d3472015-10-22 15:47:18 +02001396 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001397 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001398 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001399 }
Michal Vaskoad611702015-12-03 13:41:51 +01001400 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001401
Michal Vaskoef578332016-01-25 13:20:09 +01001402 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001403 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001404 }
1405
1406 /* store information into the dictionary */
1407 session->host = lydict_insert(ctx, host, 0);
1408 session->port = port;
1409 session->username = lydict_insert(ctx, username, 0);
1410
Radek Krejciac6d3472015-10-22 15:47:18 +02001411 return session;
1412
Michal Vasko7b62fed2015-10-26 15:39:46 +01001413fail:
Radek Krejciac6d3472015-10-22 15:47:18 +02001414 nc_session_free(session);
1415 return NULL;
1416}
1417
1418API struct nc_session *
1419nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1420{
Michal Vasko30e2c872016-02-18 10:03:21 +01001421 return _nc_connect_libssh(ssh_session, ctx, &ssh_opts);
Radek Krejciac6d3472015-10-22 15:47:18 +02001422}
1423
1424API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001425nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001426{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001427 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001428
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001429 if (!session) {
1430 ERRARG;
1431 return NULL;
1432 }
1433
Michal Vasko7b62fed2015-10-26 15:39:46 +01001434 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001435 new_session = calloc(1, sizeof *new_session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001436 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001437 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001438 return NULL;
1439 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001440 new_session->status = NC_STATUS_STARTING;
1441 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001442
1443 /* share some parameters including the session lock */
1444 new_session->ti_type = NC_TI_LIBSSH;
1445 new_session->ti_lock = session->ti_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001446 new_session->ti.libssh.session = session->ti.libssh.session;
1447
1448 /* create the channel safely */
1449 pthread_mutex_lock(new_session->ti_lock);
1450
1451 /* open a channel */
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001452 if (open_netconf_channel(new_session)) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001453 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001454 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001455
1456 /* assign context (dicionary needed for handshake) */
1457 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001458 if (client_opts.schema_searchpath) {
1459 ctx = ly_ctx_new(client_opts.schema_searchpath);
1460 } else {
1461 ctx = ly_ctx_new(SCHEMAS_DIR);
1462 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001463 } else {
Michal Vasko56b5bf72016-01-19 11:20:35 +01001464 new_session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001465 }
Michal Vasko56b5bf72016-01-19 11:20:35 +01001466 new_session->ctx = ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001467
Michal Vasko7b62fed2015-10-26 15:39:46 +01001468 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001469 if (nc_handshake(new_session)) {
1470 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001471 }
Michal Vaskoad611702015-12-03 13:41:51 +01001472 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001473
Michal Vasko56b5bf72016-01-19 11:20:35 +01001474 pthread_mutex_unlock(new_session->ti_lock);
1475
Michal Vaskoef578332016-01-25 13:20:09 +01001476 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001477 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001478 }
1479
1480 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001481 new_session->host = lydict_insert(ctx, session->host, 0);
1482 new_session->port = session->port;
1483 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001484
1485 /* append to the session ring list */
1486 if (!session->ti.libssh.next) {
1487 session->ti.libssh.next = new_session;
1488 new_session->ti.libssh.next = session;
1489 } else {
1490 ptr = session->ti.libssh.next;
1491 session->ti.libssh.next = new_session;
1492 new_session->ti.libssh.next = ptr;
1493 }
1494
1495 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001496
1497fail:
1498 nc_session_free(new_session);
1499 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001500}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001501
Michal Vasko3031aae2016-01-27 16:07:18 +01001502struct nc_session *
Michal Vaskoaf58cd92016-01-28 11:56:02 +01001503nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001504{
1505 const int ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko3031aae2016-01-27 16:07:18 +01001506 struct passwd *pw;
Michal Vasko30e2c872016-02-18 10:03:21 +01001507 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001508 ssh_session sess;
1509
Michal Vasko80cad7f2015-12-08 14:42:27 +01001510 sess = ssh_new();
1511 if (!sess) {
1512 ERR("Unable to initialize an SSH session.");
1513 close(sock);
1514 return NULL;
1515 }
1516
1517 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001518 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001519 ssh_options_set(sess, SSH_OPTIONS_PORT, &port);
1520 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001521 if (!ssh_ch_opts.username) {
1522 pw = getpwuid(getuid());
1523 if (!pw) {
1524 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1525 return NULL;
1526 }
1527 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1528 } else {
1529 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001530 }
Michal Vasko086311b2016-01-08 09:53:11 +01001531 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1532 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1533 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1534 /* ecdsa is probably not supported... */
1535 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1536 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001537
Michal Vasko30e2c872016-02-18 10:03:21 +01001538 session = _nc_connect_libssh(sess, ctx, &ssh_ch_opts);
Michal Vasko4282fae2016-02-18 10:03:42 +01001539 if (session) {
1540 session->flags |= NC_SESSION_CALLHOME;
1541 }
1542
Michal Vasko30e2c872016-02-18 10:03:21 +01001543 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001544}