blob: bda77e60d997d2a87de67cbf8ca9c07c271148ea [file] [log] [blame]
Radek Krejciac6d3472015-10-22 15:47:18 +02001/**
2 * \file session_ssh.c
3 * \author Radek Krejci <rkrejci@cesnet.cz>
4 * \brief libnetconf2 - SSH specific session transport functions
5 *
6 * This source is compiled only with libssh.
7 *
8 * Copyright (c) 2015 CESNET, z.s.p.o.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in
17 * the documentation and/or other materials provided with the
18 * distribution.
19 * 3. Neither the name of the Company nor the names of its contributors
20 * may be used to endorse or promote products derived from this
21 * software without specific prior written permission.
22 *
23 */
24
Michal Vasko7b62fed2015-10-26 15:39:46 +010025#define _GNU_SOURCE
Radek Krejciac6d3472015-10-22 15:47:18 +020026#include <assert.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010027#include <stdlib.h>
28#include <stddef.h>
29#include <stdio.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020030#include <string.h>
Michal Vasko7b62fed2015-10-26 15:39:46 +010031#include <errno.h>
32#include <fcntl.h>
33#include <termios.h>
34#include <sys/types.h>
35#include <sys/stat.h>
36#include <pwd.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020037#include <unistd.h>
38
Michal Vasko7b62fed2015-10-26 15:39:46 +010039#ifdef ENABLE_DNSSEC
40# include <validator/validator.h>
41# include <validator/resolver.h>
42# include <validator/validator-compat.h>
43#endif
44
Michal Vasko745ff832015-12-08 14:40:29 +010045#include <libssh/libssh.h>
Radek Krejciac6d3472015-10-22 15:47:18 +020046#include <libyang/libyang.h>
47
48#include "libnetconf.h"
Michal Vasko7b62fed2015-10-26 15:39:46 +010049#include "session.h"
Michal Vasko9e2d3a32015-11-10 13:09:18 +010050#include "session_p.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020051
Michal Vasko7b62fed2015-10-26 15:39:46 +010052static struct nc_ssh_auth_opts ssh_opts = {
Michal Vasko206d3b12015-12-04 11:08:42 +010053 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}}
Michal Vasko7b62fed2015-10-26 15:39:46 +010054};
Radek Krejciac6d3472015-10-22 15:47:18 +020055
Michal Vasko990089e2015-10-27 15:05:25 +010056API void
Michal Vaskoc111ac52015-12-08 14:36:36 +010057nc_ssh_client_init(void)
Michal Vasko990089e2015-10-27 15:05:25 +010058{
59 ssh_threads_set_callbacks(ssh_threads_get_pthread());
60 ssh_init();
61}
62
63API void
Michal Vaskoc111ac52015-12-08 14:36:36 +010064nc_ssh_client_destroy(void)
Michal Vasko990089e2015-10-27 15:05:25 +010065{
66 int i;
67
68 for (i = 0; i < ssh_opts.key_count; ++i) {
69 free(ssh_opts.keys[i].pubkey_path);
70 free(ssh_opts.keys[i].privkey_path);
71 }
72
73 free(ssh_opts.keys);
74 ssh_opts.keys = NULL;
75 ssh_opts.key_count = 0;
76}
77
Michal Vasko7b62fed2015-10-26 15:39:46 +010078static char *
79sshauth_password(const char *username, const char *hostname)
Radek Krejciac6d3472015-10-22 15:47:18 +020080{
Michal Vasko7b62fed2015-10-26 15:39:46 +010081 char *buf, *newbuf;
82 int buflen = 1024, len = 0;
83 char c = 0;
84 struct termios newterm, oldterm;
85 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +020086
Michal Vasko7b62fed2015-10-26 15:39:46 +010087 buf = malloc(buflen * sizeof *buf);
88 if (!buf) {
89 ERRMEM;
90 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +020091 }
92
Michal Vasko7b62fed2015-10-26 15:39:46 +010093 if (!(tty = fopen("/dev/tty", "r+"))) {
94 ERR("Unable to open the current terminal (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
95 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +020096 }
97
Michal Vasko7b62fed2015-10-26 15:39:46 +010098 if (tcgetattr(fileno(tty), &oldterm)) {
99 ERR("Unable to get terminal settings (%d: %s).", __LINE__, strerror(errno));
100 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)) {
112 ERR("Unable to change terminal settings for hiding password (%d: %s).", __LINE__, strerror(errno));
113 return NULL;
114 }
115
116 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
117 if (len >= buflen - 1) {
118 buflen *= 2;
119 newbuf = realloc(buf, buflen * sizeof *newbuf);
120 if (!newbuf) {
121 ERR("Memory allocation failed (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
122
123 /* remove content of the buffer */
124 memset(buf, 0, len);
125 free(buf);
126
127 /* restore terminal settings */
128 if (tcsetattr(fileno(tty), TCSANOW, &oldterm) != 0) {
129 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
130 }
131 return NULL;
132 } else {
133 buf = newbuf;
134 }
135 }
136 buf[len++] = c;
137 }
138 buf[len++] = 0; /* terminating null byte */
139
140 /* system ("stty echo"); */
141 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
142 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
143 /*
144 * terminal probably still hides input characters, but we have password
145 * and anyway we are unable to set terminal to the previous state, so
146 * just continue
147 */
148 }
149 fprintf(tty, "\n");
150
151 fclose(tty);
152 return buf;
153}
154
155static char *
156sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo)
157{
158 unsigned int buflen = 8, response_len;
159 char c = 0;
160 struct termios newterm, oldterm;
161 char *newtext, *response;
162 FILE *tty;
163
164 if (!(tty = fopen("/dev/tty", "r+"))) {
165 ERR("Unable to open the current terminal (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
166 return NULL;
167 }
168
169 if (tcgetattr(fileno(tty), &oldterm) != 0) {
170 ERR("Unable to get terminal settings (%d: %s).", __LINE__, strerror(errno));
171 return NULL;
172 }
173
174 if (auth_name && (!fwrite(auth_name, sizeof(char), strlen(auth_name), tty)
175 || !fwrite("\n", sizeof(char), 1, tty))) {
176 ERR("Writing the auth method name into stdout failed.");
177 return NULL;
178 }
179
180 if (instruction && (!fwrite(instruction, sizeof(char), strlen(instruction), tty)
181 || !fwrite("\n", sizeof(char), 1, tty))) {
182 ERR("Writing the instruction into stdout failed.");
183 return NULL;
184 }
185
186 if (!fwrite(prompt, sizeof(char), strlen(prompt), tty)) {
187 ERR("Writing the authentication prompt into stdout failed.");
188 return NULL;
189 }
190 fflush(tty);
191 if (!echo) {
192 /* system("stty -echo"); */
193 newterm = oldterm;
194 newterm.c_lflag &= ~ECHO;
195 tcflush(fileno(tty), TCIFLUSH);
196 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
197 ERR("Unable to change terminal settings for hiding password (%d: %s).", __LINE__, strerror(errno));
198 return NULL;
199 }
200 }
201
202 response = malloc(buflen * sizeof *response);
203 response_len = 0;
204 if (!response) {
205 ERRMEM;
206 /* restore terminal settings */
207 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
208 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
209 }
210 return NULL;
211 }
212
213 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
214 if (response_len >= buflen - 1) {
215 buflen *= 2;
216 newtext = realloc(response, buflen * sizeof *newtext);
217 if (!newtext) {
218 ERR("Memory allocation failed (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
219 free(response);
220
221 /* restore terminal settings */
222 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
223 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
224 }
225 return NULL;
226 } else {
227 response = newtext;
228 }
229 }
230 response[response_len++] = c;
231 }
232 /* terminating null byte */
233 response[response_len++] = '\0';
234
235 /* system ("stty echo"); */
236 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
237 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
238 /*
239 * terminal probably still hides input characters, but we have password
240 * and anyway we are unable to set terminal to the previous state, so
241 * just continue
242 */
243 }
244
245 fprintf(tty, "\n");
246 fclose(tty);
247 return response;
248}
249
250static char *
251sshauth_passphrase(const char* privkey_path)
252{
253 char c, *buf, *newbuf;
254 int buflen = 1024, len = 0;
255 struct termios newterm, oldterm;
256 FILE *tty;
257
258 buf = malloc(buflen * sizeof *buf);
259 if (!buf) {
260 ERR("Memory allocation failed (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
261 return NULL;
262 }
263
264 if (!(tty = fopen("/dev/tty", "r+"))) {
265 ERR("Unable to open the current terminal (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
266 return NULL;
267 }
268
269 if (tcgetattr(fileno(tty), &oldterm)) {
270 ERR("Unable to get terminal settings (%d: %s).", __LINE__, strerror(errno));
271 return NULL;
272 }
273
274 fprintf(tty, "Enter passphrase for the key '%s':", privkey_path);
275 fflush(tty);
276
277 /* system("stty -echo"); */
278 newterm = oldterm;
279 newterm.c_lflag &= ~ECHO;
280 newterm.c_lflag &= ~ICANON;
281 tcflush(fileno(tty), TCIFLUSH);
282 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
283 ERR("Unable to change terminal settings for hiding password (%d: %s).", __LINE__, strerror(errno));
284 return NULL;
285 }
286
287 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
288 if (len >= buflen - 1) {
289 buflen *= 2;
290 newbuf = realloc(buf, buflen * sizeof *newbuf);
291 if (!newbuf) {
292 ERRMEM;
293 /* remove content of the buffer */
294 memset(buf, 0, len);
295 free(buf);
296
297 /* restore terminal settings */
298 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
299 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
300 }
301
302 return NULL;
303 }
304 buf = newbuf;
305 }
306 buf[len++] = (char)c;
307 }
308 buf[len++] = 0; /* terminating null byte */
309
310 /* system ("stty echo"); */
311 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
312 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
313 /*
314 * terminal probably still hides input characters, but we have password
315 * and anyway we are unable to set terminal to the previous state, so
316 * just continue
317 */
318 }
319 fprintf(tty, "\n");
320
321 fclose(tty);
322 return buf;
323}
324
325/* TODO define this switch */
326#ifdef ENABLE_DNSSEC
327
328/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
329/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
330static int
331sshauth_hostkey_hash_dnssec_check(const char *hostname, const char *sha1hash, int type, int alg) {
332 ns_msg handle;
333 ns_rr rr;
334 val_status_t val_status;
335 const unsigned char* rdata;
336 unsigned char buf[4096];
337 int buf_len = 4096;
338 int ret = 0, i, j, len;
339
340 /* class 1 - internet, type 44 - SSHFP */
341 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
342
343 if ((len < 0) || !val_istrusted(val_status)) {
344 ret = 2;
345 goto finish;
346 }
347
348 if (ns_initparse(buf, len, &handle) < 0) {
349 ERR("Failed to initialize DNSSEC response parser.");
350 ret = 2;
351 goto finish;
352 }
353
354 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
355 ERR("DNSSEC query returned %d.", i);
356 ret = 2;
357 goto finish;
358 }
359
360 if (!libsres_msg_getflag(handle, ns_f_ad)) {
361 /* response not secured by DNSSEC */
362 ret = 1;
363 }
364
365 /* query section */
366 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
367 ERROR("DNSSEC query section parser fail.");
368 ret = 2;
369 goto finish;
370 }
371
372 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
373 ERROR("DNSSEC query in the answer does not match the original query.");
374 ret = 2;
375 goto finish;
376 }
377
378 /* answer section */
379 i = 0;
380 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
381 if (ns_rr_type(rr) != 44) {
382 ++i;
383 continue;
384 }
385
386 rdata = ns_rr_rdata(rr);
387 if (rdata[0] != type) {
388 ++i;
389 continue;
390 }
391 if (rdata[1] != alg) {
392 ++i;
393 continue;
394 }
395
396 /* we found the correct SSHFP entry */
397 rdata += 2;
398 for (j = 0; j < 20; ++j) {
399 if (rdata[j] != (unsigned char)sha1hash[j]) {
400 ret = 2;
401 goto finish;
402 }
403 }
404
405 /* server fingerprint is supported by a DNS entry,
406 * we have already determined if DNSSEC was used or not
407 */
408 goto finish;
409 }
410
411 /* no match */
412 ret = 2;
413
414finish:
415 val_free_validator_state();
416 return ret;
417}
418
419#endif
420
421static int
422sshauth_hostkey_check(const char *hostname, ssh_session session)
423{
424 char *hexa;
425 int c, state, ret;
426 ssh_key srv_pubkey;
427 unsigned char *hash_sha1 = NULL;
428 size_t hlen;
429 enum ssh_keytypes_e srv_pubkey_type;
430 char answer[5];
431
432 state = ssh_is_server_known(session);
433
434 ret = ssh_get_publickey(session, &srv_pubkey);
435 if (ret < 0) {
436 ERR("Unable to get server public key.");
437 return EXIT_FAILURE;
438 }
439
440 srv_pubkey_type = ssh_key_type(srv_pubkey);
441 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
442 ssh_key_free(srv_pubkey);
443 if (ret < 0) {
444 ERR("Failed to calculate SHA1 hash of the server public key.");
445 return EXIT_FAILURE;
446 }
447
448 hexa = ssh_get_hexa(hash_sha1, hlen);
449
450 switch (state) {
451 case SSH_SERVER_KNOWN_OK:
452 break; /* ok */
453
454 case SSH_SERVER_KNOWN_CHANGED:
455 ERR("Remote host key changed, the connection will be terminated!");
456 goto fail;
457
458 case SSH_SERVER_FOUND_OTHER:
459 ERR("The remote host key was not found but another type of key was, the connection will be terminated.");
460 goto fail;
461
462 case SSH_SERVER_FILE_NOT_FOUND:
463 WRN("Could not find the known hosts file.");
464 /* fallback to SSH_SERVER_NOT_KNOWN behavior */
465
466 case SSH_SERVER_NOT_KNOWN:
467#ifdef ENABLE_DNSSEC
468 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
469 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
470 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
471 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
472 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
473 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
474 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
475 }
476
477 /* DNSSEC SSHFP check successful, that's enough */
478 if (!ret) {
479 DBG("DNSSEC SSHFP check successful");
480 ssh_write_knownhost(session);
481 ssh_clean_pubkey_hash(&hash_sha1);
482 ssh_string_free_char(hexa);
483 return EXIT_SUCCESS;
484 }
485 }
486#endif
487
488 /* try to get result from user */
489 fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname);
490 fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa);
491
492#ifdef ENABLE_DNSSEC
493 if (ret == 2) {
494 fprintf(stdout, "No matching host key fingerprint found in DNS.\n");
495 } else if (ret == 1) {
496 fprintf(stdout, "Matching host key fingerprint found in DNS.\n");
497 }
498#endif
499
500 fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? ");
501
502 do {
503 if (fscanf(stdin, "%4s", answer) == EOF) {
504 ERR("fscanf() failed (%s).", strerror(errno));
505 goto fail;
506 }
507 while (((c = getchar()) != EOF) && (c != '\n'));
508
509 fflush(stdin);
510 if (!strcmp("yes", answer)) {
511 /* store the key into the host file */
512 ret = ssh_write_knownhost(session);
513 if (ret < 0) {
Michal Vaskoc111ac52015-12-08 14:36:36 +0100514 WRN("Adding the known host %s failed (%s).", hostname, ssh_get_error(session));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100515 }
516 } else if (!strcmp("no", answer)) {
517 goto fail;
518 } else {
519 fprintf(stdout, "Please type 'yes' or 'no': ");
520 }
521 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
522
523 break;
524
525 case SSH_SERVER_ERROR:
526 ssh_clean_pubkey_hash(&hash_sha1);
527 fprintf(stderr,"%s",ssh_get_error(session));
528 return -1;
529 }
530
531 ssh_clean_pubkey_hash(&hash_sha1);
532 ssh_string_free_char(hexa);
533 return EXIT_SUCCESS;
534
535fail:
536 ssh_clean_pubkey_hash(&hash_sha1);
537 ssh_string_free_char(hexa);
538 return EXIT_FAILURE;
539}
540
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100541API int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100542nc_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100543{
544 int i;
545 FILE *key;
546 char line[128];
547
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100548 if (!pub_key || !priv_key) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100549 return EXIT_FAILURE;
550 }
551
552 for (i = 0; i < ssh_opts.key_count; ++i) {
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100553 if (!strcmp(ssh_opts.keys[i].pubkey_path, pub_key) || !strcmp(ssh_opts.keys[i].privkey_path, priv_key)) {
554 if (strcmp(ssh_opts.keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100555 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100556 priv_key, ssh_opts.keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100557 continue;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100558 } else if (strcmp(ssh_opts.keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100559 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100560 pub_key, ssh_opts.keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100561 continue;
562 }
563
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100564 ERR("SSH key pair already set.");
565 return EXIT_FAILURE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100566 }
567 }
568
Michal Vasko7b62fed2015-10-26 15:39:46 +0100569 /* add the keys safely */
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100570 ++ssh_opts.key_count;
571 ssh_opts.keys = realloc(ssh_opts.keys, ssh_opts.key_count * sizeof *ssh_opts.keys);
572 ssh_opts.keys[ssh_opts.key_count - 1].pubkey_path = strdup(pub_key);
573 ssh_opts.keys[ssh_opts.key_count - 1].privkey_path = strdup(priv_key);
574 ssh_opts.keys[ssh_opts.key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100575
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100576 /* check encryption */
577 if ((key = fopen(priv_key, "r"))) {
578 /* 1st line - key type */
579 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100580 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100581 ERR("fgets() on %s failed.", priv_key);
582 return EXIT_FAILURE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100583 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100584 /* 2nd line - encryption information or key */
585 if (!fgets(line, sizeof line, key)) {
586 fclose(key);
587 ERR("fgets() on %s failed.", priv_key);
588 return EXIT_FAILURE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100589 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100590 fclose(key);
591 if (strcasestr(line, "encrypted")) {
592 ssh_opts.keys[ssh_opts.key_count - 1].privkey_crypt = 1;
593 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100594 }
595
596 return EXIT_SUCCESS;
597}
598
599API int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100600nc_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100601{
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100602 if (idx >= ssh_opts.key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100603 return EXIT_FAILURE;
604 }
605
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100606 free(ssh_opts.keys[idx].pubkey_path);
607 free(ssh_opts.keys[idx].privkey_path);
608
609 --ssh_opts.key_count;
610
611 memmove(ssh_opts.keys + idx, ssh_opts.keys + idx + 1, (ssh_opts.key_count - idx) * sizeof *ssh_opts.keys);
612 ssh_opts.keys = realloc(ssh_opts.keys, ssh_opts.key_count * sizeof *ssh_opts.keys);
613
Michal Vasko7b62fed2015-10-26 15:39:46 +0100614 return EXIT_SUCCESS;
615}
616
617API int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100618nc_ssh_get_keypair_count(void)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100619{
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100620 return ssh_opts.key_count;
621}
622
623API int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100624nc_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100625{
626 if (idx >= ssh_opts.key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100627 return EXIT_FAILURE;
628 }
629
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100630 if (pub_key) {
631 *pub_key = ssh_opts.keys[idx].pubkey_path;
632 }
633 if (priv_key) {
634 *priv_key = ssh_opts.keys[idx].privkey_path;
635 }
636
Michal Vasko7b62fed2015-10-26 15:39:46 +0100637 return EXIT_SUCCESS;
638}
639
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100640API void
Michal Vaskoc111ac52015-12-08 14:36:36 +0100641nc_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, short int pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100642{
643 if (pref < 0) {
644 pref = -1;
645 }
646
647 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
648 ssh_opts.auth_pref[0].value = pref;
649 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
650 ssh_opts.auth_pref[1].value = pref;
651 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
652 ssh_opts.auth_pref[2].value = pref;
653 }
654}
655
656API short int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100657nc_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100658{
659 short int pref = 0;
660
661 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
662 pref = ssh_opts.auth_pref[0].value;
663 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
664 pref = ssh_opts.auth_pref[1].value;
665 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
666 pref = ssh_opts.auth_pref[2].value;
667 }
668
669 return pref;
670}
671
Michal Vasko7b62fed2015-10-26 15:39:46 +0100672/* Establish a secure SSH connection, authenticate, and create a channel with the 'netconf' subsystem.
673 * Host, port, username, and a connected socket is expected to be set.
674 */
675static int
676connect_ssh_session_netconf(struct nc_session *session)
677{
678 int i, j, ret_auth, userauthlist;
679 int auth = 0;
680 const char* prompt;
681 char *s, *answer, echo;
682 ssh_key pubkey, privkey;
683 ssh_session ssh_sess;
684
685 ssh_sess = session->ti.libssh.session;
686
687 if (ssh_connect(ssh_sess) != SSH_OK) {
688 ERR("Starting the SSH session failed (%s)", ssh_get_error(ssh_sess));
689 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
690 return EXIT_FAILURE;
691 }
692
693 if (sshauth_hostkey_check(session->host, ssh_sess)) {
694 ERR("Checking the host key failed.");
695 return EXIT_FAILURE;
696 }
697
698 if ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_ERROR) {
699 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
700 return EXIT_FAILURE;
701 }
702
703 /* check what authentication methods are available */
704 userauthlist = ssh_userauth_list(ssh_sess, NULL);
705 if (userauthlist & SSH_AUTH_METHOD_PASSWORD) {
706 auth |= NC_SSH_AUTH_PASSWORD;
707 }
708 if (userauthlist & SSH_AUTH_METHOD_PUBLICKEY) {
Michal Vasko206d3b12015-12-04 11:08:42 +0100709 auth |= NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100710 }
711 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
712 auth |= NC_SSH_AUTH_INTERACTIVE;
713 }
714 if (!auth && (ret_auth != SSH_AUTH_SUCCESS)) {
715 ERR("Unable to authenticate to the remote server (Authentication methods not supported).");
716 return EXIT_FAILURE;
717 }
718
719 /* select authentication according to preferences */
Michal Vaskoc111ac52015-12-08 14:36:36 +0100720 for (i = 0; i < NC_SSH_AUTH_COUNT; i++) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100721 if (!(ssh_opts.auth_pref[i].type & auth)) {
722 /* method not supported by server, skip */
723 continue;
724 }
725
726 if (ssh_opts.auth_pref[i].value < 0) {
727 /* all following auth methods are disabled via negative preference value */
728 ERR("Unable to authenticate to the remote server (method disabled or permission denied).");
729 return EXIT_FAILURE;
730 }
731
732 /* found common authentication method */
733 switch (ssh_opts.auth_pref[i].type) {
734 case NC_SSH_AUTH_PASSWORD:
735 VRB("Password authentication (host %s, user %s)", session->host, session->username);
736 s = sshauth_password(session->username, session->host);
737 if ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) != SSH_AUTH_SUCCESS) {
738 memset(s, 0, strlen(s));
739 VRB("Authentication failed (%s)", ssh_get_error(ssh_sess));
740 }
741 free(s);
742 break;
743 case NC_SSH_AUTH_INTERACTIVE:
744 VRB("Keyboard-interactive authentication");
745 while ((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) {
746 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
747 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
748 if (prompt == NULL) {
749 break;
750 }
751 answer = sshauth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
752 ssh_userauth_kbdint_getinstruction(ssh_sess),
753 prompt, echo);
754 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
755 free(answer);
756 break;
757 }
758 free(answer);
759 }
760 }
761
762 if (ret_auth == SSH_AUTH_ERROR) {
763 VRB("Authentication failed (%s)", ssh_get_error(ssh_sess));
764 }
765
766 break;
Michal Vasko206d3b12015-12-04 11:08:42 +0100767 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko7b62fed2015-10-26 15:39:46 +0100768 VRB("Publickey athentication");
769
770 /* if publickeys path not provided, we cannot continue */
771 if (!ssh_opts.key_count) {
772 VRB("No key pair specified.");
773 break;
774 }
775
776 for (j = 0; j < ssh_opts.key_count; j++) {
777 VRB("Trying to authenticate using %spair %s %s",
778 ssh_opts.keys[j].privkey_crypt ? "password-protected " : "", ssh_opts.keys[j].privkey_path,
779 ssh_opts.keys[j].pubkey_path);
780
781 if (ssh_pki_import_pubkey_file(ssh_opts.keys[j].pubkey_path, &pubkey) != SSH_OK) {
782 WRN("Failed to import the key \"%s\".", ssh_opts.keys[j].pubkey_path);
783 continue;
784 }
785 ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey);
786 if ((ret_auth == SSH_AUTH_DENIED) || (ret_auth == SSH_AUTH_PARTIAL)) {
787 ssh_key_free(pubkey);
788 continue;
789 }
790 if (ret_auth == SSH_AUTH_ERROR) {
791 ERR("Authentication failed (%s)", ssh_get_error(ssh_sess));
792 ssh_key_free(pubkey);
793 break;
794 }
795
796 if (ssh_opts.keys[j].privkey_crypt) {
797 s = sshauth_passphrase(ssh_opts.keys[j].privkey_path);
798 } else {
799 s = NULL;
800 }
801
802 if (ssh_pki_import_privkey_file(ssh_opts.keys[j].privkey_path, s, NULL, NULL, &privkey) != SSH_OK) {
803 WRN("Failed to import the key \"%s\".", ssh_opts.keys[j].privkey_path);
804 if (s) {
805 memset(s, 0, strlen(s));
806 free(s);
807 }
808 ssh_key_free(pubkey);
809 continue;
810 }
811
812 if (s) {
813 memset(s, 0, strlen(s));
814 free(s);
815 }
816
817 ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey);
818 ssh_key_free(pubkey);
819 ssh_key_free(privkey);
820
821 if (ret_auth == SSH_AUTH_ERROR) {
822 ERR("Authentication failed (%s)", ssh_get_error(ssh_sess));
823 }
824 if (ret_auth == SSH_AUTH_SUCCESS) {
825 break;
826 }
827 }
828 break;
829 }
830
831 if (ret_auth == SSH_AUTH_SUCCESS) {
832 break;
833 }
834 }
835
836 /* check a state of authentication */
837 if (ret_auth != SSH_AUTH_SUCCESS) {
838 ERR("Authentication failed.");
839 return EXIT_FAILURE;
840 }
841
842 /* open a channel */
843 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
844 if (ssh_channel_open_session(session->ti.libssh.channel) != SSH_OK) {
845 ssh_channel_free(session->ti.libssh.channel);
846 session->ti.libssh.channel = NULL;
847 ERR("Opening an SSH channel failed (%s)", ssh_get_error(ssh_sess));
848 return EXIT_FAILURE;
849 }
850
851 /* execute the NETCONF subsystem on the channel */
852 if (ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf") != SSH_OK) {
853 ssh_channel_free(session->ti.libssh.channel);
854 session->ti.libssh.channel = NULL;
855 ERR("Starting the \"netconf\" SSH subsystem failed (%s)", ssh_get_error(ssh_sess));
856 return EXIT_FAILURE;
857 }
858
859 return EXIT_SUCCESS;
Radek Krejciac6d3472015-10-22 15:47:18 +0200860}
861
862API struct nc_session *
Michal Vaskoc111ac52015-12-08 14:36:36 +0100863nc_connect_ssh(const char *host, uint16_t port, const char *username, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +0200864{
Michal Vaskoc111ac52015-12-08 14:36:36 +0100865 const int timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100866 int sock;
Radek Krejciac6d3472015-10-22 15:47:18 +0200867 struct passwd *pw;
868 struct nc_session *session = NULL;
869
870 /* process parameters */
871 if (!host || strisempty(host)) {
872 host = "localhost";
873 }
874
875 if (!port) {
876 port = NC_PORT_SSH;
877 }
878
879 if (!username) {
880 pw = getpwuid(getuid());
881 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100882 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
883 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200884 } else {
885 username = pw->pw_name;
886 }
887 }
888
889 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100890 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +0200891 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100892 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +0200893 return NULL;
894 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100895 session->status = NC_STATUS_STARTING;
896 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +0200897
Michal Vasko7b62fed2015-10-26 15:39:46 +0100898 /* transport lock */
899 session->ti_lock = malloc(sizeof *session->ti_lock);
900 if (!session->ti_lock) {
901 ERRMEM;
902 goto fail;
903 }
904 pthread_mutex_init(session->ti_lock, NULL);
905
906 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100907 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100908 session->ti.libssh.session = ssh_new();
909 if (!session->ti.libssh.session) {
910 ERR("Unable to initialize SSH session.");
911 goto fail;
912 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200913
Michal Vasko7b62fed2015-10-26 15:39:46 +0100914 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100915 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
916 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port);
917 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100918 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
919
920 /* create and assign communication socket */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100921 sock = nc_connect_getsocket(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100922 if (sock == -1) {
923 goto fail;
924 }
925 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
926
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100927 /* temporarily, for session connection */
928 session->host = host;
929 session->username = username;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100930 if (connect_ssh_session_netconf(session)) {
931 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +0200932 }
933
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100934 /* assign context (dicionary needed for handshake) */
935 if (!ctx) {
936 ctx = ly_ctx_new(SCHEMAS_DIR);
937 } else {
938 session->flags |= NC_SESSION_SHAREDCTX;
939 }
940 session->ctx = ctx;
941
Radek Krejciac6d3472015-10-22 15:47:18 +0200942 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100943 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100944 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +0200945 }
Michal Vaskoad611702015-12-03 13:41:51 +0100946 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +0200947
Michal Vasko57eb9402015-12-08 14:38:12 +0100948 if (nc_ctx_check_and_fill(session)) {
949 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100950 }
951
952 /* store information into the dictionary */
953 session->host = lydict_insert(ctx, host, 0);
954 session->port = port;
955 session->username = lydict_insert(ctx, username, 0);
956
Radek Krejciac6d3472015-10-22 15:47:18 +0200957 return session;
958
Michal Vasko7b62fed2015-10-26 15:39:46 +0100959fail:
Radek Krejciac6d3472015-10-22 15:47:18 +0200960 nc_session_free(session);
961 return NULL;
962}
963
964API struct nc_session *
965nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
966{
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100967 char *host = NULL, *username = NULL;
968 unsigned short port = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100969 int sock;
970 struct passwd *pw;
971 struct nc_session *session = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200972
Michal Vasko7b62fed2015-10-26 15:39:46 +0100973 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100974 session = calloc(1, sizeof *session);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100975 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100976 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100977 return NULL;
978 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100979 session->status = NC_STATUS_STARTING;
980 session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100981
982 /* transport lock */
983 session->ti_lock = malloc(sizeof *session->ti_lock);
984 if (!session->ti_lock) {
985 ERRMEM;
986 goto fail;
987 }
988 pthread_mutex_init(session->ti_lock, NULL);
989
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100990 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100991 session->ti.libssh.session = ssh_session;
992
Michal Vasko745ff832015-12-08 14:40:29 +0100993 /* was port set? */
994 ssh_options_get_port(ssh_session, (unsigned int *)&port);
995
996 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100997 /*
Michal Vasko745ff832015-12-08 14:40:29 +0100998 * There is no file descriptor (detected based on the host, there is no way to check
999 * the SSH_OPTIONS_FD directly :/), we need to create it. (TCP/IP layer)
Michal Vasko7b62fed2015-10-26 15:39:46 +01001000 */
1001
Michal Vasko7b62fed2015-10-26 15:39:46 +01001002 /* remember host */
Michal Vasko745ff832015-12-08 14:40:29 +01001003 host = strdup("localhost");
1004 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001005
1006 /* create and connect socket */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001007 sock = nc_connect_getsocket(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001008 if (sock == -1) {
1009 goto fail;
1010 }
1011 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1012 }
1013
Michal Vasko745ff832015-12-08 14:40:29 +01001014 /* was username set? */
1015 ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username);
1016
Michal Vasko7b62fed2015-10-26 15:39:46 +01001017 if (!ssh_is_connected(ssh_session)) {
1018 /*
1019 * We are connected, but not SSH authenticated. (Transport layer)
1020 */
1021
Michal Vasko7b62fed2015-10-26 15:39:46 +01001022 /* remember username */
1023 if (!username) {
1024 pw = getpwuid(getuid());
1025 if (!pw) {
1026 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1027 goto fail;
1028 }
1029
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001030 username = strdup(pw->pw_name);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001031 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001032 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001033
1034 /* authenticate SSH session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001035 session->host = host;
1036 session->username = username;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001037 if (connect_ssh_session_netconf(session)) {
1038 goto fail;
1039 }
1040 }
1041
1042 /*
1043 * SSH session is established, create NETCONF session. (Application layer)
1044 */
1045
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001046 /* assign context (dicionary needed for handshake) */
1047 if (!ctx) {
1048 ctx = ly_ctx_new(SCHEMAS_DIR);
1049 } else {
1050 session->flags |= NC_SESSION_SHAREDCTX;
1051 }
1052 session->ctx = ctx;
1053
Michal Vasko7b62fed2015-10-26 15:39:46 +01001054 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001055 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001056 goto fail;
1057 }
Michal Vaskoad611702015-12-03 13:41:51 +01001058 session->status = NC_STATUS_RUNNING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001059
Michal Vasko57eb9402015-12-08 14:38:12 +01001060 if (nc_ctx_check_and_fill(session)) {
1061 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001062 }
1063
1064 /* store information into the dictionary */
1065 if (host) {
1066 session->host = lydict_insert_zc(ctx, host);
1067 }
1068 if (port) {
1069 session->port = port;
1070 }
1071 if (username) {
1072 session->username = lydict_insert_zc(ctx, username);
1073 }
1074
Michal Vasko7b62fed2015-10-26 15:39:46 +01001075 return session;
1076
1077fail:
1078 nc_session_free(session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001079 return NULL;
1080}
1081
1082API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001083nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001084{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001085 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001086
Michal Vasko7b62fed2015-10-26 15:39:46 +01001087 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001088 new_session = calloc(1, sizeof *new_session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001089 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001090 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001091 return NULL;
1092 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001093 new_session->status = NC_STATUS_STARTING;
1094 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001095
1096 /* share some parameters including the session lock */
1097 new_session->ti_type = NC_TI_LIBSSH;
1098 new_session->ti_lock = session->ti_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001099 new_session->ti.libssh.session = session->ti.libssh.session;
1100
1101 /* create the channel safely */
1102 pthread_mutex_lock(new_session->ti_lock);
1103
1104 /* open a channel */
1105 new_session->ti.libssh.channel = ssh_channel_new(new_session->ti.libssh.session);
1106 if (ssh_channel_open_session(new_session->ti.libssh.channel) != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001107 ERR("Opening an SSH channel failed (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001108 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001109 }
1110 /* execute the NETCONF subsystem on the channel */
1111 if (ssh_channel_request_subsystem(new_session->ti.libssh.channel, "netconf") != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001112 ERR("Starting the \"netconf\" SSH subsystem failed (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001113 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001114 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001115
1116 /* assign context (dicionary needed for handshake) */
1117 if (!ctx) {
1118 ctx = ly_ctx_new(SCHEMAS_DIR);
1119 } else {
1120 session->flags |= NC_SESSION_SHAREDCTX;
1121 }
1122 session->ctx = ctx;
1123
Michal Vasko7b62fed2015-10-26 15:39:46 +01001124 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001125 if (nc_handshake(new_session)) {
1126 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001127 }
Michal Vaskoad611702015-12-03 13:41:51 +01001128 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001129
Michal Vasko57eb9402015-12-08 14:38:12 +01001130 if (nc_ctx_check_and_fill(session)) {
1131 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001132 }
1133
1134 /* store information into session and the dictionary */
1135 session->host = lydict_insert(ctx, session->host, 0);
1136 session->port = session->port;
1137 session->username = lydict_insert(ctx, session->username, 0);
1138
Michal Vasko7b62fed2015-10-26 15:39:46 +01001139 pthread_mutex_unlock(new_session->ti_lock);
1140
1141 /* append to the session ring list */
1142 if (!session->ti.libssh.next) {
1143 session->ti.libssh.next = new_session;
1144 new_session->ti.libssh.next = session;
1145 } else {
1146 ptr = session->ti.libssh.next;
1147 session->ti.libssh.next = new_session;
1148 new_session->ti.libssh.next = ptr;
1149 }
1150
1151 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001152
1153fail:
1154 nc_session_free(new_session);
1155 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001156}
Michal Vasko80cad7f2015-12-08 14:42:27 +01001157
1158API struct nc_session *
1159nc_callhome_accept_ssh(uint16_t port, const char *username, int32_t timeout, struct ly_ctx *ctx)
1160{
1161 const int ssh_timeout = NC_SSH_TIMEOUT;
1162 int sock;
1163 char *server_host;
1164 ssh_session sess;
1165
1166 if (!port) {
1167 port = NC_PORT_CH_SSH;
1168 }
1169
1170 sock = nc_callhome_accept_connection(port, timeout, NULL, &server_host);
1171 if (sock == -1) {
1172 return NULL;
1173 }
1174
1175 sess = ssh_new();
1176 if (!sess) {
1177 ERR("Unable to initialize an SSH session.");
1178 close(sock);
1179 return NULL;
1180 }
1181
1182 ssh_options_set(sess, SSH_OPTIONS_FD, &sock);
1183 ssh_options_set(sess, SSH_OPTIONS_HOST, server_host);
1184 ssh_options_set(sess, SSH_OPTIONS_PORT, &port);
1185 ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout);
1186 if (username) {
1187 ssh_options_set(sess, SSH_OPTIONS_USER, username);
1188 }
1189
1190 free(server_host);
1191
1192 return nc_connect_libssh(sess, ctx);
1193}