blob: 412fbac0103141cbab4831271c8f0cc2ef50d44e [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
17#include <stdlib.h>
18#include <string.h>
19#include <sys/types.h>
Michal Vasko27252692017-03-21 15:34:13 +010020#include <sys/stat.h>
Michal Vasko086311b2016-01-08 09:53:11 +010021#include <pwd.h>
Claus Klein22091912020-01-20 13:45:47 +010022#ifndef __APPLE__
Michal Vasko086311b2016-01-08 09:53:11 +010023#include <shadow.h>
24#include <crypt.h>
Claus Klein22091912020-01-20 13:45:47 +010025#endif
Michal Vasko086311b2016-01-08 09:53:11 +010026#include <errno.h>
Michal Vasko9f6275e2017-10-05 13:50:05 +020027#include <time.h>
Claus Klein22091912020-01-20 13:45:47 +010028#include <fcntl.h>
29#include <unistd.h>
Michal Vasko086311b2016-01-08 09:53:11 +010030
Michal Vasko857425d2019-12-10 09:55:03 +010031#include "config.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010032#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010033#include "session_server_ch.h"
34#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010035
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +020036#if !defined(HAVE_CRYPT_R)
37pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER;
38#endif
39
Michal Vasko086311b2016-01-08 09:53:11 +010040extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010041
Michal Vasko4c1fb492017-01-30 14:31:07 +010042static char *
Michal Vaskoddce1212019-05-24 09:58:49 +020043base64der_key_to_tmp_file(const char *in, const char *key_str)
Michal Vasko086311b2016-01-08 09:53:11 +010044{
Michal Vasko4c1fb492017-01-30 14:31:07 +010045 char path[12] = "/tmp/XXXXXX";
46 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010047 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010048 FILE *file;
49
50 if (in == NULL) {
51 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010052 }
53
mekleob31878b2019-09-09 14:10:47 +020054 umode = umask(0177);
Michal Vasko4c1fb492017-01-30 14:31:07 +010055 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010056 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010057 if (fd == -1) {
58 return NULL;
59 }
60
Michal Vasko3964a832018-09-18 14:37:39 +020061 file = fdopen(fd, "w");
Michal Vasko4c1fb492017-01-30 14:31:07 +010062 if (!file) {
63 close(fd);
64 return NULL;
65 }
66
67 /* write the key into the file */
68 written = fwrite("-----BEGIN ", 1, 11, file);
Michal Vaskoddce1212019-05-24 09:58:49 +020069 written += fwrite(key_str, 1, strlen(key_str), file);
Michal Vasko4c1fb492017-01-30 14:31:07 +010070 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
71 written += fwrite(in, 1, strlen(in), file);
72 written += fwrite("\n-----END ", 1, 10, file);
Michal Vaskoddce1212019-05-24 09:58:49 +020073 written += fwrite(key_str, 1, strlen(key_str), file);
Michal Vasko4c1fb492017-01-30 14:31:07 +010074 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
75
76 fclose(file);
77 if ((unsigned)written != 62 + strlen(in)) {
78 unlink(path);
79 return NULL;
80 }
81
82 return strdup(path);
83}
84
85static int
Michal Vasko7d255882017-02-09 13:35:08 +010086nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +010087{
Michal Vaskofbfe8b62017-02-14 10:22:30 +010088 uint8_t i;
89
Michal Vasko4c1fb492017-01-30 14:31:07 +010090 if (!name) {
91 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +010092 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +010093 } else if (idx > opts->hostkey_count) {
94 ERRARG("idx");
95 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010096 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010097
Michal Vaskofbfe8b62017-02-14 10:22:30 +010098 for (i = 0; i < opts->hostkey_count; ++i) {
99 if (!strcmp(opts->hostkeys[i], name)) {
100 ERRARG("name");
101 return -1;
102 }
103 }
104
Michal Vaskoe2713da2016-08-22 16:06:40 +0200105 ++opts->hostkey_count;
106 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
107 if (!opts->hostkeys) {
108 ERRMEM;
109 return -1;
110 }
Michal Vasko7d255882017-02-09 13:35:08 +0100111
112 if (idx < 0) {
113 idx = opts->hostkey_count - 1;
114 }
115 if (idx != opts->hostkey_count - 1) {
116 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
117 }
118 opts->hostkeys[idx] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200119
Michal Vasko5fcc7142016-02-02 12:21:10 +0100120 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100121}
122
123API int
Michal Vasko7d255882017-02-09 13:35:08 +0100124nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100125{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100126 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100127 struct nc_endpt *endpt;
128
Michal Vasko51e514d2016-02-02 15:51:52 +0100129 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100130 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100131 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100132 return -1;
133 }
Michal Vasko7d255882017-02-09 13:35:08 +0100134 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100135 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100136 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100137
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100138 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100139}
140
Michal Vasko974410a2018-04-03 09:36:57 +0200141API void
142nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data),
143 void *user_data, void (*free_user_data)(void *user_data))
144{
145 server_opts.passwd_auth_clb = passwd_auth_clb;
146 server_opts.passwd_auth_data = user_data;
147 server_opts.passwd_auth_data_free = free_user_data;
148}
149
bhart1bb7cdb2018-07-02 15:03:30 -0500150API void
151nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data),
152 void *user_data, void (*free_user_data)(void *user_data))
153{
154 server_opts.interactive_auth_clb = interactive_auth_clb;
155 server_opts.interactive_auth_data = user_data;
156 server_opts.interactive_auth_data_free = free_user_data;
157}
Michal Vasko733c0bd2018-07-03 13:14:40 +0200158
bhart1bb7cdb2018-07-02 15:03:30 -0500159API void
160nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data),
161 void *user_data, void (*free_user_data)(void *user_data))
162{
163 server_opts.pubkey_auth_clb = pubkey_auth_clb;
164 server_opts.pubkey_auth_data = user_data;
165 server_opts.pubkey_auth_data_free = free_user_data;
166}
167
Michal Vaskob05053d2016-01-22 16:12:06 +0100168API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200169nc_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 +0100170{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100171 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200172 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200173 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100174
Michal Vasko2e6defd2016-10-07 15:48:15 +0200175 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200176 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
177 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200178 return -1;
179 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200180 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200181 /* UNLOCK */
182 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200183
184 return ret;
185}
186
Michal Vasko4c1fb492017-01-30 14:31:07 +0100187API void
188nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200189 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 +0100190{
191 if (!hostkey_clb) {
192 ERRARG("hostkey_clb");
193 return;
194 }
195
196 server_opts.hostkey_clb = hostkey_clb;
197 server_opts.hostkey_data = user_data;
198 server_opts.hostkey_data_free = free_user_data;
199}
200
Michal Vaskoe2713da2016-08-22 16:06:40 +0200201static int
Michal Vasko7d255882017-02-09 13:35:08 +0100202nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200203{
204 uint8_t i;
205
Michal Vasko7d255882017-02-09 13:35:08 +0100206 if (name && (idx > -1)) {
207 ERRARG("name and idx");
208 return -1;
209 } else if (idx >= opts->hostkey_count) {
210 ERRARG("idx");
211 }
212
213 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200214 for (i = 0; i < opts->hostkey_count; ++i) {
215 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
216 }
217 free(opts->hostkeys);
218 opts->hostkeys = NULL;
219 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100220 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200221 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100222 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100223 idx = i;
224 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200225 }
226 }
227
Michal Vasko7d255882017-02-09 13:35:08 +0100228 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200229 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100230 } else {
231remove_idx:
232 --opts->hostkey_count;
233 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
234 if (idx < opts->hostkey_count - 1) {
235 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
236 }
237 if (!opts->hostkey_count) {
238 free(opts->hostkeys);
239 opts->hostkeys = NULL;
240 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200241 }
242
243 return 0;
244}
245
246API int
Michal Vasko7d255882017-02-09 13:35:08 +0100247nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200248{
249 int ret;
250 struct nc_endpt *endpt;
251
252 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100253 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200254 if (!endpt) {
255 return -1;
256 }
Michal Vasko7d255882017-02-09 13:35:08 +0100257 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200258 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100259 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200260
261 return ret;
262}
263
264API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200265nc_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 +0200266{
267 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200268 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200269 struct nc_ch_endpt *endpt;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200270
Michal Vasko2e6defd2016-10-07 15:48:15 +0200271 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200272 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
273 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200274 return -1;
275 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200276 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200277 /* UNLOCK */
278 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100279
280 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100281}
282
283static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100284nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
285{
286 uint8_t i;
287 int16_t mov_idx = -1, after_idx = -1;
288 const char *bckup;
289
290 if (!key_mov) {
291 ERRARG("key_mov");
292 return -1;
293 }
294
295 for (i = 0; i < opts->hostkey_count; ++i) {
296 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
297 after_idx = i;
298 }
299 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
300 mov_idx = i;
301 }
302
303 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
304 break;
305 }
306 }
307
308 if (key_after && (after_idx == -1)) {
309 ERRARG("key_after");
310 return -1;
311 }
312 if (mov_idx == -1) {
313 ERRARG("key_mov");
314 return -1;
315 }
316 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
317 /* nothing to do */
318 return 0;
319 }
320
321 /* finally move the key */
322 bckup = opts->hostkeys[mov_idx];
323 if (mov_idx > after_idx) {
324 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
325 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
326 opts->hostkeys[after_idx + 1] = bckup;
327 } else {
328 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
329 opts->hostkeys[after_idx] = bckup;
330 }
331
332 return 0;
333}
334
335API int
336nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
337{
338 int ret;
339 struct nc_endpt *endpt;
340
341 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100342 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100343 if (!endpt) {
344 return -1;
345 }
346 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
347 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100348 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100349
350 return ret;
351}
352
353API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200354nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
355 const char *key_after)
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100356{
357 int ret;
358 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200359 struct nc_ch_endpt *endpt;
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100360
361 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200362 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100363 if (!endpt) {
364 return -1;
365 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200366 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100367 /* UNLOCK */
368 nc_server_ch_client_unlock(client);
369
370 return ret;
371}
372
373static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100374nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100375{
Michal Vaskob05053d2016-01-22 16:12:06 +0100376 opts->auth_methods = auth_methods;
377 return 0;
378}
379
380API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100381nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100382{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100383 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100384 struct nc_endpt *endpt;
385
Michal Vasko51e514d2016-02-02 15:51:52 +0100386 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100387 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100388 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100389 return -1;
390 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200391 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100392 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100393 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100394
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100395 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100396}
397
398API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200399nc_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 +0100400{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100401 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200402 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200403 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100404
Michal Vasko2e6defd2016-10-07 15:48:15 +0200405 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200406 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
407 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200408 return -1;
409 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200410 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200411 /* UNLOCK */
412 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100413
414 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100415}
416
Michal Vaskoddce1212019-05-24 09:58:49 +0200417API int
418nc_server_ssh_endpt_get_auth_methods(const char *endpt_name)
419{
420 int ret;
421 struct nc_endpt *endpt;
422
423 /* LOCK */
424 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
425 if (!endpt) {
426 return -1;
427 }
428 ret = endpt->opts.ssh->auth_methods;
429 /* UNLOCK */
430 pthread_rwlock_unlock(&server_opts.endpt_lock);
431
432 return ret;
433}
434
435API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200436nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name)
Michal Vaskoddce1212019-05-24 09:58:49 +0200437{
438 int ret;
439 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200440 struct nc_ch_endpt *endpt;
Michal Vaskoddce1212019-05-24 09:58:49 +0200441
442 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200443 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
444 if (!endpt) {
Michal Vaskoddce1212019-05-24 09:58:49 +0200445 return -1;
446 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200447 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoddce1212019-05-24 09:58:49 +0200448 /* UNLOCK */
449 nc_server_ch_client_unlock(client);
450
451 return ret;
452}
453
Michal Vaskob05053d2016-01-22 16:12:06 +0100454static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100455nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100456{
Michal Vaskob05053d2016-01-22 16:12:06 +0100457 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200458 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100459 return -1;
460 }
461
Michal Vaskob05053d2016-01-22 16:12:06 +0100462 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100463 return 0;
464}
465
466API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100467nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100468{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100469 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100470 struct nc_endpt *endpt;
471
Michal Vasko51e514d2016-02-02 15:51:52 +0100472 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100473 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100474 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100475 return -1;
476 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200477 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100478 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100479 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100480
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100481 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100482}
483
484API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200485nc_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 +0100486{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100487 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200488 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200489 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100490
Michal Vasko2e6defd2016-10-07 15:48:15 +0200491 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200492 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
493 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200494 return -1;
495 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200496 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200497 /* UNLOCK */
498 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100499
500 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100501}
502
503static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100504nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100505{
Michal Vaskob05053d2016-01-22 16:12:06 +0100506 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200507 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100508 return -1;
509 }
510
Michal Vaskob05053d2016-01-22 16:12:06 +0100511 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100512 return 0;
513}
514
515API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100516nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100517{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100518 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100519 struct nc_endpt *endpt;
520
Michal Vasko51e514d2016-02-02 15:51:52 +0100521 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100522 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100523 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100524 return -1;
525 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200526 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100527 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100528 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100529
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100530 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100531}
532
533API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200534nc_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 +0100535{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100536 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200537 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200538 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100539
Michal Vasko2e6defd2016-10-07 15:48:15 +0200540 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200541 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
542 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200543 return -1;
544 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200545 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200546 /* UNLOCK */
547 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100548
549 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100550}
551
552static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100553_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
554 const char *username)
555{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100556 /* LOCK */
557 pthread_mutex_lock(&server_opts.authkey_lock);
558
Michal Vasko17dfda92016-12-01 14:06:16 +0100559 ++server_opts.authkey_count;
560 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
561 if (!server_opts.authkeys) {
562 ERRMEM;
563 return -1;
564 }
565 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
566 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
567 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
568 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
569
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100570 /* UNLOCK */
571 pthread_mutex_unlock(&server_opts.authkey_lock);
572
Michal Vasko17dfda92016-12-01 14:06:16 +0100573 return 0;
574}
575
576API int
577nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100578{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200579 if (!pubkey_path) {
580 ERRARG("pubkey_path");
581 return -1;
582 } else if (!username) {
583 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100584 return -1;
585 }
586
Michal Vasko17dfda92016-12-01 14:06:16 +0100587 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100588}
589
590API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100591nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100592{
Michal Vasko17dfda92016-12-01 14:06:16 +0100593 if (!pubkey_base64) {
594 ERRARG("pubkey_base64");
595 return -1;
596 } else if (!type) {
597 ERRARG("type");
598 return -1;
599 } else if (!username) {
600 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100601 return -1;
602 }
603
Michal Vasko17dfda92016-12-01 14:06:16 +0100604 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100605}
606
607API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100608nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
609 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100610{
Michal Vasko086311b2016-01-08 09:53:11 +0100611 uint32_t i;
612 int ret = -1;
613
Michal Vasko17dfda92016-12-01 14:06:16 +0100614 /* LOCK */
615 pthread_mutex_lock(&server_opts.authkey_lock);
616
617 if (!pubkey_path && !pubkey_base64 && !type && !username) {
618 for (i = 0; i < server_opts.authkey_count; ++i) {
619 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
620 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
621 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100622
Michal Vasko086311b2016-01-08 09:53:11 +0100623 ret = 0;
624 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100625 free(server_opts.authkeys);
626 server_opts.authkeys = NULL;
627 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100628 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100629 for (i = 0; i < server_opts.authkey_count; ++i) {
630 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
Michal Vaskoe846aee2019-03-28 08:38:41 +0100631 && (!pubkey_base64 || !strcmp(server_opts.authkeys[i].base64, pubkey_base64))
Michal Vasko17dfda92016-12-01 14:06:16 +0100632 && (!type || (server_opts.authkeys[i].type == type))
633 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
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 Vasko1a38c862016-01-15 15:50:07 +0100637
Michal Vasko17dfda92016-12-01 14:06:16 +0100638 --server_opts.authkey_count;
639 if (i < server_opts.authkey_count) {
640 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
641 sizeof *server_opts.authkeys);
642 } else if (!server_opts.authkey_count) {
643 free(server_opts.authkeys);
644 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100645 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100646
647 ret = 0;
648 }
649 }
Michal Vasko086311b2016-01-08 09:53:11 +0100650 }
651
Michal Vasko51e514d2016-02-02 15:51:52 +0100652 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100653 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100654
655 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100656}
657
658void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100659nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100660{
Michal Vasko7d255882017-02-09 13:35:08 +0100661 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100662}
663
Michal Vasko086311b2016-01-08 09:53:11 +0100664static char *
665auth_password_get_pwd_hash(const char *username)
666{
Claus Klein22091912020-01-20 13:45:47 +0100667#ifndef __APPLE__
Michal Vasko086311b2016-01-08 09:53:11 +0100668 struct passwd *pwd, pwd_buf;
669 struct spwd *spwd, spwd_buf;
670 char *pass_hash = NULL, buf[256];
671
672 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
673 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100674 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100675 return NULL;
676 }
677
678 if (!strcmp(pwd->pw_passwd, "x")) {
679 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
680 if (!spwd) {
681 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
682 return NULL;
683 }
684
685 pass_hash = spwd->sp_pwdp;
686 } else {
687 pass_hash = pwd->pw_passwd;
688 }
689
690 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100691 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100692 return NULL;
693 }
694
695 /* check the hash structure for special meaning */
696 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
697 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
698 return NULL;
699 }
700 if (!strcmp(pass_hash, "*NP*")) {
701 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
702 return NULL;
703 }
704
705 return strdup(pass_hash);
Claus Klein22091912020-01-20 13:45:47 +0100706#else
707 return strdup("");
708#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100709}
710
711static int
712auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
713{
714 char *new_pass_hash;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200715#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100716 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200717#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100718
719 if (!pass_hash[0]) {
720 if (!pass_clear[0]) {
721 WRN("User authentication successful with an empty password!");
722 return 0;
723 } else {
724 /* the user did now know he does not need any password,
725 * (which should not be used) so deny authentication */
726 return 1;
727 }
728 }
729
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200730#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100731 cdata.initialized = 0;
732 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200733#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200734 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200735 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200736 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200737#endif
Andrew Langefeld158d6fd2018-06-11 18:51:44 -0500738
739 if (!new_pass_hash) {
740 return 1;
741 }
742
Michal Vasko086311b2016-01-08 09:53:11 +0100743 return strcmp(new_pass_hash, pass_hash);
744}
745
746static void
747nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
748{
749 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100750 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100751
Michal Vaskoebba7602018-03-23 13:14:08 +0100752 if (server_opts.passwd_auth_clb) {
753 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
754 } else {
755 pass_hash = auth_password_get_pwd_hash(session->username);
756 if (pass_hash) {
757 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
758 free(pass_hash);
759 }
Michal Vasko086311b2016-01-08 09:53:11 +0100760 }
761
Michal Vaskoebba7602018-03-23 13:14:08 +0100762 if (!auth_ret) {
763 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
764 VRB("User \"%s\" authenticated.", session->username);
765 ssh_message_auth_reply_success(msg, 0);
766 } else {
767 ++session->opts.server.ssh_auth_attempts;
768 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
769 ssh_message_reply_default(msg);
770 }
Michal Vasko086311b2016-01-08 09:53:11 +0100771}
772
773static void
774nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
775{
bhart3bc2f582018-06-05 12:40:32 -0500776 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100777 char *pass_hash;
778
bhart1bb7cdb2018-07-02 15:03:30 -0500779 if (server_opts.interactive_auth_clb) {
Michal Vasko733c0bd2018-07-03 13:14:40 +0200780 auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100781 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500782 if (!ssh_message_auth_kbdint_is_response(msg)) {
783 const char *prompts[] = {"Password: "};
784 char echo[] = {0};
785
786 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
Robert Vargaad7a5532018-08-10 20:40:54 +0200787 auth_ret = -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100788 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500789 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {// failed session
790 ssh_message_reply_default(msg);
791 return;
792 }
bhart3bc2f582018-06-05 12:40:32 -0500793 pass_hash = auth_password_get_pwd_hash(session->username);// get hashed password
794 if (pass_hash) {
Robert Vargaad7a5532018-08-10 20:40:54 +0200795 /* Normalize auth_password_compare_pwd result to 0 or 1 */
796 auth_ret = !!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0));
bhart3bc2f582018-06-05 12:40:32 -0500797 free(pass_hash);// free hashed password
798 }
Michal Vasko086311b2016-01-08 09:53:11 +0100799 }
bhart1bb7cdb2018-07-02 15:03:30 -0500800 }
801
Robert Vargaad7a5532018-08-10 20:40:54 +0200802 /* We have already sent a reply */
803 if (auth_ret == -1) {
804 return;
805 }
806
bhart1bb7cdb2018-07-02 15:03:30 -0500807 /* Authenticate message based on outcome */
808 if (!auth_ret) {
809 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
810 VRB("User \"%s\" authenticated.", session->username);
811 ssh_message_auth_reply_success(msg, 0);
812 } else {
813 ++session->opts.server.ssh_auth_attempts;
814 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
815 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100816 }
817}
818
819static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100820auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100821{
822 uint32_t i;
823 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100824 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100825 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100826
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100827 /* LOCK */
828 pthread_mutex_lock(&server_opts.authkey_lock);
829
Michal Vasko17dfda92016-12-01 14:06:16 +0100830 for (i = 0; i < server_opts.authkey_count; ++i) {
831 switch (server_opts.authkeys[i].type) {
832 case NC_SSH_KEY_UNKNOWN:
833 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
834 break;
835 case NC_SSH_KEY_DSA:
836 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
837 break;
838 case NC_SSH_KEY_RSA:
839 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
840 break;
841 case NC_SSH_KEY_ECDSA:
842 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
843 break;
844 }
845
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200846 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100847 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200848 continue;
849 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100850 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100851 continue;
852 }
853
854 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
855 ssh_key_free(pub_key);
856 break;
857 }
858
859 ssh_key_free(pub_key);
860 }
861
Michal Vasko17dfda92016-12-01 14:06:16 +0100862 if (i < server_opts.authkey_count) {
863 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100864 }
865
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100866 /* UNLOCK */
867 pthread_mutex_unlock(&server_opts.authkey_lock);
868
Michal Vasko086311b2016-01-08 09:53:11 +0100869 return username;
870}
871
872static void
873nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
874{
875 const char *username;
876 int signature_state;
877
Michal Vasko733c0bd2018-07-03 13:14:40 +0200878 if (server_opts.pubkey_auth_clb) {
879 if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) {
bhart3bc2f582018-06-05 12:40:32 -0500880 goto fail;
881 }
Michal Vasko733c0bd2018-07-03 13:14:40 +0200882 } else {
bhart3bc2f582018-06-05 12:40:32 -0500883 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
884 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
885 goto fail;
886 } else if (strcmp(session->username, username)) {
887 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
888 goto fail;
889 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200890 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200891
Michal Vasko086311b2016-01-08 09:53:11 +0100892 signature_state = ssh_message_auth_publickey_state(msg);
893 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
894 VRB("User \"%s\" authenticated.", session->username);
895 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
896 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100897 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200898 /* accepting only the use of a public key */
899 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100900 }
901
Michal Vaskobd13a932016-09-14 09:00:35 +0200902 return;
903
904fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200905 ++session->opts.server.ssh_auth_attempts;
906 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100907 ssh_message_reply_default(msg);
908}
909
910static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100911nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100912{
Michal Vasko96164bf2016-01-21 15:41:58 +0100913 ssh_channel chan;
914
915 /* first channel request */
916 if (!session->ti.libssh.channel) {
917 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100918 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100919 return -1;
920 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100921 chan = ssh_message_channel_request_open_reply_accept(msg);
922 if (!chan) {
923 ERR("Failed to create a new SSH channel.");
924 return -1;
925 }
926 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100927
Michal Vasko96164bf2016-01-21 15:41:58 +0100928 /* additional channel request */
929 } else {
930 chan = ssh_message_channel_request_open_reply_accept(msg);
931 if (!chan) {
932 ERR("Session %u: failed to create a new SSH channel.", session->id);
933 return -1;
934 }
935 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100936 }
937
Michal Vasko086311b2016-01-08 09:53:11 +0100938 return 0;
939}
940
941static int
942nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
943{
Michal Vasko96164bf2016-01-21 15:41:58 +0100944 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100945
Michal Vasko96164bf2016-01-21 15:41:58 +0100946 if (strcmp(subsystem, "netconf")) {
947 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100948 return -1;
949 }
950
Michal Vasko96164bf2016-01-21 15:41:58 +0100951 if (session->ti.libssh.channel == channel) {
952 /* first channel requested */
953 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
954 ERRINT;
955 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100956 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100957 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
958 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
959 return -1;
960 }
961
962 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100963 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100964 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +0200965 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100966 if (!new_session) {
967 ERRMEM;
968 return -1;
969 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100970
971 /* insert the new session */
972 if (!session->ti.libssh.next) {
973 new_session->ti.libssh.next = session;
974 } else {
975 new_session->ti.libssh.next = session->ti.libssh.next;
976 }
977 session->ti.libssh.next = new_session;
978
979 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +0100980 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +0200981 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +0100982 new_session->ti.libssh.channel = channel;
983 new_session->ti.libssh.session = session->ti.libssh.session;
984 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
985 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
986 new_session->port = session->port;
987 new_session->ctx = server_opts.ctx;
Michal Vasko83d15322018-09-27 09:44:02 +0200988 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
Michal Vasko086311b2016-01-08 09:53:11 +0100989 }
990
991 return 0;
992}
993
Michal Vasko96164bf2016-01-21 15:41:58 +0100994int
Michal Vaskob48aa812016-01-18 14:13:09 +0100995nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100996{
997 const char *str_type, *str_subtype = NULL, *username;
998 int subtype, type;
999 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001000
1001 type = ssh_message_type(msg);
1002 subtype = ssh_message_subtype(msg);
1003
1004 switch (type) {
1005 case SSH_REQUEST_AUTH:
1006 str_type = "request-auth";
1007 switch (subtype) {
1008 case SSH_AUTH_METHOD_NONE:
1009 str_subtype = "none";
1010 break;
1011 case SSH_AUTH_METHOD_PASSWORD:
1012 str_subtype = "password";
1013 break;
1014 case SSH_AUTH_METHOD_PUBLICKEY:
1015 str_subtype = "publickey";
1016 break;
1017 case SSH_AUTH_METHOD_HOSTBASED:
1018 str_subtype = "hostbased";
1019 break;
1020 case SSH_AUTH_METHOD_INTERACTIVE:
1021 str_subtype = "interactive";
1022 break;
1023 case SSH_AUTH_METHOD_GSSAPI_MIC:
1024 str_subtype = "gssapi-mic";
1025 break;
1026 }
1027 break;
1028
1029 case SSH_REQUEST_CHANNEL_OPEN:
1030 str_type = "request-channel-open";
1031 switch (subtype) {
1032 case SSH_CHANNEL_SESSION:
1033 str_subtype = "session";
1034 break;
1035 case SSH_CHANNEL_DIRECT_TCPIP:
1036 str_subtype = "direct-tcpip";
1037 break;
1038 case SSH_CHANNEL_FORWARDED_TCPIP:
1039 str_subtype = "forwarded-tcpip";
1040 break;
1041 case (int)SSH_CHANNEL_X11:
1042 str_subtype = "channel-x11";
1043 break;
1044 case SSH_CHANNEL_UNKNOWN:
1045 /* fallthrough */
1046 default:
1047 str_subtype = "unknown";
1048 break;
1049 }
1050 break;
1051
1052 case SSH_REQUEST_CHANNEL:
1053 str_type = "request-channel";
1054 switch (subtype) {
1055 case SSH_CHANNEL_REQUEST_PTY:
1056 str_subtype = "pty";
1057 break;
1058 case SSH_CHANNEL_REQUEST_EXEC:
1059 str_subtype = "exec";
1060 break;
1061 case SSH_CHANNEL_REQUEST_SHELL:
1062 str_subtype = "shell";
1063 break;
1064 case SSH_CHANNEL_REQUEST_ENV:
1065 str_subtype = "env";
1066 break;
1067 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1068 str_subtype = "subsystem";
1069 break;
1070 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1071 str_subtype = "window-change";
1072 break;
1073 case SSH_CHANNEL_REQUEST_X11:
1074 str_subtype = "x11";
1075 break;
1076 case SSH_CHANNEL_REQUEST_UNKNOWN:
1077 /* fallthrough */
1078 default:
1079 str_subtype = "unknown";
1080 break;
1081 }
1082 break;
1083
1084 case SSH_REQUEST_SERVICE:
1085 str_type = "request-service";
1086 str_subtype = ssh_message_service_service(msg);
1087 break;
1088
1089 case SSH_REQUEST_GLOBAL:
1090 str_type = "request-global";
1091 switch (subtype) {
1092 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1093 str_subtype = "tcpip-forward";
1094 break;
1095 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1096 str_subtype = "cancel-tcpip-forward";
1097 break;
1098 case SSH_GLOBAL_REQUEST_UNKNOWN:
1099 /* fallthrough */
1100 default:
1101 str_subtype = "unknown";
1102 break;
1103 }
1104 break;
1105
1106 default:
1107 str_type = "unknown";
1108 str_subtype = "unknown";
1109 break;
1110 }
1111
1112 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001113 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1114 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1115 * but we got it now, during session free */
1116 VRB("SSH message arrived on a %s session, the request will be denied.",
1117 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1118 ssh_message_reply_default(msg);
1119 return 0;
1120 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001121 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001122
1123 /*
1124 * process known messages
1125 */
1126 if (type == SSH_REQUEST_AUTH) {
1127 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1128 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1129 ssh_message_reply_default(msg);
1130 return 0;
1131 }
1132
Michal Vasko086311b2016-01-08 09:53:11 +01001133 /* save the username, do not let the client change it */
1134 username = ssh_message_auth_user(msg);
1135 if (!session->username) {
1136 if (!username) {
1137 ERR("Denying an auth request without a username.");
1138 return 1;
1139 }
1140
Michal Vasko05ba9df2016-01-13 14:40:27 +01001141 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001142 } else if (username) {
1143 if (strcmp(username, session->username)) {
1144 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1145 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001146 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001147 return 1;
1148 }
1149 }
1150
1151 if (subtype == SSH_AUTH_METHOD_NONE) {
1152 /* libssh will return the supported auth methods */
1153 return 1;
1154 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1155 nc_sshcb_auth_password(session, msg);
1156 return 0;
1157 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1158 nc_sshcb_auth_pubkey(session, msg);
1159 return 0;
1160 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1161 nc_sshcb_auth_kbdint(session, msg);
1162 return 0;
1163 }
1164 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001165 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001166 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001167 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001168 }
Michal Vasko086311b2016-01-08 09:53:11 +01001169 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001170
Michal Vasko0df67562016-01-21 15:50:11 +01001171 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001172 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1173 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001174 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001175 } else {
1176 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001177 }
1178 return 0;
1179 }
1180 }
1181
1182 /* we did not process it */
1183 return 1;
1184}
1185
Michal Vasko1a38c862016-01-15 15:50:07 +01001186/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001187static int
1188nc_open_netconf_channel(struct nc_session *session, int timeout)
1189{
Michal Vasko36c7be82017-02-22 13:37:59 +01001190 int ret;
1191 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001192
1193 /* message callback is executed twice to give chance for the channel to be
1194 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001195 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001196 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001197 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001198 return -1;
1199 }
1200
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001201 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1202 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001203 ERR("Failed to receive SSH messages on a session (%s).",
1204 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001205 return -1;
1206 }
1207
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001208 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001209 return 0;
1210 }
1211
1212 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1213 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001214 ERR("Failed to receive SSH messages on a session (%s).",
1215 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001216 return -1;
1217 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001218
1219 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1220 /* we did not receive subsystem-request, timeout */
1221 return 0;
1222 }
1223
1224 return 1;
1225 }
1226
Michal Vasko36c7be82017-02-22 13:37:59 +01001227 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001228 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001229 nc_addtimespec(&ts_timeout, timeout);
1230 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001231 while (1) {
1232 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001233 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001234 return -1;
1235 }
1236
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001237 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1238 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001239 ERR("Failed to receive SSH messages on a session (%s).",
1240 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001241 return -1;
1242 }
1243
Michal Vasko086311b2016-01-08 09:53:11 +01001244 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001245 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001246 }
1247
Michal Vasko086311b2016-01-08 09:53:11 +01001248 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001249 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001250 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001251 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1252 /* timeout */
1253 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1254 break;
1255 }
1256 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001257 }
Michal Vasko086311b2016-01-08 09:53:11 +01001258
Michal Vasko1a38c862016-01-15 15:50:07 +01001259 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001260}
1261
Michal Vasko4c1fb492017-01-30 14:31:07 +01001262static int
1263nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1264{
1265 uint8_t i;
1266 char *privkey_path, *privkey_data;
Michal Vaskoddce1212019-05-24 09:58:49 +02001267 int ret;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001268 NC_SSH_KEY_TYPE privkey_type;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001269
1270 if (!server_opts.hostkey_clb) {
1271 ERR("Callback for retrieving SSH host keys not set.");
1272 return -1;
1273 }
1274
1275 for (i = 0; i < hostkey_count; ++i) {
1276 privkey_path = privkey_data = NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +02001277 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_type)) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001278 ERR("Host key callback failed.");
1279 return -1;
1280 }
1281
1282 if (privkey_data) {
Michal Vaskoddce1212019-05-24 09:58:49 +02001283 privkey_path = base64der_key_to_tmp_file(privkey_data, nc_keytype2str(privkey_type));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001284 if (!privkey_path) {
1285 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1286 free(privkey_data);
1287 return -1;
1288 }
1289 }
1290
1291 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1292
1293 /* cleanup */
1294 if (privkey_data && unlink(privkey_path)) {
1295 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1296 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001297 free(privkey_data);
1298
1299 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001300 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1301 }
1302 free(privkey_path);
1303
1304 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001305 return -1;
1306 }
1307 }
1308
1309 return 0;
1310}
1311
Michal Vasko96164bf2016-01-21 15:41:58 +01001312int
Michal Vasko0190bc32016-03-02 15:47:49 +01001313nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001314{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001315 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001316 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001317 int libssh_auth_methods = 0, ret;
1318 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001319
Michal Vasko2cc4c682016-03-01 09:16:48 +01001320 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001321
Michal Vasko086311b2016-01-08 09:53:11 +01001322 /* other transport-specific data */
1323 session->ti_type = NC_TI_LIBSSH;
1324 session->ti.libssh.session = ssh_new();
1325 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001326 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001327 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001328 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001329 }
1330
Michal Vaskoc61c4492016-01-25 11:13:34 +01001331 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001332 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1333 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001334 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001335 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1336 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001337 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001338 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1339 }
1340 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1341
Michal Vaskoe2713da2016-08-22 16:06:40 +02001342 sbind = ssh_bind_new();
1343 if (!sbind) {
1344 ERR("Failed to create an SSH bind.");
1345 close(sock);
1346 return -1;
1347 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001348
1349 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1350 close(sock);
1351 ssh_bind_free(sbind);
1352 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001353 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001354
Michal Vasko086311b2016-01-08 09:53:11 +01001355 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001356 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001357 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001358
Michal Vaskoe2713da2016-08-22 16:06:40 +02001359 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1360 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001361 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001362 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001363 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001364 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001365 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001366
Michal Vasko0190bc32016-03-02 15:47:49 +01001367 ssh_set_blocking(session->ti.libssh.session, 0);
1368
Michal Vasko36c7be82017-02-22 13:37:59 +01001369 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001370 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001371 nc_addtimespec(&ts_timeout, timeout);
1372 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001373 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001374 /* this tends to take longer */
1375 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001376 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001377 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001378 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1379 break;
1380 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001381 }
1382 }
1383 if (ret == SSH_AGAIN) {
1384 ERR("SSH key exchange timeout.");
1385 return 0;
1386 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001387 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001388 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001389 }
1390
1391 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001392 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001393 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001394 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1395 }
1396 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001397 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001398 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001399 return -1;
1400 }
1401
Michal Vasko086311b2016-01-08 09:53:11 +01001402 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001403 ERR("Failed to receive SSH messages on a session (%s).",
1404 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001405 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001406 }
1407
Michal Vasko36c7be82017-02-22 13:37:59 +01001408 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1409 break;
1410 }
1411
Michal Vasko145ae672017-02-07 10:57:27 +01001412 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1413 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1414 return -1;
1415 }
1416
Michal Vasko086311b2016-01-08 09:53:11 +01001417 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001418 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001419 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001420 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1421 /* timeout */
1422 break;
1423 }
1424 }
1425 }
Michal Vasko086311b2016-01-08 09:53:11 +01001426
1427 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1428 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001429 if (session->username) {
1430 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1431 } else {
1432 ERR("User failed to authenticate for too long, disconnecting.");
1433 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001434 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001435 }
1436
Michal Vasko086311b2016-01-08 09:53:11 +01001437 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001438 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001439 if (ret < 1) {
1440 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001441 }
1442
Michal Vasko96164bf2016-01-21 15:41:58 +01001443 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001444 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001445}
1446
Michal Vasko71090fc2016-05-24 16:37:28 +02001447API NC_MSG_TYPE
1448nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1449{
1450 NC_MSG_TYPE msgtype;
1451 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001452 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001453
1454 if (!orig_session) {
1455 ERRARG("orig_session");
1456 return NC_MSG_ERROR;
1457 } else if (!session) {
1458 ERRARG("session");
1459 return NC_MSG_ERROR;
1460 }
1461
1462 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1463 && orig_session->ti.libssh.next) {
1464 for (new_session = orig_session->ti.libssh.next;
1465 new_session != orig_session;
1466 new_session = new_session->ti.libssh.next) {
1467 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1468 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1469 /* we found our session */
1470 break;
1471 }
1472 }
1473 if (new_session == orig_session) {
1474 new_session = NULL;
1475 }
1476 }
1477
1478 if (!new_session) {
1479 ERR("Session does not have a NETCONF SSH channel ready.");
1480 return NC_MSG_ERROR;
1481 }
1482
1483 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01001484 new_session->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001485
1486 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001487 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001488 if (msgtype != NC_MSG_HELLO) {
1489 return msgtype;
1490 }
1491
Michal Vasko9f6275e2017-10-05 13:50:05 +02001492 nc_gettimespec_real(&ts_cur);
1493 new_session->opts.server.session_start = ts_cur.tv_sec;
1494 nc_gettimespec_mono(&ts_cur);
1495 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001496 new_session->status = NC_STATUS_RUNNING;
1497 *session = new_session;
1498
1499 return msgtype;
1500}
1501
1502API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001503nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001504{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001505 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001506 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001507 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001508 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001509 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001510
Michal Vasko45e53ae2016-04-07 11:46:03 +02001511 if (!ps) {
1512 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001513 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001514 } else if (!session) {
1515 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001516 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001517 }
1518
Michal Vasko48a63ed2016-03-01 09:48:21 +01001519 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001520 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001521 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001522 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001523
Michal Vasko96164bf2016-01-21 15:41:58 +01001524 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001525 cur_session = ps->sessions[i]->session;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001526 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1527 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001528 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001529 for (new_session = cur_session->ti.libssh.next;
1530 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001531 new_session = new_session->ti.libssh.next) {
1532 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1533 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1534 /* we found our session */
1535 break;
1536 }
1537 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001538 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001539 break;
1540 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001541
Michal Vasko96164bf2016-01-21 15:41:58 +01001542 new_session = NULL;
1543 }
1544 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001545
Michal Vasko48a63ed2016-03-01 09:48:21 +01001546 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001547 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001548
Michal Vasko96164bf2016-01-21 15:41:58 +01001549 if (!new_session) {
1550 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001551 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001552 }
1553
1554 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01001555 new_session->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskofb89d772016-01-08 12:25:35 +01001556
Michal Vasko086311b2016-01-08 09:53:11 +01001557 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001558 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001559 if (msgtype != NC_MSG_HELLO) {
1560 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001561 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001562
Michal Vasko9f6275e2017-10-05 13:50:05 +02001563 nc_gettimespec_real(&ts_cur);
1564 new_session->opts.server.session_start = ts_cur.tv_sec;
1565 nc_gettimespec_mono(&ts_cur);
1566 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001567 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001568 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001569
Michal Vasko71090fc2016-05-24 16:37:28 +02001570 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001571}