blob: 22ae67ae913a4c9b58eca89593f40d5af7dadd5c [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
Radek Krejciac6d3472015-10-22 15:47:18 +020045#include <libyang/libyang.h>
46
47#include "libnetconf.h"
Michal Vasko7b62fed2015-10-26 15:39:46 +010048#include "session.h"
Michal Vasko9e2d3a32015-11-10 13:09:18 +010049#include "session_p.h"
Radek Krejciac6d3472015-10-22 15:47:18 +020050
Michal Vasko7b62fed2015-10-26 15:39:46 +010051static struct nc_ssh_auth_opts ssh_opts = {
Michal Vasko206d3b12015-12-04 11:08:42 +010052 .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}}
Michal Vasko7b62fed2015-10-26 15:39:46 +010053};
Radek Krejciac6d3472015-10-22 15:47:18 +020054
Michal Vasko990089e2015-10-27 15:05:25 +010055API void
Michal Vaskoc111ac52015-12-08 14:36:36 +010056nc_ssh_client_init(void)
Michal Vasko990089e2015-10-27 15:05:25 +010057{
58 ssh_threads_set_callbacks(ssh_threads_get_pthread());
59 ssh_init();
60}
61
62API void
Michal Vaskoc111ac52015-12-08 14:36:36 +010063nc_ssh_client_destroy(void)
Michal Vasko990089e2015-10-27 15:05:25 +010064{
65 int i;
66
67 for (i = 0; i < ssh_opts.key_count; ++i) {
68 free(ssh_opts.keys[i].pubkey_path);
69 free(ssh_opts.keys[i].privkey_path);
70 }
71
72 free(ssh_opts.keys);
73 ssh_opts.keys = NULL;
74 ssh_opts.key_count = 0;
75}
76
Michal Vasko7b62fed2015-10-26 15:39:46 +010077static char *
78sshauth_password(const char *username, const char *hostname)
Radek Krejciac6d3472015-10-22 15:47:18 +020079{
Michal Vasko7b62fed2015-10-26 15:39:46 +010080 char *buf, *newbuf;
81 int buflen = 1024, len = 0;
82 char c = 0;
83 struct termios newterm, oldterm;
84 FILE *tty;
Radek Krejciac6d3472015-10-22 15:47:18 +020085
Michal Vasko7b62fed2015-10-26 15:39:46 +010086 buf = malloc(buflen * sizeof *buf);
87 if (!buf) {
88 ERRMEM;
89 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +020090 }
91
Michal Vasko7b62fed2015-10-26 15:39:46 +010092 if (!(tty = fopen("/dev/tty", "r+"))) {
93 ERR("Unable to open the current terminal (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
94 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +020095 }
96
Michal Vasko7b62fed2015-10-26 15:39:46 +010097 if (tcgetattr(fileno(tty), &oldterm)) {
98 ERR("Unable to get terminal settings (%d: %s).", __LINE__, strerror(errno));
99 return NULL;
100 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200101
Michal Vasko7b62fed2015-10-26 15:39:46 +0100102 fprintf(tty, "%s@%s password: ", username, hostname);
103 fflush(tty);
Radek Krejciac6d3472015-10-22 15:47:18 +0200104
Michal Vasko7b62fed2015-10-26 15:39:46 +0100105 /* system("stty -echo"); */
106 newterm = oldterm;
107 newterm.c_lflag &= ~ECHO;
108 newterm.c_lflag &= ~ICANON;
109 tcflush(fileno(tty), TCIFLUSH);
110 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
111 ERR("Unable to change terminal settings for hiding password (%d: %s).", __LINE__, strerror(errno));
112 return NULL;
113 }
114
115 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
116 if (len >= buflen - 1) {
117 buflen *= 2;
118 newbuf = realloc(buf, buflen * sizeof *newbuf);
119 if (!newbuf) {
120 ERR("Memory allocation failed (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
121
122 /* remove content of the buffer */
123 memset(buf, 0, len);
124 free(buf);
125
126 /* restore terminal settings */
127 if (tcsetattr(fileno(tty), TCSANOW, &oldterm) != 0) {
128 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
129 }
130 return NULL;
131 } else {
132 buf = newbuf;
133 }
134 }
135 buf[len++] = c;
136 }
137 buf[len++] = 0; /* terminating null byte */
138
139 /* system ("stty echo"); */
140 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
141 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
142 /*
143 * terminal probably still hides input characters, but we have password
144 * and anyway we are unable to set terminal to the previous state, so
145 * just continue
146 */
147 }
148 fprintf(tty, "\n");
149
150 fclose(tty);
151 return buf;
152}
153
154static char *
155sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo)
156{
157 unsigned int buflen = 8, response_len;
158 char c = 0;
159 struct termios newterm, oldterm;
160 char *newtext, *response;
161 FILE *tty;
162
163 if (!(tty = fopen("/dev/tty", "r+"))) {
164 ERR("Unable to open the current terminal (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
165 return NULL;
166 }
167
168 if (tcgetattr(fileno(tty), &oldterm) != 0) {
169 ERR("Unable to get terminal settings (%d: %s).", __LINE__, strerror(errno));
170 return NULL;
171 }
172
173 if (auth_name && (!fwrite(auth_name, sizeof(char), strlen(auth_name), tty)
174 || !fwrite("\n", sizeof(char), 1, tty))) {
175 ERR("Writing the auth method name into stdout failed.");
176 return NULL;
177 }
178
179 if (instruction && (!fwrite(instruction, sizeof(char), strlen(instruction), tty)
180 || !fwrite("\n", sizeof(char), 1, tty))) {
181 ERR("Writing the instruction into stdout failed.");
182 return NULL;
183 }
184
185 if (!fwrite(prompt, sizeof(char), strlen(prompt), tty)) {
186 ERR("Writing the authentication prompt into stdout failed.");
187 return NULL;
188 }
189 fflush(tty);
190 if (!echo) {
191 /* system("stty -echo"); */
192 newterm = oldterm;
193 newterm.c_lflag &= ~ECHO;
194 tcflush(fileno(tty), TCIFLUSH);
195 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
196 ERR("Unable to change terminal settings for hiding password (%d: %s).", __LINE__, strerror(errno));
197 return NULL;
198 }
199 }
200
201 response = malloc(buflen * sizeof *response);
202 response_len = 0;
203 if (!response) {
204 ERRMEM;
205 /* restore terminal settings */
206 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
207 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
208 }
209 return NULL;
210 }
211
212 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
213 if (response_len >= buflen - 1) {
214 buflen *= 2;
215 newtext = realloc(response, buflen * sizeof *newtext);
216 if (!newtext) {
217 ERR("Memory allocation failed (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
218 free(response);
219
220 /* restore terminal settings */
221 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
222 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
223 }
224 return NULL;
225 } else {
226 response = newtext;
227 }
228 }
229 response[response_len++] = c;
230 }
231 /* terminating null byte */
232 response[response_len++] = '\0';
233
234 /* system ("stty echo"); */
235 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
236 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
237 /*
238 * terminal probably still hides input characters, but we have password
239 * and anyway we are unable to set terminal to the previous state, so
240 * just continue
241 */
242 }
243
244 fprintf(tty, "\n");
245 fclose(tty);
246 return response;
247}
248
249static char *
250sshauth_passphrase(const char* privkey_path)
251{
252 char c, *buf, *newbuf;
253 int buflen = 1024, len = 0;
254 struct termios newterm, oldterm;
255 FILE *tty;
256
257 buf = malloc(buflen * sizeof *buf);
258 if (!buf) {
259 ERR("Memory allocation failed (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
260 return NULL;
261 }
262
263 if (!(tty = fopen("/dev/tty", "r+"))) {
264 ERR("Unable to open the current terminal (%s:%d - %s).", __FILE__, __LINE__, strerror(errno));
265 return NULL;
266 }
267
268 if (tcgetattr(fileno(tty), &oldterm)) {
269 ERR("Unable to get terminal settings (%d: %s).", __LINE__, strerror(errno));
270 return NULL;
271 }
272
273 fprintf(tty, "Enter passphrase for the key '%s':", privkey_path);
274 fflush(tty);
275
276 /* system("stty -echo"); */
277 newterm = oldterm;
278 newterm.c_lflag &= ~ECHO;
279 newterm.c_lflag &= ~ICANON;
280 tcflush(fileno(tty), TCIFLUSH);
281 if (tcsetattr(fileno(tty), TCSANOW, &newterm)) {
282 ERR("Unable to change terminal settings for hiding password (%d: %s).", __LINE__, strerror(errno));
283 return NULL;
284 }
285
286 while ((fread(&c, 1, 1, tty) == 1) && (c != '\n')) {
287 if (len >= buflen - 1) {
288 buflen *= 2;
289 newbuf = realloc(buf, buflen * sizeof *newbuf);
290 if (!newbuf) {
291 ERRMEM;
292 /* remove content of the buffer */
293 memset(buf, 0, len);
294 free(buf);
295
296 /* restore terminal settings */
297 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
298 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
299 }
300
301 return NULL;
302 }
303 buf = newbuf;
304 }
305 buf[len++] = (char)c;
306 }
307 buf[len++] = 0; /* terminating null byte */
308
309 /* system ("stty echo"); */
310 if (tcsetattr(fileno(tty), TCSANOW, &oldterm)) {
311 ERR("Unable to restore terminal settings (%d: %s).", __LINE__, strerror(errno));
312 /*
313 * terminal probably still hides input characters, but we have password
314 * and anyway we are unable to set terminal to the previous state, so
315 * just continue
316 */
317 }
318 fprintf(tty, "\n");
319
320 fclose(tty);
321 return buf;
322}
323
324/* TODO define this switch */
325#ifdef ENABLE_DNSSEC
326
327/* return 0 (DNSSEC + key valid), 1 (unsecure DNS + key valid), 2 (key not found or an error) */
328/* type - 1 (RSA), 2 (DSA), 3 (ECDSA); alg - 1 (SHA1), 2 (SHA-256) */
329static int
330sshauth_hostkey_hash_dnssec_check(const char *hostname, const char *sha1hash, int type, int alg) {
331 ns_msg handle;
332 ns_rr rr;
333 val_status_t val_status;
334 const unsigned char* rdata;
335 unsigned char buf[4096];
336 int buf_len = 4096;
337 int ret = 0, i, j, len;
338
339 /* class 1 - internet, type 44 - SSHFP */
340 len = val_res_query(NULL, hostname, 1, 44, buf, buf_len, &val_status);
341
342 if ((len < 0) || !val_istrusted(val_status)) {
343 ret = 2;
344 goto finish;
345 }
346
347 if (ns_initparse(buf, len, &handle) < 0) {
348 ERR("Failed to initialize DNSSEC response parser.");
349 ret = 2;
350 goto finish;
351 }
352
353 if ((i = libsres_msg_getflag(handle, ns_f_rcode))) {
354 ERR("DNSSEC query returned %d.", i);
355 ret = 2;
356 goto finish;
357 }
358
359 if (!libsres_msg_getflag(handle, ns_f_ad)) {
360 /* response not secured by DNSSEC */
361 ret = 1;
362 }
363
364 /* query section */
365 if (ns_parserr(&handle, ns_s_qd, 0, &rr)) {
366 ERROR("DNSSEC query section parser fail.");
367 ret = 2;
368 goto finish;
369 }
370
371 if (strcmp(hostname, ns_rr_name(rr)) || (ns_rr_type(rr) != 44) || (ns_rr_class(rr) != 1)) {
372 ERROR("DNSSEC query in the answer does not match the original query.");
373 ret = 2;
374 goto finish;
375 }
376
377 /* answer section */
378 i = 0;
379 while (!ns_parserr(&handle, ns_s_an, i, &rr)) {
380 if (ns_rr_type(rr) != 44) {
381 ++i;
382 continue;
383 }
384
385 rdata = ns_rr_rdata(rr);
386 if (rdata[0] != type) {
387 ++i;
388 continue;
389 }
390 if (rdata[1] != alg) {
391 ++i;
392 continue;
393 }
394
395 /* we found the correct SSHFP entry */
396 rdata += 2;
397 for (j = 0; j < 20; ++j) {
398 if (rdata[j] != (unsigned char)sha1hash[j]) {
399 ret = 2;
400 goto finish;
401 }
402 }
403
404 /* server fingerprint is supported by a DNS entry,
405 * we have already determined if DNSSEC was used or not
406 */
407 goto finish;
408 }
409
410 /* no match */
411 ret = 2;
412
413finish:
414 val_free_validator_state();
415 return ret;
416}
417
418#endif
419
420static int
421sshauth_hostkey_check(const char *hostname, ssh_session session)
422{
423 char *hexa;
424 int c, state, ret;
425 ssh_key srv_pubkey;
426 unsigned char *hash_sha1 = NULL;
427 size_t hlen;
428 enum ssh_keytypes_e srv_pubkey_type;
429 char answer[5];
430
431 state = ssh_is_server_known(session);
432
433 ret = ssh_get_publickey(session, &srv_pubkey);
434 if (ret < 0) {
435 ERR("Unable to get server public key.");
436 return EXIT_FAILURE;
437 }
438
439 srv_pubkey_type = ssh_key_type(srv_pubkey);
440 ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen);
441 ssh_key_free(srv_pubkey);
442 if (ret < 0) {
443 ERR("Failed to calculate SHA1 hash of the server public key.");
444 return EXIT_FAILURE;
445 }
446
447 hexa = ssh_get_hexa(hash_sha1, hlen);
448
449 switch (state) {
450 case SSH_SERVER_KNOWN_OK:
451 break; /* ok */
452
453 case SSH_SERVER_KNOWN_CHANGED:
454 ERR("Remote host key changed, the connection will be terminated!");
455 goto fail;
456
457 case SSH_SERVER_FOUND_OTHER:
458 ERR("The remote host key was not found but another type of key was, the connection will be terminated.");
459 goto fail;
460
461 case SSH_SERVER_FILE_NOT_FOUND:
462 WRN("Could not find the known hosts file.");
463 /* fallback to SSH_SERVER_NOT_KNOWN behavior */
464
465 case SSH_SERVER_NOT_KNOWN:
466#ifdef ENABLE_DNSSEC
467 if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) || (srv_pubkey_type != SSH_KEYTYPE_RSA1)) {
468 if (srv_pubkey_type == SSH_KEYTYPE_DSS) {
469 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1);
470 } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) {
471 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1);
472 } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) {
473 ret = callback_ssh_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1);
474 }
475
476 /* DNSSEC SSHFP check successful, that's enough */
477 if (!ret) {
478 DBG("DNSSEC SSHFP check successful");
479 ssh_write_knownhost(session);
480 ssh_clean_pubkey_hash(&hash_sha1);
481 ssh_string_free_char(hexa);
482 return EXIT_SUCCESS;
483 }
484 }
485#endif
486
487 /* try to get result from user */
488 fprintf(stdout, "The authenticity of the host \'%s\' cannot be established.\n", hostname);
489 fprintf(stdout, "%s key fingerprint is %s.\n", ssh_key_type_to_char(srv_pubkey_type), hexa);
490
491#ifdef ENABLE_DNSSEC
492 if (ret == 2) {
493 fprintf(stdout, "No matching host key fingerprint found in DNS.\n");
494 } else if (ret == 1) {
495 fprintf(stdout, "Matching host key fingerprint found in DNS.\n");
496 }
497#endif
498
499 fprintf(stdout, "Are you sure you want to continue connecting (yes/no)? ");
500
501 do {
502 if (fscanf(stdin, "%4s", answer) == EOF) {
503 ERR("fscanf() failed (%s).", strerror(errno));
504 goto fail;
505 }
506 while (((c = getchar()) != EOF) && (c != '\n'));
507
508 fflush(stdin);
509 if (!strcmp("yes", answer)) {
510 /* store the key into the host file */
511 ret = ssh_write_knownhost(session);
512 if (ret < 0) {
Michal Vaskoc111ac52015-12-08 14:36:36 +0100513 WRN("Adding the known host %s failed (%s).", hostname, ssh_get_error(session));
Michal Vasko7b62fed2015-10-26 15:39:46 +0100514 }
515 } else if (!strcmp("no", answer)) {
516 goto fail;
517 } else {
518 fprintf(stdout, "Please type 'yes' or 'no': ");
519 }
520 } while (strcmp(answer, "yes") && strcmp(answer, "no"));
521
522 break;
523
524 case SSH_SERVER_ERROR:
525 ssh_clean_pubkey_hash(&hash_sha1);
526 fprintf(stderr,"%s",ssh_get_error(session));
527 return -1;
528 }
529
530 ssh_clean_pubkey_hash(&hash_sha1);
531 ssh_string_free_char(hexa);
532 return EXIT_SUCCESS;
533
534fail:
535 ssh_clean_pubkey_hash(&hash_sha1);
536 ssh_string_free_char(hexa);
537 return EXIT_FAILURE;
538}
539
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100540API int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100541nc_ssh_add_keypair(const char *pub_key, const char *priv_key)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100542{
543 int i;
544 FILE *key;
545 char line[128];
546
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100547 if (!pub_key || !priv_key) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100548 return EXIT_FAILURE;
549 }
550
551 for (i = 0; i < ssh_opts.key_count; ++i) {
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100552 if (!strcmp(ssh_opts.keys[i].pubkey_path, pub_key) || !strcmp(ssh_opts.keys[i].privkey_path, priv_key)) {
553 if (strcmp(ssh_opts.keys[i].pubkey_path, pub_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100554 WRN("Private key \"%s\" found with another public key \"%s\".",
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100555 priv_key, ssh_opts.keys[i].pubkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100556 continue;
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100557 } else if (strcmp(ssh_opts.keys[i].privkey_path, priv_key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100558 WRN("Public key \"%s\" found with another private key \"%s\".",
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100559 pub_key, ssh_opts.keys[i].privkey_path);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100560 continue;
561 }
562
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100563 ERR("SSH key pair already set.");
564 return EXIT_FAILURE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100565 }
566 }
567
Michal Vasko7b62fed2015-10-26 15:39:46 +0100568 /* add the keys safely */
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100569 ++ssh_opts.key_count;
570 ssh_opts.keys = realloc(ssh_opts.keys, ssh_opts.key_count * sizeof *ssh_opts.keys);
571 ssh_opts.keys[ssh_opts.key_count - 1].pubkey_path = strdup(pub_key);
572 ssh_opts.keys[ssh_opts.key_count - 1].privkey_path = strdup(priv_key);
573 ssh_opts.keys[ssh_opts.key_count - 1].privkey_crypt = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100574
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100575 /* check encryption */
576 if ((key = fopen(priv_key, "r"))) {
577 /* 1st line - key type */
578 if (!fgets(line, sizeof line, key)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100579 fclose(key);
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100580 ERR("fgets() on %s failed.", priv_key);
581 return EXIT_FAILURE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100582 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100583 /* 2nd line - encryption information or key */
584 if (!fgets(line, sizeof line, key)) {
585 fclose(key);
586 ERR("fgets() on %s failed.", priv_key);
587 return EXIT_FAILURE;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100588 }
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100589 fclose(key);
590 if (strcasestr(line, "encrypted")) {
591 ssh_opts.keys[ssh_opts.key_count - 1].privkey_crypt = 1;
592 }
Michal Vasko7b62fed2015-10-26 15:39:46 +0100593 }
594
595 return EXIT_SUCCESS;
596}
597
598API int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100599nc_ssh_del_keypair(int idx)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100600{
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100601 if (idx >= ssh_opts.key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100602 return EXIT_FAILURE;
603 }
604
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100605 free(ssh_opts.keys[idx].pubkey_path);
606 free(ssh_opts.keys[idx].privkey_path);
607
608 --ssh_opts.key_count;
609
610 memmove(ssh_opts.keys + idx, ssh_opts.keys + idx + 1, (ssh_opts.key_count - idx) * sizeof *ssh_opts.keys);
611 ssh_opts.keys = realloc(ssh_opts.keys, ssh_opts.key_count * sizeof *ssh_opts.keys);
612
Michal Vasko7b62fed2015-10-26 15:39:46 +0100613 return EXIT_SUCCESS;
614}
615
616API int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100617nc_ssh_get_keypair_count(void)
Michal Vasko7b62fed2015-10-26 15:39:46 +0100618{
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100619 return ssh_opts.key_count;
620}
621
622API int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100623nc_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100624{
625 if (idx >= ssh_opts.key_count) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100626 return EXIT_FAILURE;
627 }
628
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100629 if (pub_key) {
630 *pub_key = ssh_opts.keys[idx].pubkey_path;
631 }
632 if (priv_key) {
633 *priv_key = ssh_opts.keys[idx].privkey_path;
634 }
635
Michal Vasko7b62fed2015-10-26 15:39:46 +0100636 return EXIT_SUCCESS;
637}
638
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100639API void
Michal Vaskoc111ac52015-12-08 14:36:36 +0100640nc_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, short int pref)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100641{
642 if (pref < 0) {
643 pref = -1;
644 }
645
646 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
647 ssh_opts.auth_pref[0].value = pref;
648 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
649 ssh_opts.auth_pref[1].value = pref;
650 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
651 ssh_opts.auth_pref[2].value = pref;
652 }
653}
654
655API short int
Michal Vaskoc111ac52015-12-08 14:36:36 +0100656nc_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type)
Michal Vaskoe9bc8142015-12-04 11:09:35 +0100657{
658 short int pref = 0;
659
660 if (auth_type == NC_SSH_AUTH_INTERACTIVE) {
661 pref = ssh_opts.auth_pref[0].value;
662 } else if (auth_type == NC_SSH_AUTH_PASSWORD) {
663 pref = ssh_opts.auth_pref[1].value;
664 } else if (auth_type == NC_SSH_AUTH_PUBLICKEY) {
665 pref = ssh_opts.auth_pref[2].value;
666 }
667
668 return pref;
669}
670
Michal Vasko7b62fed2015-10-26 15:39:46 +0100671/* Establish a secure SSH connection, authenticate, and create a channel with the 'netconf' subsystem.
672 * Host, port, username, and a connected socket is expected to be set.
673 */
674static int
675connect_ssh_session_netconf(struct nc_session *session)
676{
677 int i, j, ret_auth, userauthlist;
678 int auth = 0;
679 const char* prompt;
680 char *s, *answer, echo;
681 ssh_key pubkey, privkey;
682 ssh_session ssh_sess;
683
684 ssh_sess = session->ti.libssh.session;
685
686 if (ssh_connect(ssh_sess) != SSH_OK) {
687 ERR("Starting the SSH session failed (%s)", ssh_get_error(ssh_sess));
688 DBG("Error code %d.", ssh_get_error_code(ssh_sess));
689 return EXIT_FAILURE;
690 }
691
692 if (sshauth_hostkey_check(session->host, ssh_sess)) {
693 ERR("Checking the host key failed.");
694 return EXIT_FAILURE;
695 }
696
697 if ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_ERROR) {
698 ERR("Authentication failed (%s).", ssh_get_error(ssh_sess));
699 return EXIT_FAILURE;
700 }
701
702 /* check what authentication methods are available */
703 userauthlist = ssh_userauth_list(ssh_sess, NULL);
704 if (userauthlist & SSH_AUTH_METHOD_PASSWORD) {
705 auth |= NC_SSH_AUTH_PASSWORD;
706 }
707 if (userauthlist & SSH_AUTH_METHOD_PUBLICKEY) {
Michal Vasko206d3b12015-12-04 11:08:42 +0100708 auth |= NC_SSH_AUTH_PUBLICKEY;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100709 }
710 if (userauthlist & SSH_AUTH_METHOD_INTERACTIVE) {
711 auth |= NC_SSH_AUTH_INTERACTIVE;
712 }
713 if (!auth && (ret_auth != SSH_AUTH_SUCCESS)) {
714 ERR("Unable to authenticate to the remote server (Authentication methods not supported).");
715 return EXIT_FAILURE;
716 }
717
718 /* select authentication according to preferences */
Michal Vaskoc111ac52015-12-08 14:36:36 +0100719 for (i = 0; i < NC_SSH_AUTH_COUNT; i++) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100720 if (!(ssh_opts.auth_pref[i].type & auth)) {
721 /* method not supported by server, skip */
722 continue;
723 }
724
725 if (ssh_opts.auth_pref[i].value < 0) {
726 /* all following auth methods are disabled via negative preference value */
727 ERR("Unable to authenticate to the remote server (method disabled or permission denied).");
728 return EXIT_FAILURE;
729 }
730
731 /* found common authentication method */
732 switch (ssh_opts.auth_pref[i].type) {
733 case NC_SSH_AUTH_PASSWORD:
734 VRB("Password authentication (host %s, user %s)", session->host, session->username);
735 s = sshauth_password(session->username, session->host);
736 if ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) != SSH_AUTH_SUCCESS) {
737 memset(s, 0, strlen(s));
738 VRB("Authentication failed (%s)", ssh_get_error(ssh_sess));
739 }
740 free(s);
741 break;
742 case NC_SSH_AUTH_INTERACTIVE:
743 VRB("Keyboard-interactive authentication");
744 while ((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) {
745 for (j = 0; j < ssh_userauth_kbdint_getnprompts(ssh_sess); ++j) {
746 prompt = ssh_userauth_kbdint_getprompt(ssh_sess, j, &echo);
747 if (prompt == NULL) {
748 break;
749 }
750 answer = sshauth_interactive(ssh_userauth_kbdint_getname(ssh_sess),
751 ssh_userauth_kbdint_getinstruction(ssh_sess),
752 prompt, echo);
753 if (ssh_userauth_kbdint_setanswer(ssh_sess, j, answer) < 0) {
754 free(answer);
755 break;
756 }
757 free(answer);
758 }
759 }
760
761 if (ret_auth == SSH_AUTH_ERROR) {
762 VRB("Authentication failed (%s)", ssh_get_error(ssh_sess));
763 }
764
765 break;
Michal Vasko206d3b12015-12-04 11:08:42 +0100766 case NC_SSH_AUTH_PUBLICKEY:
Michal Vasko7b62fed2015-10-26 15:39:46 +0100767 VRB("Publickey athentication");
768
769 /* if publickeys path not provided, we cannot continue */
770 if (!ssh_opts.key_count) {
771 VRB("No key pair specified.");
772 break;
773 }
774
775 for (j = 0; j < ssh_opts.key_count; j++) {
776 VRB("Trying to authenticate using %spair %s %s",
777 ssh_opts.keys[j].privkey_crypt ? "password-protected " : "", ssh_opts.keys[j].privkey_path,
778 ssh_opts.keys[j].pubkey_path);
779
780 if (ssh_pki_import_pubkey_file(ssh_opts.keys[j].pubkey_path, &pubkey) != SSH_OK) {
781 WRN("Failed to import the key \"%s\".", ssh_opts.keys[j].pubkey_path);
782 continue;
783 }
784 ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey);
785 if ((ret_auth == SSH_AUTH_DENIED) || (ret_auth == SSH_AUTH_PARTIAL)) {
786 ssh_key_free(pubkey);
787 continue;
788 }
789 if (ret_auth == SSH_AUTH_ERROR) {
790 ERR("Authentication failed (%s)", ssh_get_error(ssh_sess));
791 ssh_key_free(pubkey);
792 break;
793 }
794
795 if (ssh_opts.keys[j].privkey_crypt) {
796 s = sshauth_passphrase(ssh_opts.keys[j].privkey_path);
797 } else {
798 s = NULL;
799 }
800
801 if (ssh_pki_import_privkey_file(ssh_opts.keys[j].privkey_path, s, NULL, NULL, &privkey) != SSH_OK) {
802 WRN("Failed to import the key \"%s\".", ssh_opts.keys[j].privkey_path);
803 if (s) {
804 memset(s, 0, strlen(s));
805 free(s);
806 }
807 ssh_key_free(pubkey);
808 continue;
809 }
810
811 if (s) {
812 memset(s, 0, strlen(s));
813 free(s);
814 }
815
816 ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey);
817 ssh_key_free(pubkey);
818 ssh_key_free(privkey);
819
820 if (ret_auth == SSH_AUTH_ERROR) {
821 ERR("Authentication failed (%s)", ssh_get_error(ssh_sess));
822 }
823 if (ret_auth == SSH_AUTH_SUCCESS) {
824 break;
825 }
826 }
827 break;
828 }
829
830 if (ret_auth == SSH_AUTH_SUCCESS) {
831 break;
832 }
833 }
834
835 /* check a state of authentication */
836 if (ret_auth != SSH_AUTH_SUCCESS) {
837 ERR("Authentication failed.");
838 return EXIT_FAILURE;
839 }
840
841 /* open a channel */
842 session->ti.libssh.channel = ssh_channel_new(ssh_sess);
843 if (ssh_channel_open_session(session->ti.libssh.channel) != SSH_OK) {
844 ssh_channel_free(session->ti.libssh.channel);
845 session->ti.libssh.channel = NULL;
846 ERR("Opening an SSH channel failed (%s)", ssh_get_error(ssh_sess));
847 return EXIT_FAILURE;
848 }
849
850 /* execute the NETCONF subsystem on the channel */
851 if (ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf") != SSH_OK) {
852 ssh_channel_free(session->ti.libssh.channel);
853 session->ti.libssh.channel = NULL;
854 ERR("Starting the \"netconf\" SSH subsystem failed (%s)", ssh_get_error(ssh_sess));
855 return EXIT_FAILURE;
856 }
857
858 return EXIT_SUCCESS;
Radek Krejciac6d3472015-10-22 15:47:18 +0200859}
860
861API struct nc_session *
Michal Vaskoc111ac52015-12-08 14:36:36 +0100862nc_connect_ssh(const char *host, uint16_t port, const char *username, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +0200863{
Michal Vaskoc111ac52015-12-08 14:36:36 +0100864 const int timeout = NC_SSH_TIMEOUT;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100865 int sock;
Radek Krejciac6d3472015-10-22 15:47:18 +0200866 struct passwd *pw;
867 struct nc_session *session = NULL;
868
869 /* process parameters */
870 if (!host || strisempty(host)) {
871 host = "localhost";
872 }
873
874 if (!port) {
875 port = NC_PORT_SSH;
876 }
877
878 if (!username) {
879 pw = getpwuid(getuid());
880 if (!pw) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100881 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
882 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200883 } else {
884 username = pw->pw_name;
885 }
886 }
887
888 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100889 session = calloc(1, sizeof *session);
Radek Krejciac6d3472015-10-22 15:47:18 +0200890 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100891 ERRMEM;
Radek Krejciac6d3472015-10-22 15:47:18 +0200892 return NULL;
893 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100894 session->status = NC_STATUS_STARTING;
895 session->side = NC_CLIENT;
Radek Krejciac6d3472015-10-22 15:47:18 +0200896
Michal Vasko7b62fed2015-10-26 15:39:46 +0100897 /* transport lock */
898 session->ti_lock = malloc(sizeof *session->ti_lock);
899 if (!session->ti_lock) {
900 ERRMEM;
901 goto fail;
902 }
903 pthread_mutex_init(session->ti_lock, NULL);
904
905 /* other transport-specific data */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100906 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100907 session->ti.libssh.session = ssh_new();
908 if (!session->ti.libssh.session) {
909 ERR("Unable to initialize SSH session.");
910 goto fail;
911 }
Radek Krejciac6d3472015-10-22 15:47:18 +0200912
Michal Vasko7b62fed2015-10-26 15:39:46 +0100913 /* set some basic SSH session options */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100914 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
915 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port);
916 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100917 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout);
918
919 /* create and assign communication socket */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100920 sock = nc_connect_getsocket(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100921 if (sock == -1) {
922 goto fail;
923 }
924 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
925
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100926 /* temporarily, for session connection */
927 session->host = host;
928 session->username = username;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100929 if (connect_ssh_session_netconf(session)) {
930 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +0200931 }
932
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100933 /* assign context (dicionary needed for handshake) */
934 if (!ctx) {
935 ctx = ly_ctx_new(SCHEMAS_DIR);
936 } else {
937 session->flags |= NC_SESSION_SHAREDCTX;
938 }
939 session->ctx = ctx;
940
Radek Krejciac6d3472015-10-22 15:47:18 +0200941 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100942 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100943 goto fail;
Radek Krejciac6d3472015-10-22 15:47:18 +0200944 }
Michal Vaskoad611702015-12-03 13:41:51 +0100945 session->status = NC_STATUS_RUNNING;
Radek Krejciac6d3472015-10-22 15:47:18 +0200946
Michal Vasko57eb9402015-12-08 14:38:12 +0100947 if (nc_ctx_check_and_fill(session)) {
948 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100949 }
950
951 /* store information into the dictionary */
952 session->host = lydict_insert(ctx, host, 0);
953 session->port = port;
954 session->username = lydict_insert(ctx, username, 0);
955
Radek Krejciac6d3472015-10-22 15:47:18 +0200956 return session;
957
Michal Vasko7b62fed2015-10-26 15:39:46 +0100958fail:
Radek Krejciac6d3472015-10-22 15:47:18 +0200959 nc_session_free(session);
960 return NULL;
961}
962
963API struct nc_session *
964nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx)
965{
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100966 char *host = NULL, *username = NULL;
967 unsigned short port = 0;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100968 int sock;
969 struct passwd *pw;
970 struct nc_session *session = NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +0200971
Michal Vasko7b62fed2015-10-26 15:39:46 +0100972 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100973 session = calloc(1, sizeof *session);
Michal Vasko7b62fed2015-10-26 15:39:46 +0100974 if (!session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100975 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100976 return NULL;
977 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100978 session->status = NC_STATUS_STARTING;
979 session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100980
981 /* transport lock */
982 session->ti_lock = malloc(sizeof *session->ti_lock);
983 if (!session->ti_lock) {
984 ERRMEM;
985 goto fail;
986 }
987 pthread_mutex_init(session->ti_lock, NULL);
988
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100989 session->ti_type = NC_TI_LIBSSH;
Michal Vasko7b62fed2015-10-26 15:39:46 +0100990 session->ti.libssh.session = ssh_session;
991
Michal Vasko7b62fed2015-10-26 15:39:46 +0100992 if (ssh_get_fd(ssh_session) == -1) {
993 /*
994 * There is no file descriptor, we need to create it. (TCP/IP layer)
995 */
996
997 /* was host, port set? */
Michal Vasko9e2d3a32015-11-10 13:09:18 +0100998 if (ssh_options_get(ssh_session, SSH_OPTIONS_HOST, &host) != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +0100999 host = NULL;
1000 }
1001 ssh_options_get_port(ssh_session, (unsigned int *)&port);
1002
1003 /* remember host */
1004 if (!host) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001005 host = strdup("localhost");
Michal Vasko7b62fed2015-10-26 15:39:46 +01001006 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001007 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001008
1009 /* create and connect socket */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001010 sock = nc_connect_getsocket(host, port);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001011 if (sock == -1) {
1012 goto fail;
1013 }
1014 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock);
1015 }
1016
1017 if (!ssh_is_connected(ssh_session)) {
1018 /*
1019 * We are connected, but not SSH authenticated. (Transport layer)
1020 */
1021
1022 /* was username set? */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001023 if (ssh_options_get(ssh_session, SSH_OPTIONS_USER, &username) != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001024 username = NULL;
1025 }
1026
1027 /* remember username */
1028 if (!username) {
1029 pw = getpwuid(getuid());
1030 if (!pw) {
1031 ERR("Unknown username for the SSH connection (%s).", strerror(errno));
1032 goto fail;
1033 }
1034
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001035 username = strdup(pw->pw_name);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001036 ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001037 }
Michal Vasko7b62fed2015-10-26 15:39:46 +01001038
1039 /* authenticate SSH session */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001040 session->host = host;
1041 session->username = username;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001042 if (connect_ssh_session_netconf(session)) {
1043 goto fail;
1044 }
1045 }
1046
1047 /*
1048 * SSH session is established, create NETCONF session. (Application layer)
1049 */
1050
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001051 /* assign context (dicionary needed for handshake) */
1052 if (!ctx) {
1053 ctx = ly_ctx_new(SCHEMAS_DIR);
1054 } else {
1055 session->flags |= NC_SESSION_SHAREDCTX;
1056 }
1057 session->ctx = ctx;
1058
Michal Vasko7b62fed2015-10-26 15:39:46 +01001059 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001060 if (nc_handshake(session)) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001061 goto fail;
1062 }
Michal Vaskoad611702015-12-03 13:41:51 +01001063 session->status = NC_STATUS_RUNNING;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001064
Michal Vasko57eb9402015-12-08 14:38:12 +01001065 if (nc_ctx_check_and_fill(session)) {
1066 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001067 }
1068
1069 /* store information into the dictionary */
1070 if (host) {
1071 session->host = lydict_insert_zc(ctx, host);
1072 }
1073 if (port) {
1074 session->port = port;
1075 }
1076 if (username) {
1077 session->username = lydict_insert_zc(ctx, username);
1078 }
1079
Michal Vasko7b62fed2015-10-26 15:39:46 +01001080 return session;
1081
1082fail:
1083 nc_session_free(session);
Radek Krejciac6d3472015-10-22 15:47:18 +02001084 return NULL;
1085}
1086
1087API struct nc_session *
Michal Vasko7b62fed2015-10-26 15:39:46 +01001088nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx)
Radek Krejciac6d3472015-10-22 15:47:18 +02001089{
Michal Vasko7b62fed2015-10-26 15:39:46 +01001090 struct nc_session *new_session, *ptr;
Radek Krejciac6d3472015-10-22 15:47:18 +02001091
Michal Vasko7b62fed2015-10-26 15:39:46 +01001092 /* prepare session structure */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001093 new_session = calloc(1, sizeof *new_session);
Michal Vasko7b62fed2015-10-26 15:39:46 +01001094 if (!new_session) {
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001095 ERRMEM;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001096 return NULL;
1097 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001098 new_session->status = NC_STATUS_STARTING;
1099 new_session->side = NC_CLIENT;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001100
1101 /* share some parameters including the session lock */
1102 new_session->ti_type = NC_TI_LIBSSH;
1103 new_session->ti_lock = session->ti_lock;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001104 new_session->ti.libssh.session = session->ti.libssh.session;
1105
1106 /* create the channel safely */
1107 pthread_mutex_lock(new_session->ti_lock);
1108
1109 /* open a channel */
1110 new_session->ti.libssh.channel = ssh_channel_new(new_session->ti.libssh.session);
1111 if (ssh_channel_open_session(new_session->ti.libssh.channel) != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001112 ERR("Opening an SSH channel 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 }
1115 /* execute the NETCONF subsystem on the channel */
1116 if (ssh_channel_request_subsystem(new_session->ti.libssh.channel, "netconf") != SSH_OK) {
Michal Vasko7b62fed2015-10-26 15:39:46 +01001117 ERR("Starting the \"netconf\" SSH subsystem failed (%s)", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001118 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001119 }
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001120
1121 /* assign context (dicionary needed for handshake) */
1122 if (!ctx) {
1123 ctx = ly_ctx_new(SCHEMAS_DIR);
1124 } else {
1125 session->flags |= NC_SESSION_SHAREDCTX;
1126 }
1127 session->ctx = ctx;
1128
Michal Vasko7b62fed2015-10-26 15:39:46 +01001129 /* NETCONF handshake */
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001130 if (nc_handshake(new_session)) {
1131 goto fail;
Michal Vasko7b62fed2015-10-26 15:39:46 +01001132 }
Michal Vaskoad611702015-12-03 13:41:51 +01001133 new_session->status = NC_STATUS_RUNNING;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001134
Michal Vasko57eb9402015-12-08 14:38:12 +01001135 if (nc_ctx_check_and_fill(session)) {
1136 goto fail;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001137 }
1138
1139 /* store information into session and the dictionary */
1140 session->host = lydict_insert(ctx, session->host, 0);
1141 session->port = session->port;
1142 session->username = lydict_insert(ctx, session->username, 0);
1143
Michal Vasko7b62fed2015-10-26 15:39:46 +01001144 pthread_mutex_unlock(new_session->ti_lock);
1145
1146 /* append to the session ring list */
1147 if (!session->ti.libssh.next) {
1148 session->ti.libssh.next = new_session;
1149 new_session->ti.libssh.next = session;
1150 } else {
1151 ptr = session->ti.libssh.next;
1152 session->ti.libssh.next = new_session;
1153 new_session->ti.libssh.next = ptr;
1154 }
1155
1156 return new_session;
Michal Vasko9e2d3a32015-11-10 13:09:18 +01001157
1158fail:
1159 nc_session_free(new_session);
1160 return NULL;
Radek Krejciac6d3472015-10-22 15:47:18 +02001161}