blob: d36a13a708338e0c9ceb7cf54e91f5b393199339 [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 Vasko3031aae2016-01-27 16:07:18 +010053static struct nc_client_ssh_opts ssh_opts = {
Michal Vasko206d3b12015-12-04 11:08:42 +010054 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}}
Michal Vasko7b62fed2015-10-26 15:39:46 +010055};
Radek Krejciac6d3472015-10-22 15:47:18 +020056
Michal Vasko3031aae2016-01-27 16:07:18 +010057static struct nc_client_ssh_opts ssh_ch_opts = {
58 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}}
59};
60
Michal Vaskoe22c6732016-01-29 11:03:02 +010061static void
62_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +010063{
64 int i;
65
Michal Vaskoe22c6732016-01-29 11:03:02 +010066 for (i = 0; i < opts->key_count; ++i) {
67 free(opts->keys[i].pubkey_path);
68 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +010069 }
Michal Vaskoe22c6732016-01-29 11:03:02 +010070 free(opts->keys);
71 free(opts->username);
72}
Michal Vasko990089e2015-10-27 15:05:25 +010073
Michal Vaskoe22c6732016-01-29 11:03:02 +010074API void
75nc_client_ssh_destroy_opts(void)
76{
77 _nc_client_ssh_destroy_opts(&ssh_opts);
78 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +010079}
80
Michal Vasko7b62fed2015-10-26 15:39:46 +010081static char *
82sshauth_password(const char *username, const char *hostname)
Radek Krejciac6d3472015-10-22 15:47:18 +020083{
Michal Vasko7b62fed2015-10-26 15:39:46 +010084 char *buf, *newbuf;
85 int buflen = 1024, len = 0;
86 char c = 0;
87 struct termios newterm, oldterm;
88 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +020089
Michal Vasko7b62fed2015-10-26 15:39:46 +010090 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +010091 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +010092 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +020093 }
94
Michal Vasko7b62fed2015-10-26 15:39:46 +010095 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +010096 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +010097 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +010098 return NULL;
99 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200100
Michal Vasko7b62fed2015-10-26 15:39:46 +0100101 fprintf(tty, "%s@%s password: ", username, hostname);
102 fflush(tty);
Radek Krejciac6d3472015-10-22 15:47:18 +0200103
Michal Vasko7b62fed2015-10-26 15:39:46 +0100104 /* system("stty -echo"); */
105 newterm = oldterm;
106 newterm.c_lflag &= ~ECHO;
107 newterm.c_lflag &= ~ICANON;
108 tcflush(fileno(tty), TCIFLUSH);
109 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100110 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100111 fclose(tty);
112 return NULL;
113 }
114
115 buf = malloc(buflen * sizeof *buf);
116 if (!buf) {
117 ERRMEM;
118 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100119 return NULL;
120 }
121
122 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
123 if (len >= buflen - 1) {
124 buflen *= 2;
125 newbuf = realloc(buf, buflen * sizeof *newbuf);
126 if (!newbuf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100127 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100128
129 /* remove content of the buffer */
130 memset(buf, 0, len);
131 free(buf);
132
133 /* restore terminal settings */
134 if (tcsetattr(fileno(tty), TCSANOW, &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100135 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100136 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100137 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100138 return NULL;
139 } else {
140 buf = newbuf;
141 }
142 }
143 buf[len++] = c;
144 }
145 buf[len++] = 0; /* terminating null byte */
146
147 /* system ("stty echo"); */
148 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100149 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100150 /*
151 * terminal probably still hides input characters, but we have password
152 * and anyway we are unable to set terminal to the previous state, so
153 * just continue
154 */
155 }
156 fprintf(tty, "\n");
157
158 fclose(tty);
159 return buf;
160}
161
162static char *
163sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo)
164{
165 unsigned int buflen = 8, response_len;
166 char c = 0;
167 struct termios newterm, oldterm;
168 char *newtext, *response;
169 FILE *tty;
170
171 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100172 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100173 return NULL;
174 }
175
176 if (tcgetattr(fileno(tty), &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100177 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100178 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100179 return NULL;
180 }
181
182 if (auth_name && (!fwrite(auth_name, sizeof(char), strlen(auth_name), tty)
183 || !fwrite("\n", sizeof(char), 1, tty))) {
184 ERR("Writing the auth method name into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100185 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100186 return NULL;
187 }
188
189 if (instruction && (!fwrite(instruction, sizeof(char), strlen(instruction), tty)
190 || !fwrite("\n", sizeof(char), 1, tty))) {
191 ERR("Writing the instruction into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100192 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100193 return NULL;
194 }
195
196 if (!fwrite(prompt, sizeof(char), strlen(prompt), tty)) {
197 ERR("Writing the authentication prompt into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100198 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100199 return NULL;
200 }
201 fflush(tty);
202 if (!echo) {
203 /* system("stty -echo"); */
204 newterm = oldterm;
205 newterm.c_lflag &= ~ECHO;
206 tcflush(fileno(tty), TCIFLUSH);
207 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100208 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100209 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100210 return NULL;
211 }
212 }
213
214 response = malloc(buflen * sizeof *response);
215 response_len = 0;
216 if (!response) {
217 ERRMEM;
218 /* restore terminal settings */
219 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100220 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100221 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100222 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100223 return NULL;
224 }
225
226 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
227 if (response_len >= buflen - 1) {
228 buflen *= 2;
229 newtext = realloc(response, buflen * sizeof *newtext);
230 if (!newtext) {
Michal Vaskod083db62016-01-19 10:31:29 +0100231 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100232 free(response);
233
234 /* restore terminal settings */
235 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100236 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100237 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100238 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100239 return NULL;
240 } else {
241 response = newtext;
242 }
243 }
244 response[response_len++] = c;
245 }
246 /* terminating null byte */
247 response[response_len++] = '\0';
248
249 /* system ("stty echo"); */
250 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100251 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100252 /*
253 * terminal probably still hides input characters, but we have password
254 * and anyway we are unable to set terminal to the previous state, so
255 * just continue
256 */
257 }
258
259 fprintf(tty, "\n");
260 fclose(tty);
261 return response;
262}
263
264static char *
265sshauth_passphrase(const char* privkey_path)
266{
267 char c, *buf, *newbuf;
268 int buflen = 1024, len = 0;
269 struct termios newterm, oldterm;
270 FILE *tty;
271
272 buf = malloc(buflen * sizeof *buf);
273 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100274 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100275 return NULL;
276 }
277
278 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100279 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100280 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100281 }
282
283 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100284 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100285 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100286 }
287
288 fprintf(tty, "Enter passphrase for the key '%s':", privkey_path);
289 fflush(tty);
290
291 /* system("stty -echo"); */
292 newterm = oldterm;
293 newterm.c_lflag &= ~ECHO;
294 newterm.c_lflag &= ~ICANON;
295 tcflush(fileno(tty), TCIFLUSH);
296 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100297 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100298 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100299 }
300
301 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
302 if (len >= buflen - 1) {
303 buflen *= 2;
304 newbuf = realloc(buf, buflen * sizeof *newbuf);
305 if (!newbuf) {
306 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100307 /* restore terminal settings */
308 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100309 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100310 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100311 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100312 }
313 buf = newbuf;
314 }
315 buf[len++] = (char)c;
316 }
317 buf[len++] = 0; /* terminating null byte */
318
319 /* system ("stty echo"); */
320 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100321 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100322 /*
323 * terminal probably still hides input characters, but we have password
324 * and anyway we are unable to set terminal to the previous state, so
325 * just continue
326 */
327 }
328 fprintf(tty, "\n");
329
330 fclose(tty);
331 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100332
333fail:
334 free(buf);
Michal Vaskode581a82016-01-22 13:15:35 +0100335 if (tty) {
336 fclose(tty);
337 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100338 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100339}
340
Michal Vasko7b62fed2015-10-26 15:39:46 +0100341#ifdef ENABLE_DNSSEC
342
343/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
344/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
345static int
346sshauth_hostkey_hash_dnssec_check(const char *hostname, const char *sha1hash, int type, int alg) {
347 ns_msg handle;
348 ns_rr rr;
349 val_status_t val_status;
350 const unsigned char* rdata;
351 unsigned char buf[4096];
352 int buf_len = 4096;
353 int ret = 0, i, j, len;
354
355 /* class 1 - internet, type 44 - SSHFP */
356 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
357
358 if ((len < 0) || !val_istrusted(val_status)) {
359 ret = 2;
360 goto finish;
361 }
362
363 if (ns_initparse(buf, len, &handle) < 0) {
364 ERR("Failed to initialize DNSSEC response parser.");
365 ret = 2;
366 goto finish;
367 }
368
369 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
370 ERR("DNSSEC query returned %d.", i);
371 ret = 2;
372 goto finish;
373 }
374
375 if (!libsres_msg_getflag(handle, ns_f_ad)) {
376 /* response not secured by DNSSEC */
377 ret = 1;
378 }
379
380 /* query section */
381 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
382 ERROR("DNSSEC query section parser fail.");
383 ret = 2;
384 goto finish;
385 }
386
387 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
388 ERROR("DNSSEC query in the answer does not match the original query.");
389 ret = 2;
390 goto finish;
391 }
392
393 /* answer section */
394 i = 0;
395 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
396 if (ns_rr_type(rr) != 44) {
397 ++i;
398 continue;
399 }
400
401 rdata = ns_rr_rdata(rr);
402 if (rdata[0] != type) {
403 ++i;
404 continue;
405 }
406 if (rdata[1] != alg) {
407 ++i;
408 continue;
409 }
410
411 /* we found the correct SSHFP entry */
412 rdata += 2;
413 for (j = 0; j < 20; ++j) {
414 if (rdata[j] != (unsigned char)sha1hash[j]) {
415 ret = 2;
416 goto finish;
417 }
418 }
419
420 /* server fingerprint is supported by a DNS entry,
421 * we have already determined if DNSSEC was used or not
422 */
423 goto finish;
424 }
425
426 /* no match */
427 ret = 2;
428
429finish:
430 val_free_validator_state();
431 return ret;
432}
433
Michal Vaskoc61c4492016-01-25 11:13:34 +0100434#endif /* ENABLE_DNSSEC */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100435
436static int
437sshauth_hostkey_check(const char *hostname, ssh_session session)
438{
439 char *hexa;
440 int c, state, ret;
441 ssh_key srv_pubkey;
442 unsigned char *hash_sha1 = NULL;
443 size_t hlen;
444 enum ssh_keytypes_e srv_pubkey_type;
445 char answer[5];
446
447 state = ssh_is_server_known(session);
448
449 ret = ssh_get_publickey(session, &srv_pubkey);
450 if (ret < 0) {
451 ERR("Unable to get server public key.");
Michal Vaskod083db62016-01-19 10:31:29 +0100452 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100453 }
454
455 srv_pubkey_type = ssh_key_type(srv_pubkey);
456 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
457 ssh_key_free(srv_pubkey);
458 if (ret < 0) {
459 ERR("Failed to calculate SHA1 hash of the server public key.");
Michal Vaskod083db62016-01-19 10:31:29 +0100460 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100461 }
462
463 hexa = ssh_get_hexa(hash_sha1, hlen);
464
465 switch (state) {
466 case SSH_SERVER_KNOWN_OK:
467 break; /* ok */
468
469 case SSH_SERVER_KNOWN_CHANGED:
470 ERR("Remote host key changed, the connection will be terminated!");
471 goto fail;
472
473 case SSH_SERVER_FOUND_OTHER:
Michal Vasko086311b2016-01-08 09:53:11 +0100474 WRN("Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
475 goto hostkey_not_known;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100476
477 case SSH_SERVER_FILE_NOT_FOUND:
478 WRN("Could not find the known hosts file.");
Michal Vasko086311b2016-01-08 09:53:11 +0100479 goto hostkey_not_known;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100480
481 case SSH_SERVER_NOT_KNOWN:
Michal Vasko086311b2016-01-08 09:53:11 +0100482hostkey_not_known:
Michal Vasko7b62fed2015-10-26 15:39:46 +0100483#ifdef ENABLE_DNSSEC
484 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
485 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
486 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
487 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
488 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
489 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
490 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
491 }
492
493 /* DNSSEC SSHFP check successful, that's enough */
494 if (!ret) {
Michal Vasko86440232016-02-01 15:59:09 +0100495 VRB("DNSSEC SSHFP check successful.");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100496 ssh_write_knownhost(session);
497 ssh_clean_pubkey_hash(&hash_sha1);
498 ssh_string_free_char(hexa);
Michal Vaskod083db62016-01-19 10:31:29 +0100499 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100500 }
501 }
502#endif
503
504 /* try to get result from user */
505 fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname);
506 fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa);
507
508#ifdef ENABLE_DNSSEC
509 if (ret == 2) {
Michal Vasko86440232016-02-01 15:59:09 +0100510 fprintf(stdout, "No matching host key fingerprint found using DNS.\n");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100511 } else if (ret == 1) {
Michal Vasko86440232016-02-01 15:59:09 +0100512 fprintf(stdout, "Matching host key fingerprint found using DNS.\n");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100513 }
514#endif
515
516 fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? ");
517
518 do {
519 if (fscanf(stdin, "%4s", answer) == EOF) {
520 ERR("fscanf() failed (%s).", strerror(errno));
521 goto fail;
522 }
523 while (((c = getchar()) != EOF) && (c != '\n'));
524
525 fflush(stdin);
526 if (!strcmp("yes", answer)) {
527 /* store the key into the host file */
528 ret = ssh_write_knownhost(session);
Michal Vasko86440232016-02-01 15:59:09 +0100529 if (ret != SSH_OK) {
530 WRN("Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100531 }
532 } else if (!strcmp("no", answer)) {
533 goto fail;
534 } else {
535 fprintf(stdout, "Please type 'yes' or 'no': ");
536 }
537 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
538
539 break;
540
541 case SSH_SERVER_ERROR:
542 ssh_clean_pubkey_hash(&hash_sha1);
543 fprintf(stderr,"%s",ssh_get_error(session));
544 return -1;
545 }
546
547 ssh_clean_pubkey_hash(&hash_sha1);
548 ssh_string_free_char(hexa);
Michal Vaskod083db62016-01-19 10:31:29 +0100549 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100550
551fail:
552 ssh_clean_pubkey_hash(&hash_sha1);
553 ssh_string_free_char(hexa);
Michal Vaskod083db62016-01-19 10:31:29 +0100554 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100555}
556
Michal Vasko3031aae2016-01-27 16:07:18 +0100557static int
558_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 +0100559{
560 int i;
561 FILE *key;
562 char line[128];
563
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100564 if (!pub_key || !priv_key) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100565 ERRARG;
566 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100567 }
568
Michal Vasko3031aae2016-01-27 16:07:18 +0100569 for (i = 0; i < opts->key_count; ++i) {
570 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
571 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100572 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100573 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100574 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100575 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100576 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100577 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100578 continue;
579 }
580
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100581 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100582 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100583 }
584 }
585
Michal Vasko3031aae2016-01-27 16:07:18 +0100586 /* add the keys */
587 ++opts->key_count;
588 opts->keys = realloc(opts->keys, opts->key_count * sizeof *opts->keys);
589 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
590 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
591 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100592
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100593 /* check encryption */
594 if ((key = fopen(priv_key, "r"))) {
595 /* 1st line - key type */
596 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100597 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100598 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100599 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100600 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100601 /* 2nd line - encryption information or key */
602 if (!fgets(line, sizeof line, key)) {
603 fclose(key);
604 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100605 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100606 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100607 fclose(key);
608 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100609 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100610 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100611 }
612
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100613 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100614}
615
616API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100617nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100618{
Michal Vasko3031aae2016-01-27 16:07:18 +0100619 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
620}
621
622API int
623nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
624{
625 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
626}
627
628static int
629_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
630{
631 if (idx >= opts->key_count) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100632 ERRARG;
633 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100634 }
635
Michal Vasko3031aae2016-01-27 16:07:18 +0100636 free(opts->keys[idx].pubkey_path);
637 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100638
Michal Vasko3031aae2016-01-27 16:07:18 +0100639 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100640 if (idx < opts->key_count) {
641 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
642 }
643 if (opts->key_count) {
644 opts->keys = realloc(opts->keys, opts->key_count * sizeof *opts->keys);
645 } else {
646 free(opts->keys);
647 opts->keys = NULL;
648 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100649
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100650 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100651}
652
653API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100654nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100655{
Michal Vasko3031aae2016-01-27 16:07:18 +0100656 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100657}
658
659API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100660nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100661{
Michal Vasko3031aae2016-01-27 16:07:18 +0100662 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
663}
664
665static int
666_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
667{
668 return opts->key_count;
669}
670
671API int
672nc_client_ssh_get_keypair_count(void)
673{
674 return _nc_client_ssh_get_keypair_count(&ssh_opts);
675}
676
677API int
678nc_client_ssh_ch_get_keypair_count(void)
679{
680 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
681}
682
683static int
684_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
685{
686 if ((idx >= opts->key_count) || (!pub_key && !priv_key)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100687 ERRARG;
688 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100689 }
690
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100691 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100692 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100693 }
694 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100695 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100696 }
697
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100698 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100699}
700
Michal Vasko3031aae2016-01-27 16:07:18 +0100701API int
702nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
703{
704 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
705}
706
707API int
708nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
709{
710 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
711}
712
713static void
714_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 +0100715{
716 if (pref < 0) {
717 pref = -1;
718 }
719
720 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100721 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100722 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100723 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100724 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100725 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100726 }
727}
728
Michal Vasko3031aae2016-01-27 16:07:18 +0100729API void
730nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100731{
Michal Vasko3031aae2016-01-27 16:07:18 +0100732 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
733}
734
735API void
736nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
737{
738 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
739}
740
741static int16_t
742_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
743{
744 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100745
746 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100747 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100748 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100749 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100750 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100751 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100752 }
753
754 return pref;
755}
756
Michal Vasko3031aae2016-01-27 16:07:18 +0100757API int16_t
758nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
759{
760 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
761}
762
763API int16_t
764nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
765{
766 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
767}
768
769static int
770_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
771{
772 if (opts->username) {
773 free(opts->username);
774 }
775 if (username) {
776 opts->username = strdup(username);
777 if (!opts->username) {
778 ERRMEM;
779 return -1;
780 }
781 } else {
782 opts->username = NULL;
783 }
784
785 return 0;
786}
787
788API int
789nc_client_ssh_set_username(const char *username)
790{
791 return _nc_client_ssh_set_username(username, &ssh_opts);
792}
793
794API int
795nc_client_ssh_ch_set_username(const char *username)
796{
797 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
798}
799
Michal Vaskoe22c6732016-01-29 11:03:02 +0100800static const char *
801_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
802{
803 return opts->username;
804}
805
806API const char *
807nc_client_ssh_get_username(void)
808{
809 return _nc_client_ssh_get_username(&ssh_opts);
810}
811
812API const char *
813nc_client_ssh_ch_get_username(void)
814{
815 return _nc_client_ssh_get_username(&ssh_ch_opts);
816}
817
Michal Vasko3031aae2016-01-27 16:07:18 +0100818API int
819nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
820{
821 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
822}
823
824API int
825nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
826{
827 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
828}
829
Michal Vasko8e2f4a62016-02-01 15:59:48 +0100830/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +0100831 * Host, port, username, and a connected socket is expected to be set.
832 */
833static int
Michal Vasko8e2f4a62016-02-01 15:59:48 +0100834connect_ssh_session(struct nc_session *session)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100835{
Michal Vasko235efdc2015-12-17 12:05:04 +0100836 int j, ret_auth, userauthlist;
837 NC_SSH_AUTH_TYPE auth;
838 short int pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100839 const char* prompt;
840 char *s, *answer, echo;
841 ssh_key pubkey, privkey;
842 ssh_session ssh_sess;
843
844 ssh_sess = session->ti.libssh.session;
845
846 if (ssh_connect(ssh_sess) != SSH_OK) {
847 ERR("Starting the SSH session failed (%s)", ssh_get_error(ssh_sess));
848 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100849 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100850 }
851
852 if (sshauth_hostkey_check(session->host, ssh_sess)) {
853 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +0100854 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100855 }
856
857 if ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_ERROR) {
858 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100859 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100860 }
861
862 /* check what authentication methods are available */
863 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +0100864
865 /* remove those disabled */
866 if (ssh_opts.auth_pref[0].value < 0) {
867 VRB("Interactive SSH authentication method was disabled.");
868 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100869 }
Michal Vasko235efdc2015-12-17 12:05:04 +0100870 if (ssh_opts.auth_pref[1].value < 0) {
871 VRB("Password SSH authentication method was disabled.");
872 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100873 }
Michal Vasko235efdc2015-12-17 12:05:04 +0100874 if (ssh_opts.auth_pref[2].value < 0) {
875 VRB("Publickey SSH authentication method was disabled.");
876 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100877 }
878
Michal Vasko235efdc2015-12-17 12:05:04 +0100879 while (ret_auth != SSH_AUTH_SUCCESS) {
880 auth = 0;
881 pref = 0;
882 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
883 auth = NC_SSH_AUTH_INTERACTIVE;
884 pref = ssh_opts.auth_pref[0].value;
885 }
886 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (ssh_opts.auth_pref[1].value > pref)) {
887 auth = NC_SSH_AUTH_PASSWORD;
888 pref = ssh_opts.auth_pref[1].value;
889 }
890 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (ssh_opts.auth_pref[2].value > pref)) {
891 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100892 }
893
Michal Vasko235efdc2015-12-17 12:05:04 +0100894 if (!auth) {
Michal Vaskod083db62016-01-19 10:31:29 +0100895 ERR("Unable to authenticate to the remote server (no supported authentication methods left).");
Michal Vasko235efdc2015-12-17 12:05:04 +0100896 break;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100897 }
898
899 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +0100900 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100901 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +0100902 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
903
Michal Vaskoef578332016-01-25 13:20:09 +0100904 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100905 s = sshauth_password(session->username, session->host);
906 if ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) != SSH_AUTH_SUCCESS) {
907 memset(s, 0, strlen(s));
Michal Vaskod083db62016-01-19 10:31:29 +0100908 VRB("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100909 }
910 free(s);
911 break;
912 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +0100913 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
914
Michal Vaskod083db62016-01-19 10:31:29 +0100915 VRB("Keyboard-interactive authentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100916 while ((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) {
917 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
918 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
919 if (prompt == NULL) {
920 break;
921 }
922 answer = sshauth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
923 ssh_userauth_kbdint_getinstruction(ssh_sess),
924 prompt, echo);
925 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
926 free(answer);
927 break;
928 }
929 free(answer);
930 }
931 }
932
933 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100934 VRB("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100935 }
936
937 break;
Michal Vasko206d3b12015-12-04 11:08:42 +0100938 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +0100939 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
940
Michal Vaskod083db62016-01-19 10:31:29 +0100941 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100942
943 /* if publickeys path not provided, we cannot continue */
944 if (!ssh_opts.key_count) {
945 VRB("No key pair specified.");
946 break;
947 }
948
949 for (j = 0; j < ssh_opts.key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +0100950 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko7b62fed2015-10-26 15:39:46 +0100951 ssh_opts.keys[j].privkey_crypt ? "password-protected " : "", ssh_opts.keys[j].privkey_path,
952 ssh_opts.keys[j].pubkey_path);
953
954 if (ssh_pki_import_pubkey_file(ssh_opts.keys[j].pubkey_path, &pubkey) != SSH_OK) {
955 WRN("Failed to import the key \"%s\".", ssh_opts.keys[j].pubkey_path);
956 continue;
957 }
958 ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey);
959 if ((ret_auth == SSH_AUTH_DENIED) || (ret_auth == SSH_AUTH_PARTIAL)) {
960 ssh_key_free(pubkey);
961 continue;
962 }
963 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100964 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100965 ssh_key_free(pubkey);
966 break;
967 }
968
969 if (ssh_opts.keys[j].privkey_crypt) {
970 s = sshauth_passphrase(ssh_opts.keys[j].privkey_path);
971 } else {
972 s = NULL;
973 }
974
975 if (ssh_pki_import_privkey_file(ssh_opts.keys[j].privkey_path, s, NULL, NULL, &privkey) != SSH_OK) {
976 WRN("Failed to import the key \"%s\".", ssh_opts.keys[j].privkey_path);
977 if (s) {
978 memset(s, 0, strlen(s));
979 free(s);
980 }
981 ssh_key_free(pubkey);
982 continue;
983 }
984
985 if (s) {
986 memset(s, 0, strlen(s));
987 free(s);
988 }
989
990 ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey);
991 ssh_key_free(pubkey);
992 ssh_key_free(privkey);
993
994 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100995 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100996 }
997 if (ret_auth == SSH_AUTH_SUCCESS) {
998 break;
999 }
1000 }
1001 break;
1002 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001003 }
1004
1005 /* check a state of authentication */
1006 if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vaskod083db62016-01-19 10:31:29 +01001007 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001008 }
1009
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001010 return 0;
1011}
1012
1013/* Open new SSH channel and request the 'netconf' subsystem.
1014 * SSH connection is expected to be established.
1015 */
1016static int
1017open_netconf_channel(struct nc_session *session)
1018{
1019 ssh_session ssh_sess;
1020
1021 ssh_sess = session->ti.libssh.session;
1022
1023 if (!ssh_is_connected(ssh_sess)) {
1024 ERR("SSH session not connected.");
1025 return -1;
1026 }
1027
1028 if (session->ti.libssh.channel) {
1029 ERR("SSH channel already created.");
1030 return -1;
1031 }
1032
Michal Vasko7b62fed2015-10-26 15:39:46 +01001033 /* open a channel */
1034 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
1035 if (ssh_channel_open_session(session->ti.libssh.channel) != SSH_OK) {
1036 ssh_channel_free(session->ti.libssh.channel);
1037 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001038 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
1039 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001040 }
1041
1042 /* execute the NETCONF subsystem on the channel */
1043 if (ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf") != SSH_OK) {
1044 ssh_channel_free(session->ti.libssh.channel);
1045 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001046 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
1047 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001048 }
1049
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001050 return 0;
Radek Krejciac6d3472015-10-22 15:47:18 +02001051}
1052
1053API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001054nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001055{
Michal Vaskoc111ac52015-12-08 14:36:36 +01001056 const int timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001057 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001058 uint32_t port_uint;
Michal Vasko3031aae2016-01-27 16:07:18 +01001059 char *username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001060 struct passwd *pw;
1061 struct nc_session *session = NULL;
1062
1063 /* process parameters */
1064 if (!host || strisempty(host)) {
1065 host = "localhost";
1066 }
1067
1068 if (!port) {
1069 port = NC_PORT_SSH;
1070 }
Michal Vasko55fded62016-02-02 12:19:34 +01001071 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001072
Michal Vasko3031aae2016-01-27 16:07:18 +01001073 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001074 pw = getpwuid(getuid());
1075 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001076 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1077 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001078 } else {
1079 username = pw->pw_name;
1080 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001081 } else {
1082 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001083 }
1084
1085 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001086 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001087 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001088 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001089 return NULL;
1090 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001091 session->status = NC_STATUS_STARTING;
1092 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +02001093
Michal Vasko7b62fed2015-10-26 15:39:46 +01001094 /* transport lock */
1095 session->ti_lock = malloc(sizeof *session->ti_lock);
1096 if (!session->ti_lock) {
1097 ERRMEM;
1098 goto fail;
1099 }
1100 pthread_mutex_init(session->ti_lock, NULL);
1101
1102 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001103 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001104 session->ti.libssh.session = ssh_new();
1105 if (!session->ti.libssh.session) {
1106 ERR("Unable to initialize SSH session.");
1107 goto fail;
1108 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001109
Michal Vasko7b62fed2015-10-26 15:39:46 +01001110 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001111 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001112 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001113 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001114 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko086311b2016-01-08 09:53:11 +01001115 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS,
1116 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1117 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1118 /* ecdsa is probably not supported... */
1119 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1120 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001121
1122 /* create and assign communication socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001123 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001124 if (sock == -1) {
1125 goto fail;
1126 }
1127 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1128
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001129 /* temporarily, for session connection */
1130 session->host = host;
1131 session->username = username;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001132 if (connect_ssh_session(session) || open_netconf_channel(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001133 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001134 }
1135
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001136 /* assign context (dicionary needed for handshake) */
1137 if (!ctx) {
1138 ctx = ly_ctx_new(SCHEMAS_DIR);
1139 } else {
1140 session->flags |= NC_SESSION_SHAREDCTX;
1141 }
1142 session->ctx = ctx;
1143
Radek Krejciac6d3472015-10-22 15:47:18 +02001144 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001145 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001146 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001147 }
Michal Vaskoad611702015-12-03 13:41:51 +01001148 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001149
Michal Vaskoef578332016-01-25 13:20:09 +01001150 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001151 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001152 }
1153
1154 /* store information into the dictionary */
1155 session->host = lydict_insert(ctx, host, 0);
1156 session->port = port;
1157 session->username = lydict_insert(ctx, username, 0);
1158
Radek Krejciac6d3472015-10-22 15:47:18 +02001159 return session;
1160
Michal Vasko7b62fed2015-10-26 15:39:46 +01001161fail:
Radek Krejciac6d3472015-10-22 15:47:18 +02001162 nc_session_free(session);
1163 return NULL;
1164}
1165
1166API struct nc_session *
1167nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1168{
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001169 char *host = NULL, *username = NULL;
1170 unsigned short port = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001171 int sock;
1172 struct passwd *pw;
1173 struct nc_session *session = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001174
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001175 if (!ssh_session) {
1176 ERRARG;
1177 return NULL;
1178 }
1179
Michal Vasko7b62fed2015-10-26 15:39:46 +01001180 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001181 session = calloc(1, sizeof *session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001182 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001183 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001184 return NULL;
1185 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001186 session->status = NC_STATUS_STARTING;
1187 session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001188
1189 /* transport lock */
1190 session->ti_lock = malloc(sizeof *session->ti_lock);
1191 if (!session->ti_lock) {
1192 ERRMEM;
1193 goto fail;
1194 }
1195 pthread_mutex_init(session->ti_lock, NULL);
1196
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001197 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001198 session->ti.libssh.session = ssh_session;
1199
Michal Vasko745ff832015-12-08 14:40:29 +01001200 /* was port set? */
1201 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1202
1203 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001204 /*
Michal Vasko745ff832015-12-08 14:40:29 +01001205 * There is no file descriptor (detected based on the host, there is no way to check
1206 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001207 */
1208
Michal Vasko7b62fed2015-10-26 15:39:46 +01001209 /* remember host */
Michal Vasko745ff832015-12-08 14:40:29 +01001210 host = strdup("localhost");
1211 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001212
1213 /* create and connect socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001214 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001215 if (sock == -1) {
1216 goto fail;
1217 }
1218 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1219 }
1220
Michal Vasko745ff832015-12-08 14:40:29 +01001221 /* was username set? */
1222 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1223
Michal Vasko7b62fed2015-10-26 15:39:46 +01001224 if (!ssh_is_connected(ssh_session)) {
1225 /*
1226 * We are connected, but not SSH authenticated. (Transport layer)
1227 */
1228
Michal Vasko7b62fed2015-10-26 15:39:46 +01001229 /* remember username */
1230 if (!username) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001231 if (!ssh_opts.username) {
1232 pw = getpwuid(getuid());
1233 if (!pw) {
1234 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1235 goto fail;
1236 }
1237 username = strdup(pw->pw_name);
1238 } else {
1239 username = strdup(ssh_opts.username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001240 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001241 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001242 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001243
1244 /* authenticate SSH session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001245 session->host = host;
1246 session->username = username;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001247 if (connect_ssh_session(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001248 goto fail;
1249 }
1250 }
1251
1252 /*
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001253 * Almost done, open a netconf channel. (Transport layer / application layer)
1254 */
1255 if (open_netconf_channel(session)) {
1256 goto fail;
1257 }
1258
1259 /*
1260 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001261 */
1262
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001263 /* assign context (dicionary needed for handshake) */
1264 if (!ctx) {
1265 ctx = ly_ctx_new(SCHEMAS_DIR);
1266 } else {
1267 session->flags |= NC_SESSION_SHAREDCTX;
1268 }
1269 session->ctx = ctx;
1270
Michal Vasko7b62fed2015-10-26 15:39:46 +01001271 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001272 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001273 goto fail;
1274 }
Michal Vaskoad611702015-12-03 13:41:51 +01001275 session->status = NC_STATUS_RUNNING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001276
Michal Vaskoef578332016-01-25 13:20:09 +01001277 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001278 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001279 }
1280
1281 /* store information into the dictionary */
1282 if (host) {
1283 session->host = lydict_insert_zc(ctx, host);
1284 }
1285 if (port) {
1286 session->port = port;
1287 }
1288 if (username) {
1289 session->username = lydict_insert_zc(ctx, username);
1290 }
1291
Michal Vasko7b62fed2015-10-26 15:39:46 +01001292 return session;
1293
1294fail:
1295 nc_session_free(session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001296 return NULL;
1297}
1298
1299API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001300nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001301{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001302 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001303
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001304 if (!session) {
1305 ERRARG;
1306 return NULL;
1307 }
1308
Michal Vasko7b62fed2015-10-26 15:39:46 +01001309 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001310 new_session = calloc(1, sizeof *new_session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001311 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001312 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001313 return NULL;
1314 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001315 new_session->status = NC_STATUS_STARTING;
1316 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001317
1318 /* share some parameters including the session lock */
1319 new_session->ti_type = NC_TI_LIBSSH;
1320 new_session->ti_lock = session->ti_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001321 new_session->ti.libssh.session = session->ti.libssh.session;
1322
1323 /* create the channel safely */
1324 pthread_mutex_lock(new_session->ti_lock);
1325
1326 /* open a channel */
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001327 if (open_netconf_channel(new_session)) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001328 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001329 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001330
1331 /* assign context (dicionary needed for handshake) */
1332 if (!ctx) {
1333 ctx = ly_ctx_new(SCHEMAS_DIR);
1334 } else {
Michal Vasko56b5bf72016-01-19 11:20:35 +01001335 new_session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001336 }
Michal Vasko56b5bf72016-01-19 11:20:35 +01001337 new_session->ctx = ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001338
Michal Vasko7b62fed2015-10-26 15:39:46 +01001339 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001340 if (nc_handshake(new_session)) {
1341 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001342 }
Michal Vaskoad611702015-12-03 13:41:51 +01001343 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001344
Michal Vasko56b5bf72016-01-19 11:20:35 +01001345 pthread_mutex_unlock(new_session->ti_lock);
1346
Michal Vaskoef578332016-01-25 13:20:09 +01001347 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001348 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001349 }
1350
1351 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001352 new_session->host = lydict_insert(ctx, session->host, 0);
1353 new_session->port = session->port;
1354 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001355
1356 /* append to the session ring list */
1357 if (!session->ti.libssh.next) {
1358 session->ti.libssh.next = new_session;
1359 new_session->ti.libssh.next = session;
1360 } else {
1361 ptr = session->ti.libssh.next;
1362 session->ti.libssh.next = new_session;
1363 new_session->ti.libssh.next = ptr;
1364 }
1365
1366 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001367
1368fail:
1369 nc_session_free(new_session);
1370 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001371}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001372
Michal Vasko3031aae2016-01-27 16:07:18 +01001373struct nc_session *
Michal Vaskoaf58cd92016-01-28 11:56:02 +01001374nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001375{
1376 const int ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko3031aae2016-01-27 16:07:18 +01001377 struct passwd *pw;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001378 ssh_session sess;
1379
Michal Vasko80cad7f2015-12-08 14:42:27 +01001380 sess = ssh_new();
1381 if (!sess) {
1382 ERR("Unable to initialize an SSH session.");
1383 close(sock);
1384 return NULL;
1385 }
1386
1387 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001388 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001389 ssh_options_set(sess, SSH_OPTIONS_PORT, &port);
1390 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001391 if (!ssh_ch_opts.username) {
1392 pw = getpwuid(getuid());
1393 if (!pw) {
1394 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1395 return NULL;
1396 }
1397 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1398 } else {
1399 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001400 }
Michal Vasko086311b2016-01-08 09:53:11 +01001401 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1402 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1403 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1404 /* ecdsa is probably not supported... */
1405 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1406 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001407
Michal Vasko80cad7f2015-12-08 14:42:27 +01001408 return nc_connect_libssh(sess, ctx);
1409}