blob: baa9fa352dccb849292ae227356949772783cac7 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server_ssh.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 SSH server session manipulation functions
5 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of the Company nor the names of its contributors
18 * may be used to endorse or promote products derived from this
19 * software without specific prior written permission.
20 *
21 */
22
23#define _GNU_SOURCE
24
25#include <stdlib.h>
26#include <string.h>
27#include <sys/types.h>
28#include <pwd.h>
29#include <shadow.h>
30#include <crypt.h>
31#include <errno.h>
32
33#include "session_server.h"
34#include "session_p.h"
35
36extern struct nc_server_opts server_opts;
37static struct nc_ssh_server_opts ssh_opts = {
38 .auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE,
39 .auth_attempts = 3,
40 .auth_timeout = 10
41};
42
43API int
44nc_ssh_server_set_hostkeys(const char *dsakey, const char *rsakey, const char *ecdsakey)
45{
46 if (!dsakey && !rsakey && !ecdsakey) {
47 ERRARG;
48 return -1;
49 }
50
51 if (!ssh_opts.sshbind) {
52 ssh_opts.sshbind = ssh_bind_new();
53 if (!ssh_opts.sshbind) {
54 ERR("%s: failed to create a new ssh_bind", __func__);
55 return -1;
56 }
57 }
58
59 if (dsakey) {
60 ssh_bind_options_set(ssh_opts.sshbind, SSH_BIND_OPTIONS_DSAKEY, dsakey);
61 }
62 if (rsakey) {
63 ssh_bind_options_set(ssh_opts.sshbind, SSH_BIND_OPTIONS_RSAKEY, rsakey);
64 }
65 if (ecdsakey) {
66 ssh_bind_options_set(ssh_opts.sshbind, SSH_BIND_OPTIONS_ECDSAKEY, ecdsakey);
67 }
68 return 0;
69}
70
71API int
72nc_ssh_server_set_banner(const char *banner)
73{
74 if (!banner) {
75 ERRARG;
76 return -1;
77 }
78
79 if (!ssh_opts.sshbind) {
80 ssh_opts.sshbind = ssh_bind_new();
81 if (!ssh_opts.sshbind) {
82 ERR("%s: failed to create a new ssh_bind", __func__);
83 return -1;
84 }
85 }
86
87 ssh_bind_options_set(ssh_opts.sshbind, SSH_BIND_OPTIONS_BANNER, banner);
88 return 0;
89}
90
91API int
92nc_ssh_server_set_auth_methods(int auth_methods)
93{
94 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
95 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
96 ERRARG;
97 return -1;
98 }
99
100 ssh_opts.auth_methods = auth_methods;
101 return 0;
102}
103
104API int
105nc_ssh_server_set_auth_attempts(uint16_t auth_attempts)
106{
107 if (!auth_attempts) {
108 ERRARG;
109 return -1;
110 }
111
112 ssh_opts.auth_attempts = auth_attempts;
113 return 0;
114}
115
116API int
117nc_ssh_server_set_auth_timeout(uint16_t auth_timeout)
118{
119 if (!auth_timeout) {
120 ERRARG;
121 return -1;
122 }
123
124 ssh_opts.auth_timeout = auth_timeout;
125 return 0;
126}
127
128API int
129nc_ssh_server_add_authkey(const char *keypath, const char *username)
130{
131 if (!keypath || !username) {
132 ERRARG;
133 return -1;
134 }
135
136 ++ssh_opts.authkey_count;
137 ssh_opts.authkeys = realloc(ssh_opts.authkeys, ssh_opts.authkey_count * sizeof *ssh_opts.authkeys);
138
139 ssh_opts.authkeys[ssh_opts.authkey_count - 1].path = strdup(keypath);
140 ssh_opts.authkeys[ssh_opts.authkey_count - 1].username = strdup(username);
141
142 return 0;
143}
144
145API int
146nc_ssh_server_del_authkey(const char *keypath, const char *username)
147{
148 uint32_t i;
149 int ret = -1;
150
151 for (i = 0; i < ssh_opts.authkey_count; ++i) {
152 if ((!keypath || !strcmp(ssh_opts.authkeys[i].path, keypath))
153 && (!username || !strcmp(ssh_opts.authkeys[i].username, username))) {
154 free(ssh_opts.authkeys[i].path);
155 free(ssh_opts.authkeys[i].username);
156
157 --ssh_opts.authkey_count;
158 memmove(&ssh_opts.authkeys[i], &ssh_opts.authkeys[i + 1], (ssh_opts.authkey_count - i) * sizeof *ssh_opts.authkeys);
159
160 ret = 0;
161 }
162 }
163
164 return ret;
165}
166
Michal Vasko086311b2016-01-08 09:53:11 +0100167API void
Michal Vasko9e036d52016-01-08 10:49:26 +0100168nc_ssh_server_free_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100169{
170 int i;
171
Michal Vasko086311b2016-01-08 09:53:11 +0100172 if (ssh_opts.sshbind) {
173 ssh_bind_free(ssh_opts.sshbind);
174 }
175
176 if (ssh_opts.authkeys) {
177 for (i = 0; i < ssh_opts.authkey_count; ++i) {
178 free(ssh_opts.authkeys[i].path);
179 free(ssh_opts.authkeys[i].username);
180 }
181 free(ssh_opts.authkeys);
182 }
183}
184
185static char *
186auth_password_get_pwd_hash(const char *username)
187{
188 struct passwd *pwd, pwd_buf;
189 struct spwd *spwd, spwd_buf;
190 char *pass_hash = NULL, buf[256];
191
192 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
193 if (!pwd) {
194 VRB("User '%s' not found locally.", username);
195 return NULL;
196 }
197
198 if (!strcmp(pwd->pw_passwd, "x")) {
199 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
200 if (!spwd) {
201 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
202 return NULL;
203 }
204
205 pass_hash = spwd->sp_pwdp;
206 } else {
207 pass_hash = pwd->pw_passwd;
208 }
209
210 if (!pass_hash) {
211 ERR("%s: no password could be retrieved for \"%s\".", __func__, username);
212 return NULL;
213 }
214
215 /* check the hash structure for special meaning */
216 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
217 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
218 return NULL;
219 }
220 if (!strcmp(pass_hash, "*NP*")) {
221 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
222 return NULL;
223 }
224
225 return strdup(pass_hash);
226}
227
228static int
229auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
230{
231 char *new_pass_hash;
232 struct crypt_data cdata;
233
234 if (!pass_hash[0]) {
235 if (!pass_clear[0]) {
236 WRN("User authentication successful with an empty password!");
237 return 0;
238 } else {
239 /* the user did now know he does not need any password,
240 * (which should not be used) so deny authentication */
241 return 1;
242 }
243 }
244
245 cdata.initialized = 0;
246 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
247 return strcmp(new_pass_hash, pass_hash);
248}
249
250static void
251nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
252{
253 char *pass_hash;
254
255 pass_hash = auth_password_get_pwd_hash(session->username);
256 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
257 VRB("User '%s' authenticated.", session->username);
258 ssh_message_auth_reply_success(msg, 0);
259 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
260 free(pass_hash);
261 return;
262 }
263
264 free(pass_hash);
265 ++session->auth_attempts;
266 VRB("Failed user '%s' authentication attempt (#%d).", session->username, session->auth_attempts);
267 ssh_message_reply_default(msg);
268}
269
270static void
271nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
272{
273 char *pass_hash;
274
275 if (!ssh_message_auth_kbdint_is_response(msg)) {
276 const char *prompts[] = {"Password: "};
277 char echo[] = {0};
278
279 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
280 } else {
281 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
282 ssh_message_reply_default(msg);
283 return;
284 }
285 pass_hash = auth_password_get_pwd_hash(session->username);
286 if (!pass_hash) {
287 ssh_message_reply_default(msg);
288 return;
289 }
290 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
291 VRB("User \"%s\" authenticated.", session->username);
292 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
293 ssh_message_auth_reply_success(msg, 0);
294 } else {
295 ++session->auth_attempts;
296 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->auth_attempts);
297 ssh_message_reply_default(msg);
298 }
299 }
300}
301
302static const char *
303auth_pubkey_compare_key(ssh_key key)
304{
305 uint32_t i;
306 ssh_key pub_key;
307 char *username = NULL;
308
309 for (i = 0; i < ssh_opts.authkey_count; ++i) {
310 if (ssh_pki_import_pubkey_file(ssh_opts.authkeys[i].path, &pub_key) != SSH_OK) {
311 if (eaccess(ssh_opts.authkeys[i].path, R_OK)) {
312 VRB("%s: failed to import the public key \"%s\" (%s)", __func__, ssh_opts.authkeys[i].path, strerror(errno));
313 } else {
314 VRB("%s: failed to import the public key \"%s\" (%s)", __func__, ssh_opts.authkeys[i].path, ssh_get_error(pub_key));
315 }
316 continue;
317 }
318
319 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
320 ssh_key_free(pub_key);
321 break;
322 }
323
324 ssh_key_free(pub_key);
325 }
326
327 if (i < ssh_opts.authkey_count) {
328 username = ssh_opts.authkeys[i].username;
329 }
330
331 return username;
332}
333
334static void
335nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
336{
337 const char *username;
338 int signature_state;
339
340 signature_state = ssh_message_auth_publickey_state(msg);
341 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
342 VRB("User \"%s\" authenticated.", session->username);
343 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
344 ssh_message_auth_reply_success(msg, 0);
345 return;
346
347 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
348 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
349 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
350
351 } else if (strcmp(session->username, username)) {
352 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
353
354 } else {
355 /* accepting only the use of a public key */
356 ssh_message_auth_reply_pk_ok_simple(msg);
357 return;
358 }
359 }
360
361 ++session->auth_attempts;
362 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->auth_attempts);
363 ssh_message_reply_default(msg);
364}
365
366static int
367nc_sshcb_channel_open(struct nc_session *session, ssh_channel channel)
368{
369 while (session->ti.libssh.next) {
370 if (session->status == NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100371 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100372 return -1;
373 }
374 session = session->ti.libssh.next;
375 }
376
377 if ((session->status != NC_STATUS_STARTING) || session->ti.libssh.channel) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100378 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100379 return -1;
380 }
381
382 session->ti.libssh.channel = channel;
383
384 return 0;
385}
386
387static int
388nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
389{
390 while (session && (session->ti.libssh.channel != channel)) {
391 session = session->ti.libssh.next;
392 }
393
394 if (!session) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100395 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100396 return -1;
397 }
398
399 if (!strcmp(subsystem, "netconf")) {
400 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
401 WRN("Client \"%s\" requested subsystem 'netconf' for the second time.", session->username);
402 } else {
403 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
404 }
405 } else {
406 WRN("Client \"%s\" requested an unknown subsystem '%s'.", session->username, subsystem);
407 return -1;
408 }
409
410 return 0;
411}
412
413static int
414nc_sshcb_msg(ssh_session sshsession, ssh_message msg, void *data)
415{
416 const char *str_type, *str_subtype = NULL, *username;
417 int subtype, type;
418 struct nc_session *session = (struct nc_session *)data;
419 (void)sshsession;
420
421 type = ssh_message_type(msg);
422 subtype = ssh_message_subtype(msg);
423
424 switch (type) {
425 case SSH_REQUEST_AUTH:
426 str_type = "request-auth";
427 switch (subtype) {
428 case SSH_AUTH_METHOD_NONE:
429 str_subtype = "none";
430 break;
431 case SSH_AUTH_METHOD_PASSWORD:
432 str_subtype = "password";
433 break;
434 case SSH_AUTH_METHOD_PUBLICKEY:
435 str_subtype = "publickey";
436 break;
437 case SSH_AUTH_METHOD_HOSTBASED:
438 str_subtype = "hostbased";
439 break;
440 case SSH_AUTH_METHOD_INTERACTIVE:
441 str_subtype = "interactive";
442 break;
443 case SSH_AUTH_METHOD_GSSAPI_MIC:
444 str_subtype = "gssapi-mic";
445 break;
446 }
447 break;
448
449 case SSH_REQUEST_CHANNEL_OPEN:
450 str_type = "request-channel-open";
451 switch (subtype) {
452 case SSH_CHANNEL_SESSION:
453 str_subtype = "session";
454 break;
455 case SSH_CHANNEL_DIRECT_TCPIP:
456 str_subtype = "direct-tcpip";
457 break;
458 case SSH_CHANNEL_FORWARDED_TCPIP:
459 str_subtype = "forwarded-tcpip";
460 break;
461 case (int)SSH_CHANNEL_X11:
462 str_subtype = "channel-x11";
463 break;
464 case SSH_CHANNEL_UNKNOWN:
465 /* fallthrough */
466 default:
467 str_subtype = "unknown";
468 break;
469 }
470 break;
471
472 case SSH_REQUEST_CHANNEL:
473 str_type = "request-channel";
474 switch (subtype) {
475 case SSH_CHANNEL_REQUEST_PTY:
476 str_subtype = "pty";
477 break;
478 case SSH_CHANNEL_REQUEST_EXEC:
479 str_subtype = "exec";
480 break;
481 case SSH_CHANNEL_REQUEST_SHELL:
482 str_subtype = "shell";
483 break;
484 case SSH_CHANNEL_REQUEST_ENV:
485 str_subtype = "env";
486 break;
487 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
488 str_subtype = "subsystem";
489 break;
490 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
491 str_subtype = "window-change";
492 break;
493 case SSH_CHANNEL_REQUEST_X11:
494 str_subtype = "x11";
495 break;
496 case SSH_CHANNEL_REQUEST_UNKNOWN:
497 /* fallthrough */
498 default:
499 str_subtype = "unknown";
500 break;
501 }
502 break;
503
504 case SSH_REQUEST_SERVICE:
505 str_type = "request-service";
506 str_subtype = ssh_message_service_service(msg);
507 break;
508
509 case SSH_REQUEST_GLOBAL:
510 str_type = "request-global";
511 switch (subtype) {
512 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
513 str_subtype = "tcpip-forward";
514 break;
515 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
516 str_subtype = "cancel-tcpip-forward";
517 break;
518 case SSH_GLOBAL_REQUEST_UNKNOWN:
519 /* fallthrough */
520 default:
521 str_subtype = "unknown";
522 break;
523 }
524 break;
525
526 default:
527 str_type = "unknown";
528 str_subtype = "unknown";
529 break;
530 }
531
532 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
533
534 /*
535 * process known messages
536 */
537 if (type == SSH_REQUEST_AUTH) {
538 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
539 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
540 ssh_message_reply_default(msg);
541 return 0;
542 }
543
544 if (session->auth_attempts >= ssh_opts.auth_attempts) {
545 /* too many failed attempts */
546 ssh_message_reply_default(msg);
547 return 0;
548 }
549
550 /* save the username, do not let the client change it */
551 username = ssh_message_auth_user(msg);
552 if (!session->username) {
553 if (!username) {
554 ERR("Denying an auth request without a username.");
555 return 1;
556 }
557
558 session->username = lydict_insert(session->ctx, username, 0);
559 } else if (username) {
560 if (strcmp(username, session->username)) {
561 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
562 session->status = NC_STATUS_INVALID;
563 return 1;
564 }
565 }
566
567 if (subtype == SSH_AUTH_METHOD_NONE) {
568 /* libssh will return the supported auth methods */
569 return 1;
570 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
571 nc_sshcb_auth_password(session, msg);
572 return 0;
573 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
574 nc_sshcb_auth_pubkey(session, msg);
575 return 0;
576 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
577 nc_sshcb_auth_kbdint(session, msg);
578 return 0;
579 }
580 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
581 if ((type == SSH_REQUEST_CHANNEL_OPEN) && (subtype == (int)SSH_CHANNEL_SESSION)) {
582 ssh_channel chan;
583 if ((chan = ssh_message_channel_request_open_reply_accept(msg)) == NULL) {
584 ssh_message_reply_default(msg);
585 return 0;
586 }
587 nc_sshcb_channel_open(session, chan);
588 return 0;
589 } else if ((type == SSH_REQUEST_CHANNEL) && (subtype == (int)SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
590 if (!nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
591 ssh_message_channel_request_subsystem(msg))) {
592 ssh_message_channel_request_reply_success(msg);
593 } else {
594 ssh_message_reply_default(msg);
595 }
596 return 0;
597 }
598 }
599
600 /* we did not process it */
601 return 1;
602}
603
604static int
605nc_open_netconf_channel(struct nc_session *session, int timeout)
606{
607 int elapsed = 0;
608
609 /* message callback is executed twice to give chance for the channel to be
610 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
611 do {
612 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
613 ERR("%s: failed to receive new messages on the SSH session (%s)",
614 __func__, ssh_get_error(session->ti.libssh.session));
615 return -1;
616 }
617
618 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
619 return 0;
620 }
621
622 usleep(NC_TIMEOUT_STEP);
623 elapsed += NC_TIMEOUT_STEP;
624 if ((timeout > NC_TIMEOUT_STEP) && (elapsed >= timeout)) {
625 break;
626 }
627
628 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
629 ERR("%s: failed to receive new messages on the SSH session (%s)",
630 __func__, ssh_get_error(session->ti.libssh.session));
631 return -1;
632 }
633
634 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
635 return 0;
636 }
637
638 usleep(NC_TIMEOUT_STEP);
639 elapsed += NC_TIMEOUT_STEP;
640 } while ((timeout == -1) || (timeout && (elapsed < timeout)));
641
642 return 1;
643}
644
Michal Vasko9e036d52016-01-08 10:49:26 +0100645int
646nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100647{
Michal Vasko9e036d52016-01-08 10:49:26 +0100648 int libssh_auth_methods = 0, elapsed = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100649
650 /* other transport-specific data */
651 session->ti_type = NC_TI_LIBSSH;
652 session->ti.libssh.session = ssh_new();
653 if (!session->ti.libssh.session) {
654 ERR("%s: failed to initialize SSH session", __func__);
Michal Vasko9e036d52016-01-08 10:49:26 +0100655 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100656 }
657
658 if (ssh_opts.auth_methods & NC_SSH_AUTH_PUBLICKEY) {
659 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
660 }
661 if (ssh_opts.auth_methods & NC_SSH_AUTH_PASSWORD) {
662 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
663 }
664 if (ssh_opts.auth_methods & NC_SSH_AUTH_INTERACTIVE) {
665 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
666 }
667 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
668
669 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
670
671 if (ssh_bind_accept_fd(ssh_opts.sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
672 ERR("%s: SSH failed to accept a new connection (%s)", __func__, ssh_get_error(ssh_opts.sshbind));
Michal Vasko9e036d52016-01-08 10:49:26 +0100673 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100674 }
675
676 if (ssh_handle_key_exchange(session->ti.libssh.session) != SSH_OK) {
677 ERR("%s: SSH key exchange error (%s)", __func__, ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +0100678 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100679 }
680
681 /* authenticate */
682 do {
683 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
684 ERR("%s: failed to receive new messages on the SSH session (%s)",
685 __func__, ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +0100686 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100687 }
688
689 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
690 break;
691 }
692
693 usleep(NC_TIMEOUT_STEP);
694 elapsed += NC_TIMEOUT_STEP;
695 } while ((timeout == -1) || (timeout && (elapsed < timeout)));
696
697 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
698 /* timeout */
Michal Vasko9e036d52016-01-08 10:49:26 +0100699 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100700 }
701
702 if (timeout > 0) {
703 timeout -= elapsed;
704 }
705
706 /* open channel */
707 if (nc_open_netconf_channel(session, timeout)) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100708 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100709 }
710
Michal Vasko9e036d52016-01-08 10:49:26 +0100711 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100712}
713
714API struct nc_session *
715nc_accept_ssh_channel(struct nc_session *session, int timeout)
716{
717 struct nc_session *new_session;
718 int ret;
719
Michal Vaskofb89d772016-01-08 12:25:35 +0100720 new_session = calloc(1, sizeof *new_session);
Michal Vasko086311b2016-01-08 09:53:11 +0100721 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko086311b2016-01-08 09:53:11 +0100722 ret = nc_open_netconf_channel(new_session, timeout);
723 if (ret) {
724 if (ret == -1) {
725 do {
726 session->status = NC_STATUS_INVALID;
727 session = session->ti.libssh.next;
728 } while (session);
729 }
730 goto fail;
731 }
732
Michal Vaskofb89d772016-01-08 12:25:35 +0100733 /* new channel was requested and opened, fill in the whole session now */
734 for (; session->ti.libssh.next; session = session->ti.libssh.next);
735 session->ti.libssh.next = new_session;
736
737 new_session->status = NC_STATUS_STARTING;
738 new_session->side = NC_SERVER;
739 new_session->ti_type = NC_TI_LIBSSH;
740 new_session->ti_lock = session->ti_lock;
741 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SHAREDCTX;
742 new_session->ctx = session->ctx;
743
744 new_session->username = lydict_insert(new_session->ctx, session->username, 0);
745 new_session->host = lydict_insert(new_session->ctx, session->host, 0);
746 new_session->port = session->port;
747
Michal Vasko086311b2016-01-08 09:53:11 +0100748 /* NETCONF handshake */
749 if (nc_handshake(new_session)) {
750 goto fail;
751 }
752 new_session->status = NC_STATUS_RUNNING;
753
754 return new_session;
755
756fail:
757 nc_session_free(new_session);
758
759 return NULL;
760}