blob: 4fc58f8b32e82c1c1bf74cbdc1a896eee5eef529 [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 Vaskodaf9a092016-02-09 10:42:05 +010053extern struct nc_client_opts client_opts;
54
Michal Vasko3031aae2016-01-27 16:07:18 +010055static struct nc_client_ssh_opts ssh_opts = {
Michal Vasko206d3b12015-12-04 11:08:42 +010056 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}}
Michal Vasko7b62fed2015-10-26 15:39:46 +010057};
Radek Krejciac6d3472015-10-22 15:47:18 +020058
Michal Vasko3031aae2016-01-27 16:07:18 +010059static struct nc_client_ssh_opts ssh_ch_opts = {
60 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}}
61};
62
Michal Vaskoe22c6732016-01-29 11:03:02 +010063static void
64_nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts)
Michal Vasko990089e2015-10-27 15:05:25 +010065{
66 int i;
67
Michal Vaskoe22c6732016-01-29 11:03:02 +010068 for (i = 0; i < opts->key_count; ++i) {
69 free(opts->keys[i].pubkey_path);
70 free(opts->keys[i].privkey_path);
Michal Vasko990089e2015-10-27 15:05:25 +010071 }
Michal Vaskoe22c6732016-01-29 11:03:02 +010072 free(opts->keys);
73 free(opts->username);
74}
Michal Vasko990089e2015-10-27 15:05:25 +010075
Michal Vaskoe22c6732016-01-29 11:03:02 +010076API void
77nc_client_ssh_destroy_opts(void)
78{
79 _nc_client_ssh_destroy_opts(&ssh_opts);
80 _nc_client_ssh_destroy_opts(&ssh_ch_opts);
Michal Vasko990089e2015-10-27 15:05:25 +010081}
82
Michal Vasko7b62fed2015-10-26 15:39:46 +010083static char *
84sshauth_password(const char *username, const char *hostname)
Radek Krejciac6d3472015-10-22 15:47:18 +020085{
Michal Vasko7b62fed2015-10-26 15:39:46 +010086 char *buf, *newbuf;
87 int buflen = 1024, len = 0;
88 char c = 0;
89 struct termios newterm, oldterm;
90 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +020091
Michal Vasko7b62fed2015-10-26 15:39:46 +010092 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +010093 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +010094 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +020095 }
96
Michal Vasko7b62fed2015-10-26 15:39:46 +010097 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +010098 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +010099 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100100 return NULL;
101 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200102
Michal Vasko7b62fed2015-10-26 15:39:46 +0100103 fprintf(tty, "%s@%s password: ", username, hostname);
104 fflush(tty);
Radek Krejciac6d3472015-10-22 15:47:18 +0200105
Michal Vasko7b62fed2015-10-26 15:39:46 +0100106 /* system("stty -echo"); */
107 newterm = oldterm;
108 newterm.c_lflag &= ~ECHO;
109 newterm.c_lflag &= ~ICANON;
110 tcflush(fileno(tty), TCIFLUSH);
111 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100112 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100113 fclose(tty);
114 return NULL;
115 }
116
117 buf = malloc(buflen * sizeof *buf);
118 if (!buf) {
119 ERRMEM;
120 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100121 return NULL;
122 }
123
124 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
125 if (len >= buflen - 1) {
126 buflen *= 2;
127 newbuf = realloc(buf, buflen * sizeof *newbuf);
128 if (!newbuf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100129 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100130
131 /* remove content of the buffer */
132 memset(buf, 0, len);
133 free(buf);
134
135 /* restore terminal settings */
136 if (tcsetattr(fileno(tty), TCSANOW, &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100137 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100138 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100139 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100140 return NULL;
141 } else {
142 buf = newbuf;
143 }
144 }
145 buf[len++] = c;
146 }
147 buf[len++] = 0; /* terminating null byte */
148
149 /* system ("stty echo"); */
150 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100151 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100152 /*
153 * terminal probably still hides input characters, but we have password
154 * and anyway we are unable to set terminal to the previous state, so
155 * just continue
156 */
157 }
158 fprintf(tty, "\n");
159
160 fclose(tty);
161 return buf;
162}
163
164static char *
165sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo)
166{
167 unsigned int buflen = 8, response_len;
168 char c = 0;
169 struct termios newterm, oldterm;
170 char *newtext, *response;
171 FILE *tty;
172
173 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100174 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100175 return NULL;
176 }
177
178 if (tcgetattr(fileno(tty), &oldterm) != 0) {
Michal Vaskod083db62016-01-19 10:31:29 +0100179 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100180 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100181 return NULL;
182 }
183
184 if (auth_name && (!fwrite(auth_name, sizeof(char), strlen(auth_name), tty)
185 || !fwrite("\n", sizeof(char), 1, tty))) {
186 ERR("Writing the auth method name into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100187 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100188 return NULL;
189 }
190
191 if (instruction && (!fwrite(instruction, sizeof(char), strlen(instruction), tty)
192 || !fwrite("\n", sizeof(char), 1, tty))) {
193 ERR("Writing the instruction into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100194 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100195 return NULL;
196 }
197
198 if (!fwrite(prompt, sizeof(char), strlen(prompt), tty)) {
199 ERR("Writing the authentication prompt into stdout failed.");
Michal Vasko11d142a2016-01-19 15:58:24 +0100200 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100201 return NULL;
202 }
203 fflush(tty);
204 if (!echo) {
205 /* system("stty -echo"); */
206 newterm = oldterm;
207 newterm.c_lflag &= ~ECHO;
208 tcflush(fileno(tty), TCIFLUSH);
209 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100210 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko11d142a2016-01-19 15:58:24 +0100211 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100212 return NULL;
213 }
214 }
215
216 response = malloc(buflen * sizeof *response);
217 response_len = 0;
218 if (!response) {
219 ERRMEM;
220 /* restore terminal settings */
221 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100222 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100223 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100224 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100225 return NULL;
226 }
227
228 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
229 if (response_len >= buflen - 1) {
230 buflen *= 2;
231 newtext = realloc(response, buflen * sizeof *newtext);
232 if (!newtext) {
Michal Vaskod083db62016-01-19 10:31:29 +0100233 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100234 free(response);
235
236 /* restore terminal settings */
237 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100238 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100239 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100240 fclose(tty);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100241 return NULL;
242 } else {
243 response = newtext;
244 }
245 }
246 response[response_len++] = c;
247 }
248 /* terminating null byte */
249 response[response_len++] = '\0';
250
251 /* system ("stty echo"); */
252 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100253 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100254 /*
255 * terminal probably still hides input characters, but we have password
256 * and anyway we are unable to set terminal to the previous state, so
257 * just continue
258 */
259 }
260
261 fprintf(tty, "\n");
262 fclose(tty);
263 return response;
264}
265
266static char *
267sshauth_passphrase(const char* privkey_path)
268{
269 char c, *buf, *newbuf;
270 int buflen = 1024, len = 0;
271 struct termios newterm, oldterm;
272 FILE *tty;
273
274 buf = malloc(buflen * sizeof *buf);
275 if (!buf) {
Michal Vaskod083db62016-01-19 10:31:29 +0100276 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100277 return NULL;
278 }
279
280 if (!(tty = fopen("/dev/tty", "r+"))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100281 ERR("Unable to open the current terminal (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100282 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100283 }
284
285 if (tcgetattr(fileno(tty), &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100286 ERR("Unable to get terminal settings (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100287 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100288 }
289
290 fprintf(tty, "Enter passphrase for the key '%s':", privkey_path);
291 fflush(tty);
292
293 /* system("stty -echo"); */
294 newterm = oldterm;
295 newterm.c_lflag &= ~ECHO;
296 newterm.c_lflag &= ~ICANON;
297 tcflush(fileno(tty), TCIFLUSH);
298 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100299 ERR("Unable to change terminal settings for hiding password (%s).", strerror(errno));
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100300 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100301 }
302
303 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
304 if (len >= buflen - 1) {
305 buflen *= 2;
306 newbuf = realloc(buf, buflen * sizeof *newbuf);
307 if (!newbuf) {
308 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100309 /* restore terminal settings */
310 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100311 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100312 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100313 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100314 }
315 buf = newbuf;
316 }
317 buf[len++] = (char)c;
318 }
319 buf[len++] = 0; /* terminating null byte */
320
321 /* system ("stty echo"); */
322 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100323 ERR("Unable to restore terminal settings (%s).", strerror(errno));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100324 /*
325 * terminal probably still hides input characters, but we have password
326 * and anyway we are unable to set terminal to the previous state, so
327 * just continue
328 */
329 }
330 fprintf(tty, "\n");
331
332 fclose(tty);
333 return buf;
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100334
335fail:
336 free(buf);
Michal Vaskode581a82016-01-22 13:15:35 +0100337 if (tty) {
338 fclose(tty);
339 }
Michal Vasko94e7c2d2016-01-21 15:57:57 +0100340 return NULL;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100341}
342
Michal Vasko7b62fed2015-10-26 15:39:46 +0100343#ifdef ENABLE_DNSSEC
344
345/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
346/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
347static int
348sshauth_hostkey_hash_dnssec_check(const char *hostname, const char *sha1hash, int type, int alg) {
349 ns_msg handle;
350 ns_rr rr;
351 val_status_t val_status;
352 const unsigned char* rdata;
353 unsigned char buf[4096];
354 int buf_len = 4096;
355 int ret = 0, i, j, len;
356
357 /* class 1 - internet, type 44 - SSHFP */
358 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
359
360 if ((len < 0) || !val_istrusted(val_status)) {
361 ret = 2;
362 goto finish;
363 }
364
365 if (ns_initparse(buf, len, &handle) < 0) {
366 ERR("Failed to initialize DNSSEC response parser.");
367 ret = 2;
368 goto finish;
369 }
370
371 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
372 ERR("DNSSEC query returned %d.", i);
373 ret = 2;
374 goto finish;
375 }
376
377 if (!libsres_msg_getflag(handle, ns_f_ad)) {
378 /* response not secured by DNSSEC */
379 ret = 1;
380 }
381
382 /* query section */
383 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
384 ERROR("DNSSEC query section parser fail.");
385 ret = 2;
386 goto finish;
387 }
388
389 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
390 ERROR("DNSSEC query in the answer does not match the original query.");
391 ret = 2;
392 goto finish;
393 }
394
395 /* answer section */
396 i = 0;
397 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
398 if (ns_rr_type(rr) != 44) {
399 ++i;
400 continue;
401 }
402
403 rdata = ns_rr_rdata(rr);
404 if (rdata[0] != type) {
405 ++i;
406 continue;
407 }
408 if (rdata[1] != alg) {
409 ++i;
410 continue;
411 }
412
413 /* we found the correct SSHFP entry */
414 rdata += 2;
415 for (j = 0; j < 20; ++j) {
416 if (rdata[j] != (unsigned char)sha1hash[j]) {
417 ret = 2;
418 goto finish;
419 }
420 }
421
422 /* server fingerprint is supported by a DNS entry,
423 * we have already determined if DNSSEC was used or not
424 */
425 goto finish;
426 }
427
428 /* no match */
429 ret = 2;
430
431finish:
432 val_free_validator_state();
433 return ret;
434}
435
Michal Vaskoc61c4492016-01-25 11:13:34 +0100436#endif /* ENABLE_DNSSEC */
Michal Vasko7b62fed2015-10-26 15:39:46 +0100437
438static int
439sshauth_hostkey_check(const char *hostname, ssh_session session)
440{
441 char *hexa;
442 int c, state, ret;
443 ssh_key srv_pubkey;
444 unsigned char *hash_sha1 = NULL;
445 size_t hlen;
446 enum ssh_keytypes_e srv_pubkey_type;
447 char answer[5];
448
449 state = ssh_is_server_known(session);
450
451 ret = ssh_get_publickey(session, &srv_pubkey);
452 if (ret < 0) {
453 ERR("Unable to get server public key.");
Michal Vaskod083db62016-01-19 10:31:29 +0100454 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100455 }
456
457 srv_pubkey_type = ssh_key_type(srv_pubkey);
458 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
459 ssh_key_free(srv_pubkey);
460 if (ret < 0) {
461 ERR("Failed to calculate SHA1 hash of the server public key.");
Michal Vaskod083db62016-01-19 10:31:29 +0100462 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100463 }
464
465 hexa = ssh_get_hexa(hash_sha1, hlen);
466
467 switch (state) {
468 case SSH_SERVER_KNOWN_OK:
469 break; /* ok */
470
471 case SSH_SERVER_KNOWN_CHANGED:
472 ERR("Remote host key changed, the connection will be terminated!");
473 goto fail;
474
475 case SSH_SERVER_FOUND_OTHER:
Michal Vasko086311b2016-01-08 09:53:11 +0100476 WRN("Remote host key is not known, but a key of another type for this host is known. Continue with caution.");
477 goto hostkey_not_known;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100478
479 case SSH_SERVER_FILE_NOT_FOUND:
480 WRN("Could not find the known hosts file.");
Michal Vasko086311b2016-01-08 09:53:11 +0100481 goto hostkey_not_known;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100482
483 case SSH_SERVER_NOT_KNOWN:
Michal Vasko086311b2016-01-08 09:53:11 +0100484hostkey_not_known:
Michal Vasko7b62fed2015-10-26 15:39:46 +0100485#ifdef ENABLE_DNSSEC
486 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
487 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
488 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
489 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
490 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
491 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
492 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
493 }
494
495 /* DNSSEC SSHFP check successful, that's enough */
496 if (!ret) {
Michal Vasko86440232016-02-01 15:59:09 +0100497 VRB("DNSSEC SSHFP check successful.");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100498 ssh_write_knownhost(session);
499 ssh_clean_pubkey_hash(&hash_sha1);
500 ssh_string_free_char(hexa);
Michal Vaskod083db62016-01-19 10:31:29 +0100501 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100502 }
503 }
504#endif
505
506 /* try to get result from user */
507 fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname);
508 fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa);
509
510#ifdef ENABLE_DNSSEC
511 if (ret == 2) {
Michal Vasko86440232016-02-01 15:59:09 +0100512 fprintf(stdout, "No matching host key fingerprint found using DNS.\n");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100513 } else if (ret == 1) {
Michal Vasko86440232016-02-01 15:59:09 +0100514 fprintf(stdout, "Matching host key fingerprint found using DNS.\n");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100515 }
516#endif
517
518 fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? ");
519
520 do {
521 if (fscanf(stdin, "%4s", answer) == EOF) {
522 ERR("fscanf() failed (%s).", strerror(errno));
523 goto fail;
524 }
525 while (((c = getchar()) != EOF) && (c != '\n'));
526
527 fflush(stdin);
528 if (!strcmp("yes", answer)) {
529 /* store the key into the host file */
530 ret = ssh_write_knownhost(session);
Michal Vasko86440232016-02-01 15:59:09 +0100531 if (ret != SSH_OK) {
532 WRN("Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100533 }
534 } else if (!strcmp("no", answer)) {
535 goto fail;
536 } else {
537 fprintf(stdout, "Please type 'yes' or 'no': ");
538 }
539 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
540
541 break;
542
543 case SSH_SERVER_ERROR:
544 ssh_clean_pubkey_hash(&hash_sha1);
545 fprintf(stderr,"%s",ssh_get_error(session));
546 return -1;
547 }
548
549 ssh_clean_pubkey_hash(&hash_sha1);
550 ssh_string_free_char(hexa);
Michal Vaskod083db62016-01-19 10:31:29 +0100551 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100552
553fail:
554 ssh_clean_pubkey_hash(&hash_sha1);
555 ssh_string_free_char(hexa);
Michal Vaskod083db62016-01-19 10:31:29 +0100556 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100557}
558
Michal Vasko3031aae2016-01-27 16:07:18 +0100559static int
560_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 +0100561{
562 int i;
563 FILE *key;
564 char line[128];
565
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100566 if (!pub_key || !priv_key) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100567 ERRARG;
568 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100569 }
570
Michal Vasko3031aae2016-01-27 16:07:18 +0100571 for (i = 0; i < opts->key_count; ++i) {
572 if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) {
573 if (strcmp(opts->keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100574 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100575 priv_key, opts->keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100576 continue;
Michal Vasko3031aae2016-01-27 16:07:18 +0100577 } else if (strcmp(opts->keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100578 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vasko3031aae2016-01-27 16:07:18 +0100579 pub_key, opts->keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100580 continue;
581 }
582
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100583 ERR("SSH key pair already set.");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100584 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100585 }
586 }
587
Michal Vasko3031aae2016-01-27 16:07:18 +0100588 /* add the keys */
589 ++opts->key_count;
590 opts->keys = realloc(opts->keys, opts->key_count * sizeof *opts->keys);
591 opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key);
592 opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key);
593 opts->keys[opts->key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100594
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100595 /* check encryption */
596 if ((key = fopen(priv_key, "r"))) {
597 /* 1st line - key type */
598 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100599 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100600 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100601 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100602 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100603 /* 2nd line - encryption information or key */
604 if (!fgets(line, sizeof line, key)) {
605 fclose(key);
606 ERR("fgets() on %s failed.", priv_key);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100607 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100608 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100609 fclose(key);
610 if (strcasestr(line, "encrypted")) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100611 opts->keys[opts->key_count - 1].privkey_crypt = 1;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100612 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100613 }
614
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100615 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100616}
617
618API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100619nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100620{
Michal Vasko3031aae2016-01-27 16:07:18 +0100621 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_opts);
622}
623
624API int
625nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key)
626{
627 return _nc_client_ssh_add_keypair(pub_key, priv_key, &ssh_ch_opts);
628}
629
630static int
631_nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts)
632{
633 if (idx >= opts->key_count) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100634 ERRARG;
635 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100636 }
637
Michal Vasko3031aae2016-01-27 16:07:18 +0100638 free(opts->keys[idx].pubkey_path);
639 free(opts->keys[idx].privkey_path);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100640
Michal Vasko3031aae2016-01-27 16:07:18 +0100641 --opts->key_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100642 if (idx < opts->key_count) {
643 memcpy(&opts->keys[idx], &opts->keys[opts->key_count], sizeof *opts->keys);
644 }
645 if (opts->key_count) {
646 opts->keys = realloc(opts->keys, opts->key_count * sizeof *opts->keys);
647 } else {
648 free(opts->keys);
649 opts->keys = NULL;
650 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100651
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100652 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100653}
654
655API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100656nc_client_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100657{
Michal Vasko3031aae2016-01-27 16:07:18 +0100658 return _nc_client_ssh_del_keypair(idx, &ssh_opts);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100659}
660
661API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100662nc_client_ssh_ch_del_keypair(int idx)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100663{
Michal Vasko3031aae2016-01-27 16:07:18 +0100664 return _nc_client_ssh_del_keypair(idx, &ssh_ch_opts);
665}
666
667static int
668_nc_client_ssh_get_keypair_count(struct nc_client_ssh_opts *opts)
669{
670 return opts->key_count;
671}
672
673API int
674nc_client_ssh_get_keypair_count(void)
675{
676 return _nc_client_ssh_get_keypair_count(&ssh_opts);
677}
678
679API int
680nc_client_ssh_ch_get_keypair_count(void)
681{
682 return _nc_client_ssh_get_keypair_count(&ssh_ch_opts);
683}
684
685static int
686_nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts)
687{
688 if ((idx >= opts->key_count) || (!pub_key && !priv_key)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100689 ERRARG;
690 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100691 }
692
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100693 if (pub_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100694 *pub_key = opts->keys[idx].pubkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100695 }
696 if (priv_key) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100697 *priv_key = opts->keys[idx].privkey_path;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100698 }
699
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100700 return 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100701}
702
Michal Vasko3031aae2016-01-27 16:07:18 +0100703API int
704nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
705{
706 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_opts);
707}
708
709API int
710nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key)
711{
712 return _nc_client_ssh_get_keypair(idx, pub_key, priv_key, &ssh_ch_opts);
713}
714
715static void
716_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 +0100717{
718 if (pref < 0) {
719 pref = -1;
720 }
721
722 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100723 opts->auth_pref[0].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100724 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100725 opts->auth_pref[1].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100726 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100727 opts->auth_pref[2].value = pref;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100728 }
729}
730
Michal Vasko3031aae2016-01-27 16:07:18 +0100731API void
732nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100733{
Michal Vasko3031aae2016-01-27 16:07:18 +0100734 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_opts);
735}
736
737API void
738nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref)
739{
740 _nc_client_ssh_set_auth_pref(auth_type, pref, &ssh_ch_opts);
741}
742
743static int16_t
744_nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type, struct nc_client_ssh_opts *opts)
745{
746 int16_t pref = 0;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100747
748 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100749 pref = opts->auth_pref[0].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100750 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100751 pref = opts->auth_pref[1].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100752 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100753 pref = opts->auth_pref[2].value;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100754 }
755
756 return pref;
757}
758
Michal Vasko3031aae2016-01-27 16:07:18 +0100759API int16_t
760nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
761{
762 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_opts);
763}
764
765API int16_t
766nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
767{
768 return _nc_client_ssh_get_auth_pref(auth_type, &ssh_ch_opts);
769}
770
771static int
772_nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opts)
773{
774 if (opts->username) {
775 free(opts->username);
776 }
777 if (username) {
778 opts->username = strdup(username);
779 if (!opts->username) {
780 ERRMEM;
781 return -1;
782 }
783 } else {
784 opts->username = NULL;
785 }
786
787 return 0;
788}
789
790API int
791nc_client_ssh_set_username(const char *username)
792{
793 return _nc_client_ssh_set_username(username, &ssh_opts);
794}
795
796API int
797nc_client_ssh_ch_set_username(const char *username)
798{
799 return _nc_client_ssh_set_username(username, &ssh_ch_opts);
800}
801
Michal Vaskoe22c6732016-01-29 11:03:02 +0100802static const char *
803_nc_client_ssh_get_username(struct nc_client_ssh_opts *opts)
804{
805 return opts->username;
806}
807
808API const char *
809nc_client_ssh_get_username(void)
810{
811 return _nc_client_ssh_get_username(&ssh_opts);
812}
813
814API const char *
815nc_client_ssh_ch_get_username(void)
816{
817 return _nc_client_ssh_get_username(&ssh_ch_opts);
818}
819
Michal Vasko3031aae2016-01-27 16:07:18 +0100820API int
821nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port)
822{
823 return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH);
824}
825
826API int
827nc_client_ssh_ch_del_bind(const char *address, uint16_t port)
828{
829 return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH);
830}
831
Michal Vasko8e2f4a62016-02-01 15:59:48 +0100832/* Establish a secure SSH connection and authenticate.
Michal Vasko7b62fed2015-10-26 15:39:46 +0100833 * Host, port, username, and a connected socket is expected to be set.
834 */
835static int
Michal Vasko8e2f4a62016-02-01 15:59:48 +0100836connect_ssh_session(struct nc_session *session)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100837{
Michal Vasko235efdc2015-12-17 12:05:04 +0100838 int j, ret_auth, userauthlist;
839 NC_SSH_AUTH_TYPE auth;
840 short int pref;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100841 const char* prompt;
842 char *s, *answer, echo;
843 ssh_key pubkey, privkey;
844 ssh_session ssh_sess;
845
846 ssh_sess = session->ti.libssh.session;
847
848 if (ssh_connect(ssh_sess) != SSH_OK) {
849 ERR("Starting the SSH session failed (%s)", ssh_get_error(ssh_sess));
850 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100851 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100852 }
853
854 if (sshauth_hostkey_check(session->host, ssh_sess)) {
855 ERR("Checking the host key failed.");
Michal Vaskod083db62016-01-19 10:31:29 +0100856 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100857 }
858
859 if ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_ERROR) {
860 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vaskod083db62016-01-19 10:31:29 +0100861 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100862 }
863
864 /* check what authentication methods are available */
865 userauthlist = ssh_userauth_list(ssh_sess, NULL);
Michal Vasko235efdc2015-12-17 12:05:04 +0100866
867 /* remove those disabled */
868 if (ssh_opts.auth_pref[0].value < 0) {
869 VRB("Interactive SSH authentication method was disabled.");
870 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100871 }
Michal Vasko235efdc2015-12-17 12:05:04 +0100872 if (ssh_opts.auth_pref[1].value < 0) {
873 VRB("Password SSH authentication method was disabled.");
874 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100875 }
Michal Vasko235efdc2015-12-17 12:05:04 +0100876 if (ssh_opts.auth_pref[2].value < 0) {
877 VRB("Publickey SSH authentication method was disabled.");
878 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100879 }
880
Michal Vasko235efdc2015-12-17 12:05:04 +0100881 while (ret_auth != SSH_AUTH_SUCCESS) {
882 auth = 0;
883 pref = 0;
884 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
885 auth = NC_SSH_AUTH_INTERACTIVE;
886 pref = ssh_opts.auth_pref[0].value;
887 }
888 if ((userauthlist & SSH_AUTH_METHOD_PASSWORD) && (ssh_opts.auth_pref[1].value > pref)) {
889 auth = NC_SSH_AUTH_PASSWORD;
890 pref = ssh_opts.auth_pref[1].value;
891 }
892 if ((userauthlist & SSH_AUTH_METHOD_PUBLICKEY) && (ssh_opts.auth_pref[2].value > pref)) {
893 auth = NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100894 }
895
Michal Vasko235efdc2015-12-17 12:05:04 +0100896 if (!auth) {
Michal Vaskod083db62016-01-19 10:31:29 +0100897 ERR("Unable to authenticate to the remote server (no supported authentication methods left).");
Michal Vasko235efdc2015-12-17 12:05:04 +0100898 break;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100899 }
900
901 /* found common authentication method */
Michal Vasko235efdc2015-12-17 12:05:04 +0100902 switch (auth) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100903 case NC_SSH_AUTH_PASSWORD:
Michal Vasko235efdc2015-12-17 12:05:04 +0100904 userauthlist &= ~SSH_AUTH_METHOD_PASSWORD;
905
Michal Vaskoef578332016-01-25 13:20:09 +0100906 VRB("Password authentication (host \"%s\", user \"%s\").", session->host, session->username);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100907 s = sshauth_password(session->username, session->host);
908 if ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) != SSH_AUTH_SUCCESS) {
909 memset(s, 0, strlen(s));
Michal Vaskod083db62016-01-19 10:31:29 +0100910 VRB("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100911 }
912 free(s);
913 break;
914 case NC_SSH_AUTH_INTERACTIVE:
Michal Vasko235efdc2015-12-17 12:05:04 +0100915 userauthlist &= ~SSH_AUTH_METHOD_INTERACTIVE;
916
Michal Vaskod083db62016-01-19 10:31:29 +0100917 VRB("Keyboard-interactive authentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100918 while ((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) {
919 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
920 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
921 if (prompt == NULL) {
922 break;
923 }
924 answer = sshauth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
925 ssh_userauth_kbdint_getinstruction(ssh_sess),
926 prompt, echo);
927 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
928 free(answer);
929 break;
930 }
931 free(answer);
932 }
933 }
934
935 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100936 VRB("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100937 }
938
939 break;
Michal Vasko206d3b12015-12-04 11:08:42 +0100940 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko235efdc2015-12-17 12:05:04 +0100941 userauthlist &= ~SSH_AUTH_METHOD_PUBLICKEY;
942
Michal Vaskod083db62016-01-19 10:31:29 +0100943 VRB("Publickey athentication.");
Michal Vasko7b62fed2015-10-26 15:39:46 +0100944
945 /* if publickeys path not provided, we cannot continue */
946 if (!ssh_opts.key_count) {
947 VRB("No key pair specified.");
948 break;
949 }
950
951 for (j = 0; j < ssh_opts.key_count; j++) {
Michal Vaskoef578332016-01-25 13:20:09 +0100952 VRB("Trying to authenticate using %spair \"%s\" \"%s\".",
Michal Vasko7b62fed2015-10-26 15:39:46 +0100953 ssh_opts.keys[j].privkey_crypt ? "password-protected " : "", ssh_opts.keys[j].privkey_path,
954 ssh_opts.keys[j].pubkey_path);
955
956 if (ssh_pki_import_pubkey_file(ssh_opts.keys[j].pubkey_path, &pubkey) != SSH_OK) {
957 WRN("Failed to import the key \"%s\".", ssh_opts.keys[j].pubkey_path);
958 continue;
959 }
960 ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey);
961 if ((ret_auth == SSH_AUTH_DENIED) || (ret_auth == SSH_AUTH_PARTIAL)) {
962 ssh_key_free(pubkey);
963 continue;
964 }
965 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100966 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100967 ssh_key_free(pubkey);
968 break;
969 }
970
971 if (ssh_opts.keys[j].privkey_crypt) {
972 s = sshauth_passphrase(ssh_opts.keys[j].privkey_path);
973 } else {
974 s = NULL;
975 }
976
977 if (ssh_pki_import_privkey_file(ssh_opts.keys[j].privkey_path, s, NULL, NULL, &privkey) != SSH_OK) {
978 WRN("Failed to import the key \"%s\".", ssh_opts.keys[j].privkey_path);
979 if (s) {
980 memset(s, 0, strlen(s));
981 free(s);
982 }
983 ssh_key_free(pubkey);
984 continue;
985 }
986
987 if (s) {
988 memset(s, 0, strlen(s));
989 free(s);
990 }
991
992 ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey);
993 ssh_key_free(pubkey);
994 ssh_key_free(privkey);
995
996 if (ret_auth == SSH_AUTH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +0100997 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100998 }
999 if (ret_auth == SSH_AUTH_SUCCESS) {
1000 break;
1001 }
1002 }
1003 break;
1004 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001005 }
1006
1007 /* check a state of authentication */
1008 if (ret_auth != SSH_AUTH_SUCCESS) {
Michal Vaskod083db62016-01-19 10:31:29 +01001009 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001010 }
1011
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001012 return 0;
1013}
1014
1015/* Open new SSH channel and request the 'netconf' subsystem.
1016 * SSH connection is expected to be established.
1017 */
1018static int
1019open_netconf_channel(struct nc_session *session)
1020{
1021 ssh_session ssh_sess;
1022
1023 ssh_sess = session->ti.libssh.session;
1024
1025 if (!ssh_is_connected(ssh_sess)) {
1026 ERR("SSH session not connected.");
1027 return -1;
1028 }
1029
1030 if (session->ti.libssh.channel) {
1031 ERR("SSH channel already created.");
1032 return -1;
1033 }
1034
Michal Vasko7b62fed2015-10-26 15:39:46 +01001035 /* open a channel */
1036 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
1037 if (ssh_channel_open_session(session->ti.libssh.channel) != SSH_OK) {
1038 ssh_channel_free(session->ti.libssh.channel);
1039 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001040 ERR("Opening an SSH channel failed (%s).", ssh_get_error(ssh_sess));
1041 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001042 }
1043
1044 /* execute the NETCONF subsystem on the channel */
1045 if (ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf") != SSH_OK) {
1046 ssh_channel_free(session->ti.libssh.channel);
1047 session->ti.libssh.channel = NULL;
Michal Vaskod083db62016-01-19 10:31:29 +01001048 ERR("Starting the \"netconf\" SSH subsystem failed (%s).", ssh_get_error(ssh_sess));
1049 return -1;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001050 }
1051
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001052 return 0;
Radek Krejciac6d3472015-10-22 15:47:18 +02001053}
1054
1055API struct nc_session *
Michal Vasko3031aae2016-01-27 16:07:18 +01001056nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001057{
Michal Vaskoc111ac52015-12-08 14:36:36 +01001058 const int timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001059 int sock;
Michal Vasko55fded62016-02-02 12:19:34 +01001060 uint32_t port_uint;
Michal Vasko3031aae2016-01-27 16:07:18 +01001061 char *username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001062 struct passwd *pw;
1063 struct nc_session *session = NULL;
1064
1065 /* process parameters */
1066 if (!host || strisempty(host)) {
1067 host = "localhost";
1068 }
1069
1070 if (!port) {
1071 port = NC_PORT_SSH;
1072 }
Michal Vasko55fded62016-02-02 12:19:34 +01001073 port_uint = port;
Radek Krejciac6d3472015-10-22 15:47:18 +02001074
Michal Vasko3031aae2016-01-27 16:07:18 +01001075 if (!ssh_opts.username) {
Radek Krejciac6d3472015-10-22 15:47:18 +02001076 pw = getpwuid(getuid());
1077 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001078 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1079 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001080 } else {
1081 username = pw->pw_name;
1082 }
Michal Vasko3031aae2016-01-27 16:07:18 +01001083 } else {
1084 username = ssh_opts.username;
Radek Krejciac6d3472015-10-22 15:47:18 +02001085 }
1086
1087 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001088 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001089 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001090 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +02001091 return NULL;
1092 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001093 session->status = NC_STATUS_STARTING;
1094 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +02001095
Michal Vasko7b62fed2015-10-26 15:39:46 +01001096 /* transport lock */
1097 session->ti_lock = malloc(sizeof *session->ti_lock);
1098 if (!session->ti_lock) {
1099 ERRMEM;
1100 goto fail;
1101 }
1102 pthread_mutex_init(session->ti_lock, NULL);
1103
1104 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001105 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001106 session->ti.libssh.session = ssh_new();
1107 if (!session->ti.libssh.session) {
1108 ERR("Unable to initialize SSH session.");
1109 goto fail;
1110 }
Radek Krejciac6d3472015-10-22 15:47:18 +02001111
Michal Vasko7b62fed2015-10-26 15:39:46 +01001112 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001113 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko55fded62016-02-02 12:19:34 +01001114 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint);
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001115 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001116 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
Michal Vasko086311b2016-01-08 09:53:11 +01001117 if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS,
1118 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1119 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1120 /* ecdsa is probably not supported... */
1121 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1122 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001123
1124 /* create and assign communication socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001125 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001126 if (sock == -1) {
1127 goto fail;
1128 }
1129 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1130
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001131 /* temporarily, for session connection */
1132 session->host = host;
1133 session->username = username;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001134 if (connect_ssh_session(session) || open_netconf_channel(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001135 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001136 }
1137
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001138 /* assign context (dicionary needed for handshake) */
1139 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001140 if (client_opts.schema_searchpath) {
1141 ctx = ly_ctx_new(client_opts.schema_searchpath);
1142 } else {
1143 ctx = ly_ctx_new(SCHEMAS_DIR);
1144 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001145 } else {
1146 session->flags |= NC_SESSION_SHAREDCTX;
1147 }
1148 session->ctx = ctx;
1149
Radek Krejciac6d3472015-10-22 15:47:18 +02001150 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001151 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001152 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +02001153 }
Michal Vaskoad611702015-12-03 13:41:51 +01001154 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +02001155
Michal Vaskoef578332016-01-25 13:20:09 +01001156 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001157 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001158 }
1159
1160 /* store information into the dictionary */
1161 session->host = lydict_insert(ctx, host, 0);
1162 session->port = port;
1163 session->username = lydict_insert(ctx, username, 0);
1164
Radek Krejciac6d3472015-10-22 15:47:18 +02001165 return session;
1166
Michal Vasko7b62fed2015-10-26 15:39:46 +01001167fail:
Radek Krejciac6d3472015-10-22 15:47:18 +02001168 nc_session_free(session);
1169 return NULL;
1170}
1171
1172API struct nc_session *
1173nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
1174{
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001175 char *host = NULL, *username = NULL;
1176 unsigned short port = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001177 int sock;
1178 struct passwd *pw;
1179 struct nc_session *session = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001180
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001181 if (!ssh_session) {
1182 ERRARG;
1183 return NULL;
1184 }
1185
Michal Vasko7b62fed2015-10-26 15:39:46 +01001186 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001187 session = calloc(1, sizeof *session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001188 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001189 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001190 return NULL;
1191 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001192 session->status = NC_STATUS_STARTING;
1193 session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001194
1195 /* transport lock */
1196 session->ti_lock = malloc(sizeof *session->ti_lock);
1197 if (!session->ti_lock) {
1198 ERRMEM;
1199 goto fail;
1200 }
1201 pthread_mutex_init(session->ti_lock, NULL);
1202
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001203 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001204 session->ti.libssh.session = ssh_session;
1205
Michal Vasko745ff832015-12-08 14:40:29 +01001206 /* was port set? */
1207 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1208
1209 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001210 /*
Michal Vasko745ff832015-12-08 14:40:29 +01001211 * There is no file descriptor (detected based on the host, there is no way to check
1212 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001213 */
1214
Michal Vasko7b62fed2015-10-26 15:39:46 +01001215 /* remember host */
Michal Vasko745ff832015-12-08 14:40:29 +01001216 host = strdup("localhost");
1217 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001218
1219 /* create and connect socket */
Michal Vaskof05562c2016-01-20 12:06:43 +01001220 sock = nc_sock_connect(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001221 if (sock == -1) {
1222 goto fail;
1223 }
1224 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1225 }
1226
Michal Vasko745ff832015-12-08 14:40:29 +01001227 /* was username set? */
1228 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1229
Michal Vasko7b62fed2015-10-26 15:39:46 +01001230 if (!ssh_is_connected(ssh_session)) {
1231 /*
1232 * We are connected, but not SSH authenticated. (Transport layer)
1233 */
1234
Michal Vasko7b62fed2015-10-26 15:39:46 +01001235 /* remember username */
1236 if (!username) {
Michal Vasko3031aae2016-01-27 16:07:18 +01001237 if (!ssh_opts.username) {
1238 pw = getpwuid(getuid());
1239 if (!pw) {
1240 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1241 goto fail;
1242 }
1243 username = strdup(pw->pw_name);
1244 } else {
1245 username = strdup(ssh_opts.username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001246 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001247 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001248 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001249
1250 /* authenticate SSH session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001251 session->host = host;
1252 session->username = username;
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001253 if (connect_ssh_session(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001254 goto fail;
1255 }
1256 }
1257
1258 /*
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001259 * Almost done, open a netconf channel. (Transport layer / application layer)
1260 */
1261 if (open_netconf_channel(session)) {
1262 goto fail;
1263 }
1264
1265 /*
1266 * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001267 */
1268
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001269 /* assign context (dicionary needed for handshake) */
1270 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001271 if (client_opts.schema_searchpath) {
1272 ctx = ly_ctx_new(client_opts.schema_searchpath);
1273 } else {
1274 ctx = ly_ctx_new(SCHEMAS_DIR);
1275 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001276 } else {
1277 session->flags |= NC_SESSION_SHAREDCTX;
1278 }
1279 session->ctx = ctx;
1280
Michal Vasko7b62fed2015-10-26 15:39:46 +01001281 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001282 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001283 goto fail;
1284 }
Michal Vaskoad611702015-12-03 13:41:51 +01001285 session->status = NC_STATUS_RUNNING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001286
Michal Vaskoef578332016-01-25 13:20:09 +01001287 if (nc_ctx_check_and_fill(session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001288 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001289 }
1290
1291 /* store information into the dictionary */
1292 if (host) {
1293 session->host = lydict_insert_zc(ctx, host);
1294 }
1295 if (port) {
1296 session->port = port;
1297 }
1298 if (username) {
1299 session->username = lydict_insert_zc(ctx, username);
1300 }
1301
Michal Vasko7b62fed2015-10-26 15:39:46 +01001302 return session;
1303
1304fail:
1305 nc_session_free(session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001306 return NULL;
1307}
1308
1309API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001310nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001311{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001312 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001313
Michal Vaskob7c4ff32016-01-21 15:35:54 +01001314 if (!session) {
1315 ERRARG;
1316 return NULL;
1317 }
1318
Michal Vasko7b62fed2015-10-26 15:39:46 +01001319 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001320 new_session = calloc(1, sizeof *new_session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001321 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001322 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001323 return NULL;
1324 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001325 new_session->status = NC_STATUS_STARTING;
1326 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001327
1328 /* share some parameters including the session lock */
1329 new_session->ti_type = NC_TI_LIBSSH;
1330 new_session->ti_lock = session->ti_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001331 new_session->ti.libssh.session = session->ti.libssh.session;
1332
1333 /* create the channel safely */
1334 pthread_mutex_lock(new_session->ti_lock);
1335
1336 /* open a channel */
Michal Vasko8e2f4a62016-02-01 15:59:48 +01001337 if (open_netconf_channel(new_session)) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001338 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001339 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001340
1341 /* assign context (dicionary needed for handshake) */
1342 if (!ctx) {
Michal Vaskodaf9a092016-02-09 10:42:05 +01001343 if (client_opts.schema_searchpath) {
1344 ctx = ly_ctx_new(client_opts.schema_searchpath);
1345 } else {
1346 ctx = ly_ctx_new(SCHEMAS_DIR);
1347 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001348 } else {
Michal Vasko56b5bf72016-01-19 11:20:35 +01001349 new_session->flags |= NC_SESSION_SHAREDCTX;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001350 }
Michal Vasko56b5bf72016-01-19 11:20:35 +01001351 new_session->ctx = ctx;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001352
Michal Vasko7b62fed2015-10-26 15:39:46 +01001353 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001354 if (nc_handshake(new_session)) {
1355 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001356 }
Michal Vaskoad611702015-12-03 13:41:51 +01001357 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001358
Michal Vasko56b5bf72016-01-19 11:20:35 +01001359 pthread_mutex_unlock(new_session->ti_lock);
1360
Michal Vaskoef578332016-01-25 13:20:09 +01001361 if (nc_ctx_check_and_fill(new_session) == -1) {
Michal Vasko57eb9402015-12-08 14:38:12 +01001362 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001363 }
1364
1365 /* store information into session and the dictionary */
Michal Vasko56b5bf72016-01-19 11:20:35 +01001366 new_session->host = lydict_insert(ctx, session->host, 0);
1367 new_session->port = session->port;
1368 new_session->username = lydict_insert(ctx, session->username, 0);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001369
1370 /* append to the session ring list */
1371 if (!session->ti.libssh.next) {
1372 session->ti.libssh.next = new_session;
1373 new_session->ti.libssh.next = session;
1374 } else {
1375 ptr = session->ti.libssh.next;
1376 session->ti.libssh.next = new_session;
1377 new_session->ti.libssh.next = ptr;
1378 }
1379
1380 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001381
1382fail:
1383 nc_session_free(new_session);
1384 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001385}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001386
Michal Vasko3031aae2016-01-27 16:07:18 +01001387struct nc_session *
Michal Vaskoaf58cd92016-01-28 11:56:02 +01001388nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx)
Michal Vasko80cad7f2015-12-08 14:42:27 +01001389{
1390 const int ssh_timeout = NC_SSH_TIMEOUT;
Michal Vasko3031aae2016-01-27 16:07:18 +01001391 struct passwd *pw;
Michal Vasko80cad7f2015-12-08 14:42:27 +01001392 ssh_session sess;
1393
Michal Vasko80cad7f2015-12-08 14:42:27 +01001394 sess = ssh_new();
1395 if (!sess) {
1396 ERR("Unable to initialize an SSH session.");
1397 close(sock);
1398 return NULL;
1399 }
1400
1401 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
Michal Vasko3031aae2016-01-27 16:07:18 +01001402 ssh_options_set(sess, SSH_OPTIONS_HOST, host);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001403 ssh_options_set(sess, SSH_OPTIONS_PORT, &port);
1404 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
Michal Vasko3031aae2016-01-27 16:07:18 +01001405 if (!ssh_ch_opts.username) {
1406 pw = getpwuid(getuid());
1407 if (!pw) {
1408 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1409 return NULL;
1410 }
1411 ssh_options_set(sess, SSH_OPTIONS_USER, pw->pw_name);
1412 } else {
1413 ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username);
Michal Vasko80cad7f2015-12-08 14:42:27 +01001414 }
Michal Vasko086311b2016-01-08 09:53:11 +01001415 if (ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS,
1416 "ssh-ed25519,ecdsa-sha2-nistp521,ecdsa-sha2-nistp384,"
1417 "ecdsa-sha2-nistp256,ssh-rsa,ssh-dss,ssh-rsa1")) {
1418 /* ecdsa is probably not supported... */
1419 ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ssh-rsa,ssh-dss,ssh-rsa1");
1420 }
Michal Vasko80cad7f2015-12-08 14:42:27 +01001421
Michal Vasko80cad7f2015-12-08 14:42:27 +01001422 return nc_connect_libssh(sess, ctx);
1423}