blob: bd5551889a80f7f4dc190eb2476b649482a7d7f0 [file] [log] [blame]
Radek Krejciac6d3472015-10-22 15:47:18 +02001/**
Michal Vasko086311b2016-01-08 09:53:11 +01002 * \file session_client_ssh.c
Radek Krejciac6d3472015-10-22 15:47:18 +02003 * \author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko086311b2016-01-08 09:53:11 +01004 * \author Michal Vasko <mvasko@cesnet.cz>
5 * \brief libnetconf2 - SSH specific client session transport functions
Radek Krejciac6d3472015-10-22 15:47:18 +02006 *
7 * This source is compiled only with libssh.
8 *
9 * Copyright (c) 2015 CESNET, z.s.p.o.
10 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010011 * This source code is licensed under BSD 3-Clause License (the "License").
12 * You may not use this file except in compliance with the License.
13 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010014 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010015 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejciac6d3472015-10-22 15:47:18 +020016 */
17
Michal Vasko7b62fed2015-10-26 15:39:46 +010018#define _GNU_SOURCE
Radek Krejciac6d3472015-10-22 15:47:18 +020019#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010020#include <stdlib.h>
21#include <stddef.h>
22#include <stdio.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020023#include <string.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010024#include <errno.h>
25#include <fcntl.h>
26#include <termios.h>
27#include <sys/types.h>
28#include <sys/stat.h>
29#include <pwd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020030#include <unistd.h>
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 Vaskob7558c52016-02-26 15:04:19 +010081void
Michal Vaskoe22c6732016-01-29 11:03:02 +010082nc_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
Michal Vasko650011a2016-02-25 14:49:29 +010093sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha1hash, int type, int alg) {
Michal Vaskoef112d72016-02-18 13:28:25 +010094 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)) {
Michal Vasko650011a2016-02-25 14:49:29 +0100129 ERR("DNSSEC query section parser fail.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100130 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)) {
Michal Vasko650011a2016-02-25 14:49:29 +0100135 ERR("DNSSEC query in the answer does not match the original query.");
Michal Vaskoef112d72016-02-18 13:28:25 +0100136 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) {
Michal Vasko650011a2016-02-25 14:49:29 +0100233 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100234 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100235 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100236 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
Michal Vasko650011a2016-02-25 14:49:29 +0100237 ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
Michal Vaskoef112d72016-02-18 13:28:25 +0100238 }
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 Vasko4eb3c312016-03-01 14:09:37 +0100307 char *buf;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100308 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;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100348 buf = nc_realloc(buf, buflen * sizeof *buf);
349 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100350 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100351
Michal Vasko7b62fed2015-10-26 15:39:46 +0100352 /* restore terminal settings */
353 if (tcsetattr(fileno(tty), TCSANOW, &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100354 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100355 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100356 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100357 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100358 }
359 }
360 buf[len++] = c;
361 }
362 buf[len++] = 0; /* terminating null byte */
363
364 /* system ("stty echo"); */
365 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
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 /*
368 * terminal probably still hides input characters, but we have password
369 * and anyway we are unable to set terminal to the previous state, so
370 * just continue
371 */
372 }
373 fprintf(tty, "\n");
374
375 fclose(tty);
376 return buf;
377}
378
379static char *
380sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo)
381{
382 unsigned int buflen = 8, response_len;
383 char c = 0;
384 struct termios newterm, oldterm;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100385 char *response;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100386 FILE *tty;
387
388 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100389 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100390 return NULL;
391 }
392
393 if (tcgetattr(fileno(tty), &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100394 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100395 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100396 return NULL;
397 }
398
399 if (auth_name && (!fwrite(auth_name, sizeof(char), strlen(auth_name), tty)
400 || !fwrite("\n", sizeof(char), 1, tty))) {
401 ERR("Writing the auth method name into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100402 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100403 return NULL;
404 }
405
406 if (instruction && (!fwrite(instruction, sizeof(char), strlen(instruction), tty)
407 || !fwrite("\n", sizeof(char), 1, tty))) {
408 ERR("Writing the instruction into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100409 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100410 return NULL;
411 }
412
413 if (!fwrite(prompt, sizeof(char), strlen(prompt), tty)) {
414 ERR("Writing the authentication prompt 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 fflush(tty);
419 if (!echo) {
420 /* system("stty -echo"); */
421 newterm = oldterm;
422 newterm.c_lflag &= ~ECHO;
423 tcflush(fileno(tty), TCIFLUSH);
424 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100425 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100426 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100427 return NULL;
428 }
429 }
430
431 response = malloc(buflen * sizeof *response);
432 response_len = 0;
433 if (!response) {
434 ERRMEM;
435 /* restore terminal settings */
436 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100437 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100438 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100439 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100440 return NULL;
441 }
442
443 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
444 if (response_len >= buflen - 1) {
445 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100446 response = nc_realloc(response, buflen * sizeof *response);
447 if (!response) {
Michal Vaskod083db62016-01-19 10:31:29 +0100448 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100449
450 /* restore terminal settings */
451 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100452 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100453 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100454 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100455 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100456 }
457 }
458 response[response_len++] = c;
459 }
460 /* terminating null byte */
461 response[response_len++] = '\0';
462
463 /* system ("stty echo"); */
464 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100465 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100466 /*
467 * terminal probably still hides input characters, but we have password
468 * and anyway we are unable to set terminal to the previous state, so
469 * just continue
470 */
471 }
472
473 fprintf(tty, "\n");
474 fclose(tty);
475 return response;
476}
477
478static char *
Michal Vasko30e2c872016-02-18 10:03:21 +0100479sshauth_privkey_passphrase(const char* privkey_path)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100480{
Michal Vasko4eb3c312016-03-01 14:09:37 +0100481 char c, *buf;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100482 int buflen = 1024, len = 0;
483 struct termios newterm, oldterm;
484 FILE *tty;
485
486 buf = malloc(buflen * sizeof *buf);
487 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100488 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100489 return NULL;
490 }
491
492 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100493 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100494 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100495 }
496
497 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100498 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100499 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100500 }
501
502 fprintf(tty, "Enter passphrase for the key '%s':", privkey_path);
503 fflush(tty);
504
505 /* system("stty -echo"); */
506 newterm = oldterm;
507 newterm.c_lflag &= ~ECHO;
508 newterm.c_lflag &= ~ICANON;
509 tcflush(fileno(tty), TCIFLUSH);
510 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100511 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100512 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100513 }
514
515 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
516 if (len >= buflen - 1) {
517 buflen *= 2;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100518 buf = nc_realloc(buf, buflen * sizeof *buf);
519 if (!buf) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100520 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100521 /* restore terminal settings */
522 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100523 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100524 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100525 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100526 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100527 }
528 buf[len++] = (char)c;
529 }
530 buf[len++] = 0; /* terminating null byte */
531
532 /* system ("stty echo"); */
533 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100534 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100535 /*
536 * terminal probably still hides input characters, but we have password
537 * and anyway we are unable to set terminal to the previous state, so
538 * just continue
539 */
540 }
541 fprintf(tty, "\n");
542
543 fclose(tty);
544 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100545
546fail:
547 free(buf);
Michal Vaskode581a82016-01-22 13:15:35 +0100548 if (tty) {
549 fclose(tty);
550 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100551 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100552}
553
Michal Vaskoef112d72016-02-18 13:28:25 +0100554static void
555_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session),
556 struct nc_client_ssh_opts *opts)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100557{
Michal Vaskoef112d72016-02-18 13:28:25 +0100558 if (auth_hostkey_check) {
559 opts->auth_hostkey_check = auth_hostkey_check;
560 } else {
561 opts->auth_hostkey_check = sshauth_hostkey_check;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100562 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100563}
564
Michal Vaskoef112d72016-02-18 13:28:25 +0100565API void
566nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session))
567{
568 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, &ssh_opts);
569}
570
571API void
572nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session))
573{
574 _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, &ssh_ch_opts);
575}
576
577
Michal Vasko30e2c872016-02-18 10:03:21 +0100578static void
579_nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname),
580 struct nc_client_ssh_opts *opts)
581{
582 if (auth_password) {
583 opts->auth_password = auth_password;
584 } else {
585 opts->auth_password = sshauth_password;
586 }
587}
588
589API void
590nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname))
591{
592 _nc_client_ssh_set_auth_password_clb(auth_password, &ssh_opts);
593}
594
595API void
596nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname))
597{
598 _nc_client_ssh_set_auth_password_clb(auth_password, &ssh_ch_opts);
599}
600
601static void
602_nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
603 const char *prompt, int echo),
604 struct nc_client_ssh_opts *opts)
605{
606 if (auth_interactive) {
607 opts->auth_interactive = auth_interactive;
608 } else {
609 opts->auth_interactive = sshauth_interactive;
610 }
611}
612
613API void
614nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
615 const char *prompt, int echo))
616{
617 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, &ssh_opts);
618}
619
620API void
621nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
622 const char *prompt, int echo))
623{
624 _nc_client_ssh_set_auth_interactive_clb(auth_interactive, &ssh_ch_opts);
625}
626
627static void
628_nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path),
629 struct nc_client_ssh_opts *opts)
630{
631 if (auth_privkey_passphrase) {
632 opts->auth_privkey_passphrase = auth_privkey_passphrase;
633 } else {
634 opts->auth_privkey_passphrase = sshauth_privkey_passphrase;
635 }
636}
637
638API void
639nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path))
640{
641 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, &ssh_opts);
642}
643
644API void
645nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path))
646{
647 _nc_client_ssh_set_auth_privkey_passphrase_clb(auth_privkey_passphrase, &ssh_ch_opts);
648}
649
Michal Vasko3031aae2016-01-27 16:07:18 +0100650static int
651_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 +0100652{
653 int i;
654 FILE *key;
655 char line[128];
656
Michal Vasko45e53ae2016-04-07 11:46:03 +0200657 if (!pub_key) {
658 ERRARG("pub_key");
659 return -1;
660 } else if (!priv_key) {
661 ERRARG("priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100662 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100663 }
664
Michal Vasko3031aae2016-01-27 16:07:18 +0100665 for (i = 0; i < opts->key_count; ++i) {
666 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
667 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100668 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100669 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100670 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100671 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100672 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100673 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100674 continue;
675 }
676
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100677 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100678 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100679 }
680 }
681
Michal Vasko3031aae2016-01-27 16:07:18 +0100682 /* add the keys */
683 ++opts->key_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100684 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
685 if (!opts->keys) {
686 ERRMEM;
687 return -1;
688 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100689 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
690 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
691 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100692
Michal Vasko4eb3c312016-03-01 14:09:37 +0100693 if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) {
694 ERRMEM;
695 return -1;
696 }
697
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100698 /* check encryption */
699 if ((key = fopen(priv_key, "r"))) {
700 /* 1st line - key type */
701 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100702 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100703 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100704 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100705 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100706 /* 2nd line - encryption information or key */
707 if (!fgets(line, sizeof line, key)) {
708 fclose(key);
709 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 fclose(key);
713 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100714 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100715 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100716 }
717
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100718 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100719}
720
721API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100722nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100723{
Michal Vasko3031aae2016-01-27 16:07:18 +0100724 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
725}
726
727API int
728nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
729{
730 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
731}
732
733static int
734_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
735{
736 if (idx >= opts->key_count) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200737 ERRARG("idx");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100738 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100739 }
740
Michal Vasko3031aae2016-01-27 16:07:18 +0100741 free(opts->keys[idx].pubkey_path);
742 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100743
Michal Vasko3031aae2016-01-27 16:07:18 +0100744 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100745 if (idx < opts->key_count) {
746 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
747 }
748 if (opts->key_count) {
Michal Vasko4eb3c312016-03-01 14:09:37 +0100749 opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys);
750 if (!opts->keys) {
751 ERRMEM;
752 return -1;
753 }
Michal Vaskoc0256492016-02-02 12:19:06 +0100754 } else {
755 free(opts->keys);
756 opts->keys = NULL;
757 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100758
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100759 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100760}
761
762API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100763nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100764{
Michal Vasko3031aae2016-01-27 16:07:18 +0100765 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100766}
767
768API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100769nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100770{
Michal Vasko3031aae2016-01-27 16:07:18 +0100771 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
772}
773
774static int
775_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
776{
777 return opts->key_count;
778}
779
780API int
781nc_client_ssh_get_keypair_count(void)
782{
783 return _nc_client_ssh_get_keypair_count(&ssh_opts);
784}
785
786API int
787nc_client_ssh_ch_get_keypair_count(void)
788{
789 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
790}
791
792static int
793_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
794{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200795 if (idx >= opts->key_count) {
796 ERRARG("idx");
797 return -1;
798 } else if (!pub_key && !priv_key) {
799 ERRARG("pub_key and priv_key");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100800 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100801 }
802
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100803 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100804 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100805 }
806 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100807 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100808 }
809
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100810 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100811}
812
Michal Vasko3031aae2016-01-27 16:07:18 +0100813API int
814nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
815{
816 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
817}
818
819API int
820nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
821{
822 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
823}
824
825static void
826_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 +0100827{
828 if (pref < 0) {
829 pref = -1;
830 }
831
832 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100833 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100834 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100835 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100836 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100837 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100838 }
839}
840
Michal Vasko3031aae2016-01-27 16:07:18 +0100841API void
842nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100843{
Michal Vasko3031aae2016-01-27 16:07:18 +0100844 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
845}
846
847API void
848nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
849{
850 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
851}
852
853static int16_t
854_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
855{
856 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100857
858 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100859 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100860 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100861 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100862 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100863 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100864 }
865
866 return pref;
867}
868
Michal Vasko3031aae2016-01-27 16:07:18 +0100869API int16_t
870nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
871{
872 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
873}
874
875API int16_t
876nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
877{
878 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
879}
880
881static int
882_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
883{
884 if (opts->username) {
885 free(opts->username);
886 }
887 if (username) {
888 opts->username = strdup(username);
889 if (!opts->username) {
890 ERRMEM;
891 return -1;
892 }
893 } else {
894 opts->username = NULL;
895 }
896
897 return 0;
898}
899
900API int
901nc_client_ssh_set_username(const char *username)
902{
903 return _nc_client_ssh_set_username(username, &ssh_opts);
904}
905
906API int
907nc_client_ssh_ch_set_username(const char *username)
908{
909 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
910}
911
Michal Vaskoe22c6732016-01-29 11:03:02 +0100912static const char *
913_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
914{
915 return opts->username;
916}
917
918API const char *
919nc_client_ssh_get_username(void)
920{
921 return _nc_client_ssh_get_username(&ssh_opts);
922}
923
924API const char *
925nc_client_ssh_ch_get_username(void)
926{
927 return _nc_client_ssh_get_username(&ssh_ch_opts);
928}
929
Michal Vasko3031aae2016-01-27 16:07:18 +0100930API int
931nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
932{
933 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
934}
935
936API int
937nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
938{
939 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
940}
941
Michal Vasko8e2f4a62016-02-01 15:59:48 +0100942/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +0100943 * Host, port, username, and a connected socket is expected to be set.
944 */
945static int
Michal Vasko0190bc32016-03-02 15:47:49 +0100946connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100947{
Michal Vasko0190bc32016-03-02 15:47:49 +0100948 int j, ret_auth, userauthlist, ret, elapsed_usec = 0;
Michal Vasko235efdc2015-12-17 12:05:04 +0100949 NC_SSH_AUTH_TYPE auth;
Michal Vasko0190bc32016-03-02 15:47:49 +0100950 int16_t pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100951 const char* prompt;
952 char *s, *answer, echo;
953 ssh_key pubkey, privkey;
954 ssh_session ssh_sess;
955
956 ssh_sess = session->ti.libssh.session;
957
Michal Vasko0190bc32016-03-02 15:47:49 +0100958 while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) {
959 usleep(NC_TIMEOUT_STEP);
960 elapsed_usec += NC_TIMEOUT_STEP;
961 if (elapsed_usec / 1000 >= NC_TRANSPORT_TIMEOUT) {
962 break;
963 }
964 }
965 if (ret == SSH_AGAIN) {
966 ERR("SSH connect timeout.");
967 return 0;
968 } else if (ret != SSH_OK) {
969 ERR("Starting the SSH session failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100970 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100971 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100972 }
973
Michal Vaskoef112d72016-02-18 13:28:25 +0100974 if (opts->auth_hostkey_check(session->host, ssh_sess)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100975 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +0100976 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100977 }
978
Michal Vasko0190bc32016-03-02 15:47:49 +0100979 elapsed_usec = 0;
980 while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) {
981 usleep(NC_TIMEOUT_STEP);
982 elapsed_usec += NC_TIMEOUT_STEP;
983 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
984 break;
985 }
986 }
987 if (ret_auth == SSH_AUTH_AGAIN) {
988 ERR("Request authentication methods timeout.");
989 return 0;
990 } else if (ret_auth == SSH_AUTH_ERROR) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100991 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100992 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100993 }
994
995 /* check what authentication methods are available */
996 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +0100997
998 /* remove those disabled */
Michal Vasko30e2c872016-02-18 10:03:21 +0100999 if (opts->auth_pref[0].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001000 VRB("Interactive SSH authentication method was disabled.");
1001 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001002 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001003 if (opts->auth_pref[1].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001004 VRB("Password SSH authentication method was disabled.");
1005 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001006 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001007 if (opts->auth_pref[2].value < 0) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001008 VRB("Publickey SSH authentication method was disabled.");
1009 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001010 }
1011
Michal Vasko0190bc32016-03-02 15:47:49 +01001012 do {
Michal Vasko235efdc2015-12-17 12:05:04 +01001013 auth = 0;
1014 pref = 0;
1015 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
1016 auth = NC_SSH_AUTH_INTERACTIVE;
Michal Vasko30e2c872016-02-18 10:03:21 +01001017 pref = opts->auth_pref[0].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001018 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001019 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (opts->auth_pref[1].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001020 auth = NC_SSH_AUTH_PASSWORD;
Michal Vasko30e2c872016-02-18 10:03:21 +01001021 pref = opts->auth_pref[1].value;
Michal Vasko235efdc2015-12-17 12:05:04 +01001022 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001023 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (opts->auth_pref[2].value > pref)) {
Michal Vasko235efdc2015-12-17 12:05:04 +01001024 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001025 }
1026
Michal Vasko235efdc2015-12-17 12:05:04 +01001027 if (!auth) {
Michal Vaskod083db62016-01-19 10:31:29 +01001028 ERR("Unable to authenticate to the remote server (no supported authentication methods left).");
Michal Vasko0190bc32016-03-02 15:47:49 +01001029 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001030 }
1031
1032 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +01001033 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001034 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +01001035 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
1036
Michal Vaskoef578332016-01-25 13:20:09 +01001037 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Michal Vasko30e2c872016-02-18 10:03:21 +01001038 s = opts->auth_password(session->username, session->host);
Michal Vasko0190bc32016-03-02 15:47:49 +01001039
1040 elapsed_usec = 0;
1041 while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) {
1042 usleep(NC_TIMEOUT_STEP);
1043 elapsed_usec += NC_TIMEOUT_STEP;
1044 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1045 break;
1046 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001047 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001048 memset(s, 0, strlen(s));
Michal Vasko7b62fed2015-10-26 15:39:46 +01001049 free(s);
1050 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001051
Michal Vasko7b62fed2015-10-26 15:39:46 +01001052 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +01001053 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
1054
Michal Vaskod083db62016-01-19 10:31:29 +01001055 VRB("Keyboard-interactive authentication.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001056
1057 elapsed_usec = 0;
1058 while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO)
1059 || (ret_auth == SSH_AUTH_AGAIN)) {
1060 if (ret_auth == SSH_AUTH_AGAIN) {
1061 usleep(NC_TIMEOUT_STEP);
1062 elapsed_usec += NC_TIMEOUT_STEP;
1063 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1064 break;
1065 }
1066 continue;
1067 }
1068
Michal Vasko7b62fed2015-10-26 15:39:46 +01001069 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
1070 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
Michal Vasko0190bc32016-03-02 15:47:49 +01001071 if (!prompt) {
1072 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001073 break;
1074 }
Michal Vasko8bf28d12016-02-24 13:29:42 +01001075
1076 /* libssh BUG - echo is always 1 for some reason, assume always 0 */
1077 echo = 0;
1078
Michal Vasko30e2c872016-02-18 10:03:21 +01001079 answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
1080 ssh_userauth_kbdint_getinstruction(ssh_sess),
1081 prompt, echo);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001082 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
1083 free(answer);
Michal Vasko0190bc32016-03-02 15:47:49 +01001084 ret_auth = SSH_AUTH_ERROR;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001085 break;
1086 }
1087 free(answer);
1088 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001089 if (ret_auth == SSH_AUTH_ERROR) {
1090 break;
1091 }
1092 elapsed_usec = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001093 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001094 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001095
Michal Vasko206d3b12015-12-04 11:08:42 +01001096 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +01001097 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
1098
Michal Vaskod083db62016-01-19 10:31:29 +01001099 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001100
1101 /* if publickeys path not provided, we cannot continue */
Michal Vasko30e2c872016-02-18 10:03:21 +01001102 if (!opts->key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001103 VRB("No key pair specified.");
1104 break;
1105 }
1106
Michal Vasko30e2c872016-02-18 10:03:21 +01001107 for (j = 0; j < opts->key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +01001108 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko30e2c872016-02-18 10:03:21 +01001109 opts->keys[j].privkey_crypt ? "password-protected " : "", opts->keys[j].privkey_path,
1110 opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001111
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001112 ret = ssh_pki_import_pubkey_file(opts->keys[j].pubkey_path, &pubkey);
1113 if (ret == SSH_EOF) {
1114 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].pubkey_path);
1115 continue;
1116 } else if (ret == SSH_ERROR) {
1117 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001118 continue;
1119 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001120
1121 elapsed_usec = 0;
1122 while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) {
1123 usleep(NC_TIMEOUT_STEP);
1124 elapsed_usec += NC_TIMEOUT_STEP;
1125 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1126 ssh_key_free(pubkey);
1127 break;
1128 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001129 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001130 ssh_key_free(pubkey);
1131
1132 if (ret_auth == SSH_AUTH_DENIED) {
1133 continue;
1134 } else if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001135 break;
1136 }
1137
Michal Vasko30e2c872016-02-18 10:03:21 +01001138 if (opts->keys[j].privkey_crypt) {
1139 s = opts->auth_privkey_passphrase(opts->keys[j].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001140 } else {
1141 s = NULL;
1142 }
1143
Michal Vasko0190bc32016-03-02 15:47:49 +01001144 ret = ssh_pki_import_privkey_file(opts->keys[j].privkey_path, s, NULL, NULL, &privkey);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001145 if (s) {
1146 memset(s, 0, strlen(s));
1147 free(s);
1148 }
Michal Vasko7abcdeb2016-05-30 15:27:00 +02001149 if (ret == SSH_EOF) {
1150 WRN("Failed to import the key \"%s\" (File access problem).", opts->keys[j].privkey_path);
1151 continue;
1152 } else if (ret == SSH_ERROR) {
1153 WRN("Failed to import the key \"%s\" (SSH error).", opts->keys[j].privkey_path);
Michal Vasko0190bc32016-03-02 15:47:49 +01001154 continue;
1155 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001156
Michal Vasko0190bc32016-03-02 15:47:49 +01001157 elapsed_usec = 0;
1158 while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) {
1159 usleep(NC_TIMEOUT_STEP);
1160 elapsed_usec += NC_TIMEOUT_STEP;
1161 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1162 ssh_key_free(privkey);
1163 break;
1164 }
1165 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001166 ssh_key_free(privkey);
1167
Michal Vasko0190bc32016-03-02 15:47:49 +01001168 if (ret_auth != SSH_AUTH_DENIED) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001169 break;
1170 }
1171 }
1172 break;
1173 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001174
Michal Vasko0190bc32016-03-02 15:47:49 +01001175 switch (ret_auth) {
1176 case SSH_AUTH_AGAIN:
1177 ERR("Authentication response timeout.");
1178 return 0;
1179 case SSH_AUTH_ERROR:
1180 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
1181 return -1;
1182 case SSH_AUTH_DENIED:
1183 WRN("Authentication denied.");
1184 break;
1185 case SSH_AUTH_PARTIAL:
1186 VRB("Partial authentication success.");
1187 break;
1188 case SSH_AUTH_SUCCESS:
1189 VRB("Authentication successful.");
1190 break;
1191 case SSH_AUTH_INFO:
1192 ERRINT;
1193 return -1;
1194 }
1195 } while (ret_auth != SSH_AUTH_SUCCESS);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001196
Michal Vasko0190bc32016-03-02 15:47:49 +01001197 return 1;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001198}
1199
1200/* Open new SSH channel and request the 'netconf' subsystem.
1201 * SSH connection is expected to be established.
1202 */
1203static int
Michal Vasko0190bc32016-03-02 15:47:49 +01001204open_netconf_channel(struct nc_session *session, int timeout)
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001205{
1206 ssh_session ssh_sess;
Michal Vasko0190bc32016-03-02 15:47:49 +01001207 int ret, elapsed_usec = 0;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001208
1209 ssh_sess = session->ti.libssh.session;
1210
1211 if (!ssh_is_connected(ssh_sess)) {
1212 ERR("SSH session not connected.");
1213 return -1;
1214 }
1215
1216 if (session->ti.libssh.channel) {
1217 ERR("SSH channel already created.");
1218 return -1;
1219 }
1220
Michal Vasko7b62fed2015-10-26 15:39:46 +01001221 /* open a channel */
1222 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
Michal Vasko0190bc32016-03-02 15:47:49 +01001223 while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) {
1224 usleep(NC_TIMEOUT_STEP);
1225 elapsed_usec += NC_TIMEOUT_STEP;
1226 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1227 break;
1228 }
1229 }
1230 if (ret == SSH_AGAIN) {
1231 ERR("Opening an SSH channel timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001232 ssh_channel_free(session->ti.libssh.channel);
1233 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001234 return 0;
1235 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001236 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001237 ssh_channel_free(session->ti.libssh.channel);
1238 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001239 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001240 }
1241
1242 /* execute the NETCONF subsystem on the channel */
Michal Vasko0190bc32016-03-02 15:47:49 +01001243 elapsed_usec = 0;
1244 while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) {
1245 usleep(NC_TIMEOUT_STEP);
1246 elapsed_usec += NC_TIMEOUT_STEP;
1247 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1248 break;
1249 }
1250 }
1251 if (ret == SSH_AGAIN) {
1252 ERR("Starting the \"netconf\" SSH subsystem timeout elapsed.");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001253 ssh_channel_free(session->ti.libssh.channel);
1254 session->ti.libssh.channel = NULL;
Michal Vasko0190bc32016-03-02 15:47:49 +01001255 return 0;
1256 } else if (ret == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001257 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko0190bc32016-03-02 15:47:49 +01001258 ssh_channel_free(session->ti.libssh.channel);
1259 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001260 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001261 }
1262
Michal Vasko0190bc32016-03-02 15:47:49 +01001263 return 1;
Radek Krejciac6d3472015-10-22 15:47:18 +02001264}
1265
Michal Vasko30e2c872016-02-18 10:03:21 +01001266static struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001267_nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_client_ssh_opts *opts, int timeout)
Michal Vasko30e2c872016-02-18 10:03:21 +01001268{
1269 char *host = NULL, *username = NULL;
1270 unsigned short port = 0;
1271 int sock;
1272 struct passwd *pw;
1273 struct nc_session *session = NULL;
1274
1275 if (!ssh_session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001276 ERRARG("ssh_session");
Michal Vasko30e2c872016-02-18 10:03:21 +01001277 return NULL;
1278 }
1279
1280 /* prepare session structure */
1281 session = calloc(1, sizeof *session);
1282 if (!session) {
1283 ERRMEM;
1284 return NULL;
1285 }
1286 session->status = NC_STATUS_STARTING;
1287 session->side = NC_CLIENT;
1288
1289 /* transport lock */
1290 session->ti_lock = malloc(sizeof *session->ti_lock);
1291 if (!session->ti_lock) {
1292 ERRMEM;
1293 goto fail;
1294 }
1295 pthread_mutex_init(session->ti_lock, NULL);
1296
1297 session->ti_type = NC_TI_LIBSSH;
1298 session->ti.libssh.session = ssh_session;
1299
1300 /* was port set? */
1301 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1302
1303 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
1304 /*
1305 * There is no file descriptor (detected based on the host, there is no way to check
1306 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
1307 */
1308
1309 /* remember host */
1310 host = strdup("localhost");
Michal Vasko4eb3c312016-03-01 14:09:37 +01001311 if (!host) {
1312 ERRMEM;
1313 goto fail;
1314 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001315 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
1316
1317 /* create and connect socket */
1318 sock = nc_sock_connect(host, port);
1319 if (sock == -1) {
1320 goto fail;
1321 }
1322 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001323 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko30e2c872016-02-18 10:03:21 +01001324 }
1325
1326 /* was username set? */
1327 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1328
1329 if (!ssh_is_connected(ssh_session)) {
1330 /*
1331 * We are connected, but not SSH authenticated. (Transport layer)
1332 */
1333
1334 /* remember username */
1335 if (!username) {
1336 if (!opts->username) {
1337 pw = getpwuid(getuid());
1338 if (!pw) {
1339 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1340 goto fail;
1341 }
1342 username = strdup(pw->pw_name);
1343 } else {
1344 username = strdup(opts->username);
1345 }
Michal Vasko4eb3c312016-03-01 14:09:37 +01001346 if (!username) {
1347 ERRMEM;
1348 goto fail;
1349 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001350 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
1351 }
1352
1353 /* connect and authenticate SSH session */
1354 session->host = host;
1355 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001356 if (connect_ssh_session(session, opts, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001357 goto fail;
1358 }
1359 }
1360
1361 /*
1362 * Almost done, open a netconf channel. (Transport layer / application layer)
1363 */
Michal Vasko0190bc32016-03-02 15:47:49 +01001364 if (open_netconf_channel(session, timeout) != 1) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001365 goto fail;
1366 }
1367
1368 /*
1369 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
1370 */
1371
1372 /* assign context (dicionary needed for handshake) */
1373 if (!ctx) {
1374 if (client_opts.schema_searchpath) {
1375 ctx = ly_ctx_new(client_opts.schema_searchpath);
1376 } else {
1377 ctx = ly_ctx_new(SCHEMAS_DIR);
1378 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +01001379 /* definitely should not happen, but be ready */
1380 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
1381 /* that's just it */
1382 goto fail;
1383 }
Michal Vasko30e2c872016-02-18 10:03:21 +01001384 } else {
1385 session->flags |= NC_SESSION_SHAREDCTX;
1386 }
1387 session->ctx = ctx;
1388
1389 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001390 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko30e2c872016-02-18 10:03:21 +01001391 goto fail;
1392 }
1393 session->status = NC_STATUS_RUNNING;
1394
1395 if (nc_ctx_check_and_fill(session) == -1) {
1396 goto fail;
1397 }
1398
1399 /* store information into the dictionary */
1400 if (host) {
1401 session->host = lydict_insert_zc(ctx, host);
1402 }
1403 if (port) {
1404 session->port = port;
1405 }
1406 if (username) {
1407 session->username = lydict_insert_zc(ctx, username);
1408 }
1409
1410 return session;
1411
1412fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001413 nc_session_free(session, NULL);
Michal Vasko30e2c872016-02-18 10:03:21 +01001414 return NULL;
1415}
1416
Radek Krejciac6d3472015-10-22 15:47:18 +02001417API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001418nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001419{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001420 const long timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001421 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001422 uint32_t port_uint;
Michal Vasko3031aae2016-01-27 16:07:18 +01001423 char *username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001424 struct passwd *pw;
1425 struct nc_session *session = NULL;
1426
1427 /* process parameters */
1428 if (!host || strisempty(host)) {
1429 host = "localhost";
1430 }
1431
1432 if (!port) {
1433 port = NC_PORT_SSH;
1434 }
Michal Vasko55fded62016-02-02 12:19:34 +01001435 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001436
Michal Vasko3031aae2016-01-27 16:07:18 +01001437 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001438 pw = getpwuid(getuid());
1439 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001440 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1441 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001442 } else {
1443 username = pw->pw_name;
1444 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001445 } else {
1446 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001447 }
1448
1449 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001450 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001451 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001452 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001453 return NULL;
1454 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001455 session->status = NC_STATUS_STARTING;
1456 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +02001457
Michal Vasko7b62fed2015-10-26 15:39:46 +01001458 /* transport lock */
1459 session->ti_lock = malloc(sizeof *session->ti_lock);
1460 if (!session->ti_lock) {
1461 ERRMEM;
1462 goto fail;
1463 }
1464 pthread_mutex_init(session->ti_lock, NULL);
1465
1466 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001467 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001468 session->ti.libssh.session = ssh_new();
1469 if (!session->ti.libssh.session) {
1470 ERR("Unable to initialize SSH session.");
1471 goto fail;
1472 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001473
Michal Vasko7b62fed2015-10-26 15:39:46 +01001474 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001475 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001476 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001477 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001478 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko086311b2016-01-08 09:53:11 +01001479 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS,
1480 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1481 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1482 /* ecdsa is probably not supported... */
1483 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1484 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001485
1486 /* create and assign communication socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001487 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001488 if (sock == -1) {
1489 goto fail;
1490 }
1491 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001492 ssh_set_blocking(session->ti.libssh.session, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001493
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001494 /* temporarily, for session connection */
1495 session->host = host;
1496 session->username = username;
Michal Vasko0190bc32016-03-02 15:47:49 +01001497 if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1)
1498 || (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001499 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001500 }
1501
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001502 /* assign context (dicionary needed for handshake) */
1503 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001504 if (client_opts.schema_searchpath) {
1505 ctx = ly_ctx_new(client_opts.schema_searchpath);
1506 } else {
1507 ctx = ly_ctx_new(SCHEMAS_DIR);
1508 }
Michal Vaskoe035b8e2016-03-11 10:10:03 +01001509 /* definitely should not happen, but be ready */
1510 if (!ctx && !(ctx = ly_ctx_new(NULL))) {
1511 /* that's just it */
1512 goto fail;
1513 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001514 } else {
1515 session->flags |= NC_SESSION_SHAREDCTX;
1516 }
1517 session->ctx = ctx;
1518
Radek Krejciac6d3472015-10-22 15:47:18 +02001519 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001520 if (nc_handshake(session) != NC_MSG_HELLO) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001521 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001522 }
Michal Vaskoad611702015-12-03 13:41:51 +01001523 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001524
Michal Vaskoef578332016-01-25 13:20:09 +01001525 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001526 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001527 }
1528
1529 /* store information into the dictionary */
1530 session->host = lydict_insert(ctx, host, 0);
1531 session->port = port;
1532 session->username = lydict_insert(ctx, username, 0);
1533
Radek Krejciac6d3472015-10-22 15:47:18 +02001534 return session;
1535
Michal Vasko7b62fed2015-10-26 15:39:46 +01001536fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001537 nc_session_free(session, NULL);
Radek Krejciac6d3472015-10-22 15:47:18 +02001538 return NULL;
1539}
1540
1541API struct nc_session *
1542nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1543{
Michal Vasko0190bc32016-03-02 15:47:49 +01001544 return _nc_connect_libssh(ssh_session, ctx, &ssh_opts, NC_TRANSPORT_TIMEOUT);
Radek Krejciac6d3472015-10-22 15:47:18 +02001545}
1546
1547API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001548nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001549{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001550 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001551
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001552 if (!session) {
Michal Vasko45e53ae2016-04-07 11:46:03 +02001553 ERRARG("session");
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001554 return NULL;
1555 }
1556
Michal Vasko7b62fed2015-10-26 15:39:46 +01001557 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001558 new_session = calloc(1, sizeof *new_session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001559 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001560 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001561 return NULL;
1562 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001563 new_session->status = NC_STATUS_STARTING;
1564 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001565
1566 /* share some parameters including the session lock */
1567 new_session->ti_type = NC_TI_LIBSSH;
1568 new_session->ti_lock = session->ti_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001569 new_session->ti.libssh.session = session->ti.libssh.session;
1570
1571 /* create the channel safely */
1572 pthread_mutex_lock(new_session->ti_lock);
1573
1574 /* open a channel */
Michal Vasko0190bc32016-03-02 15:47:49 +01001575 if (open_netconf_channel(new_session, NC_TRANSPORT_TIMEOUT) != 1) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001576 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001577 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001578
1579 /* assign context (dicionary needed for handshake) */
1580 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001581 if (client_opts.schema_searchpath) {
1582 ctx = ly_ctx_new(client_opts.schema_searchpath);
1583 } else {
1584 ctx = ly_ctx_new(SCHEMAS_DIR);
1585 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001586 } else {
Michal Vasko56b5bf72016-01-19 11:20:35 +01001587 new_session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001588 }
Michal Vasko56b5bf72016-01-19 11:20:35 +01001589 new_session->ctx = ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001590
Michal Vasko7b62fed2015-10-26 15:39:46 +01001591 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001592 if (nc_handshake(new_session) != NC_MSG_HELLO) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001593 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001594 }
Michal Vaskoad611702015-12-03 13:41:51 +01001595 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001596
Michal Vasko56b5bf72016-01-19 11:20:35 +01001597 pthread_mutex_unlock(new_session->ti_lock);
1598
Michal Vaskoef578332016-01-25 13:20:09 +01001599 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001600 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001601 }
1602
1603 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001604 new_session->host = lydict_insert(ctx, session->host, 0);
1605 new_session->port = session->port;
1606 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001607
1608 /* append to the session ring list */
1609 if (!session->ti.libssh.next) {
1610 session->ti.libssh.next = new_session;
1611 new_session->ti.libssh.next = session;
1612 } else {
1613 ptr = session->ti.libssh.next;
1614 session->ti.libssh.next = new_session;
1615 new_session->ti.libssh.next = ptr;
1616 }
1617
1618 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001619
1620fail:
Michal Vaskoe1a64ec2016-03-01 12:21:58 +01001621 nc_session_free(new_session, NULL);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001622 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001623}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001624
Michal Vasko3031aae2016-01-27 16:07:18 +01001625struct nc_session *
Michal Vasko0190bc32016-03-02 15:47:49 +01001626nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001627{
Michal Vasko1f0563a2016-03-31 08:38:44 +02001628 const long ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko3031aae2016-01-27 16:07:18 +01001629 struct passwd *pw;
Michal Vasko30e2c872016-02-18 10:03:21 +01001630 struct nc_session *session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001631 ssh_session sess;
1632
Michal Vasko80cad7f2015-12-08 14:42:27 +01001633 sess = ssh_new();
1634 if (!sess) {
1635 ERR("Unable to initialize an SSH session.");
1636 close(sock);
1637 return NULL;
1638 }
1639
1640 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko0190bc32016-03-02 15:47:49 +01001641 ssh_set_blocking(sess, 0);
Michal Vasko3031aae2016-01-27 16:07:18 +01001642 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001643 ssh_options_set(sess, SSH_OPTIONS_PORT, &port);
1644 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001645 if (!ssh_ch_opts.username) {
1646 pw = getpwuid(getuid());
1647 if (!pw) {
1648 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1649 return NULL;
1650 }
1651 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1652 } else {
1653 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001654 }
Michal Vasko086311b2016-01-08 09:53:11 +01001655 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1656 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1657 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1658 /* ecdsa is probably not supported... */
1659 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1660 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001661
Michal Vasko0190bc32016-03-02 15:47:49 +01001662 session = _nc_connect_libssh(sess, ctx, &ssh_ch_opts, timeout);
Michal Vasko4282fae2016-02-18 10:03:42 +01001663 if (session) {
1664 session->flags |= NC_SESSION_CALLHOME;
1665 }
1666
Michal Vasko30e2c872016-02-18 10:03:21 +01001667 return session;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001668}