blob: 4351407217f17d206cf3aee046e6cb6c96a9931e [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
Michal Vasko1a38c862016-01-15 15:50:07 +010033#include "libnetconf.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010034#include "session_server.h"
Michal Vasko086311b2016-01-08 09:53:11 +010035
36extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010037
Michal Vaskob48aa812016-01-18 14:13:09 +010038struct nc_ssh_server_opts ssh_opts = {
39 .sshbind_lock = PTHREAD_MUTEX_INITIALIZER,
40 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
Michal Vasko086311b2016-01-08 09:53:11 +010041 .auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE,
42 .auth_attempts = 3,
43 .auth_timeout = 10
44};
45
Michal Vaskob05053d2016-01-22 16:12:06 +010046struct nc_ssh_server_opts ssh_ch_opts = {
47 .sshbind_lock = PTHREAD_MUTEX_INITIALIZER,
48 .authkey_lock = PTHREAD_MUTEX_INITIALIZER,
49 .auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE,
50 .auth_attempts = 3,
51 .auth_timeout = 10
52};
53
54static int
55_nc_ssh_server_set_hostkey(const char *privkey_path, int ch)
Michal Vasko086311b2016-01-08 09:53:11 +010056{
Michal Vaskob05053d2016-01-22 16:12:06 +010057 struct nc_ssh_server_opts *opts;
58
Michal Vasko1a38c862016-01-15 15:50:07 +010059 if (!privkey_path) {
Michal Vasko086311b2016-01-08 09:53:11 +010060 ERRARG;
61 return -1;
62 }
63
Michal Vaskob05053d2016-01-22 16:12:06 +010064 opts = (ch ? &ssh_ch_opts : &ssh_opts);
Michal Vaskob48aa812016-01-18 14:13:09 +010065
Michal Vaskob05053d2016-01-22 16:12:06 +010066 /* LOCK */
67 pthread_mutex_lock(&opts->sshbind_lock);
68
69 if (!opts->sshbind) {
70 opts->sshbind = ssh_bind_new();
71 if (!opts->sshbind) {
Michal Vaskod083db62016-01-19 10:31:29 +010072 ERR("Failed to create a new ssh_bind.");
Michal Vaskob48aa812016-01-18 14:13:09 +010073 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +010074 }
75 }
76
Michal Vaskob05053d2016-01-22 16:12:06 +010077 if (ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path) != SSH_OK) {
78 ERR("Failed to set host key (%s).", ssh_get_error(opts->sshbind));
Michal Vaskob48aa812016-01-18 14:13:09 +010079 goto fail;
Michal Vasko086311b2016-01-08 09:53:11 +010080 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010081
Michal Vaskob48aa812016-01-18 14:13:09 +010082 /* UNLOCK */
Michal Vaskob05053d2016-01-22 16:12:06 +010083 pthread_mutex_unlock(&opts->sshbind_lock);
Michal Vasko086311b2016-01-08 09:53:11 +010084 return 0;
Michal Vaskob48aa812016-01-18 14:13:09 +010085
86fail:
87 /* UNLOCK */
Michal Vaskob05053d2016-01-22 16:12:06 +010088 pthread_mutex_unlock(&opts->sshbind_lock);
89 return -1;
90}
91
92API int
93nc_ssh_server_set_hostkey(const char *privkey_path)
94{
95 return _nc_ssh_server_set_hostkey(privkey_path, 0);
96}
97
98API int
99nc_ssh_server_ch_set_hostkey(const char *privkey_path)
100{
101 return _nc_ssh_server_set_hostkey(privkey_path, 1);
102}
103
104static int
105_nc_ssh_server_set_banner(const char *banner, int ch)
106{
107 struct nc_ssh_server_opts *opts;
108
109 if (!banner) {
110 ERRARG;
111 return -1;
112 }
113
114 opts = (ch ? &ssh_ch_opts : &ssh_opts);
115
116 /* LOCK */
117 pthread_mutex_lock(&opts->sshbind_lock);
118
119 if (!opts->sshbind) {
120 opts->sshbind = ssh_bind_new();
121 if (!opts->sshbind) {
122 ERR("Failed to create a new ssh_bind.");
123 goto fail;
124 }
125 }
126
127 ssh_bind_options_set(opts->sshbind, SSH_BIND_OPTIONS_BANNER, banner);
128
129 /* UNLOCK */
130 pthread_mutex_unlock(&opts->sshbind_lock);
131 return 0;
132
133fail:
134 /* UNLOCK */
135 pthread_mutex_unlock(&opts->sshbind_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100136 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100137}
138
139API int
140nc_ssh_server_set_banner(const char *banner)
141{
Michal Vaskob05053d2016-01-22 16:12:06 +0100142 return _nc_ssh_server_set_banner(banner, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100143}
144
145API int
Michal Vaskob05053d2016-01-22 16:12:06 +0100146nc_ssh_server_ch_set_banner(const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100147{
Michal Vaskob05053d2016-01-22 16:12:06 +0100148 return _nc_ssh_server_set_banner(banner, 1);
149}
150
151static int
152_nc_ssh_server_set_auth_methods(int auth_methods, int ch)
153{
154 struct nc_ssh_server_opts *opts;
155
Michal Vasko086311b2016-01-08 09:53:11 +0100156 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
157 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
158 ERRARG;
159 return -1;
160 }
161
Michal Vaskob05053d2016-01-22 16:12:06 +0100162 opts = (ch ? &ssh_ch_opts : &ssh_opts);
163
164 opts->auth_methods = auth_methods;
165 return 0;
166}
167
168API int
169nc_ssh_server_set_auth_methods(int auth_methods)
170{
171 return _nc_ssh_server_set_auth_methods(auth_methods, 0);
172}
173
174API int
175nc_ssh_server_ch_set_auth_methods(int auth_methods)
176{
177 return _nc_ssh_server_set_auth_methods(auth_methods, 1);
178}
179
180static int
181_nc_ssh_server_set_auth_attempts(uint16_t auth_attempts, int ch)
182{
183 struct nc_ssh_server_opts *opts;
184
185 if (!auth_attempts) {
186 ERRARG;
187 return -1;
188 }
189
190 opts = (ch ? &ssh_ch_opts : &ssh_opts);
191
192 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100193 return 0;
194}
195
196API int
197nc_ssh_server_set_auth_attempts(uint16_t auth_attempts)
198{
Michal Vaskob05053d2016-01-22 16:12:06 +0100199 return _nc_ssh_server_set_auth_attempts(auth_attempts, 0);
200}
201
202API int
203nc_ssh_server_set_ch_auth_attempts(uint16_t auth_attempts)
204{
205 return _nc_ssh_server_set_auth_attempts(auth_attempts, 1);
206}
207
208static int
209_nc_ssh_server_set_auth_timeout(uint16_t auth_timeout, int ch)
210{
211 struct nc_ssh_server_opts *opts;
212
213 if (!auth_timeout) {
Michal Vasko086311b2016-01-08 09:53:11 +0100214 ERRARG;
215 return -1;
216 }
217
Michal Vaskob05053d2016-01-22 16:12:06 +0100218 opts = (ch ? &ssh_ch_opts : &ssh_opts);
219
220 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100221 return 0;
222}
223
224API int
225nc_ssh_server_set_auth_timeout(uint16_t auth_timeout)
226{
Michal Vaskob05053d2016-01-22 16:12:06 +0100227 return _nc_ssh_server_set_auth_timeout(auth_timeout, 0);
228}
229
230API int
231nc_ssh_server_ch_set_auth_timeout(uint16_t auth_timeout)
232{
233 return _nc_ssh_server_set_auth_timeout(auth_timeout, 1);
234}
235
236static int
237_nc_ssh_server_add_authkey(const char *pubkey_path, const char *username, int ch)
238{
239 struct nc_ssh_server_opts *opts;
240
241 if (!pubkey_path || !username) {
Michal Vasko086311b2016-01-08 09:53:11 +0100242 ERRARG;
243 return -1;
244 }
245
Michal Vaskob05053d2016-01-22 16:12:06 +0100246 opts = (ch ? &ssh_ch_opts : &ssh_opts);
247
248 /* LOCK */
249 pthread_mutex_lock(&opts->authkey_lock);
250
251 ++opts->authkey_count;
252 opts->authkeys = realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
253
254 nc_ctx_lock(-1, NULL);
255 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
256 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
257 nc_ctx_unlock();
258
259 /* UNLOCK */
260 pthread_mutex_unlock(&opts->authkey_lock);
261
Michal Vasko086311b2016-01-08 09:53:11 +0100262 return 0;
263}
264
265API int
Michal Vasko1a38c862016-01-15 15:50:07 +0100266nc_ssh_server_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100267{
Michal Vaskob05053d2016-01-22 16:12:06 +0100268 return _nc_ssh_server_add_authkey(pubkey_path, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100269}
270
271API int
Michal Vaskob05053d2016-01-22 16:12:06 +0100272nc_ssh_server_ch_add_authkey(const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100273{
Michal Vaskob05053d2016-01-22 16:12:06 +0100274 return _nc_ssh_server_add_authkey(pubkey_path, username, 1);
275}
276
277static int
278_nc_ssh_server_del_authkey(const char *pubkey_path, const char *username, int ch)
279{
280 struct nc_ssh_server_opts *opts;
Michal Vasko086311b2016-01-08 09:53:11 +0100281 uint32_t i;
282 int ret = -1;
283
Michal Vaskob05053d2016-01-22 16:12:06 +0100284 opts = (ch ? &ssh_ch_opts : &ssh_opts);
285
Michal Vaskob48aa812016-01-18 14:13:09 +0100286 /* LOCK */
Michal Vaskob05053d2016-01-22 16:12:06 +0100287 pthread_mutex_lock(&opts->authkey_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100288
Michal Vasko1a38c862016-01-15 15:50:07 +0100289 if (!pubkey_path && !username) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100290 nc_ctx_lock(-1, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +0100291 for (i = 0; i < opts->authkey_count; ++i) {
292 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
293 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100294
Michal Vasko086311b2016-01-08 09:53:11 +0100295 ret = 0;
296 }
Michal Vasko11d142a2016-01-19 15:58:24 +0100297 nc_ctx_unlock();
Michal Vaskob05053d2016-01-22 16:12:06 +0100298 free(opts->authkeys);
299 opts->authkeys = NULL;
300 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100301 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100302 for (i = 0; i < opts->authkey_count; ++i) {
303 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
304 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100305 nc_ctx_lock(-1, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +0100306 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
307 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko11d142a2016-01-19 15:58:24 +0100308 nc_ctx_unlock();
Michal Vasko1a38c862016-01-15 15:50:07 +0100309
Michal Vaskob05053d2016-01-22 16:12:06 +0100310 --opts->authkey_count;
311 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
Michal Vasko1a38c862016-01-15 15:50:07 +0100312
313 ret = 0;
314 }
315 }
Michal Vasko086311b2016-01-08 09:53:11 +0100316 }
317
Michal Vaskob48aa812016-01-18 14:13:09 +0100318 /* UNLOCK */
Michal Vaskob05053d2016-01-22 16:12:06 +0100319 pthread_mutex_unlock(&opts->authkey_lock);
Michal Vaskob48aa812016-01-18 14:13:09 +0100320
Michal Vasko086311b2016-01-08 09:53:11 +0100321 return ret;
322}
323
Michal Vaskob05053d2016-01-22 16:12:06 +0100324API int
325nc_ssh_server_del_authkey(const char *pubkey_path, const char *username)
326{
327 return _nc_ssh_server_del_authkey(pubkey_path, username, 0);
328}
329
330API int
331nc_ssh_server_ch_del_authkey(const char *pubkey_path, const char *username)
332{
333 return _nc_ssh_server_del_authkey(pubkey_path, username, 1);
334}
335
Michal Vasko086311b2016-01-08 09:53:11 +0100336API void
Michal Vasko9e036d52016-01-08 10:49:26 +0100337nc_ssh_server_free_opts(void)
Michal Vasko086311b2016-01-08 09:53:11 +0100338{
Michal Vaskob48aa812016-01-18 14:13:09 +0100339 /* LOCK */
340 pthread_mutex_lock(&ssh_opts.sshbind_lock);
341
Michal Vasko086311b2016-01-08 09:53:11 +0100342 if (ssh_opts.sshbind) {
343 ssh_bind_free(ssh_opts.sshbind);
Michal Vaskob48aa812016-01-18 14:13:09 +0100344 ssh_opts.sshbind = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100345 }
346
Michal Vaskob48aa812016-01-18 14:13:09 +0100347 /* UNLOCK */
348 pthread_mutex_unlock(&ssh_opts.sshbind_lock);
349
Michal Vasko1a38c862016-01-15 15:50:07 +0100350 nc_ssh_server_del_authkey(NULL, NULL);
Michal Vaskob05053d2016-01-22 16:12:06 +0100351
352 /* Call Home */
353
354 /* LOCK */
355 pthread_mutex_lock(&ssh_ch_opts.sshbind_lock);
356
357 if (ssh_ch_opts.sshbind) {
358 ssh_bind_free(ssh_ch_opts.sshbind);
359 ssh_ch_opts.sshbind = NULL;
360 }
361
362 /* UNLOCK */
363 pthread_mutex_unlock(&ssh_ch_opts.sshbind_lock);
364
365 nc_ssh_server_ch_del_authkey(NULL, NULL);
Michal Vasko086311b2016-01-08 09:53:11 +0100366}
367
368static char *
369auth_password_get_pwd_hash(const char *username)
370{
371 struct passwd *pwd, pwd_buf;
372 struct spwd *spwd, spwd_buf;
373 char *pass_hash = NULL, buf[256];
374
375 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
376 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100377 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100378 return NULL;
379 }
380
381 if (!strcmp(pwd->pw_passwd, "x")) {
382 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
383 if (!spwd) {
384 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
385 return NULL;
386 }
387
388 pass_hash = spwd->sp_pwdp;
389 } else {
390 pass_hash = pwd->pw_passwd;
391 }
392
393 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100394 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100395 return NULL;
396 }
397
398 /* check the hash structure for special meaning */
399 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
400 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
401 return NULL;
402 }
403 if (!strcmp(pass_hash, "*NP*")) {
404 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
405 return NULL;
406 }
407
408 return strdup(pass_hash);
409}
410
411static int
412auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
413{
414 char *new_pass_hash;
415 struct crypt_data cdata;
416
417 if (!pass_hash[0]) {
418 if (!pass_clear[0]) {
419 WRN("User authentication successful with an empty password!");
420 return 0;
421 } else {
422 /* the user did now know he does not need any password,
423 * (which should not be used) so deny authentication */
424 return 1;
425 }
426 }
427
428 cdata.initialized = 0;
429 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
430 return strcmp(new_pass_hash, pass_hash);
431}
432
433static void
434nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
435{
436 char *pass_hash;
437
438 pass_hash = auth_password_get_pwd_hash(session->username);
439 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100440 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100441 ssh_message_auth_reply_success(msg, 0);
442 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
443 free(pass_hash);
444 return;
445 }
446
447 free(pass_hash);
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100448 ++session->ssh_auth_attempts;
Michal Vaskod083db62016-01-19 10:31:29 +0100449 VRB("Failed user \"'%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100450 ssh_message_reply_default(msg);
451}
452
453static void
454nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
455{
456 char *pass_hash;
457
458 if (!ssh_message_auth_kbdint_is_response(msg)) {
459 const char *prompts[] = {"Password: "};
460 char echo[] = {0};
461
462 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
463 } else {
464 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
465 ssh_message_reply_default(msg);
466 return;
467 }
468 pass_hash = auth_password_get_pwd_hash(session->username);
469 if (!pass_hash) {
470 ssh_message_reply_default(msg);
471 return;
472 }
473 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
474 VRB("User \"%s\" authenticated.", session->username);
475 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
476 ssh_message_auth_reply_success(msg, 0);
477 } else {
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100478 ++session->ssh_auth_attempts;
479 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100480 ssh_message_reply_default(msg);
481 }
482 }
483}
484
485static const char *
486auth_pubkey_compare_key(ssh_key key)
487{
488 uint32_t i;
489 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100490 const char *username = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100491
Michal Vaskob48aa812016-01-18 14:13:09 +0100492 /* LOCK */
493 pthread_mutex_lock(&ssh_opts.authkey_lock);
494
Michal Vasko086311b2016-01-08 09:53:11 +0100495 for (i = 0; i < ssh_opts.authkey_count; ++i) {
496 if (ssh_pki_import_pubkey_file(ssh_opts.authkeys[i].path, &pub_key) != SSH_OK) {
497 if (eaccess(ssh_opts.authkeys[i].path, R_OK)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100498 WRN("Failed to import the public key \"%s\" (%s).", ssh_opts.authkeys[i].path, strerror(errno));
Michal Vasko086311b2016-01-08 09:53:11 +0100499 } else {
Michal Vaskod083db62016-01-19 10:31:29 +0100500 WRN("Failed to import the public key \"%s\" (%s).", __func__, ssh_opts.authkeys[i].path, ssh_get_error(pub_key));
Michal Vasko086311b2016-01-08 09:53:11 +0100501 }
502 continue;
503 }
504
505 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
506 ssh_key_free(pub_key);
507 break;
508 }
509
510 ssh_key_free(pub_key);
511 }
512
513 if (i < ssh_opts.authkey_count) {
514 username = ssh_opts.authkeys[i].username;
515 }
516
Michal Vaskob48aa812016-01-18 14:13:09 +0100517 /* UNLOCK */
518 pthread_mutex_unlock(&ssh_opts.authkey_lock);
519
Michal Vasko086311b2016-01-08 09:53:11 +0100520 return username;
521}
522
523static void
524nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
525{
526 const char *username;
527 int signature_state;
528
529 signature_state = ssh_message_auth_publickey_state(msg);
530 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
531 VRB("User \"%s\" authenticated.", session->username);
532 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
533 ssh_message_auth_reply_success(msg, 0);
534 return;
535
536 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
537 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
538 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
539
540 } else if (strcmp(session->username, username)) {
541 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
542
543 } else {
544 /* accepting only the use of a public key */
545 ssh_message_auth_reply_pk_ok_simple(msg);
546 return;
547 }
548 }
549
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100550 ++session->ssh_auth_attempts;
551 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100552 ssh_message_reply_default(msg);
553}
554
555static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100556nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100557{
Michal Vasko96164bf2016-01-21 15:41:58 +0100558 ssh_channel chan;
559
560 /* first channel request */
561 if (!session->ti.libssh.channel) {
562 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100563 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100564 return -1;
565 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100566 chan = ssh_message_channel_request_open_reply_accept(msg);
567 if (!chan) {
568 ERR("Failed to create a new SSH channel.");
569 return -1;
570 }
571 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100572
Michal Vasko96164bf2016-01-21 15:41:58 +0100573 /* additional channel request */
574 } else {
575 chan = ssh_message_channel_request_open_reply_accept(msg);
576 if (!chan) {
577 ERR("Session %u: failed to create a new SSH channel.", session->id);
578 return -1;
579 }
580 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100581 }
582
Michal Vasko086311b2016-01-08 09:53:11 +0100583 return 0;
584}
585
586static int
587nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
588{
Michal Vasko96164bf2016-01-21 15:41:58 +0100589 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100590
Michal Vasko96164bf2016-01-21 15:41:58 +0100591 if (strcmp(subsystem, "netconf")) {
592 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100593 return -1;
594 }
595
Michal Vasko96164bf2016-01-21 15:41:58 +0100596 if (session->ti.libssh.channel == channel) {
597 /* first channel requested */
598 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
599 ERRINT;
600 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100601 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100602 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
603 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
604 return -1;
605 }
606
607 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100608 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100609 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
610 new_session = calloc(1, sizeof *new_session);
611
612 /* insert the new session */
613 if (!session->ti.libssh.next) {
614 new_session->ti.libssh.next = session;
615 } else {
616 new_session->ti.libssh.next = session->ti.libssh.next;
617 }
618 session->ti.libssh.next = new_session;
619
620 new_session->status = NC_STATUS_STARTING;
621 new_session->side = NC_SERVER;
622 new_session->ti_type = NC_TI_LIBSSH;
623 new_session->ti_lock = session->ti_lock;
624 new_session->ti.libssh.channel = channel;
625 new_session->ti.libssh.session = session->ti.libssh.session;
626 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
627 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
628 new_session->port = session->port;
629 new_session->ctx = server_opts.ctx;
630 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
Michal Vasko086311b2016-01-08 09:53:11 +0100631 }
632
633 return 0;
634}
635
Michal Vasko96164bf2016-01-21 15:41:58 +0100636int
Michal Vaskob48aa812016-01-18 14:13:09 +0100637nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100638{
639 const char *str_type, *str_subtype = NULL, *username;
640 int subtype, type;
641 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100642
643 type = ssh_message_type(msg);
644 subtype = ssh_message_subtype(msg);
645
646 switch (type) {
647 case SSH_REQUEST_AUTH:
648 str_type = "request-auth";
649 switch (subtype) {
650 case SSH_AUTH_METHOD_NONE:
651 str_subtype = "none";
652 break;
653 case SSH_AUTH_METHOD_PASSWORD:
654 str_subtype = "password";
655 break;
656 case SSH_AUTH_METHOD_PUBLICKEY:
657 str_subtype = "publickey";
658 break;
659 case SSH_AUTH_METHOD_HOSTBASED:
660 str_subtype = "hostbased";
661 break;
662 case SSH_AUTH_METHOD_INTERACTIVE:
663 str_subtype = "interactive";
664 break;
665 case SSH_AUTH_METHOD_GSSAPI_MIC:
666 str_subtype = "gssapi-mic";
667 break;
668 }
669 break;
670
671 case SSH_REQUEST_CHANNEL_OPEN:
672 str_type = "request-channel-open";
673 switch (subtype) {
674 case SSH_CHANNEL_SESSION:
675 str_subtype = "session";
676 break;
677 case SSH_CHANNEL_DIRECT_TCPIP:
678 str_subtype = "direct-tcpip";
679 break;
680 case SSH_CHANNEL_FORWARDED_TCPIP:
681 str_subtype = "forwarded-tcpip";
682 break;
683 case (int)SSH_CHANNEL_X11:
684 str_subtype = "channel-x11";
685 break;
686 case SSH_CHANNEL_UNKNOWN:
687 /* fallthrough */
688 default:
689 str_subtype = "unknown";
690 break;
691 }
692 break;
693
694 case SSH_REQUEST_CHANNEL:
695 str_type = "request-channel";
696 switch (subtype) {
697 case SSH_CHANNEL_REQUEST_PTY:
698 str_subtype = "pty";
699 break;
700 case SSH_CHANNEL_REQUEST_EXEC:
701 str_subtype = "exec";
702 break;
703 case SSH_CHANNEL_REQUEST_SHELL:
704 str_subtype = "shell";
705 break;
706 case SSH_CHANNEL_REQUEST_ENV:
707 str_subtype = "env";
708 break;
709 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
710 str_subtype = "subsystem";
711 break;
712 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
713 str_subtype = "window-change";
714 break;
715 case SSH_CHANNEL_REQUEST_X11:
716 str_subtype = "x11";
717 break;
718 case SSH_CHANNEL_REQUEST_UNKNOWN:
719 /* fallthrough */
720 default:
721 str_subtype = "unknown";
722 break;
723 }
724 break;
725
726 case SSH_REQUEST_SERVICE:
727 str_type = "request-service";
728 str_subtype = ssh_message_service_service(msg);
729 break;
730
731 case SSH_REQUEST_GLOBAL:
732 str_type = "request-global";
733 switch (subtype) {
734 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
735 str_subtype = "tcpip-forward";
736 break;
737 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
738 str_subtype = "cancel-tcpip-forward";
739 break;
740 case SSH_GLOBAL_REQUEST_UNKNOWN:
741 /* fallthrough */
742 default:
743 str_subtype = "unknown";
744 break;
745 }
746 break;
747
748 default:
749 str_type = "unknown";
750 str_subtype = "unknown";
751 break;
752 }
753
754 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vasko96164bf2016-01-21 15:41:58 +0100755 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100756
757 /*
758 * process known messages
759 */
760 if (type == SSH_REQUEST_AUTH) {
761 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
762 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
763 ssh_message_reply_default(msg);
764 return 0;
765 }
766
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100767 if (session->ssh_auth_attempts >= ssh_opts.auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100768 /* too many failed attempts */
769 ssh_message_reply_default(msg);
770 return 0;
771 }
772
773 /* save the username, do not let the client change it */
774 username = ssh_message_auth_user(msg);
775 if (!session->username) {
776 if (!username) {
777 ERR("Denying an auth request without a username.");
778 return 1;
779 }
780
Michal Vasko05ba9df2016-01-13 14:40:27 +0100781 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100782 } else if (username) {
783 if (strcmp(username, session->username)) {
784 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
785 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100786 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100787 return 1;
788 }
789 }
790
791 if (subtype == SSH_AUTH_METHOD_NONE) {
792 /* libssh will return the supported auth methods */
793 return 1;
794 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
795 nc_sshcb_auth_password(session, msg);
796 return 0;
797 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
798 nc_sshcb_auth_pubkey(session, msg);
799 return 0;
800 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
801 nc_sshcb_auth_kbdint(session, msg);
802 return 0;
803 }
804 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100805 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100806 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100807 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100808 }
Michal Vasko086311b2016-01-08 09:53:11 +0100809 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100810
Michal Vasko0df67562016-01-21 15:50:11 +0100811 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100812 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
813 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100814 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100815 } else {
816 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100817 }
818 return 0;
819 }
820 }
821
822 /* we did not process it */
823 return 1;
824}
825
Michal Vasko1a38c862016-01-15 15:50:07 +0100826/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100827static int
828nc_open_netconf_channel(struct nc_session *session, int timeout)
829{
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100830 int elapsed = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100831
832 /* message callback is executed twice to give chance for the channel to be
833 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100834 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100835 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100836 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100837 return -1;
838 }
839
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100840 ret = nc_timedlock(session->ti_lock, timeout, NULL);
841 if (ret != 1) {
842 return ret;
843 }
844
845 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
846 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100847 ERR("Failed to receive SSH messages on a session (%s).",
848 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100849 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100850 return -1;
851 }
852
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100853 if (!session->ti.libssh.channel) {
854 /* we did not receive channel-open, timeout */
855 pthread_mutex_unlock(session->ti_lock);
856 return 0;
857 }
858
859 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
860 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100861 ERR("Failed to receive SSH messages on a session (%s).",
862 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100863 pthread_mutex_unlock(session->ti_lock);
864 return -1;
865 }
866 pthread_mutex_unlock(session->ti_lock);
867
868 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
869 /* we did not receive subsystem-request, timeout */
870 return 0;
871 }
872
873 return 1;
874 }
875
876 while (1) {
877 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100878 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100879 return -1;
880 }
881
882 ret = nc_timedlock(session->ti_lock, timeout, &elapsed);
883 if (ret != 1) {
884 return ret;
885 }
886
887 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
888 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100889 ERR("Failed to receive SSH messages on a session (%s).",
890 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100891 pthread_mutex_unlock(session->ti_lock);
892 return -1;
893 }
894
895 pthread_mutex_unlock(session->ti_lock);
896
Michal Vasko086311b2016-01-08 09:53:11 +0100897 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +0100898 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100899 }
900
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100901 if ((timeout != -1) && (timeout >= elapsed)) {
902 /* timeout */
Michal Vasko086311b2016-01-08 09:53:11 +0100903 break;
904 }
905
Michal Vasko086311b2016-01-08 09:53:11 +0100906 usleep(NC_TIMEOUT_STEP);
907 elapsed += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100908 }
Michal Vasko086311b2016-01-08 09:53:11 +0100909
Michal Vasko1a38c862016-01-15 15:50:07 +0100910 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100911}
912
Michal Vasko96164bf2016-01-21 15:41:58 +0100913/* ret 0 - timeout, 1 channel has data, 2 some other channel has data,
914 * 3 status change, 4 new SSH message, 5 new NETCONF SSH channel, -1 error */
915int
916nc_ssh_pollin(struct nc_session *session, int *timeout)
917{
918 int ret, elapsed = 0;
919 struct nc_session *new;
920
921 ret = nc_timedlock(session->ti_lock, *timeout, &elapsed);
922 if (*timeout > 0) {
923 *timeout -= elapsed;
924 }
925
926 if (ret != 1) {
927 return ret;
928 }
929
930 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
931 pthread_mutex_unlock(session->ti_lock);
932
933 if (ret != SSH_OK) {
934 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
935 ssh_get_error(session->ti.libssh.session));
936 session->status = NC_STATUS_INVALID;
937 session->term_reason = NC_SESSION_TERM_OTHER;
938 return 3;
939 }
940
941 /* new SSH message */
942 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
943 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
944 if (session->ti.libssh.next) {
945 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
946 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
947 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
948 /* new NETCONF SSH channel */
949 return 5;
950 }
951 }
952 }
953
954 /* just some SSH message */
955 return 4;
956 }
957
958 /* no new SSH message, maybe NETCONF data? */
959 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
960 /* not this one */
961 if (!ret) {
962 return 2;
963 } else if (ret == SSH_ERROR) {
964 ERR("Session %u: SSH channel error (%s).", session->id,
965 ssh_get_error(session->ti.libssh.session));
966 session->status = NC_STATUS_INVALID;
967 session->term_reason = NC_SESSION_TERM_OTHER;
968 return 3;
969 } else if (ret == SSH_EOF) {
970 ERR("Session %u: communication channel unexpectedly closed (libssh).",
971 session->id);
972 session->status = NC_STATUS_INVALID;
973 session->term_reason = NC_SESSION_TERM_DROPPED;
974 return 3;
975 }
976
977 return 1;
978}
979
Michal Vasko9e036d52016-01-08 10:49:26 +0100980int
981nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100982{
Michal Vasko1a38c862016-01-15 15:50:07 +0100983 int libssh_auth_methods = 0, elapsed = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100984
985 /* other transport-specific data */
986 session->ti_type = NC_TI_LIBSSH;
987 session->ti.libssh.session = ssh_new();
988 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +0100989 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +0100990 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +0100991 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100992 }
993
994 if (ssh_opts.auth_methods & NC_SSH_AUTH_PUBLICKEY) {
995 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
996 }
997 if (ssh_opts.auth_methods & NC_SSH_AUTH_PASSWORD) {
998 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
999 }
1000 if (ssh_opts.auth_methods & NC_SSH_AUTH_INTERACTIVE) {
1001 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1002 }
1003 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1004
1005 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vasko96164bf2016-01-21 15:41:58 +01001006 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001007
Michal Vaskob48aa812016-01-18 14:13:09 +01001008 /* LOCK */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001009 ret = nc_timedlock(&ssh_opts.sshbind_lock, timeout, &elapsed);
1010 if (ret < 1) {
1011 return ret;
1012 }
Michal Vaskob48aa812016-01-18 14:13:09 +01001013
Michal Vasko086311b2016-01-08 09:53:11 +01001014 if (ssh_bind_accept_fd(ssh_opts.sshbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vaskod083db62016-01-19 10:31:29 +01001015 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(ssh_opts.sshbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001016 close(sock);
Michal Vaskob48aa812016-01-18 14:13:09 +01001017 /* UNLOCK */
1018 pthread_mutex_unlock(&ssh_opts.sshbind_lock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001019 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001020 }
1021
Michal Vaskob48aa812016-01-18 14:13:09 +01001022 /* UNLOCK */
1023 pthread_mutex_unlock(&ssh_opts.sshbind_lock);
1024
Michal Vasko086311b2016-01-08 09:53:11 +01001025 if (ssh_handle_key_exchange(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001026 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001027 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001028 }
1029
1030 /* authenticate */
1031 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001032 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001033 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001034 return -1;
1035 }
1036
Michal Vasko086311b2016-01-08 09:53:11 +01001037 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001038 ERR("Failed to receive SSH messages on a session (%s).",
1039 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001040 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001041 }
1042
1043 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1044 break;
1045 }
1046
1047 usleep(NC_TIMEOUT_STEP);
1048 elapsed += NC_TIMEOUT_STEP;
1049 } while ((timeout == -1) || (timeout && (elapsed < timeout)));
1050
1051 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1052 /* timeout */
Michal Vasko1a38c862016-01-15 15:50:07 +01001053 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001054 }
1055
1056 if (timeout > 0) {
1057 timeout -= elapsed;
1058 }
1059
1060 /* open channel */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001061 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001062 if (ret < 1) {
1063 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001064 }
1065
Michal Vasko96164bf2016-01-21 15:41:58 +01001066 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1067
Michal Vasko1a38c862016-01-15 15:50:07 +01001068 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001069}
1070
Michal Vasko96164bf2016-01-21 15:41:58 +01001071API int
1072nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001073{
Michal Vasko96164bf2016-01-21 15:41:58 +01001074 uint16_t i;
1075 struct nc_session *new_session = NULL;
Michal Vasko086311b2016-01-08 09:53:11 +01001076
Michal Vasko96164bf2016-01-21 15:41:58 +01001077 if (!ps || !session) {
1078 ERRARG;
1079 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001080 }
1081
Michal Vasko96164bf2016-01-21 15:41:58 +01001082 for (i = 0; i < ps->session_count; ++i) {
1083 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1084 && ps->sessions[i]->ti.libssh.next) {
1085 /* an SSH session with more channels */
1086 for (new_session = ps->sessions[i]->ti.libssh.next;
1087 new_session != ps->sessions[i];
1088 new_session = new_session->ti.libssh.next) {
1089 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1090 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1091 /* we found our session */
1092 break;
1093 }
1094 }
1095 if (new_session != ps->sessions[i]) {
1096 break;
1097 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001098
Michal Vasko96164bf2016-01-21 15:41:58 +01001099 new_session = NULL;
1100 }
1101 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001102
Michal Vasko96164bf2016-01-21 15:41:58 +01001103 if (!new_session) {
1104 ERR("No session with a NETCONF SSH channel ready was found.");
1105 return -1;
1106 }
1107
1108 /* assign new SID atomically */
1109 pthread_spin_lock(&server_opts.sid_lock);
1110 new_session->id = server_opts.new_session_id++;
1111 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001112
Michal Vasko086311b2016-01-08 09:53:11 +01001113 /* NETCONF handshake */
1114 if (nc_handshake(new_session)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001115 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001116 }
1117 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001118 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001119
Michal Vasko96164bf2016-01-21 15:41:58 +01001120 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001121}