blob: ec8e8e923a9d143ecde90200d2589231f1036c04 [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 Vasko086311b2016-01-08 09:53:11 +010026#include <stdlib.h>
27#include <string.h>
28#include <sys/types.h>
Michal Vasko27252692017-03-21 15:34:13 +010029#include <sys/stat.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030#include <pwd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010031#include <errno.h>
Michal Vasko9f6275e2017-10-05 13:50:05 +020032#include <time.h>
Claus Klein22091912020-01-20 13:45:47 +010033#include <fcntl.h>
34#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010035
Michal Vasko11d142a2016-01-19 15:58:24 +010036#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010037#include "session_server_ch.h"
38#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010039
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +020040#if !defined(HAVE_CRYPT_R)
41pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER;
42#endif
43
Michal Vasko086311b2016-01-08 09:53:11 +010044extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010045
Michal Vasko4c1fb492017-01-30 14:31:07 +010046static char *
Michal Vaskoddce1212019-05-24 09:58:49 +020047base64der_key_to_tmp_file(const char *in, const char *key_str)
Michal Vasko086311b2016-01-08 09:53:11 +010048{
Michal Vasko4c1fb492017-01-30 14:31:07 +010049 char path[12] = "/tmp/XXXXXX";
50 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010051 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010052 FILE *file;
53
54 if (in == NULL) {
55 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010056 }
57
mekleob31878b2019-09-09 14:10:47 +020058 umode = umask(0177);
Michal Vasko4c1fb492017-01-30 14:31:07 +010059 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010060 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010061 if (fd == -1) {
62 return NULL;
63 }
64
Michal Vasko3964a832018-09-18 14:37:39 +020065 file = fdopen(fd, "w");
Michal Vasko4c1fb492017-01-30 14:31:07 +010066 if (!file) {
67 close(fd);
68 return NULL;
69 }
70
71 /* write the key into the file */
Michal Vasko68177b72020-04-27 15:46:53 +020072 if (key_str) {
73 written = fwrite("-----BEGIN ", 1, 11, file);
74 written += fwrite(key_str, 1, strlen(key_str), file);
75 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
76 written += fwrite(in, 1, strlen(in), file);
77 written += fwrite("\n-----END ", 1, 10, file);
78 written += fwrite(key_str, 1, strlen(key_str), file);
79 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
Michal Vasko4c1fb492017-01-30 14:31:07 +010080
Michal Vasko68177b72020-04-27 15:46:53 +020081 fclose(file);
82 if ((unsigned)written != 11 + strlen(key_str) + 18 + strlen(in) + 10 + strlen(key_str) + 17) {
83 unlink(path);
84 return NULL;
85 }
86 } else {
87 written = fwrite("-----BEGIN PRIVATE KEY-----\n", 1, 28, file);
88 written += fwrite(in, 1, strlen(in), file);
89 written += fwrite("\n-----END PRIVATE KEY-----", 1, 26, file);
90
91 fclose(file);
92 if ((unsigned)written != 28 + strlen(in) + 26) {
93 unlink(path);
94 return NULL;
95 }
Michal Vasko4c1fb492017-01-30 14:31:07 +010096 }
97
98 return strdup(path);
99}
100
101static int
Michal Vasko7d255882017-02-09 13:35:08 +0100102nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +0100103{
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100104 uint8_t i;
105
Michal Vasko4c1fb492017-01-30 14:31:07 +0100106 if (!name) {
107 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +0100108 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100109 } else if (idx > opts->hostkey_count) {
110 ERRARG("idx");
111 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100112 }
Michal Vaskod45e25a2016-01-08 15:48:44 +0100113
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100114 for (i = 0; i < opts->hostkey_count; ++i) {
115 if (!strcmp(opts->hostkeys[i], name)) {
116 ERRARG("name");
117 return -1;
118 }
119 }
120
Michal Vaskoe2713da2016-08-22 16:06:40 +0200121 ++opts->hostkey_count;
122 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
123 if (!opts->hostkeys) {
124 ERRMEM;
125 return -1;
126 }
Michal Vasko7d255882017-02-09 13:35:08 +0100127
128 if (idx < 0) {
129 idx = opts->hostkey_count - 1;
130 }
131 if (idx != opts->hostkey_count - 1) {
132 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
133 }
Michal Vasko77367452021-02-16 16:32:18 +0100134 lydict_insert(server_opts.ctx, name, 0, &opts->hostkeys[idx]);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200135
Michal Vasko5fcc7142016-02-02 12:21:10 +0100136 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100137}
138
139API int
Michal Vasko7d255882017-02-09 13:35:08 +0100140nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100141{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100142 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100143 struct nc_endpt *endpt;
144
Michal Vasko51e514d2016-02-02 15:51:52 +0100145 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100146 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100147 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100148 return -1;
149 }
Michal Vasko7d255882017-02-09 13:35:08 +0100150 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100151 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100152 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100153
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100154 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100155}
156
Michal Vasko974410a2018-04-03 09:36:57 +0200157API void
158nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data),
159 void *user_data, void (*free_user_data)(void *user_data))
160{
161 server_opts.passwd_auth_clb = passwd_auth_clb;
162 server_opts.passwd_auth_data = user_data;
163 server_opts.passwd_auth_data_free = free_user_data;
164}
165
bhart1bb7cdb2018-07-02 15:03:30 -0500166API void
167nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data),
168 void *user_data, void (*free_user_data)(void *user_data))
169{
170 server_opts.interactive_auth_clb = interactive_auth_clb;
171 server_opts.interactive_auth_data = user_data;
172 server_opts.interactive_auth_data_free = free_user_data;
173}
Michal Vasko733c0bd2018-07-03 13:14:40 +0200174
bhart1bb7cdb2018-07-02 15:03:30 -0500175API void
176nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data),
177 void *user_data, void (*free_user_data)(void *user_data))
178{
179 server_opts.pubkey_auth_clb = pubkey_auth_clb;
180 server_opts.pubkey_auth_data = user_data;
181 server_opts.pubkey_auth_data_free = free_user_data;
182}
183
Michal Vaskob05053d2016-01-22 16:12:06 +0100184API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200185nc_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 +0100186{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100187 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200188 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200189 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100190
Michal Vasko2e6defd2016-10-07 15:48:15 +0200191 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200192 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
193 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200194 return -1;
195 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200196 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200197 /* UNLOCK */
198 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200199
200 return ret;
201}
202
Michal Vasko4c1fb492017-01-30 14:31:07 +0100203API void
204nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200205 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 +0100206{
207 if (!hostkey_clb) {
208 ERRARG("hostkey_clb");
209 return;
210 }
211
212 server_opts.hostkey_clb = hostkey_clb;
213 server_opts.hostkey_data = user_data;
214 server_opts.hostkey_data_free = free_user_data;
215}
216
Michal Vaskoe2713da2016-08-22 16:06:40 +0200217static int
Michal Vasko7d255882017-02-09 13:35:08 +0100218nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200219{
220 uint8_t i;
221
Michal Vasko7d255882017-02-09 13:35:08 +0100222 if (name && (idx > -1)) {
223 ERRARG("name and idx");
224 return -1;
225 } else if (idx >= opts->hostkey_count) {
226 ERRARG("idx");
227 }
228
229 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200230 for (i = 0; i < opts->hostkey_count; ++i) {
231 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
232 }
233 free(opts->hostkeys);
234 opts->hostkeys = NULL;
235 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100236 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200237 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100238 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100239 idx = i;
240 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200241 }
242 }
243
Michal Vasko7d255882017-02-09 13:35:08 +0100244 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200245 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100246 } else {
247remove_idx:
248 --opts->hostkey_count;
249 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
250 if (idx < opts->hostkey_count - 1) {
251 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
252 }
253 if (!opts->hostkey_count) {
254 free(opts->hostkeys);
255 opts->hostkeys = NULL;
256 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200257 }
258
259 return 0;
260}
261
262API int
Michal Vasko7d255882017-02-09 13:35:08 +0100263nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200264{
265 int ret;
266 struct nc_endpt *endpt;
267
268 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100269 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200270 if (!endpt) {
271 return -1;
272 }
Michal Vasko7d255882017-02-09 13:35:08 +0100273 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200274 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100275 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200276
277 return ret;
278}
279
280API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200281nc_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 +0200282{
283 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200284 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200285 struct nc_ch_endpt *endpt;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200286
Michal Vasko2e6defd2016-10-07 15:48:15 +0200287 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200288 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
289 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200290 return -1;
291 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200292 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200293 /* UNLOCK */
294 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100295
296 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100297}
298
299static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100300nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
301{
302 uint8_t i;
303 int16_t mov_idx = -1, after_idx = -1;
304 const char *bckup;
305
306 if (!key_mov) {
307 ERRARG("key_mov");
308 return -1;
309 }
310
311 for (i = 0; i < opts->hostkey_count; ++i) {
312 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
313 after_idx = i;
314 }
315 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
316 mov_idx = i;
317 }
318
319 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
320 break;
321 }
322 }
323
324 if (key_after && (after_idx == -1)) {
325 ERRARG("key_after");
326 return -1;
327 }
328 if (mov_idx == -1) {
329 ERRARG("key_mov");
330 return -1;
331 }
332 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
333 /* nothing to do */
334 return 0;
335 }
336
337 /* finally move the key */
338 bckup = opts->hostkeys[mov_idx];
339 if (mov_idx > after_idx) {
340 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
341 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
342 opts->hostkeys[after_idx + 1] = bckup;
343 } else {
344 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
345 opts->hostkeys[after_idx] = bckup;
346 }
347
348 return 0;
349}
350
351API int
352nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
353{
354 int ret;
355 struct nc_endpt *endpt;
356
357 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100358 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100359 if (!endpt) {
360 return -1;
361 }
362 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
363 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100364 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100365
366 return ret;
367}
368
369API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200370nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
371 const char *key_after)
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100372{
373 int ret;
374 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200375 struct nc_ch_endpt *endpt;
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100376
377 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200378 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100379 if (!endpt) {
380 return -1;
381 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200382 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100383 /* UNLOCK */
384 nc_server_ch_client_unlock(client);
385
386 return ret;
387}
388
389static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100390nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100391{
Michal Vaskob05053d2016-01-22 16:12:06 +0100392 opts->auth_methods = auth_methods;
393 return 0;
394}
395
396API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100397nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100398{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100399 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100400 struct nc_endpt *endpt;
401
Michal Vasko51e514d2016-02-02 15:51:52 +0100402 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100403 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100404 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100405 return -1;
406 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200407 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100408 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100409 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100410
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100411 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100412}
413
414API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200415nc_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 +0100416{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100417 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200418 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200419 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100420
Michal Vasko2e6defd2016-10-07 15:48:15 +0200421 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200422 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
423 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200424 return -1;
425 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200426 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200427 /* UNLOCK */
428 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100429
430 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100431}
432
Michal Vaskoddce1212019-05-24 09:58:49 +0200433API int
434nc_server_ssh_endpt_get_auth_methods(const char *endpt_name)
435{
436 int ret;
437 struct nc_endpt *endpt;
438
439 /* LOCK */
440 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
441 if (!endpt) {
442 return -1;
443 }
444 ret = endpt->opts.ssh->auth_methods;
445 /* UNLOCK */
446 pthread_rwlock_unlock(&server_opts.endpt_lock);
447
448 return ret;
449}
450
451API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200452nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name)
Michal Vaskoddce1212019-05-24 09:58:49 +0200453{
454 int ret;
455 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200456 struct nc_ch_endpt *endpt;
Michal Vaskoddce1212019-05-24 09:58:49 +0200457
458 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200459 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
460 if (!endpt) {
Michal Vaskoddce1212019-05-24 09:58:49 +0200461 return -1;
462 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200463 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoddce1212019-05-24 09:58:49 +0200464 /* UNLOCK */
465 nc_server_ch_client_unlock(client);
466
467 return ret;
468}
469
Michal Vaskob05053d2016-01-22 16:12:06 +0100470static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100471nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100472{
Michal Vaskob05053d2016-01-22 16:12:06 +0100473 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200474 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100475 return -1;
476 }
477
Michal Vaskob05053d2016-01-22 16:12:06 +0100478 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100479 return 0;
480}
481
482API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100483nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100484{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100485 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100486 struct nc_endpt *endpt;
487
Michal Vasko51e514d2016-02-02 15:51:52 +0100488 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100489 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100490 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100491 return -1;
492 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200493 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100494 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100495 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100496
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100497 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100498}
499
500API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200501nc_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 +0100502{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100503 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200504 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200505 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100506
Michal Vasko2e6defd2016-10-07 15:48:15 +0200507 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200508 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
509 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200510 return -1;
511 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200512 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200513 /* UNLOCK */
514 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100515
516 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100517}
518
519static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100520nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100521{
Michal Vaskob05053d2016-01-22 16:12:06 +0100522 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200523 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100524 return -1;
525 }
526
Michal Vaskob05053d2016-01-22 16:12:06 +0100527 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100528 return 0;
529}
530
531API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100532nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100533{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100534 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100535 struct nc_endpt *endpt;
536
Michal Vasko51e514d2016-02-02 15:51:52 +0100537 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100538 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100539 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100540 return -1;
541 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200542 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100543 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100544 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100545
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100546 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100547}
548
549API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200550nc_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 +0100551{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100552 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200553 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200554 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100555
Michal Vasko2e6defd2016-10-07 15:48:15 +0200556 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200557 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
558 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200559 return -1;
560 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200561 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200562 /* UNLOCK */
563 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100564
565 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100566}
567
568static int
Michal Vasko77367452021-02-16 16:32:18 +0100569_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 +0100570{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100571 /* LOCK */
572 pthread_mutex_lock(&server_opts.authkey_lock);
573
Michal Vasko17dfda92016-12-01 14:06:16 +0100574 ++server_opts.authkey_count;
575 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
576 if (!server_opts.authkeys) {
577 ERRMEM;
578 return -1;
579 }
Michal Vasko77367452021-02-16 16:32:18 +0100580 lydict_insert(server_opts.ctx, pubkey_path, 0, &server_opts.authkeys[server_opts.authkey_count - 1].path);
581 lydict_insert(server_opts.ctx, pubkey_base64, 0, &server_opts.authkeys[server_opts.authkey_count - 1].base64);
Michal Vasko17dfda92016-12-01 14:06:16 +0100582 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
Michal Vasko77367452021-02-16 16:32:18 +0100583 lydict_insert(server_opts.ctx, username, 0, &server_opts.authkeys[server_opts.authkey_count - 1].username);
Michal Vasko17dfda92016-12-01 14:06:16 +0100584
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100585 /* UNLOCK */
586 pthread_mutex_unlock(&server_opts.authkey_lock);
587
Michal Vasko17dfda92016-12-01 14:06:16 +0100588 return 0;
589}
590
591API int
592nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100593{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200594 if (!pubkey_path) {
595 ERRARG("pubkey_path");
596 return -1;
597 } else if (!username) {
598 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100599 return -1;
600 }
601
Michal Vasko17dfda92016-12-01 14:06:16 +0100602 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100603}
604
605API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100606nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100607{
Michal Vasko17dfda92016-12-01 14:06:16 +0100608 if (!pubkey_base64) {
609 ERRARG("pubkey_base64");
610 return -1;
611 } else if (!type) {
612 ERRARG("type");
613 return -1;
614 } else if (!username) {
615 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100616 return -1;
617 }
618
Michal Vasko17dfda92016-12-01 14:06:16 +0100619 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100620}
621
622API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100623nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
624 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100625{
Michal Vasko086311b2016-01-08 09:53:11 +0100626 uint32_t i;
627 int ret = -1;
628
Michal Vasko17dfda92016-12-01 14:06:16 +0100629 /* LOCK */
630 pthread_mutex_lock(&server_opts.authkey_lock);
631
632 if (!pubkey_path && !pubkey_base64 && !type && !username) {
633 for (i = 0; i < server_opts.authkey_count; ++i) {
634 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
635 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
636 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100637
Michal Vasko086311b2016-01-08 09:53:11 +0100638 ret = 0;
639 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100640 free(server_opts.authkeys);
641 server_opts.authkeys = NULL;
642 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100643 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100644 for (i = 0; i < server_opts.authkey_count; ++i) {
645 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
Michal Vaskoe846aee2019-03-28 08:38:41 +0100646 && (!pubkey_base64 || !strcmp(server_opts.authkeys[i].base64, pubkey_base64))
Michal Vasko17dfda92016-12-01 14:06:16 +0100647 && (!type || (server_opts.authkeys[i].type == type))
648 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
649 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
650 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
651 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100652
Michal Vasko17dfda92016-12-01 14:06:16 +0100653 --server_opts.authkey_count;
654 if (i < server_opts.authkey_count) {
655 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
656 sizeof *server_opts.authkeys);
657 } else if (!server_opts.authkey_count) {
658 free(server_opts.authkeys);
659 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100660 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100661
662 ret = 0;
663 }
664 }
Michal Vasko086311b2016-01-08 09:53:11 +0100665 }
666
Michal Vasko51e514d2016-02-02 15:51:52 +0100667 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100668 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100669
670 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100671}
672
673void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100674nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100675{
Michal Vasko7d255882017-02-09 13:35:08 +0100676 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100677}
678
Michal Vasko086311b2016-01-08 09:53:11 +0100679static char *
680auth_password_get_pwd_hash(const char *username)
681{
apropp-molex4e903c32020-04-20 03:06:58 -0400682#ifdef HAVE_SHADOW
Michal Vasko086311b2016-01-08 09:53:11 +0100683 struct passwd *pwd, pwd_buf;
684 struct spwd *spwd, spwd_buf;
685 char *pass_hash = NULL, buf[256];
686
687 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
688 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100689 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100690 return NULL;
691 }
692
693 if (!strcmp(pwd->pw_passwd, "x")) {
Michal Vaskob4ff2fd2020-11-03 14:18:13 +0100694# ifndef __QNXNTO__
695 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
696# else
697 spwd = getspnam_r(username, &spwd_buf, buf, 256);
698# endif
Michal Vasko086311b2016-01-08 09:53:11 +0100699 if (!spwd) {
700 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
701 return NULL;
Michal Vasko22b4fe72020-11-04 08:52:29 +0100702 } else if ((spwd->sp_expire > -1) && (spwd->sp_expire <= (time(NULL) / (60 * 60 * 24)))) {
703 WRN("User \"%s\" account has expired.", username);
704 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +0100705 }
706
707 pass_hash = spwd->sp_pwdp;
708 } else {
709 pass_hash = pwd->pw_passwd;
710 }
711
712 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100713 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100714 return NULL;
715 }
716
717 /* check the hash structure for special meaning */
718 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
719 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
720 return NULL;
721 }
722 if (!strcmp(pass_hash, "*NP*")) {
723 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
724 return NULL;
725 }
726
727 return strdup(pass_hash);
Claus Klein22091912020-01-20 13:45:47 +0100728#else
729 return strdup("");
730#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100731}
732
733static int
734auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
735{
736 char *new_pass_hash;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200737#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100738 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200739#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100740
741 if (!pass_hash[0]) {
742 if (!pass_clear[0]) {
743 WRN("User authentication successful with an empty password!");
744 return 0;
745 } else {
746 /* the user did now know he does not need any password,
747 * (which should not be used) so deny authentication */
748 return 1;
749 }
750 }
751
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200752#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100753 cdata.initialized = 0;
754 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200755#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200756 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200757 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200758 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200759#endif
Andrew Langefeld158d6fd2018-06-11 18:51:44 -0500760
761 if (!new_pass_hash) {
762 return 1;
763 }
764
Michal Vasko086311b2016-01-08 09:53:11 +0100765 return strcmp(new_pass_hash, pass_hash);
766}
767
768static void
769nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
770{
771 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100772 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100773
Michal Vaskoebba7602018-03-23 13:14:08 +0100774 if (server_opts.passwd_auth_clb) {
775 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
776 } else {
777 pass_hash = auth_password_get_pwd_hash(session->username);
778 if (pass_hash) {
779 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
780 free(pass_hash);
781 }
Michal Vasko086311b2016-01-08 09:53:11 +0100782 }
783
Michal Vaskoebba7602018-03-23 13:14:08 +0100784 if (!auth_ret) {
785 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
786 VRB("User \"%s\" authenticated.", session->username);
787 ssh_message_auth_reply_success(msg, 0);
788 } else {
789 ++session->opts.server.ssh_auth_attempts;
790 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
791 ssh_message_reply_default(msg);
792 }
Michal Vasko086311b2016-01-08 09:53:11 +0100793}
794
795static void
796nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
797{
bhart3bc2f582018-06-05 12:40:32 -0500798 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100799 char *pass_hash;
800
bhart1bb7cdb2018-07-02 15:03:30 -0500801 if (server_opts.interactive_auth_clb) {
Michal Vasko733c0bd2018-07-03 13:14:40 +0200802 auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100803 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500804 if (!ssh_message_auth_kbdint_is_response(msg)) {
805 const char *prompts[] = {"Password: "};
806 char echo[] = {0};
807
808 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
Robert Vargaad7a5532018-08-10 20:40:54 +0200809 auth_ret = -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100810 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500811 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {// failed session
812 ssh_message_reply_default(msg);
813 return;
814 }
bhart3bc2f582018-06-05 12:40:32 -0500815 pass_hash = auth_password_get_pwd_hash(session->username);// get hashed password
816 if (pass_hash) {
Robert Vargaad7a5532018-08-10 20:40:54 +0200817 /* Normalize auth_password_compare_pwd result to 0 or 1 */
818 auth_ret = !!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0));
bhart3bc2f582018-06-05 12:40:32 -0500819 free(pass_hash);// free hashed password
820 }
Michal Vasko086311b2016-01-08 09:53:11 +0100821 }
bhart1bb7cdb2018-07-02 15:03:30 -0500822 }
823
Robert Vargaad7a5532018-08-10 20:40:54 +0200824 /* We have already sent a reply */
825 if (auth_ret == -1) {
826 return;
827 }
828
bhart1bb7cdb2018-07-02 15:03:30 -0500829 /* Authenticate message based on outcome */
830 if (!auth_ret) {
831 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
832 VRB("User \"%s\" authenticated.", session->username);
833 ssh_message_auth_reply_success(msg, 0);
834 } else {
835 ++session->opts.server.ssh_auth_attempts;
836 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
837 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100838 }
839}
840
841static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100842auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100843{
844 uint32_t i;
845 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100846 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100847 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100848
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100849 /* LOCK */
850 pthread_mutex_lock(&server_opts.authkey_lock);
851
Michal Vasko17dfda92016-12-01 14:06:16 +0100852 for (i = 0; i < server_opts.authkey_count; ++i) {
853 switch (server_opts.authkeys[i].type) {
854 case NC_SSH_KEY_UNKNOWN:
855 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
856 break;
857 case NC_SSH_KEY_DSA:
858 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
859 break;
860 case NC_SSH_KEY_RSA:
861 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
862 break;
863 case NC_SSH_KEY_ECDSA:
864 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
865 break;
866 }
867
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200868 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100869 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200870 continue;
871 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100872 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100873 continue;
874 }
875
876 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
877 ssh_key_free(pub_key);
878 break;
879 }
880
881 ssh_key_free(pub_key);
882 }
883
Michal Vasko17dfda92016-12-01 14:06:16 +0100884 if (i < server_opts.authkey_count) {
885 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100886 }
887
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100888 /* UNLOCK */
889 pthread_mutex_unlock(&server_opts.authkey_lock);
890
Michal Vasko086311b2016-01-08 09:53:11 +0100891 return username;
892}
893
894static void
895nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
896{
897 const char *username;
898 int signature_state;
899
Michal Vasko733c0bd2018-07-03 13:14:40 +0200900 if (server_opts.pubkey_auth_clb) {
901 if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) {
bhart3bc2f582018-06-05 12:40:32 -0500902 goto fail;
903 }
Michal Vasko733c0bd2018-07-03 13:14:40 +0200904 } else {
bhart3bc2f582018-06-05 12:40:32 -0500905 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
906 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
907 goto fail;
908 } else if (strcmp(session->username, username)) {
909 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
910 goto fail;
911 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200912 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200913
Michal Vasko086311b2016-01-08 09:53:11 +0100914 signature_state = ssh_message_auth_publickey_state(msg);
915 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
916 VRB("User \"%s\" authenticated.", session->username);
917 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
918 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100919 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200920 /* accepting only the use of a public key */
921 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100922 }
923
Michal Vaskobd13a932016-09-14 09:00:35 +0200924 return;
925
926fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200927 ++session->opts.server.ssh_auth_attempts;
928 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100929 ssh_message_reply_default(msg);
930}
931
932static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100933nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100934{
Michal Vasko96164bf2016-01-21 15:41:58 +0100935 ssh_channel chan;
936
937 /* first channel request */
938 if (!session->ti.libssh.channel) {
939 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100940 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100941 return -1;
942 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100943 chan = ssh_message_channel_request_open_reply_accept(msg);
944 if (!chan) {
945 ERR("Failed to create a new SSH channel.");
946 return -1;
947 }
948 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100949
Michal Vasko96164bf2016-01-21 15:41:58 +0100950 /* additional channel request */
951 } else {
952 chan = ssh_message_channel_request_open_reply_accept(msg);
953 if (!chan) {
954 ERR("Session %u: failed to create a new SSH channel.", session->id);
955 return -1;
956 }
957 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100958 }
959
Michal Vasko086311b2016-01-08 09:53:11 +0100960 return 0;
961}
962
963static int
964nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
965{
Michal Vasko96164bf2016-01-21 15:41:58 +0100966 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100967
Michal Vasko96164bf2016-01-21 15:41:58 +0100968 if (strcmp(subsystem, "netconf")) {
969 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100970 return -1;
971 }
972
Michal Vasko96164bf2016-01-21 15:41:58 +0100973 if (session->ti.libssh.channel == channel) {
974 /* first channel requested */
975 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
976 ERRINT;
977 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100978 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100979 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
980 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
981 return -1;
982 }
983
984 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100985 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100986 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +0200987 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100988 if (!new_session) {
989 ERRMEM;
990 return -1;
991 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100992
993 /* insert the new session */
994 if (!session->ti.libssh.next) {
995 new_session->ti.libssh.next = session;
996 } else {
997 new_session->ti.libssh.next = session->ti.libssh.next;
998 }
999 session->ti.libssh.next = new_session;
1000
1001 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001002 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +02001003 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +01001004 new_session->ti.libssh.channel = channel;
1005 new_session->ti.libssh.session = session->ti.libssh.session;
Michal Vasko77367452021-02-16 16:32:18 +01001006 lydict_insert(server_opts.ctx, session->username, 0, &new_session->username);
1007 lydict_insert(server_opts.ctx, session->host, 0, &new_session->host);
Michal Vasko96164bf2016-01-21 15:41:58 +01001008 new_session->port = session->port;
1009 new_session->ctx = server_opts.ctx;
Michal Vasko83d15322018-09-27 09:44:02 +02001010 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
Michal Vasko086311b2016-01-08 09:53:11 +01001011 }
1012
1013 return 0;
1014}
1015
Michal Vasko96164bf2016-01-21 15:41:58 +01001016int
Michal Vaskob48aa812016-01-18 14:13:09 +01001017nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001018{
1019 const char *str_type, *str_subtype = NULL, *username;
1020 int subtype, type;
1021 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001022
1023 type = ssh_message_type(msg);
1024 subtype = ssh_message_subtype(msg);
1025
1026 switch (type) {
1027 case SSH_REQUEST_AUTH:
1028 str_type = "request-auth";
1029 switch (subtype) {
1030 case SSH_AUTH_METHOD_NONE:
1031 str_subtype = "none";
1032 break;
1033 case SSH_AUTH_METHOD_PASSWORD:
1034 str_subtype = "password";
1035 break;
1036 case SSH_AUTH_METHOD_PUBLICKEY:
1037 str_subtype = "publickey";
1038 break;
1039 case SSH_AUTH_METHOD_HOSTBASED:
1040 str_subtype = "hostbased";
1041 break;
1042 case SSH_AUTH_METHOD_INTERACTIVE:
1043 str_subtype = "interactive";
1044 break;
1045 case SSH_AUTH_METHOD_GSSAPI_MIC:
1046 str_subtype = "gssapi-mic";
1047 break;
1048 }
1049 break;
1050
1051 case SSH_REQUEST_CHANNEL_OPEN:
1052 str_type = "request-channel-open";
1053 switch (subtype) {
1054 case SSH_CHANNEL_SESSION:
1055 str_subtype = "session";
1056 break;
1057 case SSH_CHANNEL_DIRECT_TCPIP:
1058 str_subtype = "direct-tcpip";
1059 break;
1060 case SSH_CHANNEL_FORWARDED_TCPIP:
1061 str_subtype = "forwarded-tcpip";
1062 break;
1063 case (int)SSH_CHANNEL_X11:
1064 str_subtype = "channel-x11";
1065 break;
1066 case SSH_CHANNEL_UNKNOWN:
1067 /* fallthrough */
1068 default:
1069 str_subtype = "unknown";
1070 break;
1071 }
1072 break;
1073
1074 case SSH_REQUEST_CHANNEL:
1075 str_type = "request-channel";
1076 switch (subtype) {
1077 case SSH_CHANNEL_REQUEST_PTY:
1078 str_subtype = "pty";
1079 break;
1080 case SSH_CHANNEL_REQUEST_EXEC:
1081 str_subtype = "exec";
1082 break;
1083 case SSH_CHANNEL_REQUEST_SHELL:
1084 str_subtype = "shell";
1085 break;
1086 case SSH_CHANNEL_REQUEST_ENV:
1087 str_subtype = "env";
1088 break;
1089 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1090 str_subtype = "subsystem";
1091 break;
1092 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1093 str_subtype = "window-change";
1094 break;
1095 case SSH_CHANNEL_REQUEST_X11:
1096 str_subtype = "x11";
1097 break;
1098 case SSH_CHANNEL_REQUEST_UNKNOWN:
1099 /* fallthrough */
1100 default:
1101 str_subtype = "unknown";
1102 break;
1103 }
1104 break;
1105
1106 case SSH_REQUEST_SERVICE:
1107 str_type = "request-service";
1108 str_subtype = ssh_message_service_service(msg);
1109 break;
1110
1111 case SSH_REQUEST_GLOBAL:
1112 str_type = "request-global";
1113 switch (subtype) {
1114 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1115 str_subtype = "tcpip-forward";
1116 break;
1117 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1118 str_subtype = "cancel-tcpip-forward";
1119 break;
1120 case SSH_GLOBAL_REQUEST_UNKNOWN:
1121 /* fallthrough */
1122 default:
1123 str_subtype = "unknown";
1124 break;
1125 }
1126 break;
1127
1128 default:
1129 str_type = "unknown";
1130 str_subtype = "unknown";
1131 break;
1132 }
1133
1134 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vasko5e0edd82020-07-29 15:26:13 +02001135 if (!session || (session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
Michal Vaskoce319162016-02-03 15:33:08 +01001136 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1137 * but we got it now, during session free */
1138 VRB("SSH message arrived on a %s session, the request will be denied.",
Michal Vasko5e0edd82020-07-29 15:26:13 +02001139 (session && session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
Michal Vaskoce319162016-02-03 15:33:08 +01001140 ssh_message_reply_default(msg);
1141 return 0;
1142 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001143 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001144
1145 /*
1146 * process known messages
1147 */
1148 if (type == SSH_REQUEST_AUTH) {
1149 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1150 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1151 ssh_message_reply_default(msg);
1152 return 0;
1153 }
1154
Michal Vasko086311b2016-01-08 09:53:11 +01001155 /* save the username, do not let the client change it */
1156 username = ssh_message_auth_user(msg);
1157 if (!session->username) {
1158 if (!username) {
1159 ERR("Denying an auth request without a username.");
1160 return 1;
1161 }
1162
Michal Vasko77367452021-02-16 16:32:18 +01001163 lydict_insert(server_opts.ctx, username, 0, &session->username);
Michal Vasko086311b2016-01-08 09:53:11 +01001164 } else if (username) {
1165 if (strcmp(username, session->username)) {
1166 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1167 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001168 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001169 return 1;
1170 }
1171 }
1172
1173 if (subtype == SSH_AUTH_METHOD_NONE) {
1174 /* libssh will return the supported auth methods */
1175 return 1;
1176 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1177 nc_sshcb_auth_password(session, msg);
1178 return 0;
1179 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1180 nc_sshcb_auth_pubkey(session, msg);
1181 return 0;
1182 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1183 nc_sshcb_auth_kbdint(session, msg);
1184 return 0;
1185 }
1186 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001187 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001188 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001189 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001190 }
Michal Vasko086311b2016-01-08 09:53:11 +01001191 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001192
Michal Vasko0df67562016-01-21 15:50:11 +01001193 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001194 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1195 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001196 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001197 } else {
1198 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001199 }
1200 return 0;
1201 }
1202 }
1203
1204 /* we did not process it */
1205 return 1;
1206}
1207
Michal Vasko1a38c862016-01-15 15:50:07 +01001208/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001209static int
1210nc_open_netconf_channel(struct nc_session *session, int timeout)
1211{
Michal Vasko36c7be82017-02-22 13:37:59 +01001212 int ret;
1213 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001214
1215 /* message callback is executed twice to give chance for the channel to be
1216 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001217 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001218 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001219 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001220 return -1;
1221 }
1222
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001223 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1224 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001225 ERR("Failed to receive SSH messages on a session (%s).",
1226 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001227 return -1;
1228 }
1229
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001230 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001231 return 0;
1232 }
1233
1234 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1235 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001236 ERR("Failed to receive SSH messages on a session (%s).",
1237 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001238 return -1;
1239 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001240
1241 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1242 /* we did not receive subsystem-request, timeout */
1243 return 0;
1244 }
1245
1246 return 1;
1247 }
1248
Michal Vasko36c7be82017-02-22 13:37:59 +01001249 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001250 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001251 nc_addtimespec(&ts_timeout, timeout);
1252 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001253 while (1) {
1254 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001255 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001256 return -1;
1257 }
1258
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001259 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1260 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001261 ERR("Failed to receive SSH messages on a session (%s).",
1262 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001263 return -1;
1264 }
1265
Michal Vasko086311b2016-01-08 09:53:11 +01001266 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001267 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001268 }
1269
Michal Vasko086311b2016-01-08 09:53:11 +01001270 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001271 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001272 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001273 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1274 /* timeout */
1275 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1276 break;
1277 }
1278 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001279 }
Michal Vasko086311b2016-01-08 09:53:11 +01001280
Michal Vasko1a38c862016-01-15 15:50:07 +01001281 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001282}
1283
Michal Vasko4c1fb492017-01-30 14:31:07 +01001284static int
1285nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1286{
1287 uint8_t i;
1288 char *privkey_path, *privkey_data;
Michal Vaskoddce1212019-05-24 09:58:49 +02001289 int ret;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001290 NC_SSH_KEY_TYPE privkey_type;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001291
1292 if (!server_opts.hostkey_clb) {
1293 ERR("Callback for retrieving SSH host keys not set.");
1294 return -1;
1295 }
1296
1297 for (i = 0; i < hostkey_count; ++i) {
1298 privkey_path = privkey_data = NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +02001299 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_type)) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001300 ERR("Host key callback failed.");
1301 return -1;
1302 }
1303
1304 if (privkey_data) {
Michal Vaskoddce1212019-05-24 09:58:49 +02001305 privkey_path = base64der_key_to_tmp_file(privkey_data, nc_keytype2str(privkey_type));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001306 if (!privkey_path) {
1307 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1308 free(privkey_data);
1309 return -1;
1310 }
1311 }
1312
1313 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1314
1315 /* cleanup */
1316 if (privkey_data && unlink(privkey_path)) {
1317 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1318 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001319 free(privkey_data);
1320
1321 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001322 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1323 }
1324 free(privkey_path);
1325
1326 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001327 return -1;
1328 }
1329 }
1330
1331 return 0;
1332}
1333
Michal Vasko96164bf2016-01-21 15:41:58 +01001334int
Michal Vasko0190bc32016-03-02 15:47:49 +01001335nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001336{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001337 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001338 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001339 int libssh_auth_methods = 0, ret;
1340 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001341
Michal Vasko2cc4c682016-03-01 09:16:48 +01001342 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001343
Michal Vasko086311b2016-01-08 09:53:11 +01001344 /* other transport-specific data */
1345 session->ti_type = NC_TI_LIBSSH;
1346 session->ti.libssh.session = ssh_new();
1347 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001348 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001349 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001350 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001351 }
1352
Michal Vaskoc61c4492016-01-25 11:13:34 +01001353 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001354 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1355 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001356 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001357 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1358 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001359 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001360 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1361 }
1362 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1363
Michal Vaskoe2713da2016-08-22 16:06:40 +02001364 sbind = ssh_bind_new();
1365 if (!sbind) {
1366 ERR("Failed to create an SSH bind.");
1367 close(sock);
1368 return -1;
1369 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001370
1371 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1372 close(sock);
1373 ssh_bind_free(sbind);
1374 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001375 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001376
Michal Vasko086311b2016-01-08 09:53:11 +01001377 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001378 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001379 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001380
Michal Vaskoe2713da2016-08-22 16:06:40 +02001381 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1382 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001383 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001384 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001385 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001386 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001387 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001388
Michal Vasko0190bc32016-03-02 15:47:49 +01001389 ssh_set_blocking(session->ti.libssh.session, 0);
1390
Michal Vasko36c7be82017-02-22 13:37:59 +01001391 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001392 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001393 nc_addtimespec(&ts_timeout, timeout);
1394 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001395 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001396 /* this tends to take longer */
1397 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001398 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001399 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001400 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1401 break;
1402 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001403 }
1404 }
1405 if (ret == SSH_AGAIN) {
1406 ERR("SSH key exchange timeout.");
1407 return 0;
1408 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001409 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001410 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001411 }
1412
1413 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001414 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001415 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001416 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1417 }
1418 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001419 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001420 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001421 return -1;
1422 }
1423
Michal Vasko086311b2016-01-08 09:53:11 +01001424 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001425 ERR("Failed to receive SSH messages on a session (%s).",
1426 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001427 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001428 }
1429
Michal Vasko36c7be82017-02-22 13:37:59 +01001430 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1431 break;
1432 }
1433
Michal Vasko145ae672017-02-07 10:57:27 +01001434 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1435 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1436 return -1;
1437 }
1438
Michal Vasko086311b2016-01-08 09:53:11 +01001439 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001440 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001441 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001442 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1443 /* timeout */
1444 break;
1445 }
1446 }
1447 }
Michal Vasko086311b2016-01-08 09:53:11 +01001448
1449 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1450 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001451 if (session->username) {
1452 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1453 } else {
1454 ERR("User failed to authenticate for too long, disconnecting.");
1455 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001456 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001457 }
1458
Michal Vasko086311b2016-01-08 09:53:11 +01001459 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001460 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001461 if (ret < 1) {
1462 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001463 }
1464
Michal Vasko96164bf2016-01-21 15:41:58 +01001465 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001466 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001467}
1468
Michal Vasko71090fc2016-05-24 16:37:28 +02001469API NC_MSG_TYPE
1470nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1471{
1472 NC_MSG_TYPE msgtype;
1473 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001474 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001475
1476 if (!orig_session) {
1477 ERRARG("orig_session");
1478 return NC_MSG_ERROR;
1479 } else if (!session) {
1480 ERRARG("session");
1481 return NC_MSG_ERROR;
1482 }
1483
1484 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1485 && orig_session->ti.libssh.next) {
1486 for (new_session = orig_session->ti.libssh.next;
1487 new_session != orig_session;
1488 new_session = new_session->ti.libssh.next) {
1489 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1490 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1491 /* we found our session */
1492 break;
1493 }
1494 }
1495 if (new_session == orig_session) {
1496 new_session = NULL;
1497 }
1498 }
1499
1500 if (!new_session) {
1501 ERR("Session does not have a NETCONF SSH channel ready.");
1502 return NC_MSG_ERROR;
1503 }
1504
1505 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02001506 new_session->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001507
1508 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001509 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001510 if (msgtype != NC_MSG_HELLO) {
1511 return msgtype;
1512 }
1513
Michal Vasko9f6275e2017-10-05 13:50:05 +02001514 nc_gettimespec_real(&ts_cur);
1515 new_session->opts.server.session_start = ts_cur.tv_sec;
1516 nc_gettimespec_mono(&ts_cur);
1517 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001518 new_session->status = NC_STATUS_RUNNING;
1519 *session = new_session;
1520
1521 return msgtype;
1522}
1523
1524API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001525nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001526{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001527 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001528 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001529 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001530 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001531 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001532
Michal Vasko45e53ae2016-04-07 11:46:03 +02001533 if (!ps) {
1534 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001535 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001536 } else if (!session) {
1537 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001538 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001539 }
1540
Michal Vasko48a63ed2016-03-01 09:48:21 +01001541 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001542 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001543 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001544 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001545
Michal Vasko96164bf2016-01-21 15:41:58 +01001546 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001547 cur_session = ps->sessions[i]->session;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001548 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1549 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001550 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001551 for (new_session = cur_session->ti.libssh.next;
1552 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001553 new_session = new_session->ti.libssh.next) {
1554 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1555 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1556 /* we found our session */
1557 break;
1558 }
1559 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001560 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001561 break;
1562 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001563
Michal Vasko96164bf2016-01-21 15:41:58 +01001564 new_session = NULL;
1565 }
1566 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001567
Michal Vasko48a63ed2016-03-01 09:48:21 +01001568 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001569 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001570
Michal Vasko96164bf2016-01-21 15:41:58 +01001571 if (!new_session) {
1572 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001573 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001574 }
1575
1576 /* assign new SID atomically */
Michal Vasko7f1fa3c2020-09-08 16:30:41 +02001577 new_session->id = ATOMIC_INC(server_opts.new_session_id);
Michal Vaskofb89d772016-01-08 12:25:35 +01001578
Michal Vasko086311b2016-01-08 09:53:11 +01001579 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001580 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001581 if (msgtype != NC_MSG_HELLO) {
1582 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001583 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001584
Michal Vasko9f6275e2017-10-05 13:50:05 +02001585 nc_gettimespec_real(&ts_cur);
1586 new_session->opts.server.session_start = ts_cur.tv_sec;
1587 nc_gettimespec_mono(&ts_cur);
1588 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001589 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001590 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001591
Michal Vasko71090fc2016-05-24 16:37:28 +02001592 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001593}