blob: 2f77d26773ac8a4fa5fa04cf0ecbbaf89de66274 [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 Vaskoc46b3df2021-07-26 09:30:05 +0200151
Michal Vasko7d255882017-02-09 13:35:08 +0100152 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200153
Michal Vasko51e514d2016-02-02 15:51:52 +0100154 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100155 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100156
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100157 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100158}
159
Michal Vasko974410a2018-04-03 09:36:57 +0200160API void
161nc_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 +0200162 void *user_data, void (*free_user_data)(void *user_data))
Michal Vasko974410a2018-04-03 09:36:57 +0200163{
164 server_opts.passwd_auth_clb = passwd_auth_clb;
165 server_opts.passwd_auth_data = user_data;
166 server_opts.passwd_auth_data_free = free_user_data;
167}
168
bhart1bb7cdb2018-07-02 15:03:30 -0500169API void
170nc_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 +0200171 void *user_data, void (*free_user_data)(void *user_data))
bhart1bb7cdb2018-07-02 15:03:30 -0500172{
173 server_opts.interactive_auth_clb = interactive_auth_clb;
174 server_opts.interactive_auth_data = user_data;
175 server_opts.interactive_auth_data_free = free_user_data;
176}
Michal Vasko733c0bd2018-07-03 13:14:40 +0200177
bhart1bb7cdb2018-07-02 15:03:30 -0500178API void
179nc_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 +0200180 void *user_data, void (*free_user_data)(void *user_data))
bhart1bb7cdb2018-07-02 15:03:30 -0500181{
182 server_opts.pubkey_auth_clb = pubkey_auth_clb;
183 server_opts.pubkey_auth_data = user_data;
184 server_opts.pubkey_auth_data_free = free_user_data;
185}
186
Michal Vaskob05053d2016-01-22 16:12:06 +0100187API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200188nc_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 +0100189{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100190 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200191 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200192 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100193
Michal Vasko2e6defd2016-10-07 15:48:15 +0200194 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200195 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
196 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200197 return -1;
198 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200199
Michal Vaskoadf30f02019-06-24 09:34:47 +0200200 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200201
Michal Vasko2e6defd2016-10-07 15:48:15 +0200202 /* UNLOCK */
203 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200204
205 return ret;
206}
207
Michal Vasko4c1fb492017-01-30 14:31:07 +0100208API void
209nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200210 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 +0100211{
212 if (!hostkey_clb) {
213 ERRARG("hostkey_clb");
214 return;
215 }
216
217 server_opts.hostkey_clb = hostkey_clb;
218 server_opts.hostkey_data = user_data;
219 server_opts.hostkey_data_free = free_user_data;
220}
221
Michal Vaskoe2713da2016-08-22 16:06:40 +0200222static int
Michal Vasko7d255882017-02-09 13:35:08 +0100223nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200224{
225 uint8_t i;
226
Michal Vasko7d255882017-02-09 13:35:08 +0100227 if (name && (idx > -1)) {
228 ERRARG("name and idx");
229 return -1;
230 } else if (idx >= opts->hostkey_count) {
231 ERRARG("idx");
232 }
233
234 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200235 for (i = 0; i < opts->hostkey_count; ++i) {
236 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
237 }
238 free(opts->hostkeys);
239 opts->hostkeys = NULL;
240 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100241 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200242 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100243 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100244 idx = i;
245 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200246 }
247 }
248
Michal Vasko7d255882017-02-09 13:35:08 +0100249 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200250 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100251 } else {
252remove_idx:
253 --opts->hostkey_count;
254 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
255 if (idx < opts->hostkey_count - 1) {
256 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
257 }
258 if (!opts->hostkey_count) {
259 free(opts->hostkeys);
260 opts->hostkeys = NULL;
261 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200262 }
263
264 return 0;
265}
266
267API int
Michal Vasko7d255882017-02-09 13:35:08 +0100268nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200269{
270 int ret;
271 struct nc_endpt *endpt;
272
273 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100274 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200275 if (!endpt) {
276 return -1;
277 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200278
Michal Vasko7d255882017-02-09 13:35:08 +0100279 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200280
Michal Vaskoe2713da2016-08-22 16:06:40 +0200281 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100282 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200283
284 return ret;
285}
286
287API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200288nc_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 +0200289{
290 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200291 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200292 struct nc_ch_endpt *endpt;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200293
Michal Vasko2e6defd2016-10-07 15:48:15 +0200294 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200295 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
296 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200297 return -1;
298 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200299
Michal Vaskoadf30f02019-06-24 09:34:47 +0200300 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200301
Michal Vasko2e6defd2016-10-07 15:48:15 +0200302 /* UNLOCK */
303 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100304
305 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100306}
307
308static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100309nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
310{
311 uint8_t i;
312 int16_t mov_idx = -1, after_idx = -1;
313 const char *bckup;
314
315 if (!key_mov) {
316 ERRARG("key_mov");
317 return -1;
318 }
319
320 for (i = 0; i < opts->hostkey_count; ++i) {
321 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
322 after_idx = i;
323 }
324 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
325 mov_idx = i;
326 }
327
328 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
329 break;
330 }
331 }
332
333 if (key_after && (after_idx == -1)) {
334 ERRARG("key_after");
335 return -1;
336 }
337 if (mov_idx == -1) {
338 ERRARG("key_mov");
339 return -1;
340 }
341 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
342 /* nothing to do */
343 return 0;
344 }
345
346 /* finally move the key */
347 bckup = opts->hostkeys[mov_idx];
348 if (mov_idx > after_idx) {
349 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
350 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
351 opts->hostkeys[after_idx + 1] = bckup;
352 } else {
353 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
354 opts->hostkeys[after_idx] = bckup;
355 }
356
357 return 0;
358}
359
360API int
361nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
362{
363 int ret;
364 struct nc_endpt *endpt;
365
366 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100367 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100368 if (!endpt) {
369 return -1;
370 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200371
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100372 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200373
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100374 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100375 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100376
377 return ret;
378}
379
380API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200381nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
382 const char *key_after)
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100383{
384 int ret;
385 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200386 struct nc_ch_endpt *endpt;
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100387
388 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200389 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100390 if (!endpt) {
391 return -1;
392 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200393
Michal Vaskoadf30f02019-06-24 09:34:47 +0200394 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200395
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100396 /* UNLOCK */
397 nc_server_ch_client_unlock(client);
398
399 return ret;
400}
401
402static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100403nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100404{
Michal Vaskob05053d2016-01-22 16:12:06 +0100405 opts->auth_methods = auth_methods;
406 return 0;
407}
408
409API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100410nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100411{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100412 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100413 struct nc_endpt *endpt;
414
Michal Vasko51e514d2016-02-02 15:51:52 +0100415 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100416 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100417 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100418 return -1;
419 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200420
Michal Vasko2e6defd2016-10-07 15:48:15 +0200421 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200422
Michal Vasko51e514d2016-02-02 15:51:52 +0100423 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100424 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100425
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100426 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100427}
428
429API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200430nc_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 +0100431{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100432 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200433 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200434 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100435
Michal Vasko2e6defd2016-10-07 15:48:15 +0200436 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200437 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
438 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200439 return -1;
440 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200441
Michal Vaskoadf30f02019-06-24 09:34:47 +0200442 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200443
Michal Vasko2e6defd2016-10-07 15:48:15 +0200444 /* UNLOCK */
445 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100446
447 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100448}
449
Michal Vaskoddce1212019-05-24 09:58:49 +0200450API int
451nc_server_ssh_endpt_get_auth_methods(const char *endpt_name)
452{
453 int ret;
454 struct nc_endpt *endpt;
455
456 /* LOCK */
457 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
458 if (!endpt) {
459 return -1;
460 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200461
Michal Vaskoddce1212019-05-24 09:58:49 +0200462 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200463
Michal Vaskoddce1212019-05-24 09:58:49 +0200464 /* UNLOCK */
465 pthread_rwlock_unlock(&server_opts.endpt_lock);
466
467 return ret;
468}
469
470API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200471nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name)
Michal Vaskoddce1212019-05-24 09:58:49 +0200472{
473 int ret;
474 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200475 struct nc_ch_endpt *endpt;
Michal Vaskoddce1212019-05-24 09:58:49 +0200476
477 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200478 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
479 if (!endpt) {
Michal Vaskoddce1212019-05-24 09:58:49 +0200480 return -1;
481 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200482
Michal Vaskoadf30f02019-06-24 09:34:47 +0200483 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200484
Michal Vaskoddce1212019-05-24 09:58:49 +0200485 /* UNLOCK */
486 nc_server_ch_client_unlock(client);
487
488 return ret;
489}
490
Michal Vaskob05053d2016-01-22 16:12:06 +0100491static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100492nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100493{
Michal Vaskob05053d2016-01-22 16:12:06 +0100494 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200495 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100496 return -1;
497 }
498
Michal Vaskob05053d2016-01-22 16:12:06 +0100499 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100500 return 0;
501}
502
503API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100504nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100505{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100506 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100507 struct nc_endpt *endpt;
508
Michal Vasko51e514d2016-02-02 15:51:52 +0100509 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100510 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100511 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100512 return -1;
513 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200514
Michal Vasko2e6defd2016-10-07 15:48:15 +0200515 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200516
Michal Vasko51e514d2016-02-02 15:51:52 +0100517 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100518 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100519
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100520 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100521}
522
523API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200524nc_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 +0100525{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100526 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200527 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200528 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100529
Michal Vasko2e6defd2016-10-07 15:48:15 +0200530 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200531 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
532 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200533 return -1;
534 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200535
Michal Vaskoadf30f02019-06-24 09:34:47 +0200536 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200537
Michal Vasko2e6defd2016-10-07 15:48:15 +0200538 /* UNLOCK */
539 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100540
541 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100542}
543
544static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100545nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100546{
Michal Vaskob05053d2016-01-22 16:12:06 +0100547 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200548 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100549 return -1;
550 }
551
Michal Vaskob05053d2016-01-22 16:12:06 +0100552 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100553 return 0;
554}
555
556API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100557nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100558{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100559 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100560 struct nc_endpt *endpt;
561
Michal Vasko51e514d2016-02-02 15:51:52 +0100562 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100563 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100564 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100565 return -1;
566 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200567
Michal Vasko2e6defd2016-10-07 15:48:15 +0200568 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200569
Michal Vasko51e514d2016-02-02 15:51:52 +0100570 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100571 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100572
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100573 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100574}
575
576API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200577nc_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 +0100578{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100579 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200580 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200581 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100582
Michal Vasko2e6defd2016-10-07 15:48:15 +0200583 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200584 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
585 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200586 return -1;
587 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200588
Michal Vaskoadf30f02019-06-24 09:34:47 +0200589 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200590
Michal Vasko2e6defd2016-10-07 15:48:15 +0200591 /* UNLOCK */
592 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100593
594 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100595}
596
597static int
Michal Vasko77367452021-02-16 16:32:18 +0100598_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 +0100599{
Michal Vasko09111892021-07-26 09:30:20 +0200600 int ret = 0;
601
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100602 /* LOCK */
603 pthread_mutex_lock(&server_opts.authkey_lock);
604
Michal Vasko17dfda92016-12-01 14:06:16 +0100605 ++server_opts.authkey_count;
606 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
607 if (!server_opts.authkeys) {
608 ERRMEM;
Michal Vasko09111892021-07-26 09:30:20 +0200609 ret = -1;
610 goto cleanup;
Michal Vasko17dfda92016-12-01 14:06:16 +0100611 }
Michal Vasko77367452021-02-16 16:32:18 +0100612 lydict_insert(server_opts.ctx, pubkey_path, 0, &server_opts.authkeys[server_opts.authkey_count - 1].path);
613 lydict_insert(server_opts.ctx, pubkey_base64, 0, &server_opts.authkeys[server_opts.authkey_count - 1].base64);
Michal Vasko17dfda92016-12-01 14:06:16 +0100614 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
Michal Vasko77367452021-02-16 16:32:18 +0100615 lydict_insert(server_opts.ctx, username, 0, &server_opts.authkeys[server_opts.authkey_count - 1].username);
Michal Vasko17dfda92016-12-01 14:06:16 +0100616
Michal Vasko09111892021-07-26 09:30:20 +0200617cleanup:
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100618 /* UNLOCK */
619 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vasko09111892021-07-26 09:30:20 +0200620 return ret;
Michal Vasko17dfda92016-12-01 14:06:16 +0100621}
622
623API int
624nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100625{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200626 if (!pubkey_path) {
627 ERRARG("pubkey_path");
628 return -1;
629 } else if (!username) {
630 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100631 return -1;
632 }
633
Michal Vasko17dfda92016-12-01 14:06:16 +0100634 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100635}
636
637API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100638nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100639{
Michal Vasko17dfda92016-12-01 14:06:16 +0100640 if (!pubkey_base64) {
641 ERRARG("pubkey_base64");
642 return -1;
643 } else if (!type) {
644 ERRARG("type");
645 return -1;
646 } else if (!username) {
647 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100648 return -1;
649 }
650
Michal Vasko17dfda92016-12-01 14:06:16 +0100651 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100652}
653
654API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100655nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200656 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100657{
Michal Vasko086311b2016-01-08 09:53:11 +0100658 uint32_t i;
659 int ret = -1;
660
Michal Vasko17dfda92016-12-01 14:06:16 +0100661 /* LOCK */
662 pthread_mutex_lock(&server_opts.authkey_lock);
663
664 if (!pubkey_path && !pubkey_base64 && !type && !username) {
665 for (i = 0; i < server_opts.authkey_count; ++i) {
666 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
667 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
668 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100669
Michal Vasko086311b2016-01-08 09:53:11 +0100670 ret = 0;
671 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100672 free(server_opts.authkeys);
673 server_opts.authkeys = NULL;
674 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100675 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100676 for (i = 0; i < server_opts.authkey_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200677 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path)) &&
678 (!pubkey_base64 || !strcmp(server_opts.authkeys[i].base64, pubkey_base64)) &&
679 (!type || (server_opts.authkeys[i].type == type)) &&
680 (!username || !strcmp(server_opts.authkeys[i].username, username))) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100681 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
682 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
683 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100684
Michal Vasko17dfda92016-12-01 14:06:16 +0100685 --server_opts.authkey_count;
686 if (i < server_opts.authkey_count) {
687 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200688 sizeof *server_opts.authkeys);
Michal Vasko17dfda92016-12-01 14:06:16 +0100689 } else if (!server_opts.authkey_count) {
690 free(server_opts.authkeys);
691 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100692 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100693
694 ret = 0;
695 }
696 }
Michal Vasko086311b2016-01-08 09:53:11 +0100697 }
698
Michal Vasko51e514d2016-02-02 15:51:52 +0100699 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100700 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100701
702 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100703}
704
705void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100706nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100707{
Michal Vasko7d255882017-02-09 13:35:08 +0100708 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100709}
710
Michal Vasko7a866152021-07-22 11:01:13 +0200711#ifdef HAVE_SHADOW
712
713static struct passwd *
714auth_password_getpwnam(const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size)
715{
716 struct passwd *pwd = NULL;
717 char *mem;
718
719 do {
720 errno = 0;
721 getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd);
722 if (pwd) {
723 /* entry found */
724 break;
725 }
726
727 if (errno == ERANGE) {
728 /* small buffer, enlarge */
729 *buf_size <<= 2;
730 mem = realloc(*buf, *buf_size);
731 if (!mem) {
732 ERRMEM;
733 return NULL;
734 }
735 *buf = mem;
736 }
737 } while (errno == ERANGE);
738
739 return pwd;
740}
741
742static struct spwd *
743auth_password_getspnam(const char *username, struct spwd *spwd_buf, char **buf, size_t *buf_size)
744{
745 struct spwd *spwd = NULL;
746 char *mem;
747
748 do {
749 errno = 0;
750# ifndef __QNXNTO__
751 getspnam_r(username, spwd_buf, *buf, *buf_size, &spwd);
752# else
753 spwd = getspnam_r(username, spwd_buf, *buf, *buf_size);
754# endif
755 if (spwd) {
756 /* entry found */
757 break;
758 }
759
760 if (errno == ERANGE) {
761 /* small buffer, enlarge */
762 *buf_size <<= 2;
763 mem = realloc(*buf, *buf_size);
764 if (!mem) {
765 ERRMEM;
766 return NULL;
767 }
768 *buf = mem;
769 }
770 } while (errno == ERANGE);
771
772 return spwd;
773}
774
Michal Vasko086311b2016-01-08 09:53:11 +0100775static char *
776auth_password_get_pwd_hash(const char *username)
777{
778 struct passwd *pwd, pwd_buf;
779 struct spwd *spwd, spwd_buf;
Michal Vasko7a866152021-07-22 11:01:13 +0200780 char *pass_hash = NULL, *buf = NULL;
781 size_t buf_size = 256;
Michal Vasko086311b2016-01-08 09:53:11 +0100782
Michal Vasko7a866152021-07-22 11:01:13 +0200783 buf = malloc(buf_size);
784 if (!buf) {
785 ERRMEM;
786 goto error;
787 }
788
789 pwd = auth_password_getpwnam(username, &pwd_buf, &buf, &buf_size);
Michal Vasko086311b2016-01-08 09:53:11 +0100790 if (!pwd) {
Michal Vasko05532772021-06-03 12:12:38 +0200791 VRB(NULL, "User \"%s\" not found locally.", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200792 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100793 }
794
795 if (!strcmp(pwd->pw_passwd, "x")) {
Michal Vasko7a866152021-07-22 11:01:13 +0200796 spwd = auth_password_getspnam(username, &spwd_buf, &buf, &buf_size);
Michal Vasko086311b2016-01-08 09:53:11 +0100797 if (!spwd) {
Michal Vasko05532772021-06-03 12:12:38 +0200798 VRB(NULL, "Failed to retrieve the shadow entry for \"%s\".", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200799 goto error;
Michal Vasko22b4fe72020-11-04 08:52:29 +0100800 } else if ((spwd->sp_expire > -1) && (spwd->sp_expire <= (time(NULL) / (60 * 60 * 24)))) {
Michal Vasko05532772021-06-03 12:12:38 +0200801 WRN(NULL, "User \"%s\" account has expired.", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200802 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100803 }
804
805 pass_hash = spwd->sp_pwdp;
806 } else {
807 pass_hash = pwd->pw_passwd;
808 }
809
810 if (!pass_hash) {
Michal Vasko05532772021-06-03 12:12:38 +0200811 ERR(NULL, "No password could be retrieved for \"%s\".", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200812 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100813 }
814
815 /* check the hash structure for special meaning */
816 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
Michal Vasko05532772021-06-03 12:12:38 +0200817 VRB(NULL, "User \"%s\" is not allowed to authenticate using a password.", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200818 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100819 }
820 if (!strcmp(pass_hash, "*NP*")) {
Michal Vasko05532772021-06-03 12:12:38 +0200821 VRB(NULL, "Retrieving password for \"%s\" from a NIS+ server not supported.", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200822 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100823 }
824
Michal Vasko963f6c02021-07-26 15:55:44 +0200825 pass_hash = strdup(pass_hash);
Michal Vasko7a866152021-07-22 11:01:13 +0200826 free(buf);
Michal Vasko963f6c02021-07-26 15:55:44 +0200827 return pass_hash;
Michal Vasko7a866152021-07-22 11:01:13 +0200828
829error:
830 free(buf);
831 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100832}
833
Michal Vasko7a866152021-07-22 11:01:13 +0200834#else
835
836static char *
837auth_password_get_pwd_hash(const char *username)
838{
839 (void)username;
840 return strdup("");
841}
842
843#endif
844
Michal Vasko086311b2016-01-08 09:53:11 +0100845static int
846auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
847{
848 char *new_pass_hash;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200849
850#if defined (HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100851 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200852#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100853
854 if (!pass_hash[0]) {
855 if (!pass_clear[0]) {
Michal Vasko05532772021-06-03 12:12:38 +0200856 WRN(NULL, "User authentication successful with an empty password!");
Michal Vasko086311b2016-01-08 09:53:11 +0100857 return 0;
858 } else {
859 /* the user did now know he does not need any password,
860 * (which should not be used) so deny authentication */
861 return 1;
862 }
863 }
864
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200865#if defined (HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100866 cdata.initialized = 0;
867 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200868#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200869 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200870 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200871 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200872#endif
Andrew Langefeld158d6fd2018-06-11 18:51:44 -0500873
874 if (!new_pass_hash) {
875 return 1;
876 }
877
Michal Vasko086311b2016-01-08 09:53:11 +0100878 return strcmp(new_pass_hash, pass_hash);
879}
880
881static void
882nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
883{
884 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100885 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100886
Michal Vaskoebba7602018-03-23 13:14:08 +0100887 if (server_opts.passwd_auth_clb) {
888 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
889 } else {
890 pass_hash = auth_password_get_pwd_hash(session->username);
891 if (pass_hash) {
892 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
893 free(pass_hash);
894 }
Michal Vasko086311b2016-01-08 09:53:11 +0100895 }
896
Michal Vaskoebba7602018-03-23 13:14:08 +0100897 if (!auth_ret) {
898 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
Michal Vasko05532772021-06-03 12:12:38 +0200899 VRB(session, "User \"%s\" authenticated.", session->username);
Michal Vaskoebba7602018-03-23 13:14:08 +0100900 ssh_message_auth_reply_success(msg, 0);
901 } else {
902 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +0200903 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
904 session->opts.server.ssh_auth_attempts);
Michal Vaskoebba7602018-03-23 13:14:08 +0100905 ssh_message_reply_default(msg);
906 }
Michal Vasko086311b2016-01-08 09:53:11 +0100907}
908
909static void
910nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
911{
bhart3bc2f582018-06-05 12:40:32 -0500912 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100913 char *pass_hash;
914
bhart1bb7cdb2018-07-02 15:03:30 -0500915 if (server_opts.interactive_auth_clb) {
Michal Vasko733c0bd2018-07-03 13:14:40 +0200916 auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100917 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500918 if (!ssh_message_auth_kbdint_is_response(msg)) {
919 const char *prompts[] = {"Password: "};
920 char echo[] = {0};
921
922 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
Robert Vargaad7a5532018-08-10 20:40:54 +0200923 auth_ret = -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100924 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500925 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {// failed session
926 ssh_message_reply_default(msg);
927 return;
928 }
bhart3bc2f582018-06-05 12:40:32 -0500929 pass_hash = auth_password_get_pwd_hash(session->username);// get hashed password
930 if (pass_hash) {
Robert Vargaad7a5532018-08-10 20:40:54 +0200931 /* Normalize auth_password_compare_pwd result to 0 or 1 */
932 auth_ret = !!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0));
bhart3bc2f582018-06-05 12:40:32 -0500933 free(pass_hash);// free hashed password
934 }
Michal Vasko086311b2016-01-08 09:53:11 +0100935 }
bhart1bb7cdb2018-07-02 15:03:30 -0500936 }
937
Robert Vargaad7a5532018-08-10 20:40:54 +0200938 /* We have already sent a reply */
939 if (auth_ret == -1) {
940 return;
941 }
942
bhart1bb7cdb2018-07-02 15:03:30 -0500943 /* Authenticate message based on outcome */
944 if (!auth_ret) {
945 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
Michal Vasko05532772021-06-03 12:12:38 +0200946 VRB(session, "User \"%s\" authenticated.", session->username);
bhart1bb7cdb2018-07-02 15:03:30 -0500947 ssh_message_auth_reply_success(msg, 0);
948 } else {
949 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +0200950 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
951 session->opts.server.ssh_auth_attempts);
bhart1bb7cdb2018-07-02 15:03:30 -0500952 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100953 }
954}
955
956static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100957auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100958{
959 uint32_t i;
960 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100961 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100962 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100963
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100964 /* LOCK */
965 pthread_mutex_lock(&server_opts.authkey_lock);
966
Michal Vasko17dfda92016-12-01 14:06:16 +0100967 for (i = 0; i < server_opts.authkey_count; ++i) {
968 switch (server_opts.authkeys[i].type) {
969 case NC_SSH_KEY_UNKNOWN:
970 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
971 break;
972 case NC_SSH_KEY_DSA:
973 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
974 break;
975 case NC_SSH_KEY_RSA:
976 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
977 break;
978 case NC_SSH_KEY_ECDSA:
979 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
980 break;
981 }
982
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200983 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200984 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 +0200985 continue;
986 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200987 WRN(NULL, "Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100988 continue;
989 }
990
991 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
992 ssh_key_free(pub_key);
993 break;
994 }
995
996 ssh_key_free(pub_key);
997 }
998
Michal Vasko17dfda92016-12-01 14:06:16 +0100999 if (i < server_opts.authkey_count) {
1000 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +01001001 }
1002
Michal Vaskoa05c7b12017-01-30 14:33:08 +01001003 /* UNLOCK */
1004 pthread_mutex_unlock(&server_opts.authkey_lock);
1005
Michal Vasko086311b2016-01-08 09:53:11 +01001006 return username;
1007}
1008
1009static void
1010nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
1011{
1012 const char *username;
1013 int signature_state;
1014
Michal Vasko733c0bd2018-07-03 13:14:40 +02001015 if (server_opts.pubkey_auth_clb) {
1016 if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) {
bhart3bc2f582018-06-05 12:40:32 -05001017 goto fail;
1018 }
Michal Vasko733c0bd2018-07-03 13:14:40 +02001019 } else {
bhart3bc2f582018-06-05 12:40:32 -05001020 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001021 VRB(session, "User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
bhart3bc2f582018-06-05 12:40:32 -05001022 goto fail;
1023 } else if (strcmp(session->username, username)) {
Michal Vasko05532772021-06-03 12:12:38 +02001024 VRB(session, "User \"%s\" is not the username identified with the presented public key.", session->username);
bhart3bc2f582018-06-05 12:40:32 -05001025 goto fail;
1026 }
Michal Vaskobd13a932016-09-14 09:00:35 +02001027 }
Michal Vaskobd13a932016-09-14 09:00:35 +02001028
Michal Vasko086311b2016-01-08 09:53:11 +01001029 signature_state = ssh_message_auth_publickey_state(msg);
1030 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
Michal Vasko05532772021-06-03 12:12:38 +02001031 VRB(session, "User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +01001032 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
1033 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001034 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +02001035 /* accepting only the use of a public key */
1036 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001037 }
1038
Michal Vaskobd13a932016-09-14 09:00:35 +02001039 return;
1040
1041fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +02001042 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +02001043 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
1044 session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +01001045 ssh_message_reply_default(msg);
1046}
1047
1048static int
Michal Vasko96164bf2016-01-21 15:41:58 +01001049nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +01001050{
Michal Vasko96164bf2016-01-21 15:41:58 +01001051 ssh_channel chan;
1052
1053 /* first channel request */
1054 if (!session->ti.libssh.channel) {
1055 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001056 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +01001057 return -1;
1058 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001059 chan = ssh_message_channel_request_open_reply_accept(msg);
1060 if (!chan) {
Michal Vasko05532772021-06-03 12:12:38 +02001061 ERR(session, "Failed to create a new SSH channel.");
Michal Vasko96164bf2016-01-21 15:41:58 +01001062 return -1;
1063 }
1064 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +01001065
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001066 /* additional channel request */
Michal Vasko96164bf2016-01-21 15:41:58 +01001067 } else {
1068 chan = ssh_message_channel_request_open_reply_accept(msg);
1069 if (!chan) {
Michal Vasko05532772021-06-03 12:12:38 +02001070 ERR(session, "Session %u: failed to create a new SSH channel.", session->id);
Michal Vasko96164bf2016-01-21 15:41:58 +01001071 return -1;
1072 }
1073 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +01001074 }
1075
Michal Vasko086311b2016-01-08 09:53:11 +01001076 return 0;
1077}
1078
1079static int
1080nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
1081{
Michal Vasko96164bf2016-01-21 15:41:58 +01001082 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001083
Michal Vasko96164bf2016-01-21 15:41:58 +01001084 if (strcmp(subsystem, "netconf")) {
Michal Vasko05532772021-06-03 12:12:38 +02001085 WRN(session, "Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +01001086 return -1;
1087 }
1088
Michal Vasko96164bf2016-01-21 15:41:58 +01001089 if (session->ti.libssh.channel == channel) {
1090 /* first channel requested */
1091 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
1092 ERRINT;
1093 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001094 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001095 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
Michal Vasko05532772021-06-03 12:12:38 +02001096 ERR(session, "Subsystem \"netconf\" requested for the second time.");
Michal Vasko96164bf2016-01-21 15:41:58 +01001097 return -1;
1098 }
1099
1100 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +01001101 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +01001102 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +02001103 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001104 if (!new_session) {
1105 ERRMEM;
1106 return -1;
1107 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001108
1109 /* insert the new session */
1110 if (!session->ti.libssh.next) {
1111 new_session->ti.libssh.next = session;
1112 } else {
1113 new_session->ti.libssh.next = session->ti.libssh.next;
1114 }
1115 session->ti.libssh.next = new_session;
1116
1117 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001118 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +02001119 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +01001120 new_session->ti.libssh.channel = channel;
1121 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko77367452021-02-16 16:32:18 +01001122 lydict_insert(server_opts.ctx, session->username, 0, &new_session->username);
1123 lydict_insert(server_opts.ctx, session->host, 0, &new_session->host);
Michal Vasko96164bf2016-01-21 15:41:58 +01001124 new_session->port = session->port;
1125 new_session->ctx = server_opts.ctx;
Michal Vasko83d15322018-09-27 09:44:02 +02001126 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
Michal Vasko086311b2016-01-08 09:53:11 +01001127 }
1128
1129 return 0;
1130}
1131
Michal Vasko96164bf2016-01-21 15:41:58 +01001132int
Michal Vaskob48aa812016-01-18 14:13:09 +01001133nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001134{
1135 const char *str_type, *str_subtype = NULL, *username;
1136 int subtype, type;
1137 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001138
1139 type = ssh_message_type(msg);
1140 subtype = ssh_message_subtype(msg);
1141
1142 switch (type) {
1143 case SSH_REQUEST_AUTH:
1144 str_type = "request-auth";
1145 switch (subtype) {
1146 case SSH_AUTH_METHOD_NONE:
1147 str_subtype = "none";
1148 break;
1149 case SSH_AUTH_METHOD_PASSWORD:
1150 str_subtype = "password";
1151 break;
1152 case SSH_AUTH_METHOD_PUBLICKEY:
1153 str_subtype = "publickey";
1154 break;
1155 case SSH_AUTH_METHOD_HOSTBASED:
1156 str_subtype = "hostbased";
1157 break;
1158 case SSH_AUTH_METHOD_INTERACTIVE:
1159 str_subtype = "interactive";
1160 break;
1161 case SSH_AUTH_METHOD_GSSAPI_MIC:
1162 str_subtype = "gssapi-mic";
1163 break;
1164 }
1165 break;
1166
1167 case SSH_REQUEST_CHANNEL_OPEN:
1168 str_type = "request-channel-open";
1169 switch (subtype) {
1170 case SSH_CHANNEL_SESSION:
1171 str_subtype = "session";
1172 break;
1173 case SSH_CHANNEL_DIRECT_TCPIP:
1174 str_subtype = "direct-tcpip";
1175 break;
1176 case SSH_CHANNEL_FORWARDED_TCPIP:
1177 str_subtype = "forwarded-tcpip";
1178 break;
1179 case (int)SSH_CHANNEL_X11:
1180 str_subtype = "channel-x11";
1181 break;
1182 case SSH_CHANNEL_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001183 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001184 default:
1185 str_subtype = "unknown";
1186 break;
1187 }
1188 break;
1189
1190 case SSH_REQUEST_CHANNEL:
1191 str_type = "request-channel";
1192 switch (subtype) {
1193 case SSH_CHANNEL_REQUEST_PTY:
1194 str_subtype = "pty";
1195 break;
1196 case SSH_CHANNEL_REQUEST_EXEC:
1197 str_subtype = "exec";
1198 break;
1199 case SSH_CHANNEL_REQUEST_SHELL:
1200 str_subtype = "shell";
1201 break;
1202 case SSH_CHANNEL_REQUEST_ENV:
1203 str_subtype = "env";
1204 break;
1205 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1206 str_subtype = "subsystem";
1207 break;
1208 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1209 str_subtype = "window-change";
1210 break;
1211 case SSH_CHANNEL_REQUEST_X11:
1212 str_subtype = "x11";
1213 break;
1214 case SSH_CHANNEL_REQUEST_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001215 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001216 default:
1217 str_subtype = "unknown";
1218 break;
1219 }
1220 break;
1221
1222 case SSH_REQUEST_SERVICE:
1223 str_type = "request-service";
1224 str_subtype = ssh_message_service_service(msg);
1225 break;
1226
1227 case SSH_REQUEST_GLOBAL:
1228 str_type = "request-global";
1229 switch (subtype) {
1230 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1231 str_subtype = "tcpip-forward";
1232 break;
1233 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1234 str_subtype = "cancel-tcpip-forward";
1235 break;
1236 case SSH_GLOBAL_REQUEST_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001237 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001238 default:
1239 str_subtype = "unknown";
1240 break;
1241 }
1242 break;
1243
1244 default:
1245 str_type = "unknown";
1246 str_subtype = "unknown";
1247 break;
1248 }
1249
Michal Vasko05532772021-06-03 12:12:38 +02001250 VRB(session, "Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vasko5e0edd82020-07-29 15:26:13 +02001251 if (!session || (session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
Michal Vaskoce319162016-02-03 15:33:08 +01001252 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1253 * but we got it now, during session free */
Michal Vasko05532772021-06-03 12:12:38 +02001254 VRB(session, "SSH message arrived on a %s session, the request will be denied.",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001255 (session && session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
Michal Vaskoce319162016-02-03 15:33:08 +01001256 ssh_message_reply_default(msg);
1257 return 0;
1258 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001259 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001260
1261 /*
1262 * process known messages
1263 */
1264 if (type == SSH_REQUEST_AUTH) {
1265 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko05532772021-06-03 12:12:38 +02001266 ERR(session, "User \"%s\" authenticated, but requested another authentication.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +01001267 ssh_message_reply_default(msg);
1268 return 0;
1269 }
1270
Michal Vasko086311b2016-01-08 09:53:11 +01001271 /* save the username, do not let the client change it */
1272 username = ssh_message_auth_user(msg);
1273 if (!session->username) {
1274 if (!username) {
Michal Vasko05532772021-06-03 12:12:38 +02001275 ERR(session, "Denying an auth request without a username.");
Michal Vasko086311b2016-01-08 09:53:11 +01001276 return 1;
1277 }
1278
Michal Vasko77367452021-02-16 16:32:18 +01001279 lydict_insert(server_opts.ctx, username, 0, &session->username);
Michal Vasko086311b2016-01-08 09:53:11 +01001280 } else if (username) {
1281 if (strcmp(username, session->username)) {
Michal Vasko05532772021-06-03 12:12:38 +02001282 ERR(session, "User \"%s\" changed its username to \"%s\".", session->username, username);
Michal Vasko086311b2016-01-08 09:53:11 +01001283 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001284 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001285 return 1;
1286 }
1287 }
1288
1289 if (subtype == SSH_AUTH_METHOD_NONE) {
1290 /* libssh will return the supported auth methods */
1291 return 1;
1292 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1293 nc_sshcb_auth_password(session, msg);
1294 return 0;
1295 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1296 nc_sshcb_auth_pubkey(session, msg);
1297 return 0;
1298 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1299 nc_sshcb_auth_kbdint(session, msg);
1300 return 0;
1301 }
1302 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001303 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001304 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001305 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001306 }
Michal Vasko086311b2016-01-08 09:53:11 +01001307 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001308
Michal Vasko0df67562016-01-21 15:50:11 +01001309 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001310 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1311 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001312 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001313 } else {
1314 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001315 }
1316 return 0;
1317 }
1318 }
1319
1320 /* we did not process it */
1321 return 1;
1322}
1323
Michal Vasko1a38c862016-01-15 15:50:07 +01001324/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001325static int
1326nc_open_netconf_channel(struct nc_session *session, int timeout)
1327{
Michal Vasko36c7be82017-02-22 13:37:59 +01001328 int ret;
1329 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001330
1331 /* message callback is executed twice to give chance for the channel to be
1332 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001333 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001334 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +02001335 ERR(session, "Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001336 return -1;
1337 }
1338
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001339 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1340 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001341 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001342 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001343 return -1;
1344 }
1345
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001346 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001347 return 0;
1348 }
1349
1350 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1351 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001352 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001353 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001354 return -1;
1355 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001356
1357 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1358 /* we did not receive subsystem-request, timeout */
1359 return 0;
1360 }
1361
1362 return 1;
1363 }
1364
Michal Vasko36c7be82017-02-22 13:37:59 +01001365 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001366 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001367 nc_addtimespec(&ts_timeout, timeout);
1368 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001369 while (1) {
1370 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +02001371 ERR(session, "Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001372 return -1;
1373 }
1374
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001375 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1376 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001377 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001378 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001379 return -1;
1380 }
1381
Michal Vasko086311b2016-01-08 09:53:11 +01001382 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001383 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001384 }
1385
Michal Vasko086311b2016-01-08 09:53:11 +01001386 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001387 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001388 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001389 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1390 /* timeout */
Michal Vasko05532772021-06-03 12:12:38 +02001391 ERR(session, "Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko36c7be82017-02-22 13:37:59 +01001392 break;
1393 }
1394 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001395 }
Michal Vasko086311b2016-01-08 09:53:11 +01001396
Michal Vasko1a38c862016-01-15 15:50:07 +01001397 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001398}
1399
Michal Vasko4c1fb492017-01-30 14:31:07 +01001400static int
1401nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1402{
1403 uint8_t i;
1404 char *privkey_path, *privkey_data;
Michal Vaskoddce1212019-05-24 09:58:49 +02001405 int ret;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001406 NC_SSH_KEY_TYPE privkey_type;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001407
1408 if (!server_opts.hostkey_clb) {
Michal Vasko05532772021-06-03 12:12:38 +02001409 ERR(NULL, "Callback for retrieving SSH host keys not set.");
Michal Vasko4c1fb492017-01-30 14:31:07 +01001410 return -1;
1411 }
1412
1413 for (i = 0; i < hostkey_count; ++i) {
1414 privkey_path = privkey_data = NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +02001415 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_type)) {
Michal Vasko05532772021-06-03 12:12:38 +02001416 ERR(NULL, "Host key callback failed.");
Michal Vasko4c1fb492017-01-30 14:31:07 +01001417 return -1;
1418 }
1419
1420 if (privkey_data) {
Michal Vaskoddce1212019-05-24 09:58:49 +02001421 privkey_path = base64der_key_to_tmp_file(privkey_data, nc_keytype2str(privkey_type));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001422 if (!privkey_path) {
Michal Vasko05532772021-06-03 12:12:38 +02001423 ERR(NULL, "Temporarily storing a host key into a file failed (%s).", strerror(errno));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001424 free(privkey_data);
1425 return -1;
1426 }
1427 }
1428
1429 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1430
1431 /* cleanup */
1432 if (privkey_data && unlink(privkey_path)) {
Michal Vasko05532772021-06-03 12:12:38 +02001433 WRN(NULL, "Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001434 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001435 free(privkey_data);
1436
1437 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001438 ERR(NULL, "Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
Michal Vasko80075de2017-07-10 11:38:52 +02001439 }
1440 free(privkey_path);
1441
1442 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001443 return -1;
1444 }
1445 }
1446
1447 return 0;
1448}
1449
Michal Vasko96164bf2016-01-21 15:41:58 +01001450int
Michal Vasko0190bc32016-03-02 15:47:49 +01001451nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001452{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001453 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001454 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001455 int libssh_auth_methods = 0, ret;
1456 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001457
Michal Vasko2cc4c682016-03-01 09:16:48 +01001458 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001459
Michal Vasko086311b2016-01-08 09:53:11 +01001460 /* other transport-specific data */
1461 session->ti_type = NC_TI_LIBSSH;
1462 session->ti.libssh.session = ssh_new();
1463 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001464 ERR(NULL, "Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001465 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001466 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001467 }
1468
Michal Vaskoc61c4492016-01-25 11:13:34 +01001469 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001470 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1471 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001472 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001473 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1474 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001475 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001476 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1477 }
1478 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1479
Michal Vaskoe2713da2016-08-22 16:06:40 +02001480 sbind = ssh_bind_new();
1481 if (!sbind) {
Michal Vasko05532772021-06-03 12:12:38 +02001482 ERR(session, "Failed to create an SSH bind.");
Michal Vaskoe2713da2016-08-22 16:06:40 +02001483 close(sock);
1484 return -1;
1485 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001486
1487 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1488 close(sock);
1489 ssh_bind_free(sbind);
1490 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001491 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001492
Michal Vasko086311b2016-01-08 09:53:11 +01001493 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001494 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001495 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001496
Michal Vaskoe2713da2016-08-22 16:06:40 +02001497 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001498 ERR(session, "SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001499 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001500 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001501 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001502 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001503 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001504
Michal Vasko0190bc32016-03-02 15:47:49 +01001505 ssh_set_blocking(session->ti.libssh.session, 0);
1506
Michal Vasko36c7be82017-02-22 13:37:59 +01001507 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001508 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001509 nc_addtimespec(&ts_timeout, timeout);
1510 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001511 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001512 /* this tends to take longer */
1513 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001514 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001515 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001516 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1517 break;
1518 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001519 }
1520 }
1521 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001522 ERR(session, "SSH key exchange timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001523 return 0;
1524 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001525 ERR(session, "SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001526 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001527 }
1528
1529 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001530 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001531 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001532 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1533 }
1534 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001535 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +02001536 ERR(session, "Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001537 return -1;
1538 }
1539
Michal Vasko086311b2016-01-08 09:53:11 +01001540 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001541 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001542 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001543 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001544 }
1545
Michal Vasko36c7be82017-02-22 13:37:59 +01001546 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1547 break;
1548 }
1549
Michal Vasko145ae672017-02-07 10:57:27 +01001550 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
Michal Vasko05532772021-06-03 12:12:38 +02001551 ERR(session, "Too many failed authentication attempts of user \"%s\".", session->username);
Michal Vasko145ae672017-02-07 10:57:27 +01001552 return -1;
1553 }
1554
Michal Vasko086311b2016-01-08 09:53:11 +01001555 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001556 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001557 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001558 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1559 /* timeout */
1560 break;
1561 }
1562 }
1563 }
Michal Vasko086311b2016-01-08 09:53:11 +01001564
1565 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1566 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001567 if (session->username) {
Michal Vasko05532772021-06-03 12:12:38 +02001568 ERR(session, "User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
Michal Vaskoc13da702017-02-07 10:57:57 +01001569 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001570 ERR(session, "User failed to authenticate for too long, disconnecting.");
Michal Vaskoc13da702017-02-07 10:57:57 +01001571 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001572 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001573 }
1574
Michal Vasko086311b2016-01-08 09:53:11 +01001575 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001576 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001577 if (ret < 1) {
1578 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001579 }
1580
Michal Vasko96164bf2016-01-21 15:41:58 +01001581 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001582 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001583}
1584
Michal Vasko71090fc2016-05-24 16:37:28 +02001585API NC_MSG_TYPE
1586nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1587{
1588 NC_MSG_TYPE msgtype;
1589 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001590 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001591
1592 if (!orig_session) {
1593 ERRARG("orig_session");
1594 return NC_MSG_ERROR;
1595 } else if (!session) {
1596 ERRARG("session");
1597 return NC_MSG_ERROR;
1598 }
1599
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001600 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH) &&
1601 orig_session->ti.libssh.next) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001602 for (new_session = orig_session->ti.libssh.next;
1603 new_session != orig_session;
1604 new_session = new_session->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001605 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel &&
1606 (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001607 /* we found our session */
1608 break;
1609 }
1610 }
1611 if (new_session == orig_session) {
1612 new_session = NULL;
1613 }
1614 }
1615
1616 if (!new_session) {
Michal Vasko05532772021-06-03 12:12:38 +02001617 ERR(orig_session, "Session does not have a NETCONF SSH channel ready.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001618 return NC_MSG_ERROR;
1619 }
1620
1621 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001622 new_session->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001623
1624 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001625 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001626 if (msgtype != NC_MSG_HELLO) {
1627 return msgtype;
1628 }
1629
Michal Vasko9f6275e2017-10-05 13:50:05 +02001630 nc_gettimespec_real(&ts_cur);
1631 new_session->opts.server.session_start = ts_cur.tv_sec;
1632 nc_gettimespec_mono(&ts_cur);
1633 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001634 new_session->status = NC_STATUS_RUNNING;
1635 *session = new_session;
1636
1637 return msgtype;
1638}
1639
1640API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001641nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001642{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001643 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001644 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001645 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001646 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001647 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001648
Michal Vasko45e53ae2016-04-07 11:46:03 +02001649 if (!ps) {
1650 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001651 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001652 } else if (!session) {
1653 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001654 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001655 }
1656
Michal Vasko48a63ed2016-03-01 09:48:21 +01001657 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001658 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001659 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001660 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001661
Michal Vasko96164bf2016-01-21 15:41:58 +01001662 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001663 cur_session = ps->sessions[i]->session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001664 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH) &&
1665 cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001666 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001667 for (new_session = cur_session->ti.libssh.next;
1668 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001669 new_session = new_session->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001670 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel &&
1671 (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001672 /* we found our session */
1673 break;
1674 }
1675 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001676 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001677 break;
1678 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001679
Michal Vasko96164bf2016-01-21 15:41:58 +01001680 new_session = NULL;
1681 }
1682 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001683
Michal Vasko48a63ed2016-03-01 09:48:21 +01001684 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001685 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001686
Michal Vasko96164bf2016-01-21 15:41:58 +01001687 if (!new_session) {
Michal Vasko05532772021-06-03 12:12:38 +02001688 ERR(NULL, "No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001689 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001690 }
1691
1692 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001693 new_session->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskofb89d772016-01-08 12:25:35 +01001694
Michal Vasko086311b2016-01-08 09:53:11 +01001695 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001696 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001697 if (msgtype != NC_MSG_HELLO) {
1698 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001699 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001700
Michal Vasko9f6275e2017-10-05 13:50:05 +02001701 nc_gettimespec_real(&ts_cur);
1702 new_session->opts.server.session_start = ts_cur.tv_sec;
1703 nc_gettimespec_mono(&ts_cur);
1704 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001705 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001706 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001707
Michal Vasko71090fc2016-05-24 16:37:28 +02001708 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001709}