blob: 6eeafd58320e65412cbd614dbbd20bf1354c14cc [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
Michal Vasko95ea9ff2021-11-09 12:29:14 +01002 * @file session_server_ssh.c
3 * @author Michal Vasko <mvasko@cesnet.cz>
4 * @brief libnetconf2 SSH server session manipulation functions
Michal Vasko086311b2016-01-08 09:53:11 +01005 *
Michal Vasko95ea9ff2021-11-09 12:29:14 +01006 * @copyright
7 * Copyright (c) 2017 - 2021 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01008 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01009 * This source code is licensed under BSD 3-Clause License (the "License").
10 * You may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010012 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010013 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010014 */
15
16#define _GNU_SOURCE
17
apropp-molex4e903c32020-04-20 03:06:58 -040018#include "config.h" /* Expose HAVE_SHADOW and HAVE_CRYPT */
19
20#ifdef HAVE_SHADOW
21 #include <shadow.h>
22#endif
23#ifdef HAVE_CRYPT
24 #include <crypt.h>
25#endif
26
Michal Vaskob83a3fa2021-05-26 09:53:42 +020027#include <errno.h>
28#include <fcntl.h>
29#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030#include <stdlib.h>
31#include <string.h>
Michal Vasko27252692017-03-21 15:34:13 +010032#include <sys/stat.h>
Michal Vaskob83a3fa2021-05-26 09:53:42 +020033#include <sys/types.h>
Michal Vasko9f6275e2017-10-05 13:50:05 +020034#include <time.h>
Claus Klein22091912020-01-20 13:45:47 +010035#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010036
Michal Vasko7a20d2e2021-05-19 16:40:23 +020037#include "compat.h"
38#include "libnetconf.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010039#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010040#include "session_server_ch.h"
Michal Vasko086311b2016-01-08 09:53:11 +010041
Michal Vaskob83a3fa2021-05-26 09:53:42 +020042#if !defined (HAVE_CRYPT_R)
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +020043pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER;
44#endif
45
Michal Vasko086311b2016-01-08 09:53:11 +010046extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010047
Michal Vasko4c1fb492017-01-30 14:31:07 +010048static char *
Michal Vaskoddce1212019-05-24 09:58:49 +020049base64der_key_to_tmp_file(const char *in, const char *key_str)
Michal Vasko086311b2016-01-08 09:53:11 +010050{
Michal Vasko4c1fb492017-01-30 14:31:07 +010051 char path[12] = "/tmp/XXXXXX";
52 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010053 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010054 FILE *file;
55
56 if (in == NULL) {
57 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010058 }
59
mekleob31878b2019-09-09 14:10:47 +020060 umode = umask(0177);
Michal Vasko4c1fb492017-01-30 14:31:07 +010061 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010062 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010063 if (fd == -1) {
64 return NULL;
65 }
66
Michal Vasko3964a832018-09-18 14:37:39 +020067 file = fdopen(fd, "w");
Michal Vasko4c1fb492017-01-30 14:31:07 +010068 if (!file) {
69 close(fd);
70 return NULL;
71 }
72
73 /* write the key into the file */
Michal Vasko68177b72020-04-27 15:46:53 +020074 if (key_str) {
75 written = fwrite("-----BEGIN ", 1, 11, file);
76 written += fwrite(key_str, 1, strlen(key_str), file);
77 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
78 written += fwrite(in, 1, strlen(in), file);
79 written += fwrite("\n-----END ", 1, 10, file);
80 written += fwrite(key_str, 1, strlen(key_str), file);
81 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
Michal Vasko4c1fb492017-01-30 14:31:07 +010082
Michal Vasko68177b72020-04-27 15:46:53 +020083 fclose(file);
84 if ((unsigned)written != 11 + strlen(key_str) + 18 + strlen(in) + 10 + strlen(key_str) + 17) {
85 unlink(path);
86 return NULL;
87 }
88 } else {
89 written = fwrite("-----BEGIN PRIVATE KEY-----\n", 1, 28, file);
90 written += fwrite(in, 1, strlen(in), file);
91 written += fwrite("\n-----END PRIVATE KEY-----", 1, 26, file);
92
93 fclose(file);
94 if ((unsigned)written != 28 + strlen(in) + 26) {
95 unlink(path);
96 return NULL;
97 }
Michal Vasko4c1fb492017-01-30 14:31:07 +010098 }
99
100 return strdup(path);
101}
102
103static int
Michal Vasko7d255882017-02-09 13:35:08 +0100104nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +0100105{
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100106 uint8_t i;
107
Michal Vasko4c1fb492017-01-30 14:31:07 +0100108 if (!name) {
109 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +0100110 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100111 } else if (idx > opts->hostkey_count) {
112 ERRARG("idx");
113 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100114 }
Michal Vaskod45e25a2016-01-08 15:48:44 +0100115
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100116 for (i = 0; i < opts->hostkey_count; ++i) {
117 if (!strcmp(opts->hostkeys[i], name)) {
118 ERRARG("name");
119 return -1;
120 }
121 }
122
Michal Vaskoe2713da2016-08-22 16:06:40 +0200123 ++opts->hostkey_count;
124 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
125 if (!opts->hostkeys) {
126 ERRMEM;
127 return -1;
128 }
Michal Vasko7d255882017-02-09 13:35:08 +0100129
130 if (idx < 0) {
131 idx = opts->hostkey_count - 1;
132 }
133 if (idx != opts->hostkey_count - 1) {
134 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
135 }
Michal Vasko93224072021-11-09 12:14:28 +0100136 opts->hostkeys[idx] = strdup(name);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200137
Michal Vasko5fcc7142016-02-02 12:21:10 +0100138 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100139}
140
141API int
Michal Vasko7d255882017-02-09 13:35:08 +0100142nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100143{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100144 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100145 struct nc_endpt *endpt;
146
Michal Vasko51e514d2016-02-02 15:51:52 +0100147 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100148 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100149 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100150 return -1;
151 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200152
Michal Vasko7d255882017-02-09 13:35:08 +0100153 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200154
Michal Vasko51e514d2016-02-02 15:51:52 +0100155 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100156 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100157
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100158 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100159}
160
Michal Vasko974410a2018-04-03 09:36:57 +0200161API void
162nc_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 +0200163 void *user_data, void (*free_user_data)(void *user_data))
Michal Vasko974410a2018-04-03 09:36:57 +0200164{
165 server_opts.passwd_auth_clb = passwd_auth_clb;
166 server_opts.passwd_auth_data = user_data;
167 server_opts.passwd_auth_data_free = free_user_data;
168}
169
bhart1bb7cdb2018-07-02 15:03:30 -0500170API void
171nc_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 +0200172 void *user_data, void (*free_user_data)(void *user_data))
bhart1bb7cdb2018-07-02 15:03:30 -0500173{
174 server_opts.interactive_auth_clb = interactive_auth_clb;
175 server_opts.interactive_auth_data = user_data;
176 server_opts.interactive_auth_data_free = free_user_data;
177}
Michal Vasko733c0bd2018-07-03 13:14:40 +0200178
bhart1bb7cdb2018-07-02 15:03:30 -0500179API void
180nc_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 +0200181 void *user_data, void (*free_user_data)(void *user_data))
bhart1bb7cdb2018-07-02 15:03:30 -0500182{
183 server_opts.pubkey_auth_clb = pubkey_auth_clb;
184 server_opts.pubkey_auth_data = user_data;
185 server_opts.pubkey_auth_data_free = free_user_data;
186}
187
Michal Vaskob05053d2016-01-22 16:12:06 +0100188API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200189nc_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 +0100190{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100191 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200192 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200193 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100194
Michal Vasko2e6defd2016-10-07 15:48:15 +0200195 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200196 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
197 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200198 return -1;
199 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200200
Michal Vaskoadf30f02019-06-24 09:34:47 +0200201 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200202
Michal Vasko2e6defd2016-10-07 15:48:15 +0200203 /* UNLOCK */
204 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200205
206 return ret;
207}
208
Michal Vasko4c1fb492017-01-30 14:31:07 +0100209API void
210nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200211 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 +0100212{
213 if (!hostkey_clb) {
214 ERRARG("hostkey_clb");
215 return;
216 }
217
218 server_opts.hostkey_clb = hostkey_clb;
219 server_opts.hostkey_data = user_data;
220 server_opts.hostkey_data_free = free_user_data;
221}
222
Michal Vaskoe2713da2016-08-22 16:06:40 +0200223static int
Michal Vasko7d255882017-02-09 13:35:08 +0100224nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200225{
226 uint8_t i;
227
Michal Vasko7d255882017-02-09 13:35:08 +0100228 if (name && (idx > -1)) {
229 ERRARG("name and idx");
230 return -1;
231 } else if (idx >= opts->hostkey_count) {
232 ERRARG("idx");
233 }
234
235 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200236 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +0100237 free(opts->hostkeys[i]);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200238 }
239 free(opts->hostkeys);
240 opts->hostkeys = NULL;
241 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100242 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200243 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100244 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100245 idx = i;
246 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200247 }
248 }
249
Michal Vasko7d255882017-02-09 13:35:08 +0100250 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200251 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100252 } else {
253remove_idx:
254 --opts->hostkey_count;
Michal Vasko93224072021-11-09 12:14:28 +0100255 free(opts->hostkeys[idx]);
Michal Vasko7d255882017-02-09 13:35:08 +0100256 if (idx < opts->hostkey_count - 1) {
257 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
258 }
259 if (!opts->hostkey_count) {
260 free(opts->hostkeys);
261 opts->hostkeys = NULL;
262 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200263 }
264
265 return 0;
266}
267
268API int
Michal Vasko7d255882017-02-09 13:35:08 +0100269nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200270{
271 int ret;
272 struct nc_endpt *endpt;
273
274 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100275 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200276 if (!endpt) {
277 return -1;
278 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200279
Michal Vasko7d255882017-02-09 13:35:08 +0100280 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200281
Michal Vaskoe2713da2016-08-22 16:06:40 +0200282 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100283 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200284
285 return ret;
286}
287
288API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200289nc_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 +0200290{
291 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200292 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200293 struct nc_ch_endpt *endpt;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200294
Michal Vasko2e6defd2016-10-07 15:48:15 +0200295 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200296 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
297 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200298 return -1;
299 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200300
Michal Vaskoadf30f02019-06-24 09:34:47 +0200301 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200302
Michal Vasko2e6defd2016-10-07 15:48:15 +0200303 /* UNLOCK */
304 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100305
306 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100307}
308
309static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100310nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
311{
312 uint8_t i;
313 int16_t mov_idx = -1, after_idx = -1;
Michal Vasko93224072021-11-09 12:14:28 +0100314 char *bckup;
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100315
316 if (!key_mov) {
317 ERRARG("key_mov");
318 return -1;
319 }
320
321 for (i = 0; i < opts->hostkey_count; ++i) {
322 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
323 after_idx = i;
324 }
325 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
326 mov_idx = i;
327 }
328
329 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
330 break;
331 }
332 }
333
334 if (key_after && (after_idx == -1)) {
335 ERRARG("key_after");
336 return -1;
337 }
338 if (mov_idx == -1) {
339 ERRARG("key_mov");
340 return -1;
341 }
342 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
343 /* nothing to do */
344 return 0;
345 }
346
347 /* finally move the key */
348 bckup = opts->hostkeys[mov_idx];
349 if (mov_idx > after_idx) {
350 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
351 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
352 opts->hostkeys[after_idx + 1] = bckup;
353 } else {
354 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
355 opts->hostkeys[after_idx] = bckup;
356 }
357
358 return 0;
359}
360
361API int
362nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
363{
364 int ret;
365 struct nc_endpt *endpt;
366
367 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100368 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100369 if (!endpt) {
370 return -1;
371 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200372
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100373 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200374
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100375 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100376 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100377
378 return ret;
379}
380
381API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200382nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
383 const char *key_after)
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100384{
385 int ret;
386 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200387 struct nc_ch_endpt *endpt;
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100388
389 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200390 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100391 if (!endpt) {
392 return -1;
393 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200394
Michal Vaskoadf30f02019-06-24 09:34:47 +0200395 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200396
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100397 /* UNLOCK */
398 nc_server_ch_client_unlock(client);
399
400 return ret;
401}
402
403static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100404nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100405{
Michal Vaskob05053d2016-01-22 16:12:06 +0100406 opts->auth_methods = auth_methods;
407 return 0;
408}
409
410API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100411nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100412{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100413 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100414 struct nc_endpt *endpt;
415
Michal Vasko51e514d2016-02-02 15:51:52 +0100416 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100417 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100418 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100419 return -1;
420 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200421
Michal Vasko2e6defd2016-10-07 15:48:15 +0200422 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200423
Michal Vasko51e514d2016-02-02 15:51:52 +0100424 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100425 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100426
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100427 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100428}
429
430API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200431nc_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 +0100432{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100433 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200434 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200435 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100436
Michal Vasko2e6defd2016-10-07 15:48:15 +0200437 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200438 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
439 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200440 return -1;
441 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200442
Michal Vaskoadf30f02019-06-24 09:34:47 +0200443 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200444
Michal Vasko2e6defd2016-10-07 15:48:15 +0200445 /* UNLOCK */
446 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100447
448 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100449}
450
Michal Vaskoddce1212019-05-24 09:58:49 +0200451API int
452nc_server_ssh_endpt_get_auth_methods(const char *endpt_name)
453{
454 int ret;
455 struct nc_endpt *endpt;
456
457 /* LOCK */
458 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
459 if (!endpt) {
460 return -1;
461 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200462
Michal Vaskoddce1212019-05-24 09:58:49 +0200463 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200464
Michal Vaskoddce1212019-05-24 09:58:49 +0200465 /* UNLOCK */
466 pthread_rwlock_unlock(&server_opts.endpt_lock);
467
468 return ret;
469}
470
471API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200472nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name)
Michal Vaskoddce1212019-05-24 09:58:49 +0200473{
474 int ret;
475 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200476 struct nc_ch_endpt *endpt;
Michal Vaskoddce1212019-05-24 09:58:49 +0200477
478 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200479 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
480 if (!endpt) {
Michal Vaskoddce1212019-05-24 09:58:49 +0200481 return -1;
482 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200483
Michal Vaskoadf30f02019-06-24 09:34:47 +0200484 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200485
Michal Vaskoddce1212019-05-24 09:58:49 +0200486 /* UNLOCK */
487 nc_server_ch_client_unlock(client);
488
489 return ret;
490}
491
Michal Vaskob05053d2016-01-22 16:12:06 +0100492static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100493nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100494{
Michal Vaskob05053d2016-01-22 16:12:06 +0100495 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200496 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100497 return -1;
498 }
499
Michal Vaskob05053d2016-01-22 16:12:06 +0100500 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100501 return 0;
502}
503
504API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100505nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100506{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100507 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100508 struct nc_endpt *endpt;
509
Michal Vasko51e514d2016-02-02 15:51:52 +0100510 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100511 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100512 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100513 return -1;
514 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200515
Michal Vasko2e6defd2016-10-07 15:48:15 +0200516 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200517
Michal Vasko51e514d2016-02-02 15:51:52 +0100518 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100519 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100520
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100521 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100522}
523
524API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200525nc_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 +0100526{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100527 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200528 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200529 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100530
Michal Vasko2e6defd2016-10-07 15:48:15 +0200531 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200532 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
533 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200534 return -1;
535 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200536
Michal Vaskoadf30f02019-06-24 09:34:47 +0200537 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200538
Michal Vasko2e6defd2016-10-07 15:48:15 +0200539 /* UNLOCK */
540 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100541
542 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100543}
544
545static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100546nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100547{
Michal Vaskob05053d2016-01-22 16:12:06 +0100548 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200549 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100550 return -1;
551 }
552
Michal Vaskob05053d2016-01-22 16:12:06 +0100553 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100554 return 0;
555}
556
557API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100558nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100559{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100560 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100561 struct nc_endpt *endpt;
562
Michal Vasko51e514d2016-02-02 15:51:52 +0100563 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100564 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100565 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100566 return -1;
567 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200568
Michal Vasko2e6defd2016-10-07 15:48:15 +0200569 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200570
Michal Vasko51e514d2016-02-02 15:51:52 +0100571 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100572 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100573
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100574 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100575}
576
577API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200578nc_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 +0100579{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100580 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200581 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200582 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100583
Michal Vasko2e6defd2016-10-07 15:48:15 +0200584 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200585 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
586 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200587 return -1;
588 }
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200589
Michal Vaskoadf30f02019-06-24 09:34:47 +0200590 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vaskoc46b3df2021-07-26 09:30:05 +0200591
Michal Vasko2e6defd2016-10-07 15:48:15 +0200592 /* UNLOCK */
593 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100594
595 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100596}
597
598static int
Michal Vasko77367452021-02-16 16:32:18 +0100599_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 +0100600{
Michal Vasko09111892021-07-26 09:30:20 +0200601 int ret = 0;
602
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100603 /* LOCK */
604 pthread_mutex_lock(&server_opts.authkey_lock);
605
Michal Vasko17dfda92016-12-01 14:06:16 +0100606 ++server_opts.authkey_count;
607 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
608 if (!server_opts.authkeys) {
609 ERRMEM;
Michal Vasko09111892021-07-26 09:30:20 +0200610 ret = -1;
611 goto cleanup;
Michal Vasko17dfda92016-12-01 14:06:16 +0100612 }
Michal Vasko93224072021-11-09 12:14:28 +0100613 server_opts.authkeys[server_opts.authkey_count - 1].path = pubkey_path ? strdup(pubkey_path) : NULL;
614 server_opts.authkeys[server_opts.authkey_count - 1].base64 = pubkey_base64 ? strdup(pubkey_base64) : NULL;
Michal Vasko17dfda92016-12-01 14:06:16 +0100615 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
Michal Vasko93224072021-11-09 12:14:28 +0100616 server_opts.authkeys[server_opts.authkey_count - 1].username = strdup(username);
Michal Vasko17dfda92016-12-01 14:06:16 +0100617
Michal Vasko09111892021-07-26 09:30:20 +0200618cleanup:
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100619 /* UNLOCK */
620 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vasko09111892021-07-26 09:30:20 +0200621 return ret;
Michal Vasko17dfda92016-12-01 14:06:16 +0100622}
623
624API int
625nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100626{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200627 if (!pubkey_path) {
628 ERRARG("pubkey_path");
629 return -1;
630 } else if (!username) {
631 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100632 return -1;
633 }
634
Michal Vasko17dfda92016-12-01 14:06:16 +0100635 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100636}
637
638API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100639nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100640{
Michal Vasko17dfda92016-12-01 14:06:16 +0100641 if (!pubkey_base64) {
642 ERRARG("pubkey_base64");
643 return -1;
644 } else if (!type) {
645 ERRARG("type");
646 return -1;
647 } else if (!username) {
648 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100649 return -1;
650 }
651
Michal Vasko17dfda92016-12-01 14:06:16 +0100652 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100653}
654
655API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100656nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200657 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100658{
Michal Vasko086311b2016-01-08 09:53:11 +0100659 uint32_t i;
660 int ret = -1;
661
Michal Vasko17dfda92016-12-01 14:06:16 +0100662 /* LOCK */
663 pthread_mutex_lock(&server_opts.authkey_lock);
664
665 if (!pubkey_path && !pubkey_base64 && !type && !username) {
666 for (i = 0; i < server_opts.authkey_count; ++i) {
Michal Vasko93224072021-11-09 12:14:28 +0100667 free(server_opts.authkeys[i].path);
668 free(server_opts.authkeys[i].base64);
669 free(server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100670
Michal Vasko086311b2016-01-08 09:53:11 +0100671 ret = 0;
672 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100673 free(server_opts.authkeys);
674 server_opts.authkeys = NULL;
675 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100676 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100677 for (i = 0; i < server_opts.authkey_count; ++i) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200678 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path)) &&
679 (!pubkey_base64 || !strcmp(server_opts.authkeys[i].base64, pubkey_base64)) &&
680 (!type || (server_opts.authkeys[i].type == type)) &&
681 (!username || !strcmp(server_opts.authkeys[i].username, username))) {
Michal Vasko93224072021-11-09 12:14:28 +0100682 free(server_opts.authkeys[i].path);
683 free(server_opts.authkeys[i].base64);
684 free(server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100685
Michal Vasko17dfda92016-12-01 14:06:16 +0100686 --server_opts.authkey_count;
687 if (i < server_opts.authkey_count) {
688 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200689 sizeof *server_opts.authkeys);
Michal Vasko17dfda92016-12-01 14:06:16 +0100690 } else if (!server_opts.authkey_count) {
691 free(server_opts.authkeys);
692 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100693 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100694
695 ret = 0;
696 }
697 }
Michal Vasko086311b2016-01-08 09:53:11 +0100698 }
699
Michal Vasko51e514d2016-02-02 15:51:52 +0100700 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100701 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100702
703 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100704}
705
706void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100707nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100708{
Michal Vasko7d255882017-02-09 13:35:08 +0100709 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100710}
711
Michal Vasko7a866152021-07-22 11:01:13 +0200712#ifdef HAVE_SHADOW
713
714static struct passwd *
715auth_password_getpwnam(const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size)
716{
717 struct passwd *pwd = NULL;
718 char *mem;
Jan Kundrátaa323102021-10-08 20:10:50 +0200719 int r = 0;
Michal Vasko7a866152021-07-22 11:01:13 +0200720
721 do {
Jan Kundrátaa323102021-10-08 20:10:50 +0200722 r = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd);
Michal Vasko7a866152021-07-22 11:01:13 +0200723 if (pwd) {
724 /* entry found */
725 break;
726 }
727
Jan Kundrátaa323102021-10-08 20:10:50 +0200728 if (r == ERANGE) {
Michal Vasko7a866152021-07-22 11:01:13 +0200729 /* small buffer, enlarge */
730 *buf_size <<= 2;
731 mem = realloc(*buf, *buf_size);
732 if (!mem) {
733 ERRMEM;
734 return NULL;
735 }
736 *buf = mem;
737 }
Jan Kundrátaa323102021-10-08 20:10:50 +0200738 } while (r == ERANGE);
Michal Vasko7a866152021-07-22 11:01:13 +0200739
740 return pwd;
741}
742
743static struct spwd *
744auth_password_getspnam(const char *username, struct spwd *spwd_buf, char **buf, size_t *buf_size)
745{
746 struct spwd *spwd = NULL;
747 char *mem;
Jan Kundrátaa323102021-10-08 20:10:50 +0200748 int r = 0;
Michal Vasko7a866152021-07-22 11:01:13 +0200749
750 do {
Michal Vasko7a866152021-07-22 11:01:13 +0200751# ifndef __QNXNTO__
Jan Kundrátaa323102021-10-08 20:10:50 +0200752 r = getspnam_r(username, spwd_buf, *buf, *buf_size, &spwd);
Michal Vasko7a866152021-07-22 11:01:13 +0200753# else
754 spwd = getspnam_r(username, spwd_buf, *buf, *buf_size);
755# endif
756 if (spwd) {
757 /* entry found */
758 break;
759 }
760
Jan Kundrátaa323102021-10-08 20:10:50 +0200761 if (r == ERANGE) {
Michal Vasko7a866152021-07-22 11:01:13 +0200762 /* small buffer, enlarge */
763 *buf_size <<= 2;
764 mem = realloc(*buf, *buf_size);
765 if (!mem) {
766 ERRMEM;
767 return NULL;
768 }
769 *buf = mem;
770 }
Jan Kundrátaa323102021-10-08 20:10:50 +0200771 } while (r == ERANGE);
Michal Vasko7a866152021-07-22 11:01:13 +0200772
773 return spwd;
774}
775
Michal Vasko086311b2016-01-08 09:53:11 +0100776static char *
777auth_password_get_pwd_hash(const char *username)
778{
779 struct passwd *pwd, pwd_buf;
780 struct spwd *spwd, spwd_buf;
Michal Vasko7a866152021-07-22 11:01:13 +0200781 char *pass_hash = NULL, *buf = NULL;
782 size_t buf_size = 256;
Michal Vasko086311b2016-01-08 09:53:11 +0100783
Michal Vasko7a866152021-07-22 11:01:13 +0200784 buf = malloc(buf_size);
785 if (!buf) {
786 ERRMEM;
787 goto error;
788 }
789
790 pwd = auth_password_getpwnam(username, &pwd_buf, &buf, &buf_size);
Michal Vasko086311b2016-01-08 09:53:11 +0100791 if (!pwd) {
Michal Vasko05532772021-06-03 12:12:38 +0200792 VRB(NULL, "User \"%s\" not found locally.", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200793 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100794 }
795
796 if (!strcmp(pwd->pw_passwd, "x")) {
Michal Vasko7a866152021-07-22 11:01:13 +0200797 spwd = auth_password_getspnam(username, &spwd_buf, &buf, &buf_size);
Michal Vasko086311b2016-01-08 09:53:11 +0100798 if (!spwd) {
Michal Vasko05532772021-06-03 12:12:38 +0200799 VRB(NULL, "Failed to retrieve the shadow entry for \"%s\".", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200800 goto error;
Michal Vasko22b4fe72020-11-04 08:52:29 +0100801 } else if ((spwd->sp_expire > -1) && (spwd->sp_expire <= (time(NULL) / (60 * 60 * 24)))) {
Michal Vasko05532772021-06-03 12:12:38 +0200802 WRN(NULL, "User \"%s\" account has expired.", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200803 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100804 }
805
806 pass_hash = spwd->sp_pwdp;
807 } else {
808 pass_hash = pwd->pw_passwd;
809 }
810
811 if (!pass_hash) {
Michal Vasko05532772021-06-03 12:12:38 +0200812 ERR(NULL, "No password could be retrieved for \"%s\".", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200813 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100814 }
815
816 /* check the hash structure for special meaning */
817 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
Michal Vasko05532772021-06-03 12:12:38 +0200818 VRB(NULL, "User \"%s\" is not allowed to authenticate using a password.", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200819 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100820 }
821 if (!strcmp(pass_hash, "*NP*")) {
Michal Vasko05532772021-06-03 12:12:38 +0200822 VRB(NULL, "Retrieving password for \"%s\" from a NIS+ server not supported.", username);
Michal Vasko7a866152021-07-22 11:01:13 +0200823 goto error;
Michal Vasko086311b2016-01-08 09:53:11 +0100824 }
825
Michal Vasko963f6c02021-07-26 15:55:44 +0200826 pass_hash = strdup(pass_hash);
Michal Vasko7a866152021-07-22 11:01:13 +0200827 free(buf);
Michal Vasko963f6c02021-07-26 15:55:44 +0200828 return pass_hash;
Michal Vasko7a866152021-07-22 11:01:13 +0200829
830error:
831 free(buf);
832 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100833}
834
Michal Vasko7a866152021-07-22 11:01:13 +0200835#else
836
837static char *
838auth_password_get_pwd_hash(const char *username)
839{
840 (void)username;
841 return strdup("");
842}
843
844#endif
845
Michal Vasko086311b2016-01-08 09:53:11 +0100846static int
847auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
848{
849 char *new_pass_hash;
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200850
851#if defined (HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100852 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200853#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100854
855 if (!pass_hash[0]) {
856 if (!pass_clear[0]) {
Michal Vasko05532772021-06-03 12:12:38 +0200857 WRN(NULL, "User authentication successful with an empty password!");
Michal Vasko086311b2016-01-08 09:53:11 +0100858 return 0;
859 } else {
860 /* the user did now know he does not need any password,
861 * (which should not be used) so deny authentication */
862 return 1;
863 }
864 }
865
Michal Vaskob83a3fa2021-05-26 09:53:42 +0200866#if defined (HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100867 cdata.initialized = 0;
868 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200869#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200870 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200871 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200872 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200873#endif
Andrew Langefeld158d6fd2018-06-11 18:51:44 -0500874
875 if (!new_pass_hash) {
876 return 1;
877 }
878
Michal Vasko086311b2016-01-08 09:53:11 +0100879 return strcmp(new_pass_hash, pass_hash);
880}
881
882static void
883nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
884{
885 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100886 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100887
Michal Vaskoebba7602018-03-23 13:14:08 +0100888 if (server_opts.passwd_auth_clb) {
889 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
890 } else {
891 pass_hash = auth_password_get_pwd_hash(session->username);
892 if (pass_hash) {
893 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
894 free(pass_hash);
895 }
Michal Vasko086311b2016-01-08 09:53:11 +0100896 }
897
Michal Vaskoebba7602018-03-23 13:14:08 +0100898 if (!auth_ret) {
899 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
Michal Vasko05532772021-06-03 12:12:38 +0200900 VRB(session, "User \"%s\" authenticated.", session->username);
Michal Vaskoebba7602018-03-23 13:14:08 +0100901 ssh_message_auth_reply_success(msg, 0);
902 } else {
903 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +0200904 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
905 session->opts.server.ssh_auth_attempts);
Michal Vaskoebba7602018-03-23 13:14:08 +0100906 ssh_message_reply_default(msg);
907 }
Michal Vasko086311b2016-01-08 09:53:11 +0100908}
909
910static void
911nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
912{
bhart3bc2f582018-06-05 12:40:32 -0500913 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100914 char *pass_hash;
915
bhart1bb7cdb2018-07-02 15:03:30 -0500916 if (server_opts.interactive_auth_clb) {
Michal Vasko733c0bd2018-07-03 13:14:40 +0200917 auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100918 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500919 if (!ssh_message_auth_kbdint_is_response(msg)) {
920 const char *prompts[] = {"Password: "};
921 char echo[] = {0};
922
923 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
Robert Vargaad7a5532018-08-10 20:40:54 +0200924 auth_ret = -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100925 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500926 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {// failed session
927 ssh_message_reply_default(msg);
928 return;
929 }
bhart3bc2f582018-06-05 12:40:32 -0500930 pass_hash = auth_password_get_pwd_hash(session->username);// get hashed password
931 if (pass_hash) {
Robert Vargaad7a5532018-08-10 20:40:54 +0200932 /* Normalize auth_password_compare_pwd result to 0 or 1 */
933 auth_ret = !!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0));
bhart3bc2f582018-06-05 12:40:32 -0500934 free(pass_hash);// free hashed password
935 }
Michal Vasko086311b2016-01-08 09:53:11 +0100936 }
bhart1bb7cdb2018-07-02 15:03:30 -0500937 }
938
Robert Vargaad7a5532018-08-10 20:40:54 +0200939 /* We have already sent a reply */
940 if (auth_ret == -1) {
941 return;
942 }
943
bhart1bb7cdb2018-07-02 15:03:30 -0500944 /* Authenticate message based on outcome */
945 if (!auth_ret) {
946 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
Michal Vasko05532772021-06-03 12:12:38 +0200947 VRB(session, "User \"%s\" authenticated.", session->username);
bhart1bb7cdb2018-07-02 15:03:30 -0500948 ssh_message_auth_reply_success(msg, 0);
949 } else {
950 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +0200951 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
952 session->opts.server.ssh_auth_attempts);
bhart1bb7cdb2018-07-02 15:03:30 -0500953 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100954 }
955}
956
957static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100958auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100959{
960 uint32_t i;
961 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100962 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100963 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100964
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100965 /* LOCK */
966 pthread_mutex_lock(&server_opts.authkey_lock);
967
Michal Vasko17dfda92016-12-01 14:06:16 +0100968 for (i = 0; i < server_opts.authkey_count; ++i) {
969 switch (server_opts.authkeys[i].type) {
970 case NC_SSH_KEY_UNKNOWN:
971 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
972 break;
973 case NC_SSH_KEY_DSA:
974 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
975 break;
976 case NC_SSH_KEY_RSA:
977 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
978 break;
979 case NC_SSH_KEY_ECDSA:
980 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
981 break;
982 }
983
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200984 if (ret == SSH_EOF) {
Michal Vasko05532772021-06-03 12:12:38 +0200985 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 +0200986 continue;
987 } else if (ret == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +0200988 WRN(NULL, "Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100989 continue;
990 }
991
992 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
993 ssh_key_free(pub_key);
994 break;
995 }
996
997 ssh_key_free(pub_key);
998 }
999
Michal Vasko17dfda92016-12-01 14:06:16 +01001000 if (i < server_opts.authkey_count) {
1001 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +01001002 }
1003
Michal Vaskoa05c7b12017-01-30 14:33:08 +01001004 /* UNLOCK */
1005 pthread_mutex_unlock(&server_opts.authkey_lock);
1006
Michal Vasko086311b2016-01-08 09:53:11 +01001007 return username;
1008}
1009
1010static void
1011nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
1012{
1013 const char *username;
1014 int signature_state;
1015
Michal Vasko733c0bd2018-07-03 13:14:40 +02001016 if (server_opts.pubkey_auth_clb) {
1017 if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) {
bhart3bc2f582018-06-05 12:40:32 -05001018 goto fail;
1019 }
Michal Vasko733c0bd2018-07-03 13:14:40 +02001020 } else {
bhart3bc2f582018-06-05 12:40:32 -05001021 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vasko05532772021-06-03 12:12:38 +02001022 VRB(session, "User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
bhart3bc2f582018-06-05 12:40:32 -05001023 goto fail;
1024 } else if (strcmp(session->username, username)) {
Michal Vasko05532772021-06-03 12:12:38 +02001025 VRB(session, "User \"%s\" is not the username identified with the presented public key.", session->username);
bhart3bc2f582018-06-05 12:40:32 -05001026 goto fail;
1027 }
Michal Vaskobd13a932016-09-14 09:00:35 +02001028 }
Michal Vaskobd13a932016-09-14 09:00:35 +02001029
Michal Vasko086311b2016-01-08 09:53:11 +01001030 signature_state = ssh_message_auth_publickey_state(msg);
1031 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
Michal Vasko05532772021-06-03 12:12:38 +02001032 VRB(session, "User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +01001033 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
1034 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001035 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +02001036 /* accepting only the use of a public key */
1037 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001038 }
1039
Michal Vaskobd13a932016-09-14 09:00:35 +02001040 return;
1041
1042fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +02001043 ++session->opts.server.ssh_auth_attempts;
Michal Vasko05532772021-06-03 12:12:38 +02001044 VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username,
1045 session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +01001046 ssh_message_reply_default(msg);
1047}
1048
1049static int
Michal Vasko96164bf2016-01-21 15:41:58 +01001050nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +01001051{
Michal Vasko96164bf2016-01-21 15:41:58 +01001052 ssh_channel chan;
1053
1054 /* first channel request */
1055 if (!session->ti.libssh.channel) {
1056 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +01001057 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +01001058 return -1;
1059 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001060 chan = ssh_message_channel_request_open_reply_accept(msg);
1061 if (!chan) {
Michal Vasko05532772021-06-03 12:12:38 +02001062 ERR(session, "Failed to create a new SSH channel.");
Michal Vasko96164bf2016-01-21 15:41:58 +01001063 return -1;
1064 }
1065 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +01001066
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001067 /* additional channel request */
Michal Vasko96164bf2016-01-21 15:41:58 +01001068 } else {
1069 chan = ssh_message_channel_request_open_reply_accept(msg);
1070 if (!chan) {
Michal Vasko05532772021-06-03 12:12:38 +02001071 ERR(session, "Session %u: failed to create a new SSH channel.", session->id);
Michal Vasko96164bf2016-01-21 15:41:58 +01001072 return -1;
1073 }
1074 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +01001075 }
1076
Michal Vasko086311b2016-01-08 09:53:11 +01001077 return 0;
1078}
1079
1080static int
1081nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
1082{
Michal Vasko96164bf2016-01-21 15:41:58 +01001083 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001084
Michal Vasko96164bf2016-01-21 15:41:58 +01001085 if (strcmp(subsystem, "netconf")) {
Michal Vasko05532772021-06-03 12:12:38 +02001086 WRN(session, "Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +01001087 return -1;
1088 }
1089
Michal Vasko96164bf2016-01-21 15:41:58 +01001090 if (session->ti.libssh.channel == channel) {
1091 /* first channel requested */
1092 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
1093 ERRINT;
1094 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001095 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001096 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
Michal Vasko05532772021-06-03 12:12:38 +02001097 ERR(session, "Subsystem \"netconf\" requested for the second time.");
Michal Vasko96164bf2016-01-21 15:41:58 +01001098 return -1;
1099 }
1100
1101 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +01001102 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +01001103 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +02001104 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +01001105 if (!new_session) {
1106 ERRMEM;
1107 return -1;
1108 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001109
1110 /* insert the new session */
1111 if (!session->ti.libssh.next) {
1112 new_session->ti.libssh.next = session;
1113 } else {
1114 new_session->ti.libssh.next = session->ti.libssh.next;
1115 }
1116 session->ti.libssh.next = new_session;
1117
1118 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001119 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +02001120 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +01001121 new_session->ti.libssh.channel = channel;
1122 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko93224072021-11-09 12:14:28 +01001123 new_session->username = strdup(session->username);
1124 new_session->host = strdup(session->host);
Michal Vasko96164bf2016-01-21 15:41:58 +01001125 new_session->port = session->port;
Michal Vasko93224072021-11-09 12:14:28 +01001126 new_session->ctx = (struct ly_ctx *)session->ctx;
Michal Vasko83d15322018-09-27 09:44:02 +02001127 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
Michal Vasko086311b2016-01-08 09:53:11 +01001128 }
1129
1130 return 0;
1131}
1132
Michal Vasko96164bf2016-01-21 15:41:58 +01001133int
Michal Vaskob48aa812016-01-18 14:13:09 +01001134nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001135{
1136 const char *str_type, *str_subtype = NULL, *username;
1137 int subtype, type;
1138 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001139
1140 type = ssh_message_type(msg);
1141 subtype = ssh_message_subtype(msg);
1142
1143 switch (type) {
1144 case SSH_REQUEST_AUTH:
1145 str_type = "request-auth";
1146 switch (subtype) {
1147 case SSH_AUTH_METHOD_NONE:
1148 str_subtype = "none";
1149 break;
1150 case SSH_AUTH_METHOD_PASSWORD:
1151 str_subtype = "password";
1152 break;
1153 case SSH_AUTH_METHOD_PUBLICKEY:
1154 str_subtype = "publickey";
1155 break;
1156 case SSH_AUTH_METHOD_HOSTBASED:
1157 str_subtype = "hostbased";
1158 break;
1159 case SSH_AUTH_METHOD_INTERACTIVE:
1160 str_subtype = "interactive";
1161 break;
1162 case SSH_AUTH_METHOD_GSSAPI_MIC:
1163 str_subtype = "gssapi-mic";
1164 break;
1165 }
1166 break;
1167
1168 case SSH_REQUEST_CHANNEL_OPEN:
1169 str_type = "request-channel-open";
1170 switch (subtype) {
1171 case SSH_CHANNEL_SESSION:
1172 str_subtype = "session";
1173 break;
1174 case SSH_CHANNEL_DIRECT_TCPIP:
1175 str_subtype = "direct-tcpip";
1176 break;
1177 case SSH_CHANNEL_FORWARDED_TCPIP:
1178 str_subtype = "forwarded-tcpip";
1179 break;
1180 case (int)SSH_CHANNEL_X11:
1181 str_subtype = "channel-x11";
1182 break;
1183 case SSH_CHANNEL_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001184 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001185 default:
1186 str_subtype = "unknown";
1187 break;
1188 }
1189 break;
1190
1191 case SSH_REQUEST_CHANNEL:
1192 str_type = "request-channel";
1193 switch (subtype) {
1194 case SSH_CHANNEL_REQUEST_PTY:
1195 str_subtype = "pty";
1196 break;
1197 case SSH_CHANNEL_REQUEST_EXEC:
1198 str_subtype = "exec";
1199 break;
1200 case SSH_CHANNEL_REQUEST_SHELL:
1201 str_subtype = "shell";
1202 break;
1203 case SSH_CHANNEL_REQUEST_ENV:
1204 str_subtype = "env";
1205 break;
1206 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1207 str_subtype = "subsystem";
1208 break;
1209 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1210 str_subtype = "window-change";
1211 break;
1212 case SSH_CHANNEL_REQUEST_X11:
1213 str_subtype = "x11";
1214 break;
1215 case SSH_CHANNEL_REQUEST_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001216 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001217 default:
1218 str_subtype = "unknown";
1219 break;
1220 }
1221 break;
1222
1223 case SSH_REQUEST_SERVICE:
1224 str_type = "request-service";
1225 str_subtype = ssh_message_service_service(msg);
1226 break;
1227
1228 case SSH_REQUEST_GLOBAL:
1229 str_type = "request-global";
1230 switch (subtype) {
1231 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1232 str_subtype = "tcpip-forward";
1233 break;
1234 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1235 str_subtype = "cancel-tcpip-forward";
1236 break;
1237 case SSH_GLOBAL_REQUEST_UNKNOWN:
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001238 /* fallthrough */
Michal Vasko086311b2016-01-08 09:53:11 +01001239 default:
1240 str_subtype = "unknown";
1241 break;
1242 }
1243 break;
1244
1245 default:
1246 str_type = "unknown";
1247 str_subtype = "unknown";
1248 break;
1249 }
1250
Michal Vasko05532772021-06-03 12:12:38 +02001251 VRB(session, "Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vasko5e0edd82020-07-29 15:26:13 +02001252 if (!session || (session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
Michal Vaskoce319162016-02-03 15:33:08 +01001253 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1254 * but we got it now, during session free */
Michal Vasko05532772021-06-03 12:12:38 +02001255 VRB(session, "SSH message arrived on a %s session, the request will be denied.",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001256 (session && session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
Michal Vaskoce319162016-02-03 15:33:08 +01001257 ssh_message_reply_default(msg);
1258 return 0;
1259 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001260 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001261
1262 /*
1263 * process known messages
1264 */
1265 if (type == SSH_REQUEST_AUTH) {
1266 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko05532772021-06-03 12:12:38 +02001267 ERR(session, "User \"%s\" authenticated, but requested another authentication.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +01001268 ssh_message_reply_default(msg);
1269 return 0;
1270 }
1271
Michal Vasko086311b2016-01-08 09:53:11 +01001272 /* save the username, do not let the client change it */
1273 username = ssh_message_auth_user(msg);
1274 if (!session->username) {
1275 if (!username) {
Michal Vasko05532772021-06-03 12:12:38 +02001276 ERR(session, "Denying an auth request without a username.");
Michal Vasko086311b2016-01-08 09:53:11 +01001277 return 1;
1278 }
1279
Michal Vasko93224072021-11-09 12:14:28 +01001280 session->username = strdup(username);
Michal Vasko086311b2016-01-08 09:53:11 +01001281 } else if (username) {
1282 if (strcmp(username, session->username)) {
Michal Vasko05532772021-06-03 12:12:38 +02001283 ERR(session, "User \"%s\" changed its username to \"%s\".", session->username, username);
Michal Vasko086311b2016-01-08 09:53:11 +01001284 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001285 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001286 return 1;
1287 }
1288 }
1289
1290 if (subtype == SSH_AUTH_METHOD_NONE) {
1291 /* libssh will return the supported auth methods */
1292 return 1;
1293 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1294 nc_sshcb_auth_password(session, msg);
1295 return 0;
1296 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1297 nc_sshcb_auth_pubkey(session, msg);
1298 return 0;
1299 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1300 nc_sshcb_auth_kbdint(session, msg);
1301 return 0;
1302 }
1303 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001304 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001305 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001306 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001307 }
Michal Vasko086311b2016-01-08 09:53:11 +01001308 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001309
Michal Vasko0df67562016-01-21 15:50:11 +01001310 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001311 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1312 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001313 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001314 } else {
1315 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001316 }
1317 return 0;
1318 }
1319 }
1320
1321 /* we did not process it */
1322 return 1;
1323}
1324
Michal Vasko1a38c862016-01-15 15:50:07 +01001325/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001326static int
1327nc_open_netconf_channel(struct nc_session *session, int timeout)
1328{
Michal Vasko36c7be82017-02-22 13:37:59 +01001329 int ret;
roman6ece9c52022-06-22 09:29:17 +02001330 struct timespec ts_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +01001331
1332 /* message callback is executed twice to give chance for the channel to be
1333 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001334 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001335 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +02001336 ERR(session, "Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001337 return -1;
1338 }
1339
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001340 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1341 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001342 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001343 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001344 return -1;
1345 }
1346
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001347 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001348 return 0;
1349 }
1350
1351 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1352 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001353 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001354 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001355 return -1;
1356 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001357
1358 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1359 /* we did not receive subsystem-request, timeout */
1360 return 0;
1361 }
1362
1363 return 1;
1364 }
1365
Michal Vasko36c7be82017-02-22 13:37:59 +01001366 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001367 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001368 }
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);
roman6ece9c52022-06-22 09:29:17 +02001387 if ((timeout > -1) && (nc_difftimespec_cur(&ts_timeout) < 1)) {
1388 /* timeout */
1389 ERR(session, "Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1390 break;
Michal Vasko36c7be82017-02-22 13:37:59 +01001391 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001392 }
Michal Vasko086311b2016-01-08 09:53:11 +01001393
Michal Vasko1a38c862016-01-15 15:50:07 +01001394 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001395}
1396
Michal Vasko4c1fb492017-01-30 14:31:07 +01001397static int
Michal Vasko93224072021-11-09 12:14:28 +01001398nc_ssh_bind_add_hostkeys(ssh_bind sbind, char **hostkeys, uint8_t hostkey_count)
Michal Vasko4c1fb492017-01-30 14:31:07 +01001399{
1400 uint8_t i;
1401 char *privkey_path, *privkey_data;
Michal Vaskoddce1212019-05-24 09:58:49 +02001402 int ret;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001403 NC_SSH_KEY_TYPE privkey_type;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001404
1405 if (!server_opts.hostkey_clb) {
Michal Vasko05532772021-06-03 12:12:38 +02001406 ERR(NULL, "Callback for retrieving SSH host keys not set.");
Michal Vasko4c1fb492017-01-30 14:31:07 +01001407 return -1;
1408 }
1409
1410 for (i = 0; i < hostkey_count; ++i) {
1411 privkey_path = privkey_data = NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +02001412 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_type)) {
Michal Vasko05532772021-06-03 12:12:38 +02001413 ERR(NULL, "Host key callback failed.");
Michal Vasko4c1fb492017-01-30 14:31:07 +01001414 return -1;
1415 }
1416
1417 if (privkey_data) {
Michal Vaskoddce1212019-05-24 09:58:49 +02001418 privkey_path = base64der_key_to_tmp_file(privkey_data, nc_keytype2str(privkey_type));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001419 if (!privkey_path) {
Michal Vasko05532772021-06-03 12:12:38 +02001420 ERR(NULL, "Temporarily storing a host key into a file failed (%s).", strerror(errno));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001421 free(privkey_data);
1422 return -1;
1423 }
1424 }
1425
1426 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1427
1428 /* cleanup */
1429 if (privkey_data && unlink(privkey_path)) {
Michal Vasko05532772021-06-03 12:12:38 +02001430 WRN(NULL, "Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001431 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001432 free(privkey_data);
1433
1434 if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001435 ERR(NULL, "Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
Michal Vasko80075de2017-07-10 11:38:52 +02001436 }
1437 free(privkey_path);
1438
1439 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001440 return -1;
1441 }
1442 }
1443
1444 return 0;
1445}
1446
Michal Vasko96164bf2016-01-21 15:41:58 +01001447int
Michal Vasko0190bc32016-03-02 15:47:49 +01001448nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001449{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001450 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001451 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001452 int libssh_auth_methods = 0, ret;
roman6ece9c52022-06-22 09:29:17 +02001453 struct timespec ts_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +01001454
Michal Vasko2cc4c682016-03-01 09:16:48 +01001455 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001456
Michal Vasko086311b2016-01-08 09:53:11 +01001457 /* other transport-specific data */
1458 session->ti_type = NC_TI_LIBSSH;
1459 session->ti.libssh.session = ssh_new();
1460 if (!session->ti.libssh.session) {
Michal Vasko05532772021-06-03 12:12:38 +02001461 ERR(NULL, "Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001462 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001463 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001464 }
1465
Michal Vaskoc61c4492016-01-25 11:13:34 +01001466 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001467 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1468 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001469 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001470 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1471 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001472 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001473 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1474 }
1475 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1476
Michal Vaskoe2713da2016-08-22 16:06:40 +02001477 sbind = ssh_bind_new();
1478 if (!sbind) {
Michal Vasko05532772021-06-03 12:12:38 +02001479 ERR(session, "Failed to create an SSH bind.");
Michal Vaskoe2713da2016-08-22 16:06:40 +02001480 close(sock);
1481 return -1;
1482 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001483
1484 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1485 close(sock);
1486 ssh_bind_free(sbind);
1487 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001488 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001489
Michal Vasko086311b2016-01-08 09:53:11 +01001490 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001491 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001492 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001493
Michal Vaskoe2713da2016-08-22 16:06:40 +02001494 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
Michal Vasko05532772021-06-03 12:12:38 +02001495 ERR(session, "SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001496 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001497 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001498 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001499 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001500 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001501
Michal Vasko0190bc32016-03-02 15:47:49 +01001502 ssh_set_blocking(session->ti.libssh.session, 0);
1503
Michal Vasko36c7be82017-02-22 13:37:59 +01001504 if (timeout > -1) {
roman6ece9c52022-06-22 09:29:17 +02001505 nc_gettimespec_mono_add(&ts_timeout, timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001506 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001507 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001508 /* this tends to take longer */
1509 usleep(NC_TIMEOUT_STEP * 20);
roman6ece9c52022-06-22 09:29:17 +02001510 if ((timeout > -1) && (nc_difftimespec_cur(&ts_timeout) < 1)) {
1511 break;
Michal Vasko0190bc32016-03-02 15:47:49 +01001512 }
1513 }
1514 if (ret == SSH_AGAIN) {
Michal Vasko05532772021-06-03 12:12:38 +02001515 ERR(session, "SSH key exchange timeout.");
Michal Vasko0190bc32016-03-02 15:47:49 +01001516 return 0;
1517 } else if (ret != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001518 ERR(session, "SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001519 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001520 }
1521
1522 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001523 if (opts->auth_timeout) {
roman6ece9c52022-06-22 09:29:17 +02001524 nc_gettimespec_mono_add(&ts_timeout, opts->auth_timeout * 1000);
Michal Vasko36c7be82017-02-22 13:37:59 +01001525 }
1526 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001527 if (!nc_session_is_connected(session)) {
Michal Vasko05532772021-06-03 12:12:38 +02001528 ERR(session, "Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001529 return -1;
1530 }
1531
Michal Vasko086311b2016-01-08 09:53:11 +01001532 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vasko05532772021-06-03 12:12:38 +02001533 ERR(session, "Failed to receive SSH messages on a session (%s).",
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001534 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001535 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001536 }
1537
Michal Vasko36c7be82017-02-22 13:37:59 +01001538 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1539 break;
1540 }
1541
Michal Vasko145ae672017-02-07 10:57:27 +01001542 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
Michal Vasko05532772021-06-03 12:12:38 +02001543 ERR(session, "Too many failed authentication attempts of user \"%s\".", session->username);
Michal Vasko145ae672017-02-07 10:57:27 +01001544 return -1;
1545 }
1546
Michal Vasko086311b2016-01-08 09:53:11 +01001547 usleep(NC_TIMEOUT_STEP);
roman6ece9c52022-06-22 09:29:17 +02001548 if ((opts->auth_timeout) && (nc_difftimespec_cur(&ts_timeout) < 1)) {
1549 /* timeout */
1550 break;
Michal Vasko36c7be82017-02-22 13:37:59 +01001551 }
1552 }
Michal Vasko086311b2016-01-08 09:53:11 +01001553
1554 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1555 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001556 if (session->username) {
Michal Vasko05532772021-06-03 12:12:38 +02001557 ERR(session, "User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
Michal Vaskoc13da702017-02-07 10:57:57 +01001558 } else {
Michal Vasko05532772021-06-03 12:12:38 +02001559 ERR(session, "User failed to authenticate for too long, disconnecting.");
Michal Vaskoc13da702017-02-07 10:57:57 +01001560 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001561 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001562 }
1563
Michal Vasko086311b2016-01-08 09:53:11 +01001564 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001565 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001566 if (ret < 1) {
1567 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001568 }
1569
Michal Vasko96164bf2016-01-21 15:41:58 +01001570 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001571 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001572}
1573
Michal Vasko71090fc2016-05-24 16:37:28 +02001574API NC_MSG_TYPE
1575nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1576{
1577 NC_MSG_TYPE msgtype;
1578 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001579 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001580
1581 if (!orig_session) {
1582 ERRARG("orig_session");
1583 return NC_MSG_ERROR;
1584 } else if (!session) {
1585 ERRARG("session");
1586 return NC_MSG_ERROR;
1587 }
1588
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001589 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH) &&
1590 orig_session->ti.libssh.next) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001591 for (new_session = orig_session->ti.libssh.next;
1592 new_session != orig_session;
1593 new_session = new_session->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001594 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel &&
1595 (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001596 /* we found our session */
1597 break;
1598 }
1599 }
1600 if (new_session == orig_session) {
1601 new_session = NULL;
1602 }
1603 }
1604
1605 if (!new_session) {
Michal Vasko05532772021-06-03 12:12:38 +02001606 ERR(orig_session, "Session does not have a NETCONF SSH channel ready.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001607 return NC_MSG_ERROR;
1608 }
1609
1610 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001611 new_session->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001612
1613 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001614 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001615 if (msgtype != NC_MSG_HELLO) {
1616 return msgtype;
1617 }
1618
roman6ece9c52022-06-22 09:29:17 +02001619 nc_gettimespec_real_add(&ts_cur, 0);
Michal Vasko9f6275e2017-10-05 13:50:05 +02001620 new_session->opts.server.session_start = ts_cur.tv_sec;
roman6ece9c52022-06-22 09:29:17 +02001621 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko9f6275e2017-10-05 13:50:05 +02001622 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001623 new_session->status = NC_STATUS_RUNNING;
1624 *session = new_session;
1625
1626 return msgtype;
1627}
1628
1629API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001630nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001631{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001632 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001633 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001634 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001635 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001636 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001637
Michal Vasko45e53ae2016-04-07 11:46:03 +02001638 if (!ps) {
1639 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001640 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001641 } else if (!session) {
1642 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001643 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001644 }
1645
Michal Vasko48a63ed2016-03-01 09:48:21 +01001646 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001647 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001648 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001649 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001650
Michal Vasko96164bf2016-01-21 15:41:58 +01001651 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001652 cur_session = ps->sessions[i]->session;
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001653 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH) &&
1654 cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001655 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001656 for (new_session = cur_session->ti.libssh.next;
1657 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001658 new_session = new_session->ti.libssh.next) {
Michal Vaskob83a3fa2021-05-26 09:53:42 +02001659 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel &&
1660 (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001661 /* we found our session */
1662 break;
1663 }
1664 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001665 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001666 break;
1667 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001668
Michal Vasko96164bf2016-01-21 15:41:58 +01001669 new_session = NULL;
1670 }
1671 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001672
Michal Vasko48a63ed2016-03-01 09:48:21 +01001673 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001674 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001675
Michal Vasko96164bf2016-01-21 15:41:58 +01001676 if (!new_session) {
Michal Vasko05532772021-06-03 12:12:38 +02001677 ERR(NULL, "No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001678 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001679 }
1680
1681 /* assign new SID atomically */
Michal Vasko5bd4a3f2021-06-17 16:40:10 +02001682 new_session->id = ATOMIC_INC_RELAXED(server_opts.new_session_id);
Michal Vaskofb89d772016-01-08 12:25:35 +01001683
Michal Vasko086311b2016-01-08 09:53:11 +01001684 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001685 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001686 if (msgtype != NC_MSG_HELLO) {
1687 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001688 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001689
roman6ece9c52022-06-22 09:29:17 +02001690 nc_gettimespec_real_add(&ts_cur, 0);
Michal Vasko9f6275e2017-10-05 13:50:05 +02001691 new_session->opts.server.session_start = ts_cur.tv_sec;
roman6ece9c52022-06-22 09:29:17 +02001692 nc_gettimespec_mono_add(&ts_cur, 0);
Michal Vasko9f6275e2017-10-05 13:50:05 +02001693 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001694 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001695 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001696
Michal Vasko71090fc2016-05-24 16:37:28 +02001697 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001698}