blob: c84328c14ad83003a6564713ea3e4c2db75c8979 [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 *
Michal Vasko4c1fb492017-01-30 14:31:07 +01006 * Copyright (c) 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
15#define _GNU_SOURCE
16
apropp-molex4e903c32020-04-20 03:06:58 -040017#include "config.h" /* Expose HAVE_SHADOW and HAVE_CRYPT */
18
19#ifdef HAVE_SHADOW
20 #include <shadow.h>
21#endif
22#ifdef HAVE_CRYPT
23 #include <crypt.h>
24#endif
25
Michal Vaskob83a3fa2021-05-26 09:53:42 +020026#include <errno.h>
27#include <fcntl.h>
28#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010029#include <stdlib.h>
30#include <string.h>
Michal Vasko27252692017-03-21 15:34:13 +010031#include <sys/stat.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020032#include <sys/types.h>
Michal Vasko9f6275e2017-10-05 13:50:05 +020033#include <time.h>
Claus Klein22091912020-01-20 13:45:47 +010034#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035
Michal Vasko7a20d2e2021-05-19 16:40:23 +020036#include "compat.h"
37#include "libnetconf.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010038#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010039#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010040
Michal Vaskob83a3fa2021-05-26 09:53:42 +020041#if !defined (HAVE_CRYPT_R)
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +020042pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER;
43#endif
44
Michal Vasko086311b2016-01-08 09:53:11 +010045extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010046
Michal Vasko4c1fb492017-01-30 14:31:07 +010047static char *
Michal Vaskoddce1212019-05-24 09:58:49 +020048base64der_key_to_tmp_file(const char *in, const char *key_str)
Michal Vasko086311b2016-01-08 09:53:11 +010049{
Michal Vasko4c1fb492017-01-30 14:31:07 +010050 char path[12] = "/tmp/XXXXXX";
51 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010052 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010053 FILE *file;
54
55 if (in == NULL) {
56 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010057 }
58
mekleob31878b2019-09-09 14:10:47 +020059 umode = umask(0177);
Michal Vasko4c1fb492017-01-30 14:31:07 +010060 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010061 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010062 if (fd == -1) {
63 return NULL;
64 }
65
Michal Vasko3964a832018-09-18 14:37:39 +020066 file = fdopen(fd, "w");
Michal Vasko4c1fb492017-01-30 14:31:07 +010067 if (!file) {
68 close(fd);
69 return NULL;
70 }
71
72 /* write the key into the file */
Michal Vasko68177b72020-04-27 15:46:53 +020073 if (key_str) {
74 written = fwrite("-----BEGIN ", 1, 11, file);
75 written += fwrite(key_str, 1, strlen(key_str), file);
76 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
77 written += fwrite(in, 1, strlen(in), file);
78 written += fwrite("\n-----END ", 1, 10, file);
79 written += fwrite(key_str, 1, strlen(key_str), file);
80 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
Michal Vasko4c1fb492017-01-30 14:31:07 +010081
Michal Vasko68177b72020-04-27 15:46:53 +020082 fclose(file);
83 if ((unsigned)written != 11 + strlen(key_str) + 18 + strlen(in) + 10 + strlen(key_str) + 17) {
84 unlink(path);
85 return NULL;
86 }
87 } else {
88 written = fwrite("-----BEGIN PRIVATE KEY-----\n", 1, 28, file);
89 written += fwrite(in, 1, strlen(in), file);
90 written += fwrite("\n-----END PRIVATE KEY-----", 1, 26, file);
91
92 fclose(file);
93 if ((unsigned)written != 28 + strlen(in) + 26) {
94 unlink(path);
95 return NULL;
96 }
Michal Vasko4c1fb492017-01-30 14:31:07 +010097 }
98
99 return strdup(path);
100}
101
102static int
Michal Vasko7d255882017-02-09 13:35:08 +0100103nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +0100104{
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100105 uint8_t i;
106
Michal Vasko4c1fb492017-01-30 14:31:07 +0100107 if (!name) {
108 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +0100109 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100110 } else if (idx > opts->hostkey_count) {
111 ERRARG("idx");
112 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100113 }
Michal Vaskod45e25a2016-01-08 15:48:44 +0100114
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100115 for (i = 0; i < opts->hostkey_count; ++i) {
116 if (!strcmp(opts->hostkeys[i], name)) {
117 ERRARG("name");
118 return -1;
119 }
120 }
121
Michal Vaskoe2713da2016-08-22 16:06:40 +0200122 ++opts->hostkey_count;
123 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
124 if (!opts->hostkeys) {
125 ERRMEM;
126 return -1;
127 }
Michal Vasko7d255882017-02-09 13:35:08 +0100128
129 if (idx < 0) {
130 idx = opts->hostkey_count - 1;
131 }
132 if (idx != opts->hostkey_count - 1) {
133 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
134 }
Michal Vasko77367452021-02-16 16:32:18 +0100135 lydict_insert(server_opts.ctx, name, 0, &opts->hostkeys[idx]);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200136
Michal Vasko5fcc7142016-02-02 12:21:10 +0100137 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100138}
139
140API int
Michal Vasko7d255882017-02-09 13:35:08 +0100141nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100142{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100143 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100144 struct nc_endpt *endpt;
145
Michal Vasko51e514d2016-02-02 15:51:52 +0100146 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100147 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100148 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100149 return -1;
150 }
Michal Vasko7d255882017-02-09 13:35:08 +0100151 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100152 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100153 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100154
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100155 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100156}
157
Michal Vasko974410a2018-04-03 09:36:57 +0200158API void
159nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200160 void *user_data, void (*free_user_data)(void *user_data))
Michal Vasko974410a2018-04-03 09:36:57 +0200161{
162 server_opts.passwd_auth_clb = passwd_auth_clb;
163 server_opts.passwd_auth_data = user_data;
164 server_opts.passwd_auth_data_free = free_user_data;
165}
166
bhart1bb7cdb2018-07-02 15:03:30 -0500167API void
168nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200169 void *user_data, void (*free_user_data)(void *user_data))
bhart1bb7cdb2018-07-02 15:03:30 -0500170{
171 server_opts.interactive_auth_clb = interactive_auth_clb;
172 server_opts.interactive_auth_data = user_data;
173 server_opts.interactive_auth_data_free = free_user_data;
174}
Michal Vasko733c0bd2018-07-03 13:14:40 +0200175
bhart1bb7cdb2018-07-02 15:03:30 -0500176API void
177nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data),
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200178 void *user_data, void (*free_user_data)(void *user_data))
bhart1bb7cdb2018-07-02 15:03:30 -0500179{
180 server_opts.pubkey_auth_clb = pubkey_auth_clb;
181 server_opts.pubkey_auth_data = user_data;
182 server_opts.pubkey_auth_data_free = free_user_data;
183}
184
Michal Vaskob05053d2016-01-22 16:12:06 +0100185API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200186nc_server_ssh_ch_client_endpt_add_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100187{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100188 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200189 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200190 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100191
Michal Vasko2e6defd2016-10-07 15:48:15 +0200192 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200193 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
194 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200195 return -1;
196 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200197 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200198 /* UNLOCK */
199 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200200
201 return ret;
202}
203
Michal Vasko4c1fb492017-01-30 14:31:07 +0100204API void
205nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200206 char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, void (*free_user_data)(void *user_data))
Michal Vasko4c1fb492017-01-30 14:31:07 +0100207{
208 if (!hostkey_clb) {
209 ERRARG("hostkey_clb");
210 return;
211 }
212
213 server_opts.hostkey_clb = hostkey_clb;
214 server_opts.hostkey_data = user_data;
215 server_opts.hostkey_data_free = free_user_data;
216}
217
Michal Vaskoe2713da2016-08-22 16:06:40 +0200218static int
Michal Vasko7d255882017-02-09 13:35:08 +0100219nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200220{
221 uint8_t i;
222
Michal Vasko7d255882017-02-09 13:35:08 +0100223 if (name && (idx > -1)) {
224 ERRARG("name and idx");
225 return -1;
226 } else if (idx >= opts->hostkey_count) {
227 ERRARG("idx");
228 }
229
230 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200231 for (i = 0; i < opts->hostkey_count; ++i) {
232 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
233 }
234 free(opts->hostkeys);
235 opts->hostkeys = NULL;
236 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100237 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200238 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100239 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100240 idx = i;
241 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200242 }
243 }
244
Michal Vasko7d255882017-02-09 13:35:08 +0100245 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200246 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100247 } else {
248remove_idx:
249 --opts->hostkey_count;
250 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
251 if (idx < opts->hostkey_count - 1) {
252 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
253 }
254 if (!opts->hostkey_count) {
255 free(opts->hostkeys);
256 opts->hostkeys = NULL;
257 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200258 }
259
260 return 0;
261}
262
263API int
Michal Vasko7d255882017-02-09 13:35:08 +0100264nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200265{
266 int ret;
267 struct nc_endpt *endpt;
268
269 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100270 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200271 if (!endpt) {
272 return -1;
273 }
Michal Vasko7d255882017-02-09 13:35:08 +0100274 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200275 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100276 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200277
278 return ret;
279}
280
281API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200282nc_server_ssh_ch_client_endpt_del_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200283{
284 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200285 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200286 struct nc_ch_endpt *endpt;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200287
Michal Vasko2e6defd2016-10-07 15:48:15 +0200288 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200289 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
290 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200291 return -1;
292 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200293 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200294 /* UNLOCK */
295 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100296
297 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100298}
299
300static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100301nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
302{
303 uint8_t i;
304 int16_t mov_idx = -1, after_idx = -1;
305 const char *bckup;
306
307 if (!key_mov) {
308 ERRARG("key_mov");
309 return -1;
310 }
311
312 for (i = 0; i < opts->hostkey_count; ++i) {
313 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
314 after_idx = i;
315 }
316 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
317 mov_idx = i;
318 }
319
320 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
321 break;
322 }
323 }
324
325 if (key_after && (after_idx == -1)) {
326 ERRARG("key_after");
327 return -1;
328 }
329 if (mov_idx == -1) {
330 ERRARG("key_mov");
331 return -1;
332 }
333 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
334 /* nothing to do */
335 return 0;
336 }
337
338 /* finally move the key */
339 bckup = opts->hostkeys[mov_idx];
340 if (mov_idx > after_idx) {
341 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
342 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
343 opts->hostkeys[after_idx + 1] = bckup;
344 } else {
345 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
346 opts->hostkeys[after_idx] = bckup;
347 }
348
349 return 0;
350}
351
352API int
353nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
354{
355 int ret;
356 struct nc_endpt *endpt;
357
358 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100359 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100360 if (!endpt) {
361 return -1;
362 }
363 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
364 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100365 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100366
367 return ret;
368}
369
370API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200371nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
372 const char *key_after)
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100373{
374 int ret;
375 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200376 struct nc_ch_endpt *endpt;
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100377
378 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200379 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100380 if (!endpt) {
381 return -1;
382 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200383 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100384 /* UNLOCK */
385 nc_server_ch_client_unlock(client);
386
387 return ret;
388}
389
390static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100391nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100392{
Michal Vaskob05053d2016-01-22 16:12:06 +0100393 opts->auth_methods = auth_methods;
394 return 0;
395}
396
397API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100398nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100399{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100400 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100401 struct nc_endpt *endpt;
402
Michal Vasko51e514d2016-02-02 15:51:52 +0100403 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100404 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100405 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100406 return -1;
407 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200408 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100409 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100410 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100411
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100412 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100413}
414
415API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200416nc_server_ssh_ch_client_endpt_set_auth_methods(const char *client_name, const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100417{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100418 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200419 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200420 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100421
Michal Vasko2e6defd2016-10-07 15:48:15 +0200422 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200423 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
424 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200425 return -1;
426 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200427 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200428 /* UNLOCK */
429 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100430
431 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100432}
433
Michal Vaskoddce1212019-05-24 09:58:49 +0200434API int
435nc_server_ssh_endpt_get_auth_methods(const char *endpt_name)
436{
437 int ret;
438 struct nc_endpt *endpt;
439
440 /* LOCK */
441 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
442 if (!endpt) {
443 return -1;
444 }
445 ret = endpt->opts.ssh->auth_methods;
446 /* UNLOCK */
447 pthread_rwlock_unlock(&server_opts.endpt_lock);
448
449 return ret;
450}
451
452API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200453nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name)
Michal Vaskoddce1212019-05-24 09:58:49 +0200454{
455 int ret;
456 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200457 struct nc_ch_endpt *endpt;
Michal Vaskoddce1212019-05-24 09:58:49 +0200458
459 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200460 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
461 if (!endpt) {
Michal Vaskoddce1212019-05-24 09:58:49 +0200462 return -1;
463 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200464 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoddce1212019-05-24 09:58:49 +0200465 /* UNLOCK */
466 nc_server_ch_client_unlock(client);
467
468 return ret;
469}
470
Michal Vaskob05053d2016-01-22 16:12:06 +0100471static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100472nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100473{
Michal Vaskob05053d2016-01-22 16:12:06 +0100474 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200475 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100476 return -1;
477 }
478
Michal Vaskob05053d2016-01-22 16:12:06 +0100479 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100480 return 0;
481}
482
483API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100484nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100485{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100486 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100487 struct nc_endpt *endpt;
488
Michal Vasko51e514d2016-02-02 15:51:52 +0100489 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100490 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100491 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100492 return -1;
493 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200494 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100495 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100496 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100497
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100498 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100499}
500
501API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200502nc_server_ssh_ch_client_endpt_set_auth_attempts(const char *client_name, const char *endpt_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100503{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100504 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200505 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200506 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100507
Michal Vasko2e6defd2016-10-07 15:48:15 +0200508 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200509 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
510 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200511 return -1;
512 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200513 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200514 /* UNLOCK */
515 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100516
517 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100518}
519
520static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100521nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100522{
Michal Vaskob05053d2016-01-22 16:12:06 +0100523 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200524 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100525 return -1;
526 }
527
Michal Vaskob05053d2016-01-22 16:12:06 +0100528 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100529 return 0;
530}
531
532API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100533nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100534{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100535 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100536 struct nc_endpt *endpt;
537
Michal Vasko51e514d2016-02-02 15:51:52 +0100538 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100539 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100540 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100541 return -1;
542 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200543 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100544 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100545 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100546
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100547 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100548}
549
550API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200551nc_server_ssh_ch_client_endpt_set_auth_timeout(const char *client_name, const char *endpt_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100552{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100553 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200554 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200555 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100556
Michal Vasko2e6defd2016-10-07 15:48:15 +0200557 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200558 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
559 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200560 return -1;
561 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200562 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200563 /* UNLOCK */
564 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100565
566 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100567}
568
569static int
Michal Vasko77367452021-02-16 16:32:18 +0100570_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko17dfda92016-12-01 14:06:16 +0100571{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100572 /* LOCK */
573 pthread_mutex_lock(&server_opts.authkey_lock);
574
Michal Vasko17dfda92016-12-01 14:06:16 +0100575 ++server_opts.authkey_count;
576 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
577 if (!server_opts.authkeys) {
578 ERRMEM;
579 return -1;
580 }
Michal Vasko77367452021-02-16 16:32:18 +0100581 lydict_insert(server_opts.ctx, pubkey_path, 0, &server_opts.authkeys[server_opts.authkey_count - 1].path);
582 lydict_insert(server_opts.ctx, pubkey_base64, 0, &server_opts.authkeys[server_opts.authkey_count - 1].base64);
Michal Vasko17dfda92016-12-01 14:06:16 +0100583 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
Michal Vasko77367452021-02-16 16:32:18 +0100584 lydict_insert(server_opts.ctx, username, 0, &server_opts.authkeys[server_opts.authkey_count - 1].username);
Michal Vasko17dfda92016-12-01 14:06:16 +0100585
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100586 /* UNLOCK */
587 pthread_mutex_unlock(&server_opts.authkey_lock);
588
Michal Vasko17dfda92016-12-01 14:06:16 +0100589 return 0;
590}
591
592API int
593nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100594{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200595 if (!pubkey_path) {
596 ERRARG("pubkey_path");
597 return -1;
598 } else if (!username) {
599 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100600 return -1;
601 }
602
Michal Vasko17dfda92016-12-01 14:06:16 +0100603 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100604}
605
606API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100607nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100608{
Michal Vasko17dfda92016-12-01 14:06:16 +0100609 if (!pubkey_base64) {
610 ERRARG("pubkey_base64");
611 return -1;
612 } else if (!type) {
613 ERRARG("type");
614 return -1;
615 } else if (!username) {
616 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100617 return -1;
618 }
619
Michal Vasko17dfda92016-12-01 14:06:16 +0100620 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100621}
622
623API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100624nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200625 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100626{
Michal Vasko086311b2016-01-08 09:53:11 +0100627 uint32_t i;
628 int ret = -1;
629
Michal Vasko17dfda92016-12-01 14:06:16 +0100630 /* LOCK */
631 pthread_mutex_lock(&server_opts.authkey_lock);
632
633 if (!pubkey_path && !pubkey_base64 && !type && !username) {
634 for (i = 0; i < server_opts.authkey_count; ++i) {
635 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
636 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
637 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100638
Michal Vasko086311b2016-01-08 09:53:11 +0100639 ret = 0;
640 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100641 free(server_opts.authkeys);
642 server_opts.authkeys = NULL;
643 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100644 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100645 for (i = 0; i < server_opts.authkey_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200646 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path)) &&
647 (!pubkey_base64 || !strcmp(server_opts.authkeys[i].base64, pubkey_base64)) &&
648 (!type || (server_opts.authkeys[i].type == type)) &&
649 (!username || !strcmp(server_opts.authkeys[i].username, username))) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100650 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
651 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
652 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100653
Michal Vasko17dfda92016-12-01 14:06:16 +0100654 --server_opts.authkey_count;
655 if (i < server_opts.authkey_count) {
656 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200657 sizeof *server_opts.authkeys);
Michal Vasko17dfda92016-12-01 14:06:16 +0100658 } else if (!server_opts.authkey_count) {
659 free(server_opts.authkeys);
660 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100661 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100662
663 ret = 0;
664 }
665 }
Michal Vasko086311b2016-01-08 09:53:11 +0100666 }
667
Michal Vasko51e514d2016-02-02 15:51:52 +0100668 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100669 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100670
671 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100672}
673
674void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100675nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100676{
Michal Vasko7d255882017-02-09 13:35:08 +0100677 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100678}
679
Michal Vasko086311b2016-01-08 09:53:11 +0100680static char *
681auth_password_get_pwd_hash(const char *username)
682{
apropp-molex4e903c32020-04-20 03:06:58 -0400683#ifdef HAVE_SHADOW
Michal Vasko086311b2016-01-08 09:53:11 +0100684 struct passwd *pwd, pwd_buf;
685 struct spwd *spwd, spwd_buf;
686 char *pass_hash = NULL, buf[256];
687
688 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
689 if (!pwd) {
Michal Vasko05532772021-06-03 12:12:38 +0200690 VRB(NULL, "User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100691 return NULL;
692 }
693
694 if (!strcmp(pwd->pw_passwd, "x")) {
Michal Vaskob4ff2fd2020-11-03 14:18:13 +0100695# ifndef __QNXNTO__
696 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
697# else
698 spwd = getspnam_r(username, &spwd_buf, buf, 256);
699# endif
Michal Vasko086311b2016-01-08 09:53:11 +0100700 if (!spwd) {
Michal Vasko05532772021-06-03 12:12:38 +0200701 VRB(NULL, "Failed to retrieve the shadow entry for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100702 return NULL;
Michal Vasko22b4fe72020-11-04 08:52:29 +0100703 } else if ((spwd->sp_expire > -1) && (spwd->sp_expire <= (time(NULL) / (60 * 60 * 24)))) {
Michal Vasko05532772021-06-03 12:12:38 +0200704 WRN(NULL, "User \"%s\" account has expired.", username);
Michal Vasko22b4fe72020-11-04 08:52:29 +0100705 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100706 }
707
708 pass_hash = spwd->sp_pwdp;
709 } else {
710 pass_hash = pwd->pw_passwd;
711 }
712
713 if (!pass_hash) {
Michal Vasko05532772021-06-03 12:12:38 +0200714 ERR(NULL, "No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100715 return NULL;
716 }
717
718 /* check the hash structure for special meaning */
719 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
Michal Vasko05532772021-06-03 12:12:38 +0200720 VRB(NULL, "User \"%s\" is not allowed to authenticate using a password.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100721 return NULL;
722 }
723 if (!strcmp(pass_hash, "*NP*")) {
Michal Vasko05532772021-06-03 12:12:38 +0200724 VRB(NULL, "Retrieving password for \"%s\" from a NIS+ server not supported.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100725 return NULL;
726 }
727
728 return strdup(pass_hash);
Claus Klein22091912020-01-20 13:45:47 +0100729#else
730 return strdup("");
731#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100732}
733
734static int
735auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
736{
737 char *new_pass_hash;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200738
739#if defined (HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100740 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200741#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100742
743 if (!pass_hash[0]) {
744 if (!pass_clear[0]) {
Michal Vasko05532772021-06-03 12:12:38 +0200745 WRN(NULL, "User authentication successful with an empty password!");
Michal Vasko086311b2016-01-08 09:53:11 +0100746 return 0;
747 } else {
748 /* the user did now know he does not need any password,
749 * (which should not be used) so deny authentication */
750 return 1;
751 }
752 }
753
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200754#if defined (HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100755 cdata.initialized = 0;
756 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200757#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200758 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200759 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200760 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200761#endif
Andrew Langefeld158d6fd2018-06-11 18:51:44 -0500762
763 if (!new_pass_hash) {
764 return 1;
765 }
766
Michal Vasko086311b2016-01-08 09:53:11 +0100767 return strcmp(new_pass_hash, pass_hash);
768}
769
770static void
771nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
772{
773 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100774 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100775
Michal Vaskoebba7602018-03-23 13:14:08 +0100776 if (server_opts.passwd_auth_clb) {
777 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
778 } else {
779 pass_hash = auth_password_get_pwd_hash(session->username);
780 if (pass_hash) {
781 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
782 free(pass_hash);
783 }
Michal Vasko086311b2016-01-08 09:53:11 +0100784 }
785
Michal Vaskoebba7602018-03-23 13:14:08 +0100786 if (!auth_ret) {
787 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
Michal Vasko05532772021-06-03 12:12:38 +0200788 VRB(session, "User \"%s\" authenticated.", session->username);
Michal Vaskoebba7602018-03-23 13:14:08 +0100789 ssh_message_auth_reply_success(msg, 0);
790 } else {
791 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +0200792 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
793 session->opts.server.ssh_auth_attempts);
Michal Vaskoebba7602018-03-23 13:14:08 +0100794 ssh_message_reply_default(msg);
795 }
Michal Vasko086311b2016-01-08 09:53:11 +0100796}
797
798static void
799nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
800{
bhart3bc2f582018-06-05 12:40:32 -0500801 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100802 char *pass_hash;
803
bhart1bb7cdb2018-07-02 15:03:30 -0500804 if (server_opts.interactive_auth_clb) {
Michal Vasko733c0bd2018-07-03 13:14:40 +0200805 auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100806 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500807 if (!ssh_message_auth_kbdint_is_response(msg)) {
808 const char *prompts[] = {"Password: "};
809 char echo[] = {0};
810
811 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
Robert Vargaad7a5532018-08-10 20:40:54 +0200812 auth_ret = -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100813 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500814 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {// failed session
815 ssh_message_reply_default(msg);
816 return;
817 }
bhart3bc2f582018-06-05 12:40:32 -0500818 pass_hash = auth_password_get_pwd_hash(session->username);// get hashed password
819 if (pass_hash) {
Robert Vargaad7a5532018-08-10 20:40:54 +0200820 /* Normalize auth_password_compare_pwd result to 0 or 1 */
821 auth_ret = !!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0));
bhart3bc2f582018-06-05 12:40:32 -0500822 free(pass_hash);// free hashed password
823 }
Michal Vasko086311b2016-01-08 09:53:11 +0100824 }
bhart1bb7cdb2018-07-02 15:03:30 -0500825 }
826
Robert Vargaad7a5532018-08-10 20:40:54 +0200827 /* We have already sent a reply */
828 if (auth_ret == -1) {
829 return;
830 }
831
bhart1bb7cdb2018-07-02 15:03:30 -0500832 /* Authenticate message based on outcome */
833 if (!auth_ret) {
834 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
Michal Vasko05532772021-06-03 12:12:38 +0200835 VRB(session, "User \"%s\" authenticated.", session->username);
bhart1bb7cdb2018-07-02 15:03:30 -0500836 ssh_message_auth_reply_success(msg, 0);
837 } else {
838 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +0200839 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
840 session->opts.server.ssh_auth_attempts);
bhart1bb7cdb2018-07-02 15:03:30 -0500841 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100842 }
843}
844
845static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100846auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100847{
848 uint32_t i;
849 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100850 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100851 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100852
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100853 /* LOCK */
854 pthread_mutex_lock(&server_opts.authkey_lock);
855
Michal Vasko17dfda92016-12-01 14:06:16 +0100856 for (i = 0; i < server_opts.authkey_count; ++i) {
857 switch (server_opts.authkeys[i].type) {
858 case NC_SSH_KEY_UNKNOWN:
859 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
860 break;
861 case NC_SSH_KEY_DSA:
862 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
863 break;
864 case NC_SSH_KEY_RSA:
865 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
866 break;
867 case NC_SSH_KEY_ECDSA:
868 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
869 break;
870 }
871
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200872 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200873 WRN(NULL, "Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200874 continue;
875 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200876 WRN(NULL, "Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100877 continue;
878 }
879
880 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
881 ssh_key_free(pub_key);
882 break;
883 }
884
885 ssh_key_free(pub_key);
886 }
887
Michal Vasko17dfda92016-12-01 14:06:16 +0100888 if (i < server_opts.authkey_count) {
889 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100890 }
891
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100892 /* UNLOCK */
893 pthread_mutex_unlock(&server_opts.authkey_lock);
894
Michal Vasko086311b2016-01-08 09:53:11 +0100895 return username;
896}
897
898static void
899nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
900{
901 const char *username;
902 int signature_state;
903
Michal Vasko733c0bd2018-07-03 13:14:40 +0200904 if (server_opts.pubkey_auth_clb) {
905 if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) {
bhart3bc2f582018-06-05 12:40:32 -0500906 goto fail;
907 }
Michal Vasko733c0bd2018-07-03 13:14:40 +0200908 } else {
bhart3bc2f582018-06-05 12:40:32 -0500909 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +0200910 VRB(session, "User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
bhart3bc2f582018-06-05 12:40:32 -0500911 goto fail;
912 } else if (strcmp(session->username, username)) {
Michal Vasko05532772021-06-03 12:12:38 +0200913 VRB(session, "User \"%s\" is not the username identified with the presented public key.", session->username);
bhart3bc2f582018-06-05 12:40:32 -0500914 goto fail;
915 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200916 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200917
Michal Vasko086311b2016-01-08 09:53:11 +0100918 signature_state = ssh_message_auth_publickey_state(msg);
919 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
Michal Vasko05532772021-06-03 12:12:38 +0200920 VRB(session, "User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100921 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
922 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100923 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200924 /* accepting only the use of a public key */
925 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100926 }
927
Michal Vaskobd13a932016-09-14 09:00:35 +0200928 return;
929
930fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200931 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +0200932 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
933 session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100934 ssh_message_reply_default(msg);
935}
936
937static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100938nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100939{
Michal Vasko96164bf2016-01-21 15:41:58 +0100940 ssh_channel chan;
941
942 /* first channel request */
943 if (!session->ti.libssh.channel) {
944 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100945 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100946 return -1;
947 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100948 chan = ssh_message_channel_request_open_reply_accept(msg);
949 if (!chan) {
Michal Vasko05532772021-06-03 12:12:38 +0200950 ERR(session, "Failed to create a new SSH channel.");
Michal Vasko96164bf2016-01-21 15:41:58 +0100951 return -1;
952 }
953 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100954
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200955 /* additional channel request */
Michal Vasko96164bf2016-01-21 15:41:58 +0100956 } else {
957 chan = ssh_message_channel_request_open_reply_accept(msg);
958 if (!chan) {
Michal Vasko05532772021-06-03 12:12:38 +0200959 ERR(session, "Session %u: failed to create a new SSH channel.", session->id);
Michal Vasko96164bf2016-01-21 15:41:58 +0100960 return -1;
961 }
962 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100963 }
964
Michal Vasko086311b2016-01-08 09:53:11 +0100965 return 0;
966}
967
968static int
969nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
970{
Michal Vasko96164bf2016-01-21 15:41:58 +0100971 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100972
Michal Vasko96164bf2016-01-21 15:41:58 +0100973 if (strcmp(subsystem, "netconf")) {
Michal Vasko05532772021-06-03 12:12:38 +0200974 WRN(session, "Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100975 return -1;
976 }
977
Michal Vasko96164bf2016-01-21 15:41:58 +0100978 if (session->ti.libssh.channel == channel) {
979 /* first channel requested */
980 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
981 ERRINT;
982 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100983 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100984 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
Michal Vasko05532772021-06-03 12:12:38 +0200985 ERR(session, "Subsystem \"netconf\" requested for the second time.");
Michal Vasko96164bf2016-01-21 15:41:58 +0100986 return -1;
987 }
988
989 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100990 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100991 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +0200992 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100993 if (!new_session) {
994 ERRMEM;
995 return -1;
996 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100997
998 /* insert the new session */
999 if (!session->ti.libssh.next) {
1000 new_session->ti.libssh.next = session;
1001 } else {
1002 new_session->ti.libssh.next = session->ti.libssh.next;
1003 }
1004 session->ti.libssh.next = new_session;
1005
1006 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001007 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +02001008 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +01001009 new_session->ti.libssh.channel = channel;
1010 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko77367452021-02-16 16:32:18 +01001011 lydict_insert(server_opts.ctx, session->username, 0, &new_session->username);
1012 lydict_insert(server_opts.ctx, session->host, 0, &new_session->host);
Michal Vasko96164bf2016-01-21 15:41:58 +01001013 new_session->port = session->port;
1014 new_session->ctx = server_opts.ctx;
Michal Vasko83d15322018-09-27 09:44:02 +02001015 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
Michal Vasko086311b2016-01-08 09:53:11 +01001016 }
1017
1018 return 0;
1019}
1020
Michal Vasko96164bf2016-01-21 15:41:58 +01001021int
Michal Vaskob48aa812016-01-18 14:13:09 +01001022nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001023{
1024 const char *str_type, *str_subtype = NULL, *username;
1025 int subtype, type;
1026 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001027
1028 type = ssh_message_type(msg);
1029 subtype = ssh_message_subtype(msg);
1030
1031 switch (type) {
1032 case SSH_REQUEST_AUTH:
1033 str_type = "request-auth";
1034 switch (subtype) {
1035 case SSH_AUTH_METHOD_NONE:
1036 str_subtype = "none";
1037 break;
1038 case SSH_AUTH_METHOD_PASSWORD:
1039 str_subtype = "password";
1040 break;
1041 case SSH_AUTH_METHOD_PUBLICKEY:
1042 str_subtype = "publickey";
1043 break;
1044 case SSH_AUTH_METHOD_HOSTBASED:
1045 str_subtype = "hostbased";
1046 break;
1047 case SSH_AUTH_METHOD_INTERACTIVE:
1048 str_subtype = "interactive";
1049 break;
1050 case SSH_AUTH_METHOD_GSSAPI_MIC:
1051 str_subtype = "gssapi-mic";
1052 break;
1053 }
1054 break;
1055
1056 case SSH_REQUEST_CHANNEL_OPEN:
1057 str_type = "request-channel-open";
1058 switch (subtype) {
1059 case SSH_CHANNEL_SESSION:
1060 str_subtype = "session";
1061 break;
1062 case SSH_CHANNEL_DIRECT_TCPIP:
1063 str_subtype = "direct-tcpip";
1064 break;
1065 case SSH_CHANNEL_FORWARDED_TCPIP:
1066 str_subtype = "forwarded-tcpip";
1067 break;
1068 case (int)SSH_CHANNEL_X11:
1069 str_subtype = "channel-x11";
1070 break;
1071 case SSH_CHANNEL_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001072 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001073 default:
1074 str_subtype = "unknown";
1075 break;
1076 }
1077 break;
1078
1079 case SSH_REQUEST_CHANNEL:
1080 str_type = "request-channel";
1081 switch (subtype) {
1082 case SSH_CHANNEL_REQUEST_PTY:
1083 str_subtype = "pty";
1084 break;
1085 case SSH_CHANNEL_REQUEST_EXEC:
1086 str_subtype = "exec";
1087 break;
1088 case SSH_CHANNEL_REQUEST_SHELL:
1089 str_subtype = "shell";
1090 break;
1091 case SSH_CHANNEL_REQUEST_ENV:
1092 str_subtype = "env";
1093 break;
1094 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1095 str_subtype = "subsystem";
1096 break;
1097 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1098 str_subtype = "window-change";
1099 break;
1100 case SSH_CHANNEL_REQUEST_X11:
1101 str_subtype = "x11";
1102 break;
1103 case SSH_CHANNEL_REQUEST_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001104 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001105 default:
1106 str_subtype = "unknown";
1107 break;
1108 }
1109 break;
1110
1111 case SSH_REQUEST_SERVICE:
1112 str_type = "request-service";
1113 str_subtype = ssh_message_service_service(msg);
1114 break;
1115
1116 case SSH_REQUEST_GLOBAL:
1117 str_type = "request-global";
1118 switch (subtype) {
1119 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1120 str_subtype = "tcpip-forward";
1121 break;
1122 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1123 str_subtype = "cancel-tcpip-forward";
1124 break;
1125 case SSH_GLOBAL_REQUEST_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001126 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001127 default:
1128 str_subtype = "unknown";
1129 break;
1130 }
1131 break;
1132
1133 default:
1134 str_type = "unknown";
1135 str_subtype = "unknown";
1136 break;
1137 }
1138
Michal Vasko05532772021-06-03 12:12:38 +02001139 VRB(session, "Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vasko5e0edd82020-07-29 15:26:13 +02001140 if (!session || (session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
Michal Vaskoce319162016-02-03 15:33:08 +01001141 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1142 * but we got it now, during session free */
Michal Vasko05532772021-06-03 12:12:38 +02001143 VRB(session, "SSH message arrived on a %s session, the request will be denied.",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001144 (session && session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
Michal Vaskoce319162016-02-03 15:33:08 +01001145 ssh_message_reply_default(msg);
1146 return 0;
1147 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001148 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001149
1150 /*
1151 * process known messages
1152 */
1153 if (type == SSH_REQUEST_AUTH) {
1154 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko05532772021-06-03 12:12:38 +02001155 ERR(session, "User \"%s\" authenticated, but requested another authentication.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +01001156 ssh_message_reply_default(msg);
1157 return 0;
1158 }
1159
Michal Vasko086311b2016-01-08 09:53:11 +01001160 /* save the username, do not let the client change it */
1161 username = ssh_message_auth_user(msg);
1162 if (!session->username) {
1163 if (!username) {
Michal Vasko05532772021-06-03 12:12:38 +02001164 ERR(session, "Denying an auth request without a username.");
Michal Vasko086311b2016-01-08 09:53:11 +01001165 return 1;
1166 }
1167
Michal Vasko77367452021-02-16 16:32:18 +01001168 lydict_insert(server_opts.ctx, username, 0, &session->username);
Michal Vasko086311b2016-01-08 09:53:11 +01001169 } else if (username) {
1170 if (strcmp(username, session->username)) {
Michal Vasko05532772021-06-03 12:12:38 +02001171 ERR(session, "User \"%s\" changed its username to \"%s\".", session->username, username);
Michal Vasko086311b2016-01-08 09:53:11 +01001172 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001173 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001174 return 1;
1175 }
1176 }
1177
1178 if (subtype == SSH_AUTH_METHOD_NONE) {
1179 /* libssh will return the supported auth methods */
1180 return 1;
1181 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1182 nc_sshcb_auth_password(session, msg);
1183 return 0;
1184 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1185 nc_sshcb_auth_pubkey(session, msg);
1186 return 0;
1187 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1188 nc_sshcb_auth_kbdint(session, msg);
1189 return 0;
1190 }
1191 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001192 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001193 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001194 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001195 }
Michal Vasko086311b2016-01-08 09:53:11 +01001196 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001197
Michal Vasko0df67562016-01-21 15:50:11 +01001198 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001199 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1200 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001201 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001202 } else {
1203 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001204 }
1205 return 0;
1206 }
1207 }
1208
1209 /* we did not process it */
1210 return 1;
1211}
1212
Michal Vasko1a38c862016-01-15 15:50:07 +01001213/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001214static int
1215nc_open_netconf_channel(struct nc_session *session, int timeout)
1216{
Michal Vasko36c7be82017-02-22 13:37:59 +01001217 int ret;
1218 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001219
1220 /* message callback is executed twice to give chance for the channel to be
1221 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001222 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001223 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +02001224 ERR(session, "Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001225 return -1;
1226 }
1227
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001228 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1229 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001230 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001231 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001232 return -1;
1233 }
1234
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001235 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001236 return 0;
1237 }
1238
1239 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1240 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001241 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001242 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001243 return -1;
1244 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001245
1246 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1247 /* we did not receive subsystem-request, timeout */
1248 return 0;
1249 }
1250
1251 return 1;
1252 }
1253
Michal Vasko36c7be82017-02-22 13:37:59 +01001254 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001255 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001256 nc_addtimespec(&ts_timeout, timeout);
1257 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001258 while (1) {
1259 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +02001260 ERR(session, "Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001261 return -1;
1262 }
1263
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001264 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1265 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001266 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001267 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001268 return -1;
1269 }
1270
Michal Vasko086311b2016-01-08 09:53:11 +01001271 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001272 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001273 }
1274
Michal Vasko086311b2016-01-08 09:53:11 +01001275 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001276 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001277 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001278 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1279 /* timeout */
Michal Vasko05532772021-06-03 12:12:38 +02001280 ERR(session, "Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko36c7be82017-02-22 13:37:59 +01001281 break;
1282 }
1283 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001284 }
Michal Vasko086311b2016-01-08 09:53:11 +01001285
Michal Vasko1a38c862016-01-15 15:50:07 +01001286 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001287}
1288
Michal Vasko4c1fb492017-01-30 14:31:07 +01001289static int
1290nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1291{
1292 uint8_t i;
1293 char *privkey_path, *privkey_data;
Michal Vaskoddce1212019-05-24 09:58:49 +02001294 int ret;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001295 NC_SSH_KEY_TYPE privkey_type;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001296
1297 if (!server_opts.hostkey_clb) {
Michal Vasko05532772021-06-03 12:12:38 +02001298 ERR(NULL, "Callback for retrieving SSH host keys not set.");
Michal Vasko4c1fb492017-01-30 14:31:07 +01001299 return -1;
1300 }
1301
1302 for (i = 0; i < hostkey_count; ++i) {
1303 privkey_path = privkey_data = NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +02001304 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_type)) {
Michal Vasko05532772021-06-03 12:12:38 +02001305 ERR(NULL, "Host key callback failed.");
Michal Vasko4c1fb492017-01-30 14:31:07 +01001306 return -1;
1307 }
1308
1309 if (privkey_data) {
Michal Vaskoddce1212019-05-24 09:58:49 +02001310 privkey_path = base64der_key_to_tmp_file(privkey_data, nc_keytype2str(privkey_type));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001311 if (!privkey_path) {
Michal Vasko05532772021-06-03 12:12:38 +02001312 ERR(NULL, "Temporarily storing a host key into a file failed (%s).", strerror(errno));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001313 free(privkey_data);
1314 return -1;
1315 }
1316 }
1317
1318 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1319
1320 /* cleanup */
1321 if (privkey_data && unlink(privkey_path)) {
Michal Vasko05532772021-06-03 12:12:38 +02001322 WRN(NULL, "Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001323 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001324 free(privkey_data);
1325
1326 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001327 ERR(NULL, "Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
Michal Vasko80075de2017-07-10 11:38:52 +02001328 }
1329 free(privkey_path);
1330
1331 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001332 return -1;
1333 }
1334 }
1335
1336 return 0;
1337}
1338
Michal Vasko96164bf2016-01-21 15:41:58 +01001339int
Michal Vasko0190bc32016-03-02 15:47:49 +01001340nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001341{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001342 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001343 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001344 int libssh_auth_methods = 0, ret;
1345 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001346
Michal Vasko2cc4c682016-03-01 09:16:48 +01001347 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001348
Michal Vasko086311b2016-01-08 09:53:11 +01001349 /* other transport-specific data */
1350 session->ti_type = NC_TI_LIBSSH;
1351 session->ti.libssh.session = ssh_new();
1352 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001353 ERR(NULL, "Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001354 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001355 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001356 }
1357
Michal Vaskoc61c4492016-01-25 11:13:34 +01001358 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001359 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1360 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001361 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001362 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1363 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001364 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001365 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1366 }
1367 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1368
Michal Vaskoe2713da2016-08-22 16:06:40 +02001369 sbind = ssh_bind_new();
1370 if (!sbind) {
Michal Vasko05532772021-06-03 12:12:38 +02001371 ERR(session, "Failed to create an SSH bind.");
Michal Vaskoe2713da2016-08-22 16:06:40 +02001372 close(sock);
1373 return -1;
1374 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001375
1376 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1377 close(sock);
1378 ssh_bind_free(sbind);
1379 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001380 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001381
Michal Vasko086311b2016-01-08 09:53:11 +01001382 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001383 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001384 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001385
Michal Vaskoe2713da2016-08-22 16:06:40 +02001386 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001387 ERR(session, "SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001388 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001389 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001390 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001391 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001392 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001393
Michal Vasko0190bc32016-03-02 15:47:49 +01001394 ssh_set_blocking(session->ti.libssh.session, 0);
1395
Michal Vasko36c7be82017-02-22 13:37:59 +01001396 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001397 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001398 nc_addtimespec(&ts_timeout, timeout);
1399 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001400 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001401 /* this tends to take longer */
1402 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001403 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001404 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001405 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1406 break;
1407 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001408 }
1409 }
1410 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001411 ERR(session, "SSH key exchange timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001412 return 0;
1413 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001414 ERR(session, "SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001415 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001416 }
1417
1418 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001419 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001420 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001421 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1422 }
1423 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001424 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +02001425 ERR(session, "Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001426 return -1;
1427 }
1428
Michal Vasko086311b2016-01-08 09:53:11 +01001429 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001430 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001431 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001432 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001433 }
1434
Michal Vasko36c7be82017-02-22 13:37:59 +01001435 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1436 break;
1437 }
1438
Michal Vasko145ae672017-02-07 10:57:27 +01001439 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
Michal Vasko05532772021-06-03 12:12:38 +02001440 ERR(session, "Too many failed authentication attempts of user \"%s\".", session->username);
Michal Vasko145ae672017-02-07 10:57:27 +01001441 return -1;
1442 }
1443
Michal Vasko086311b2016-01-08 09:53:11 +01001444 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001445 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001446 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001447 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1448 /* timeout */
1449 break;
1450 }
1451 }
1452 }
Michal Vasko086311b2016-01-08 09:53:11 +01001453
1454 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1455 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001456 if (session->username) {
Michal Vasko05532772021-06-03 12:12:38 +02001457 ERR(session, "User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
Michal Vaskoc13da702017-02-07 10:57:57 +01001458 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001459 ERR(session, "User failed to authenticate for too long, disconnecting.");
Michal Vaskoc13da702017-02-07 10:57:57 +01001460 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001461 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001462 }
1463
Michal Vasko086311b2016-01-08 09:53:11 +01001464 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001465 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001466 if (ret < 1) {
1467 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001468 }
1469
Michal Vasko96164bf2016-01-21 15:41:58 +01001470 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001471 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001472}
1473
Michal Vasko71090fc2016-05-24 16:37:28 +02001474API NC_MSG_TYPE
1475nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1476{
1477 NC_MSG_TYPE msgtype;
1478 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001479 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001480
1481 if (!orig_session) {
1482 ERRARG("orig_session");
1483 return NC_MSG_ERROR;
1484 } else if (!session) {
1485 ERRARG("session");
1486 return NC_MSG_ERROR;
1487 }
1488
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001489 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH) &&
1490 orig_session->ti.libssh.next) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001491 for (new_session = orig_session->ti.libssh.next;
1492 new_session != orig_session;
1493 new_session = new_session->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001494 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel &&
1495 (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001496 /* we found our session */
1497 break;
1498 }
1499 }
1500 if (new_session == orig_session) {
1501 new_session = NULL;
1502 }
1503 }
1504
1505 if (!new_session) {
Michal Vasko05532772021-06-03 12:12:38 +02001506 ERR(orig_session, "Session does not have a NETCONF SSH channel ready.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001507 return NC_MSG_ERROR;
1508 }
1509
1510 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001511 new_session->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001512
1513 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001514 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001515 if (msgtype != NC_MSG_HELLO) {
1516 return msgtype;
1517 }
1518
Michal Vasko9f6275e2017-10-05 13:50:05 +02001519 nc_gettimespec_real(&ts_cur);
1520 new_session->opts.server.session_start = ts_cur.tv_sec;
1521 nc_gettimespec_mono(&ts_cur);
1522 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001523 new_session->status = NC_STATUS_RUNNING;
1524 *session = new_session;
1525
1526 return msgtype;
1527}
1528
1529API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001530nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001531{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001532 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001533 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001534 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001535 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001536 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001537
Michal Vasko45e53ae2016-04-07 11:46:03 +02001538 if (!ps) {
1539 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001540 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001541 } else if (!session) {
1542 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001543 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001544 }
1545
Michal Vasko48a63ed2016-03-01 09:48:21 +01001546 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001547 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001548 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001549 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001550
Michal Vasko96164bf2016-01-21 15:41:58 +01001551 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001552 cur_session = ps->sessions[i]->session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001553 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH) &&
1554 cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001555 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001556 for (new_session = cur_session->ti.libssh.next;
1557 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001558 new_session = new_session->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001559 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel &&
1560 (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001561 /* we found our session */
1562 break;
1563 }
1564 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001565 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001566 break;
1567 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001568
Michal Vasko96164bf2016-01-21 15:41:58 +01001569 new_session = NULL;
1570 }
1571 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001572
Michal Vasko48a63ed2016-03-01 09:48:21 +01001573 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001574 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001575
Michal Vasko96164bf2016-01-21 15:41:58 +01001576 if (!new_session) {
Michal Vasko05532772021-06-03 12:12:38 +02001577 ERR(NULL, "No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001578 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001579 }
1580
1581 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001582 new_session->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskofb89d772016-01-08 12:25:35 +01001583
Michal Vasko086311b2016-01-08 09:53:11 +01001584 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001585 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001586 if (msgtype != NC_MSG_HELLO) {
1587 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001588 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001589
Michal Vasko9f6275e2017-10-05 13:50:05 +02001590 nc_gettimespec_real(&ts_cur);
1591 new_session->opts.server.session_start = ts_cur.tv_sec;
1592 nc_gettimespec_mono(&ts_cur);
1593 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001594 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001595 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001596
Michal Vasko71090fc2016-05-24 16:37:28 +02001597 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001598}