blob: 19925f67f663e1708b82f7a7e39748516f3dd490 [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
Michal Vasko05ba9df2016-01-13 14:40:27 +0100554 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100555 } 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;
Michal Vasko428087d2016-01-14 16:04:28 +0100559 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100560 return 1;
561 }
562 }
563
564 if (subtype == SSH_AUTH_METHOD_NONE) {
565 /* libssh will return the supported auth methods */
566 return 1;
567 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
568 nc_sshcb_auth_password(session, msg);
569 return 0;
570 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
571 nc_sshcb_auth_pubkey(session, msg);
572 return 0;
573 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
574 nc_sshcb_auth_kbdint(session, msg);
575 return 0;
576 }
577 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
578 if ((type == SSH_REQUEST_CHANNEL_OPEN) && (subtype == (int)SSH_CHANNEL_SESSION)) {
579 ssh_channel chan;
580 if ((chan = ssh_message_channel_request_open_reply_accept(msg)) == NULL) {
581 ssh_message_reply_default(msg);
582 return 0;
583 }
584 nc_sshcb_channel_open(session, chan);
585 return 0;
586 } else if ((type == SSH_REQUEST_CHANNEL) && (subtype == (int)SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
587 if (!nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
588 ssh_message_channel_request_subsystem(msg))) {
589 ssh_message_channel_request_reply_success(msg);
590 } else {
591 ssh_message_reply_default(msg);
592 }
593 return 0;
594 }
595 }
596
597 /* we did not process it */
598 return 1;
599}
600
601static int
602nc_open_netconf_channel(struct nc_session *session, int timeout)
603{
604 int elapsed = 0;
605
606 /* message callback is executed twice to give chance for the channel to be
607 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
608 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100609 if (!nc_session_is_connected(session)) {
610 ERR("%s: communication channel unexpectedly closed (libssh).", __func__);
611 return -1;
612 }
613
Michal Vasko086311b2016-01-08 09:53:11 +0100614 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
615 ERR("%s: failed to receive new messages on the SSH session (%s)",
616 __func__, ssh_get_error(session->ti.libssh.session));
617 return -1;
618 }
619
620 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
621 return 0;
622 }
623
624 usleep(NC_TIMEOUT_STEP);
625 elapsed += NC_TIMEOUT_STEP;
626 if ((timeout > NC_TIMEOUT_STEP) && (elapsed >= timeout)) {
627 break;
628 }
629
630 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
631 ERR("%s: failed to receive new messages on the SSH session (%s)",
632 __func__, ssh_get_error(session->ti.libssh.session));
633 return -1;
634 }
635
636 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
637 return 0;
638 }
639
640 usleep(NC_TIMEOUT_STEP);
641 elapsed += NC_TIMEOUT_STEP;
642 } while ((timeout == -1) || (timeout && (elapsed < timeout)));
643
644 return 1;
645}
646
Michal Vasko9e036d52016-01-08 10:49:26 +0100647int
648nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100649{
Michal Vasko9e036d52016-01-08 10:49:26 +0100650 int libssh_auth_methods = 0, elapsed = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100651
652 /* other transport-specific data */
653 session->ti_type = NC_TI_LIBSSH;
654 session->ti.libssh.session = ssh_new();
655 if (!session->ti.libssh.session) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100656 ERR("%s: failed to initialize an SSH session.", __func__);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100657 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +0100658 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100659 }
660
661 if (ssh_opts.auth_methods & NC_SSH_AUTH_PUBLICKEY) {
662 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
663 }
664 if (ssh_opts.auth_methods & NC_SSH_AUTH_PASSWORD) {
665 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
666 }
667 if (ssh_opts.auth_methods & NC_SSH_AUTH_INTERACTIVE) {
668 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
669 }
670 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
671
672 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
673
674 if (ssh_bind_accept_fd(ssh_opts.sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100675 ERR("%s: SSH failed to accept a new connection (%s).", __func__, ssh_get_error(ssh_opts.sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100676 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +0100677 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100678 }
679
680 if (ssh_handle_key_exchange(session->ti.libssh.session) != SSH_OK) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100681 ERR("%s: SSH key exchange error (%s).", __func__, ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +0100682 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100683 }
684
685 /* authenticate */
686 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100687 if (!nc_session_is_connected(session)) {
688 ERR("%s: communication channel unexpectedly closed (libssh).", __func__);
689 return -1;
690 }
691
Michal Vasko086311b2016-01-08 09:53:11 +0100692 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100693 ERR("%s: failed to receive new messages on the SSH session (%s).",
Michal Vasko086311b2016-01-08 09:53:11 +0100694 __func__, ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +0100695 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100696 }
697
698 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
699 break;
700 }
701
702 usleep(NC_TIMEOUT_STEP);
703 elapsed += NC_TIMEOUT_STEP;
704 } while ((timeout == -1) || (timeout && (elapsed < timeout)));
705
706 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
707 /* timeout */
Michal Vasko9e036d52016-01-08 10:49:26 +0100708 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100709 }
710
711 if (timeout > 0) {
712 timeout -= elapsed;
713 }
714
715 /* open channel */
716 if (nc_open_netconf_channel(session, timeout)) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100717 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100718 }
719
Michal Vasko9e036d52016-01-08 10:49:26 +0100720 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100721}
722
723API struct nc_session *
724nc_accept_ssh_channel(struct nc_session *session, int timeout)
725{
726 struct nc_session *new_session;
727 int ret;
728
Michal Vaskofb89d772016-01-08 12:25:35 +0100729 new_session = calloc(1, sizeof *new_session);
Michal Vasko086311b2016-01-08 09:53:11 +0100730 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko086311b2016-01-08 09:53:11 +0100731 ret = nc_open_netconf_channel(new_session, timeout);
732 if (ret) {
Michal Vasko086311b2016-01-08 09:53:11 +0100733 goto fail;
734 }
735
Michal Vaskofb89d772016-01-08 12:25:35 +0100736 /* new channel was requested and opened, fill in the whole session now */
737 for (; session->ti.libssh.next; session = session->ti.libssh.next);
738 session->ti.libssh.next = new_session;
739
740 new_session->status = NC_STATUS_STARTING;
741 new_session->side = NC_SERVER;
742 new_session->ti_type = NC_TI_LIBSSH;
743 new_session->ti_lock = session->ti_lock;
744 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SHAREDCTX;
Michal Vasko05ba9df2016-01-13 14:40:27 +0100745 new_session->ctx = server_opts.ctx;
Michal Vaskofb89d772016-01-08 12:25:35 +0100746
Michal Vasko05ba9df2016-01-13 14:40:27 +0100747 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
748 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
Michal Vaskofb89d772016-01-08 12:25:35 +0100749 new_session->port = session->port;
750
Michal Vasko086311b2016-01-08 09:53:11 +0100751 /* NETCONF handshake */
752 if (nc_handshake(new_session)) {
753 goto fail;
754 }
755 new_session->status = NC_STATUS_RUNNING;
756
757 return new_session;
758
759fail:
760 nc_session_free(new_session);
761
762 return NULL;
763}