blob: 196200799a785c4299651f6bdb640df04d58c6dd [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
Michal Vaskod45e25a2016-01-08 15:48:44 +010044nc_ssh_server_set_hostkey(const char *key_path)
Michal Vasko086311b2016-01-08 09:53:11 +010045{
Michal Vaskod45e25a2016-01-08 15:48:44 +010046 if (!key_path) {
Michal Vasko086311b2016-01-08 09:53:11 +010047 ERRARG;
48 return -1;
49 }
50
51 if (!ssh_opts.sshbind) {
52 ssh_opts.sshbind = ssh_bind_new();
53 if (!ssh_opts.sshbind) {
Michal Vaskod45e25a2016-01-08 15:48:44 +010054 ERR("%s: failed to create a new ssh_bind.", __func__);
Michal Vasko086311b2016-01-08 09:53:11 +010055 return -1;
56 }
57 }
58
Michal Vaskod45e25a2016-01-08 15:48:44 +010059 if (ssh_bind_options_set(ssh_opts.sshbind, SSH_BIND_OPTIONS_HOSTKEY, key_path) != SSH_OK) {
60 ERR("%s: failed to set host key (%s).", __func__, ssh_get_error(ssh_opts.sshbind));
61 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010062 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010063
Michal Vasko086311b2016-01-08 09:53:11 +010064 return 0;
65}
66
67API int
68nc_ssh_server_set_banner(const char *banner)
69{
70 if (!banner) {
71 ERRARG;
72 return -1;
73 }
74
75 if (!ssh_opts.sshbind) {
76 ssh_opts.sshbind = ssh_bind_new();
77 if (!ssh_opts.sshbind) {
78 ERR("%s: failed to create a new ssh_bind", __func__);
79 return -1;
80 }
81 }
82
83 ssh_bind_options_set(ssh_opts.sshbind, SSH_BIND_OPTIONS_BANNER, banner);
84 return 0;
85}
86
87API int
88nc_ssh_server_set_auth_methods(int auth_methods)
89{
90 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
91 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
92 ERRARG;
93 return -1;
94 }
95
96 ssh_opts.auth_methods = auth_methods;
97 return 0;
98}
99
100API int
101nc_ssh_server_set_auth_attempts(uint16_t auth_attempts)
102{
103 if (!auth_attempts) {
104 ERRARG;
105 return -1;
106 }
107
108 ssh_opts.auth_attempts = auth_attempts;
109 return 0;
110}
111
112API int
113nc_ssh_server_set_auth_timeout(uint16_t auth_timeout)
114{
115 if (!auth_timeout) {
116 ERRARG;
117 return -1;
118 }
119
120 ssh_opts.auth_timeout = auth_timeout;
121 return 0;
122}
123
124API int
125nc_ssh_server_add_authkey(const char *keypath, const char *username)
126{
127 if (!keypath || !username) {
128 ERRARG;
129 return -1;
130 }
131
132 ++ssh_opts.authkey_count;
133 ssh_opts.authkeys = realloc(ssh_opts.authkeys, ssh_opts.authkey_count * sizeof *ssh_opts.authkeys);
134
135 ssh_opts.authkeys[ssh_opts.authkey_count - 1].path = strdup(keypath);
136 ssh_opts.authkeys[ssh_opts.authkey_count - 1].username = strdup(username);
137
138 return 0;
139}
140
141API int
142nc_ssh_server_del_authkey(const char *keypath, const char *username)
143{
144 uint32_t i;
145 int ret = -1;
146
147 for (i = 0; i < ssh_opts.authkey_count; ++i) {
148 if ((!keypath || !strcmp(ssh_opts.authkeys[i].path, keypath))
149 && (!username || !strcmp(ssh_opts.authkeys[i].username, username))) {
150 free(ssh_opts.authkeys[i].path);
151 free(ssh_opts.authkeys[i].username);
152
153 --ssh_opts.authkey_count;
154 memmove(&ssh_opts.authkeys[i], &ssh_opts.authkeys[i + 1], (ssh_opts.authkey_count - i) * sizeof *ssh_opts.authkeys);
155
156 ret = 0;
157 }
158 }
159
160 return ret;
161}
162
Michal Vasko086311b2016-01-08 09:53:11 +0100163API void
Michal Vasko9e036d52016-01-08 10:49:26 +0100164nc_ssh_server_free_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100165{
166 int i;
167
Michal Vasko086311b2016-01-08 09:53:11 +0100168 if (ssh_opts.sshbind) {
169 ssh_bind_free(ssh_opts.sshbind);
170 }
171
172 if (ssh_opts.authkeys) {
173 for (i = 0; i < ssh_opts.authkey_count; ++i) {
174 free(ssh_opts.authkeys[i].path);
175 free(ssh_opts.authkeys[i].username);
176 }
177 free(ssh_opts.authkeys);
178 }
179}
180
181static char *
182auth_password_get_pwd_hash(const char *username)
183{
184 struct passwd *pwd, pwd_buf;
185 struct spwd *spwd, spwd_buf;
186 char *pass_hash = NULL, buf[256];
187
188 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
189 if (!pwd) {
190 VRB("User '%s' not found locally.", username);
191 return NULL;
192 }
193
194 if (!strcmp(pwd->pw_passwd, "x")) {
195 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
196 if (!spwd) {
197 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
198 return NULL;
199 }
200
201 pass_hash = spwd->sp_pwdp;
202 } else {
203 pass_hash = pwd->pw_passwd;
204 }
205
206 if (!pass_hash) {
207 ERR("%s: no password could be retrieved for \"%s\".", __func__, username);
208 return NULL;
209 }
210
211 /* check the hash structure for special meaning */
212 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
213 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
214 return NULL;
215 }
216 if (!strcmp(pass_hash, "*NP*")) {
217 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
218 return NULL;
219 }
220
221 return strdup(pass_hash);
222}
223
224static int
225auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
226{
227 char *new_pass_hash;
228 struct crypt_data cdata;
229
230 if (!pass_hash[0]) {
231 if (!pass_clear[0]) {
232 WRN("User authentication successful with an empty password!");
233 return 0;
234 } else {
235 /* the user did now know he does not need any password,
236 * (which should not be used) so deny authentication */
237 return 1;
238 }
239 }
240
241 cdata.initialized = 0;
242 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
243 return strcmp(new_pass_hash, pass_hash);
244}
245
246static void
247nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
248{
249 char *pass_hash;
250
251 pass_hash = auth_password_get_pwd_hash(session->username);
252 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
253 VRB("User '%s' authenticated.", session->username);
254 ssh_message_auth_reply_success(msg, 0);
255 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
256 free(pass_hash);
257 return;
258 }
259
260 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100261 ++session->ssh_auth_attempts;
262 VRB("Failed user '%s' authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100263 ssh_message_reply_default(msg);
264}
265
266static void
267nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
268{
269 char *pass_hash;
270
271 if (!ssh_message_auth_kbdint_is_response(msg)) {
272 const char *prompts[] = {"Password: "};
273 char echo[] = {0};
274
275 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
276 } else {
277 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
278 ssh_message_reply_default(msg);
279 return;
280 }
281 pass_hash = auth_password_get_pwd_hash(session->username);
282 if (!pass_hash) {
283 ssh_message_reply_default(msg);
284 return;
285 }
286 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
287 VRB("User \"%s\" authenticated.", session->username);
288 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
289 ssh_message_auth_reply_success(msg, 0);
290 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100291 ++session->ssh_auth_attempts;
292 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100293 ssh_message_reply_default(msg);
294 }
295 }
296}
297
298static const char *
299auth_pubkey_compare_key(ssh_key key)
300{
301 uint32_t i;
302 ssh_key pub_key;
303 char *username = NULL;
304
305 for (i = 0; i < ssh_opts.authkey_count; ++i) {
306 if (ssh_pki_import_pubkey_file(ssh_opts.authkeys[i].path, &pub_key) != SSH_OK) {
307 if (eaccess(ssh_opts.authkeys[i].path, R_OK)) {
308 VRB("%s: failed to import the public key \"%s\" (%s)", __func__, ssh_opts.authkeys[i].path, strerror(errno));
309 } else {
310 VRB("%s: failed to import the public key \"%s\" (%s)", __func__, ssh_opts.authkeys[i].path, ssh_get_error(pub_key));
311 }
312 continue;
313 }
314
315 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
316 ssh_key_free(pub_key);
317 break;
318 }
319
320 ssh_key_free(pub_key);
321 }
322
323 if (i < ssh_opts.authkey_count) {
324 username = ssh_opts.authkeys[i].username;
325 }
326
327 return username;
328}
329
330static void
331nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
332{
333 const char *username;
334 int signature_state;
335
336 signature_state = ssh_message_auth_publickey_state(msg);
337 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
338 VRB("User \"%s\" authenticated.", session->username);
339 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
340 ssh_message_auth_reply_success(msg, 0);
341 return;
342
343 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
344 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
345 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
346
347 } else if (strcmp(session->username, username)) {
348 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
349
350 } else {
351 /* accepting only the use of a public key */
352 ssh_message_auth_reply_pk_ok_simple(msg);
353 return;
354 }
355 }
356
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100357 ++session->ssh_auth_attempts;
358 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100359 ssh_message_reply_default(msg);
360}
361
362static int
363nc_sshcb_channel_open(struct nc_session *session, ssh_channel channel)
364{
365 while (session->ti.libssh.next) {
366 if (session->status == NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100367 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100368 return -1;
369 }
370 session = session->ti.libssh.next;
371 }
372
373 if ((session->status != NC_STATUS_STARTING) || session->ti.libssh.channel) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100374 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100375 return -1;
376 }
377
378 session->ti.libssh.channel = channel;
379
380 return 0;
381}
382
383static int
384nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
385{
386 while (session && (session->ti.libssh.channel != channel)) {
387 session = session->ti.libssh.next;
388 }
389
390 if (!session) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100391 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100392 return -1;
393 }
394
395 if (!strcmp(subsystem, "netconf")) {
396 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
397 WRN("Client \"%s\" requested subsystem 'netconf' for the second time.", session->username);
398 } else {
399 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
400 }
401 } else {
402 WRN("Client \"%s\" requested an unknown subsystem '%s'.", session->username, subsystem);
403 return -1;
404 }
405
406 return 0;
407}
408
409static int
410nc_sshcb_msg(ssh_session sshsession, ssh_message msg, void *data)
411{
412 const char *str_type, *str_subtype = NULL, *username;
413 int subtype, type;
414 struct nc_session *session = (struct nc_session *)data;
415 (void)sshsession;
416
417 type = ssh_message_type(msg);
418 subtype = ssh_message_subtype(msg);
419
420 switch (type) {
421 case SSH_REQUEST_AUTH:
422 str_type = "request-auth";
423 switch (subtype) {
424 case SSH_AUTH_METHOD_NONE:
425 str_subtype = "none";
426 break;
427 case SSH_AUTH_METHOD_PASSWORD:
428 str_subtype = "password";
429 break;
430 case SSH_AUTH_METHOD_PUBLICKEY:
431 str_subtype = "publickey";
432 break;
433 case SSH_AUTH_METHOD_HOSTBASED:
434 str_subtype = "hostbased";
435 break;
436 case SSH_AUTH_METHOD_INTERACTIVE:
437 str_subtype = "interactive";
438 break;
439 case SSH_AUTH_METHOD_GSSAPI_MIC:
440 str_subtype = "gssapi-mic";
441 break;
442 }
443 break;
444
445 case SSH_REQUEST_CHANNEL_OPEN:
446 str_type = "request-channel-open";
447 switch (subtype) {
448 case SSH_CHANNEL_SESSION:
449 str_subtype = "session";
450 break;
451 case SSH_CHANNEL_DIRECT_TCPIP:
452 str_subtype = "direct-tcpip";
453 break;
454 case SSH_CHANNEL_FORWARDED_TCPIP:
455 str_subtype = "forwarded-tcpip";
456 break;
457 case (int)SSH_CHANNEL_X11:
458 str_subtype = "channel-x11";
459 break;
460 case SSH_CHANNEL_UNKNOWN:
461 /* fallthrough */
462 default:
463 str_subtype = "unknown";
464 break;
465 }
466 break;
467
468 case SSH_REQUEST_CHANNEL:
469 str_type = "request-channel";
470 switch (subtype) {
471 case SSH_CHANNEL_REQUEST_PTY:
472 str_subtype = "pty";
473 break;
474 case SSH_CHANNEL_REQUEST_EXEC:
475 str_subtype = "exec";
476 break;
477 case SSH_CHANNEL_REQUEST_SHELL:
478 str_subtype = "shell";
479 break;
480 case SSH_CHANNEL_REQUEST_ENV:
481 str_subtype = "env";
482 break;
483 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
484 str_subtype = "subsystem";
485 break;
486 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
487 str_subtype = "window-change";
488 break;
489 case SSH_CHANNEL_REQUEST_X11:
490 str_subtype = "x11";
491 break;
492 case SSH_CHANNEL_REQUEST_UNKNOWN:
493 /* fallthrough */
494 default:
495 str_subtype = "unknown";
496 break;
497 }
498 break;
499
500 case SSH_REQUEST_SERVICE:
501 str_type = "request-service";
502 str_subtype = ssh_message_service_service(msg);
503 break;
504
505 case SSH_REQUEST_GLOBAL:
506 str_type = "request-global";
507 switch (subtype) {
508 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
509 str_subtype = "tcpip-forward";
510 break;
511 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
512 str_subtype = "cancel-tcpip-forward";
513 break;
514 case SSH_GLOBAL_REQUEST_UNKNOWN:
515 /* fallthrough */
516 default:
517 str_subtype = "unknown";
518 break;
519 }
520 break;
521
522 default:
523 str_type = "unknown";
524 str_subtype = "unknown";
525 break;
526 }
527
528 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
529
530 /*
531 * process known messages
532 */
533 if (type == SSH_REQUEST_AUTH) {
534 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
535 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
536 ssh_message_reply_default(msg);
537 return 0;
538 }
539
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100540 if (session->ssh_auth_attempts >= ssh_opts.auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100541 /* too many failed attempts */
542 ssh_message_reply_default(msg);
543 return 0;
544 }
545
546 /* save the username, do not let the client change it */
547 username = ssh_message_auth_user(msg);
548 if (!session->username) {
549 if (!username) {
550 ERR("Denying an auth request without a username.");
551 return 1;
552 }
553
554 session->username = lydict_insert(session->ctx, username, 0);
555 } else if (username) {
556 if (strcmp(username, session->username)) {
557 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
558 session->status = NC_STATUS_INVALID;
559 return 1;
560 }
561 }
562
563 if (subtype == SSH_AUTH_METHOD_NONE) {
564 /* libssh will return the supported auth methods */
565 return 1;
566 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
567 nc_sshcb_auth_password(session, msg);
568 return 0;
569 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
570 nc_sshcb_auth_pubkey(session, msg);
571 return 0;
572 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
573 nc_sshcb_auth_kbdint(session, msg);
574 return 0;
575 }
576 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
577 if ((type == SSH_REQUEST_CHANNEL_OPEN) && (subtype == (int)SSH_CHANNEL_SESSION)) {
578 ssh_channel chan;
579 if ((chan = ssh_message_channel_request_open_reply_accept(msg)) == NULL) {
580 ssh_message_reply_default(msg);
581 return 0;
582 }
583 nc_sshcb_channel_open(session, chan);
584 return 0;
585 } else if ((type == SSH_REQUEST_CHANNEL) && (subtype == (int)SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
586 if (!nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
587 ssh_message_channel_request_subsystem(msg))) {
588 ssh_message_channel_request_reply_success(msg);
589 } else {
590 ssh_message_reply_default(msg);
591 }
592 return 0;
593 }
594 }
595
596 /* we did not process it */
597 return 1;
598}
599
600static int
601nc_open_netconf_channel(struct nc_session *session, int timeout)
602{
603 int elapsed = 0;
604
605 /* message callback is executed twice to give chance for the channel to be
606 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
607 do {
608 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
609 ERR("%s: failed to receive new messages on the SSH session (%s)",
610 __func__, ssh_get_error(session->ti.libssh.session));
611 return -1;
612 }
613
614 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
615 return 0;
616 }
617
618 usleep(NC_TIMEOUT_STEP);
619 elapsed += NC_TIMEOUT_STEP;
620 if ((timeout > NC_TIMEOUT_STEP) && (elapsed >= timeout)) {
621 break;
622 }
623
624 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
625 ERR("%s: failed to receive new messages on the SSH session (%s)",
626 __func__, ssh_get_error(session->ti.libssh.session));
627 return -1;
628 }
629
630 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
631 return 0;
632 }
633
634 usleep(NC_TIMEOUT_STEP);
635 elapsed += NC_TIMEOUT_STEP;
636 } while ((timeout == -1) || (timeout && (elapsed < timeout)));
637
638 return 1;
639}
640
Michal Vasko9e036d52016-01-08 10:49:26 +0100641int
642nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100643{
Michal Vasko9e036d52016-01-08 10:49:26 +0100644 int libssh_auth_methods = 0, elapsed = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100645
646 /* other transport-specific data */
647 session->ti_type = NC_TI_LIBSSH;
648 session->ti.libssh.session = ssh_new();
649 if (!session->ti.libssh.session) {
650 ERR("%s: failed to initialize SSH session", __func__);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100651 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +0100652 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100653 }
654
655 if (ssh_opts.auth_methods & NC_SSH_AUTH_PUBLICKEY) {
656 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
657 }
658 if (ssh_opts.auth_methods & NC_SSH_AUTH_PASSWORD) {
659 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
660 }
661 if (ssh_opts.auth_methods & NC_SSH_AUTH_INTERACTIVE) {
662 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
663 }
664 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
665
666 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
667
668 if (ssh_bind_accept_fd(ssh_opts.sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
669 ERR("%s: SSH failed to accept a new connection (%s)", __func__, ssh_get_error(ssh_opts.sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100670 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +0100671 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100672 }
673
674 if (ssh_handle_key_exchange(session->ti.libssh.session) != SSH_OK) {
675 ERR("%s: SSH key exchange error (%s)", __func__, ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +0100676 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100677 }
678
679 /* authenticate */
680 do {
681 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
682 ERR("%s: failed to receive new messages on the SSH session (%s)",
683 __func__, ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +0100684 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100685 }
686
687 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
688 break;
689 }
690
691 usleep(NC_TIMEOUT_STEP);
692 elapsed += NC_TIMEOUT_STEP;
693 } while ((timeout == -1) || (timeout && (elapsed < timeout)));
694
695 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
696 /* timeout */
Michal Vasko9e036d52016-01-08 10:49:26 +0100697 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100698 }
699
700 if (timeout > 0) {
701 timeout -= elapsed;
702 }
703
704 /* open channel */
705 if (nc_open_netconf_channel(session, timeout)) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100706 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100707 }
708
Michal Vasko9e036d52016-01-08 10:49:26 +0100709 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100710}
711
712API struct nc_session *
713nc_accept_ssh_channel(struct nc_session *session, int timeout)
714{
715 struct nc_session *new_session;
716 int ret;
717
Michal Vaskofb89d772016-01-08 12:25:35 +0100718 new_session = calloc(1, sizeof *new_session);
Michal Vasko086311b2016-01-08 09:53:11 +0100719 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko086311b2016-01-08 09:53:11 +0100720 ret = nc_open_netconf_channel(new_session, timeout);
721 if (ret) {
722 if (ret == -1) {
723 do {
724 session->status = NC_STATUS_INVALID;
725 session = session->ti.libssh.next;
726 } while (session);
727 }
728 goto fail;
729 }
730
Michal Vaskofb89d772016-01-08 12:25:35 +0100731 /* new channel was requested and opened, fill in the whole session now */
732 for (; session->ti.libssh.next; session = session->ti.libssh.next);
733 session->ti.libssh.next = new_session;
734
735 new_session->status = NC_STATUS_STARTING;
736 new_session->side = NC_SERVER;
737 new_session->ti_type = NC_TI_LIBSSH;
738 new_session->ti_lock = session->ti_lock;
739 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SHAREDCTX;
740 new_session->ctx = session->ctx;
741
742 new_session->username = lydict_insert(new_session->ctx, session->username, 0);
743 new_session->host = lydict_insert(new_session->ctx, session->host, 0);
744 new_session->port = session->port;
745
Michal Vasko086311b2016-01-08 09:53:11 +0100746 /* NETCONF handshake */
747 if (nc_handshake(new_session)) {
748 goto fail;
749 }
750 new_session->status = NC_STATUS_RUNNING;
751
752 return new_session;
753
754fail:
755 nc_session_free(new_session);
756
757 return NULL;
758}