blob: a8707c406471a973b5c3f33c0d09478d2cfe990d [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>
22#include <shadow.h>
23#include <crypt.h>
24#include <errno.h>
Michal Vasko9f6275e2017-10-05 13:50:05 +020025#include <time.h>
Michal Vasko086311b2016-01-08 09:53:11 +010026
Michal Vasko11d142a2016-01-19 15:58:24 +010027#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010028#include "session_server_ch.h"
29#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010030
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +020031#if !defined(HAVE_CRYPT_R)
32pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER;
33#endif
34
Michal Vasko086311b2016-01-08 09:53:11 +010035extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010036
Michal Vasko4c1fb492017-01-30 14:31:07 +010037static char *
Michal Vaskoddce1212019-05-24 09:58:49 +020038base64der_key_to_tmp_file(const char *in, const char *key_str)
Michal Vasko086311b2016-01-08 09:53:11 +010039{
Michal Vasko4c1fb492017-01-30 14:31:07 +010040 char path[12] = "/tmp/XXXXXX";
41 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010042 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010043 FILE *file;
44
45 if (in == NULL) {
46 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010047 }
48
Michal Vasko27252692017-03-21 15:34:13 +010049 umode = umask(0600);
Michal Vasko4c1fb492017-01-30 14:31:07 +010050 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010051 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010052 if (fd == -1) {
53 return NULL;
54 }
55
Michal Vasko3964a832018-09-18 14:37:39 +020056 file = fdopen(fd, "w");
Michal Vasko4c1fb492017-01-30 14:31:07 +010057 if (!file) {
58 close(fd);
59 return NULL;
60 }
61
62 /* write the key into the file */
63 written = fwrite("-----BEGIN ", 1, 11, file);
Michal Vaskoddce1212019-05-24 09:58:49 +020064 written += fwrite(key_str, 1, strlen(key_str), file);
Michal Vasko4c1fb492017-01-30 14:31:07 +010065 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
66 written += fwrite(in, 1, strlen(in), file);
67 written += fwrite("\n-----END ", 1, 10, file);
Michal Vaskoddce1212019-05-24 09:58:49 +020068 written += fwrite(key_str, 1, strlen(key_str), file);
Michal Vasko4c1fb492017-01-30 14:31:07 +010069 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
70
71 fclose(file);
72 if ((unsigned)written != 62 + strlen(in)) {
73 unlink(path);
74 return NULL;
75 }
76
77 return strdup(path);
78}
79
80static int
Michal Vasko7d255882017-02-09 13:35:08 +010081nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +010082{
Michal Vaskofbfe8b62017-02-14 10:22:30 +010083 uint8_t i;
84
Michal Vasko4c1fb492017-01-30 14:31:07 +010085 if (!name) {
86 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +010087 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +010088 } else if (idx > opts->hostkey_count) {
89 ERRARG("idx");
90 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010091 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010092
Michal Vaskofbfe8b62017-02-14 10:22:30 +010093 for (i = 0; i < opts->hostkey_count; ++i) {
94 if (!strcmp(opts->hostkeys[i], name)) {
95 ERRARG("name");
96 return -1;
97 }
98 }
99
Michal Vaskoe2713da2016-08-22 16:06:40 +0200100 ++opts->hostkey_count;
101 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
102 if (!opts->hostkeys) {
103 ERRMEM;
104 return -1;
105 }
Michal Vasko7d255882017-02-09 13:35:08 +0100106
107 if (idx < 0) {
108 idx = opts->hostkey_count - 1;
109 }
110 if (idx != opts->hostkey_count - 1) {
111 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
112 }
113 opts->hostkeys[idx] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200114
Michal Vasko5fcc7142016-02-02 12:21:10 +0100115 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100116}
117
118API int
Michal Vasko7d255882017-02-09 13:35:08 +0100119nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100120{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100121 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100122 struct nc_endpt *endpt;
123
Michal Vasko51e514d2016-02-02 15:51:52 +0100124 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100125 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100126 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100127 return -1;
128 }
Michal Vasko7d255882017-02-09 13:35:08 +0100129 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100130 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100131 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100132
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100133 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100134}
135
Michal Vasko974410a2018-04-03 09:36:57 +0200136API void
137nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data),
138 void *user_data, void (*free_user_data)(void *user_data))
139{
140 server_opts.passwd_auth_clb = passwd_auth_clb;
141 server_opts.passwd_auth_data = user_data;
142 server_opts.passwd_auth_data_free = free_user_data;
143}
144
bhart1bb7cdb2018-07-02 15:03:30 -0500145API void
146nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data),
147 void *user_data, void (*free_user_data)(void *user_data))
148{
149 server_opts.interactive_auth_clb = interactive_auth_clb;
150 server_opts.interactive_auth_data = user_data;
151 server_opts.interactive_auth_data_free = free_user_data;
152}
Michal Vasko733c0bd2018-07-03 13:14:40 +0200153
bhart1bb7cdb2018-07-02 15:03:30 -0500154API void
155nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data),
156 void *user_data, void (*free_user_data)(void *user_data))
157{
158 server_opts.pubkey_auth_clb = pubkey_auth_clb;
159 server_opts.pubkey_auth_data = user_data;
160 server_opts.pubkey_auth_data_free = free_user_data;
161}
162
Michal Vaskob05053d2016-01-22 16:12:06 +0100163API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200164nc_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 +0100165{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100166 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200167 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200168 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100169
Michal Vasko2e6defd2016-10-07 15:48:15 +0200170 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200171 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
172 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200173 return -1;
174 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200175 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200176 /* UNLOCK */
177 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200178
179 return ret;
180}
181
Michal Vasko4c1fb492017-01-30 14:31:07 +0100182API void
183nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200184 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 +0100185{
186 if (!hostkey_clb) {
187 ERRARG("hostkey_clb");
188 return;
189 }
190
191 server_opts.hostkey_clb = hostkey_clb;
192 server_opts.hostkey_data = user_data;
193 server_opts.hostkey_data_free = free_user_data;
194}
195
Michal Vaskoe2713da2016-08-22 16:06:40 +0200196static int
Michal Vasko7d255882017-02-09 13:35:08 +0100197nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200198{
199 uint8_t i;
200
Michal Vasko7d255882017-02-09 13:35:08 +0100201 if (name && (idx > -1)) {
202 ERRARG("name and idx");
203 return -1;
204 } else if (idx >= opts->hostkey_count) {
205 ERRARG("idx");
206 }
207
208 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200209 for (i = 0; i < opts->hostkey_count; ++i) {
210 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
211 }
212 free(opts->hostkeys);
213 opts->hostkeys = NULL;
214 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100215 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200216 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100217 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100218 idx = i;
219 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200220 }
221 }
222
Michal Vasko7d255882017-02-09 13:35:08 +0100223 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200224 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100225 } else {
226remove_idx:
227 --opts->hostkey_count;
228 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
229 if (idx < opts->hostkey_count - 1) {
230 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
231 }
232 if (!opts->hostkey_count) {
233 free(opts->hostkeys);
234 opts->hostkeys = NULL;
235 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200236 }
237
238 return 0;
239}
240
241API int
Michal Vasko7d255882017-02-09 13:35:08 +0100242nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200243{
244 int ret;
245 struct nc_endpt *endpt;
246
247 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100248 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200249 if (!endpt) {
250 return -1;
251 }
Michal Vasko7d255882017-02-09 13:35:08 +0100252 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200253 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100254 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200255
256 return ret;
257}
258
259API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200260nc_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 +0200261{
262 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200263 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200264 struct nc_ch_endpt *endpt;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200265
Michal Vasko2e6defd2016-10-07 15:48:15 +0200266 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200267 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
268 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200269 return -1;
270 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200271 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200272 /* UNLOCK */
273 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100274
275 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100276}
277
278static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100279nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
280{
281 uint8_t i;
282 int16_t mov_idx = -1, after_idx = -1;
283 const char *bckup;
284
285 if (!key_mov) {
286 ERRARG("key_mov");
287 return -1;
288 }
289
290 for (i = 0; i < opts->hostkey_count; ++i) {
291 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
292 after_idx = i;
293 }
294 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
295 mov_idx = i;
296 }
297
298 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
299 break;
300 }
301 }
302
303 if (key_after && (after_idx == -1)) {
304 ERRARG("key_after");
305 return -1;
306 }
307 if (mov_idx == -1) {
308 ERRARG("key_mov");
309 return -1;
310 }
311 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
312 /* nothing to do */
313 return 0;
314 }
315
316 /* finally move the key */
317 bckup = opts->hostkeys[mov_idx];
318 if (mov_idx > after_idx) {
319 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
320 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
321 opts->hostkeys[after_idx + 1] = bckup;
322 } else {
323 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
324 opts->hostkeys[after_idx] = bckup;
325 }
326
327 return 0;
328}
329
330API int
331nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
332{
333 int ret;
334 struct nc_endpt *endpt;
335
336 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100337 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100338 if (!endpt) {
339 return -1;
340 }
341 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
342 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100343 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100344
345 return ret;
346}
347
348API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200349nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
350 const char *key_after)
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100351{
352 int ret;
353 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200354 struct nc_ch_endpt *endpt;
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100355
356 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200357 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100358 if (!endpt) {
359 return -1;
360 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200361 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100362 /* UNLOCK */
363 nc_server_ch_client_unlock(client);
364
365 return ret;
366}
367
368static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100369nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100370{
Michal Vaskob05053d2016-01-22 16:12:06 +0100371 opts->auth_methods = auth_methods;
372 return 0;
373}
374
375API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100376nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100377{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100378 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100379 struct nc_endpt *endpt;
380
Michal Vasko51e514d2016-02-02 15:51:52 +0100381 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100382 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100383 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100384 return -1;
385 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200386 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100387 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100388 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100389
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100390 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100391}
392
393API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200394nc_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 +0100395{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100396 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200397 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200398 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100399
Michal Vasko2e6defd2016-10-07 15:48:15 +0200400 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200401 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
402 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200403 return -1;
404 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200405 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200406 /* UNLOCK */
407 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100408
409 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100410}
411
Michal Vaskoddce1212019-05-24 09:58:49 +0200412API int
413nc_server_ssh_endpt_get_auth_methods(const char *endpt_name)
414{
415 int ret;
416 struct nc_endpt *endpt;
417
418 /* LOCK */
419 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
420 if (!endpt) {
421 return -1;
422 }
423 ret = endpt->opts.ssh->auth_methods;
424 /* UNLOCK */
425 pthread_rwlock_unlock(&server_opts.endpt_lock);
426
427 return ret;
428}
429
430API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200431nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name)
Michal Vaskoddce1212019-05-24 09:58:49 +0200432{
433 int ret;
434 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200435 struct nc_ch_endpt *endpt;
Michal Vaskoddce1212019-05-24 09:58:49 +0200436
437 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200438 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
439 if (!endpt) {
Michal Vaskoddce1212019-05-24 09:58:49 +0200440 return -1;
441 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200442 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoddce1212019-05-24 09:58:49 +0200443 /* UNLOCK */
444 nc_server_ch_client_unlock(client);
445
446 return ret;
447}
448
Michal Vaskob05053d2016-01-22 16:12:06 +0100449static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100450nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100451{
Michal Vaskob05053d2016-01-22 16:12:06 +0100452 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200453 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100454 return -1;
455 }
456
Michal Vaskob05053d2016-01-22 16:12:06 +0100457 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100458 return 0;
459}
460
461API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100462nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100463{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100464 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100465 struct nc_endpt *endpt;
466
Michal Vasko51e514d2016-02-02 15:51:52 +0100467 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100468 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100469 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100470 return -1;
471 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200472 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100473 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100474 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100475
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100476 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100477}
478
479API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200480nc_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 +0100481{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100482 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200483 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200484 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100485
Michal Vasko2e6defd2016-10-07 15:48:15 +0200486 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200487 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
488 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200489 return -1;
490 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200491 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200492 /* UNLOCK */
493 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100494
495 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100496}
497
498static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100499nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100500{
Michal Vaskob05053d2016-01-22 16:12:06 +0100501 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200502 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100503 return -1;
504 }
505
Michal Vaskob05053d2016-01-22 16:12:06 +0100506 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100507 return 0;
508}
509
510API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100511nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100512{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100513 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100514 struct nc_endpt *endpt;
515
Michal Vasko51e514d2016-02-02 15:51:52 +0100516 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100517 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100518 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100519 return -1;
520 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200521 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100522 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100523 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100524
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100525 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100526}
527
528API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200529nc_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 +0100530{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100531 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200532 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200533 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100534
Michal Vasko2e6defd2016-10-07 15:48:15 +0200535 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200536 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
537 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200538 return -1;
539 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200540 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200541 /* UNLOCK */
542 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100543
544 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100545}
546
547static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100548_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
549 const char *username)
550{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100551 /* LOCK */
552 pthread_mutex_lock(&server_opts.authkey_lock);
553
Michal Vasko17dfda92016-12-01 14:06:16 +0100554 ++server_opts.authkey_count;
555 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
556 if (!server_opts.authkeys) {
557 ERRMEM;
558 return -1;
559 }
560 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
561 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
562 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
563 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
564
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100565 /* UNLOCK */
566 pthread_mutex_unlock(&server_opts.authkey_lock);
567
Michal Vasko17dfda92016-12-01 14:06:16 +0100568 return 0;
569}
570
571API int
572nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100573{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200574 if (!pubkey_path) {
575 ERRARG("pubkey_path");
576 return -1;
577 } else if (!username) {
578 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100579 return -1;
580 }
581
Michal Vasko17dfda92016-12-01 14:06:16 +0100582 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100583}
584
585API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100586nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100587{
Michal Vasko17dfda92016-12-01 14:06:16 +0100588 if (!pubkey_base64) {
589 ERRARG("pubkey_base64");
590 return -1;
591 } else if (!type) {
592 ERRARG("type");
593 return -1;
594 } else if (!username) {
595 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100596 return -1;
597 }
598
Michal Vasko17dfda92016-12-01 14:06:16 +0100599 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100600}
601
602API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100603nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
604 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100605{
Michal Vasko086311b2016-01-08 09:53:11 +0100606 uint32_t i;
607 int ret = -1;
608
Michal Vasko17dfda92016-12-01 14:06:16 +0100609 /* LOCK */
610 pthread_mutex_lock(&server_opts.authkey_lock);
611
612 if (!pubkey_path && !pubkey_base64 && !type && !username) {
613 for (i = 0; i < server_opts.authkey_count; ++i) {
614 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
615 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
616 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100617
Michal Vasko086311b2016-01-08 09:53:11 +0100618 ret = 0;
619 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100620 free(server_opts.authkeys);
621 server_opts.authkeys = NULL;
622 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100623 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100624 for (i = 0; i < server_opts.authkey_count; ++i) {
625 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
Michal Vaskoe846aee2019-03-28 08:38:41 +0100626 && (!pubkey_base64 || !strcmp(server_opts.authkeys[i].base64, pubkey_base64))
Michal Vasko17dfda92016-12-01 14:06:16 +0100627 && (!type || (server_opts.authkeys[i].type == type))
628 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
629 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
630 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
631 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100632
Michal Vasko17dfda92016-12-01 14:06:16 +0100633 --server_opts.authkey_count;
634 if (i < server_opts.authkey_count) {
635 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
636 sizeof *server_opts.authkeys);
637 } else if (!server_opts.authkey_count) {
638 free(server_opts.authkeys);
639 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100640 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100641
642 ret = 0;
643 }
644 }
Michal Vasko086311b2016-01-08 09:53:11 +0100645 }
646
Michal Vasko51e514d2016-02-02 15:51:52 +0100647 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100648 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100649
650 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100651}
652
653void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100654nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100655{
Michal Vasko7d255882017-02-09 13:35:08 +0100656 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100657}
658
Michal Vasko086311b2016-01-08 09:53:11 +0100659static char *
660auth_password_get_pwd_hash(const char *username)
661{
662 struct passwd *pwd, pwd_buf;
663 struct spwd *spwd, spwd_buf;
664 char *pass_hash = NULL, buf[256];
665
666 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
667 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100668 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100669 return NULL;
670 }
671
672 if (!strcmp(pwd->pw_passwd, "x")) {
673 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
674 if (!spwd) {
675 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
676 return NULL;
677 }
678
679 pass_hash = spwd->sp_pwdp;
680 } else {
681 pass_hash = pwd->pw_passwd;
682 }
683
684 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100685 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100686 return NULL;
687 }
688
689 /* check the hash structure for special meaning */
690 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
691 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
692 return NULL;
693 }
694 if (!strcmp(pass_hash, "*NP*")) {
695 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
696 return NULL;
697 }
698
699 return strdup(pass_hash);
700}
701
702static int
703auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
704{
705 char *new_pass_hash;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200706#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100707 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200708#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100709
710 if (!pass_hash[0]) {
711 if (!pass_clear[0]) {
712 WRN("User authentication successful with an empty password!");
713 return 0;
714 } else {
715 /* the user did now know he does not need any password,
716 * (which should not be used) so deny authentication */
717 return 1;
718 }
719 }
720
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200721#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100722 cdata.initialized = 0;
723 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200724#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200725 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200726 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200727 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200728#endif
Andrew Langefeld158d6fd2018-06-11 18:51:44 -0500729
730 if (!new_pass_hash) {
731 return 1;
732 }
733
Michal Vasko086311b2016-01-08 09:53:11 +0100734 return strcmp(new_pass_hash, pass_hash);
735}
736
737static void
738nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
739{
740 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100741 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100742
Michal Vaskoebba7602018-03-23 13:14:08 +0100743 if (server_opts.passwd_auth_clb) {
744 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
745 } else {
746 pass_hash = auth_password_get_pwd_hash(session->username);
747 if (pass_hash) {
748 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
749 free(pass_hash);
750 }
Michal Vasko086311b2016-01-08 09:53:11 +0100751 }
752
Michal Vaskoebba7602018-03-23 13:14:08 +0100753 if (!auth_ret) {
754 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
755 VRB("User \"%s\" authenticated.", session->username);
756 ssh_message_auth_reply_success(msg, 0);
757 } else {
758 ++session->opts.server.ssh_auth_attempts;
759 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
760 ssh_message_reply_default(msg);
761 }
Michal Vasko086311b2016-01-08 09:53:11 +0100762}
763
764static void
765nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
766{
bhart3bc2f582018-06-05 12:40:32 -0500767 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100768 char *pass_hash;
769
bhart1bb7cdb2018-07-02 15:03:30 -0500770 if (server_opts.interactive_auth_clb) {
Michal Vasko733c0bd2018-07-03 13:14:40 +0200771 auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100772 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500773 if (!ssh_message_auth_kbdint_is_response(msg)) {
774 const char *prompts[] = {"Password: "};
775 char echo[] = {0};
776
777 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
Robert Vargaad7a5532018-08-10 20:40:54 +0200778 auth_ret = -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100779 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500780 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {// failed session
781 ssh_message_reply_default(msg);
782 return;
783 }
bhart3bc2f582018-06-05 12:40:32 -0500784 pass_hash = auth_password_get_pwd_hash(session->username);// get hashed password
785 if (pass_hash) {
Robert Vargaad7a5532018-08-10 20:40:54 +0200786 /* Normalize auth_password_compare_pwd result to 0 or 1 */
787 auth_ret = !!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0));
bhart3bc2f582018-06-05 12:40:32 -0500788 free(pass_hash);// free hashed password
789 }
Michal Vasko086311b2016-01-08 09:53:11 +0100790 }
bhart1bb7cdb2018-07-02 15:03:30 -0500791 }
792
Robert Vargaad7a5532018-08-10 20:40:54 +0200793 /* We have already sent a reply */
794 if (auth_ret == -1) {
795 return;
796 }
797
bhart1bb7cdb2018-07-02 15:03:30 -0500798 /* Authenticate message based on outcome */
799 if (!auth_ret) {
800 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
801 VRB("User \"%s\" authenticated.", session->username);
802 ssh_message_auth_reply_success(msg, 0);
803 } else {
804 ++session->opts.server.ssh_auth_attempts;
805 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
806 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100807 }
808}
809
810static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100811auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100812{
813 uint32_t i;
814 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100815 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100816 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100817
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100818 /* LOCK */
819 pthread_mutex_lock(&server_opts.authkey_lock);
820
Michal Vasko17dfda92016-12-01 14:06:16 +0100821 for (i = 0; i < server_opts.authkey_count; ++i) {
822 switch (server_opts.authkeys[i].type) {
823 case NC_SSH_KEY_UNKNOWN:
824 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
825 break;
826 case NC_SSH_KEY_DSA:
827 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
828 break;
829 case NC_SSH_KEY_RSA:
830 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
831 break;
832 case NC_SSH_KEY_ECDSA:
833 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
834 break;
835 }
836
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200837 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100838 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200839 continue;
840 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100841 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100842 continue;
843 }
844
845 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
846 ssh_key_free(pub_key);
847 break;
848 }
849
850 ssh_key_free(pub_key);
851 }
852
Michal Vasko17dfda92016-12-01 14:06:16 +0100853 if (i < server_opts.authkey_count) {
854 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100855 }
856
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100857 /* UNLOCK */
858 pthread_mutex_unlock(&server_opts.authkey_lock);
859
Michal Vasko086311b2016-01-08 09:53:11 +0100860 return username;
861}
862
863static void
864nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
865{
866 const char *username;
867 int signature_state;
868
Michal Vasko733c0bd2018-07-03 13:14:40 +0200869 if (server_opts.pubkey_auth_clb) {
870 if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) {
bhart3bc2f582018-06-05 12:40:32 -0500871 goto fail;
872 }
Michal Vasko733c0bd2018-07-03 13:14:40 +0200873 } else {
bhart3bc2f582018-06-05 12:40:32 -0500874 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
875 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
876 goto fail;
877 } else if (strcmp(session->username, username)) {
878 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
879 goto fail;
880 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200881 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200882
Michal Vasko086311b2016-01-08 09:53:11 +0100883 signature_state = ssh_message_auth_publickey_state(msg);
884 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
885 VRB("User \"%s\" authenticated.", session->username);
886 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
887 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100888 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200889 /* accepting only the use of a public key */
890 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100891 }
892
Michal Vaskobd13a932016-09-14 09:00:35 +0200893 return;
894
895fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200896 ++session->opts.server.ssh_auth_attempts;
897 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100898 ssh_message_reply_default(msg);
899}
900
901static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100902nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100903{
Michal Vasko96164bf2016-01-21 15:41:58 +0100904 ssh_channel chan;
905
906 /* first channel request */
907 if (!session->ti.libssh.channel) {
908 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100909 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100910 return -1;
911 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100912 chan = ssh_message_channel_request_open_reply_accept(msg);
913 if (!chan) {
914 ERR("Failed to create a new SSH channel.");
915 return -1;
916 }
917 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100918
Michal Vasko96164bf2016-01-21 15:41:58 +0100919 /* additional channel request */
920 } else {
921 chan = ssh_message_channel_request_open_reply_accept(msg);
922 if (!chan) {
923 ERR("Session %u: failed to create a new SSH channel.", session->id);
924 return -1;
925 }
926 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100927 }
928
Michal Vasko086311b2016-01-08 09:53:11 +0100929 return 0;
930}
931
932static int
933nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
934{
Michal Vasko96164bf2016-01-21 15:41:58 +0100935 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100936
Michal Vasko96164bf2016-01-21 15:41:58 +0100937 if (strcmp(subsystem, "netconf")) {
938 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100939 return -1;
940 }
941
Michal Vasko96164bf2016-01-21 15:41:58 +0100942 if (session->ti.libssh.channel == channel) {
943 /* first channel requested */
944 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
945 ERRINT;
946 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100947 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100948 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
949 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
950 return -1;
951 }
952
953 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100954 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100955 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +0200956 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100957 if (!new_session) {
958 ERRMEM;
959 return -1;
960 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100961
962 /* insert the new session */
963 if (!session->ti.libssh.next) {
964 new_session->ti.libssh.next = session;
965 } else {
966 new_session->ti.libssh.next = session->ti.libssh.next;
967 }
968 session->ti.libssh.next = new_session;
969
970 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +0100971 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +0200972 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +0100973 new_session->ti.libssh.channel = channel;
974 new_session->ti.libssh.session = session->ti.libssh.session;
975 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
976 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
977 new_session->port = session->port;
978 new_session->ctx = server_opts.ctx;
Michal Vasko83d15322018-09-27 09:44:02 +0200979 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
Michal Vasko086311b2016-01-08 09:53:11 +0100980 }
981
982 return 0;
983}
984
Michal Vasko96164bf2016-01-21 15:41:58 +0100985int
Michal Vaskob48aa812016-01-18 14:13:09 +0100986nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100987{
988 const char *str_type, *str_subtype = NULL, *username;
989 int subtype, type;
990 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100991
992 type = ssh_message_type(msg);
993 subtype = ssh_message_subtype(msg);
994
995 switch (type) {
996 case SSH_REQUEST_AUTH:
997 str_type = "request-auth";
998 switch (subtype) {
999 case SSH_AUTH_METHOD_NONE:
1000 str_subtype = "none";
1001 break;
1002 case SSH_AUTH_METHOD_PASSWORD:
1003 str_subtype = "password";
1004 break;
1005 case SSH_AUTH_METHOD_PUBLICKEY:
1006 str_subtype = "publickey";
1007 break;
1008 case SSH_AUTH_METHOD_HOSTBASED:
1009 str_subtype = "hostbased";
1010 break;
1011 case SSH_AUTH_METHOD_INTERACTIVE:
1012 str_subtype = "interactive";
1013 break;
1014 case SSH_AUTH_METHOD_GSSAPI_MIC:
1015 str_subtype = "gssapi-mic";
1016 break;
1017 }
1018 break;
1019
1020 case SSH_REQUEST_CHANNEL_OPEN:
1021 str_type = "request-channel-open";
1022 switch (subtype) {
1023 case SSH_CHANNEL_SESSION:
1024 str_subtype = "session";
1025 break;
1026 case SSH_CHANNEL_DIRECT_TCPIP:
1027 str_subtype = "direct-tcpip";
1028 break;
1029 case SSH_CHANNEL_FORWARDED_TCPIP:
1030 str_subtype = "forwarded-tcpip";
1031 break;
1032 case (int)SSH_CHANNEL_X11:
1033 str_subtype = "channel-x11";
1034 break;
1035 case SSH_CHANNEL_UNKNOWN:
1036 /* fallthrough */
1037 default:
1038 str_subtype = "unknown";
1039 break;
1040 }
1041 break;
1042
1043 case SSH_REQUEST_CHANNEL:
1044 str_type = "request-channel";
1045 switch (subtype) {
1046 case SSH_CHANNEL_REQUEST_PTY:
1047 str_subtype = "pty";
1048 break;
1049 case SSH_CHANNEL_REQUEST_EXEC:
1050 str_subtype = "exec";
1051 break;
1052 case SSH_CHANNEL_REQUEST_SHELL:
1053 str_subtype = "shell";
1054 break;
1055 case SSH_CHANNEL_REQUEST_ENV:
1056 str_subtype = "env";
1057 break;
1058 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1059 str_subtype = "subsystem";
1060 break;
1061 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1062 str_subtype = "window-change";
1063 break;
1064 case SSH_CHANNEL_REQUEST_X11:
1065 str_subtype = "x11";
1066 break;
1067 case SSH_CHANNEL_REQUEST_UNKNOWN:
1068 /* fallthrough */
1069 default:
1070 str_subtype = "unknown";
1071 break;
1072 }
1073 break;
1074
1075 case SSH_REQUEST_SERVICE:
1076 str_type = "request-service";
1077 str_subtype = ssh_message_service_service(msg);
1078 break;
1079
1080 case SSH_REQUEST_GLOBAL:
1081 str_type = "request-global";
1082 switch (subtype) {
1083 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1084 str_subtype = "tcpip-forward";
1085 break;
1086 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1087 str_subtype = "cancel-tcpip-forward";
1088 break;
1089 case SSH_GLOBAL_REQUEST_UNKNOWN:
1090 /* fallthrough */
1091 default:
1092 str_subtype = "unknown";
1093 break;
1094 }
1095 break;
1096
1097 default:
1098 str_type = "unknown";
1099 str_subtype = "unknown";
1100 break;
1101 }
1102
1103 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001104 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1105 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1106 * but we got it now, during session free */
1107 VRB("SSH message arrived on a %s session, the request will be denied.",
1108 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1109 ssh_message_reply_default(msg);
1110 return 0;
1111 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001112 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001113
1114 /*
1115 * process known messages
1116 */
1117 if (type == SSH_REQUEST_AUTH) {
1118 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1119 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1120 ssh_message_reply_default(msg);
1121 return 0;
1122 }
1123
Michal Vasko086311b2016-01-08 09:53:11 +01001124 /* save the username, do not let the client change it */
1125 username = ssh_message_auth_user(msg);
1126 if (!session->username) {
1127 if (!username) {
1128 ERR("Denying an auth request without a username.");
1129 return 1;
1130 }
1131
Michal Vasko05ba9df2016-01-13 14:40:27 +01001132 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001133 } else if (username) {
1134 if (strcmp(username, session->username)) {
1135 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1136 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001137 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001138 return 1;
1139 }
1140 }
1141
1142 if (subtype == SSH_AUTH_METHOD_NONE) {
1143 /* libssh will return the supported auth methods */
1144 return 1;
1145 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1146 nc_sshcb_auth_password(session, msg);
1147 return 0;
1148 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1149 nc_sshcb_auth_pubkey(session, msg);
1150 return 0;
1151 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1152 nc_sshcb_auth_kbdint(session, msg);
1153 return 0;
1154 }
1155 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001156 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001157 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001158 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001159 }
Michal Vasko086311b2016-01-08 09:53:11 +01001160 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001161
Michal Vasko0df67562016-01-21 15:50:11 +01001162 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001163 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1164 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001165 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001166 } else {
1167 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001168 }
1169 return 0;
1170 }
1171 }
1172
1173 /* we did not process it */
1174 return 1;
1175}
1176
Michal Vasko1a38c862016-01-15 15:50:07 +01001177/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001178static int
1179nc_open_netconf_channel(struct nc_session *session, int timeout)
1180{
Michal Vasko36c7be82017-02-22 13:37:59 +01001181 int ret;
1182 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001183
1184 /* message callback is executed twice to give chance for the channel to be
1185 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001186 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001187 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001188 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001189 return -1;
1190 }
1191
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001192 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1193 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001194 ERR("Failed to receive SSH messages on a session (%s).",
1195 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001196 return -1;
1197 }
1198
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001199 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001200 return 0;
1201 }
1202
1203 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1204 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001205 ERR("Failed to receive SSH messages on a session (%s).",
1206 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001207 return -1;
1208 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001209
1210 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1211 /* we did not receive subsystem-request, timeout */
1212 return 0;
1213 }
1214
1215 return 1;
1216 }
1217
Michal Vasko36c7be82017-02-22 13:37:59 +01001218 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001219 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001220 nc_addtimespec(&ts_timeout, timeout);
1221 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001222 while (1) {
1223 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001224 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001225 return -1;
1226 }
1227
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001228 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1229 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001230 ERR("Failed to receive SSH messages on a session (%s).",
1231 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001232 return -1;
1233 }
1234
Michal Vasko086311b2016-01-08 09:53:11 +01001235 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001236 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001237 }
1238
Michal Vasko086311b2016-01-08 09:53:11 +01001239 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001240 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001241 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001242 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1243 /* timeout */
1244 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1245 break;
1246 }
1247 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001248 }
Michal Vasko086311b2016-01-08 09:53:11 +01001249
Michal Vasko1a38c862016-01-15 15:50:07 +01001250 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001251}
1252
Michal Vasko4c1fb492017-01-30 14:31:07 +01001253static int
1254nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1255{
1256 uint8_t i;
1257 char *privkey_path, *privkey_data;
Michal Vaskoddce1212019-05-24 09:58:49 +02001258 int ret;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001259 NC_SSH_KEY_TYPE privkey_type;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001260
1261 if (!server_opts.hostkey_clb) {
1262 ERR("Callback for retrieving SSH host keys not set.");
1263 return -1;
1264 }
1265
1266 for (i = 0; i < hostkey_count; ++i) {
1267 privkey_path = privkey_data = NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +02001268 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_type)) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001269 ERR("Host key callback failed.");
1270 return -1;
1271 }
1272
1273 if (privkey_data) {
Michal Vaskoddce1212019-05-24 09:58:49 +02001274 privkey_path = base64der_key_to_tmp_file(privkey_data, nc_keytype2str(privkey_type));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001275 if (!privkey_path) {
1276 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1277 free(privkey_data);
1278 return -1;
1279 }
1280 }
1281
1282 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1283
1284 /* cleanup */
1285 if (privkey_data && unlink(privkey_path)) {
1286 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1287 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001288 free(privkey_data);
1289
1290 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001291 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1292 }
1293 free(privkey_path);
1294
1295 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001296 return -1;
1297 }
1298 }
1299
1300 return 0;
1301}
1302
Michal Vasko96164bf2016-01-21 15:41:58 +01001303int
Michal Vasko0190bc32016-03-02 15:47:49 +01001304nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001305{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001306 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001307 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001308 int libssh_auth_methods = 0, ret;
1309 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001310
Michal Vasko2cc4c682016-03-01 09:16:48 +01001311 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001312
Michal Vasko086311b2016-01-08 09:53:11 +01001313 /* other transport-specific data */
1314 session->ti_type = NC_TI_LIBSSH;
1315 session->ti.libssh.session = ssh_new();
1316 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001317 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001318 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001319 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001320 }
1321
Michal Vaskoc61c4492016-01-25 11:13:34 +01001322 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001323 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1324 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001325 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001326 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1327 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001328 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001329 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1330 }
1331 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1332
Michal Vaskoe2713da2016-08-22 16:06:40 +02001333 sbind = ssh_bind_new();
1334 if (!sbind) {
1335 ERR("Failed to create an SSH bind.");
1336 close(sock);
1337 return -1;
1338 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001339
1340 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1341 close(sock);
1342 ssh_bind_free(sbind);
1343 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001344 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001345
Michal Vasko086311b2016-01-08 09:53:11 +01001346 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001347 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001348 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001349
Michal Vaskoe2713da2016-08-22 16:06:40 +02001350 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1351 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001352 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001353 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001354 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001355 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001356 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001357
Michal Vasko0190bc32016-03-02 15:47:49 +01001358 ssh_set_blocking(session->ti.libssh.session, 0);
1359
Michal Vasko36c7be82017-02-22 13:37:59 +01001360 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001361 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001362 nc_addtimespec(&ts_timeout, timeout);
1363 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001364 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001365 /* this tends to take longer */
1366 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001367 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001368 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001369 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1370 break;
1371 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001372 }
1373 }
1374 if (ret == SSH_AGAIN) {
1375 ERR("SSH key exchange timeout.");
1376 return 0;
1377 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001378 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001379 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001380 }
1381
1382 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001383 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001384 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001385 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1386 }
1387 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001388 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001389 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001390 return -1;
1391 }
1392
Michal Vasko086311b2016-01-08 09:53:11 +01001393 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001394 ERR("Failed to receive SSH messages on a session (%s).",
1395 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001396 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001397 }
1398
Michal Vasko36c7be82017-02-22 13:37:59 +01001399 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1400 break;
1401 }
1402
Michal Vasko145ae672017-02-07 10:57:27 +01001403 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1404 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1405 return -1;
1406 }
1407
Michal Vasko086311b2016-01-08 09:53:11 +01001408 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001409 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001410 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001411 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1412 /* timeout */
1413 break;
1414 }
1415 }
1416 }
Michal Vasko086311b2016-01-08 09:53:11 +01001417
1418 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1419 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001420 if (session->username) {
1421 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1422 } else {
1423 ERR("User failed to authenticate for too long, disconnecting.");
1424 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001425 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001426 }
1427
Michal Vasko086311b2016-01-08 09:53:11 +01001428 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001429 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001430 if (ret < 1) {
1431 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001432 }
1433
Michal Vasko96164bf2016-01-21 15:41:58 +01001434 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001435 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001436}
1437
Michal Vasko71090fc2016-05-24 16:37:28 +02001438API NC_MSG_TYPE
1439nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1440{
1441 NC_MSG_TYPE msgtype;
1442 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001443 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001444
1445 if (!orig_session) {
1446 ERRARG("orig_session");
1447 return NC_MSG_ERROR;
1448 } else if (!session) {
1449 ERRARG("session");
1450 return NC_MSG_ERROR;
1451 }
1452
1453 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1454 && orig_session->ti.libssh.next) {
1455 for (new_session = orig_session->ti.libssh.next;
1456 new_session != orig_session;
1457 new_session = new_session->ti.libssh.next) {
1458 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1459 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1460 /* we found our session */
1461 break;
1462 }
1463 }
1464 if (new_session == orig_session) {
1465 new_session = NULL;
1466 }
1467 }
1468
1469 if (!new_session) {
1470 ERR("Session does not have a NETCONF SSH channel ready.");
1471 return NC_MSG_ERROR;
1472 }
1473
1474 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01001475 new_session->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001476
1477 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001478 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001479 if (msgtype != NC_MSG_HELLO) {
1480 return msgtype;
1481 }
1482
Michal Vasko9f6275e2017-10-05 13:50:05 +02001483 nc_gettimespec_real(&ts_cur);
1484 new_session->opts.server.session_start = ts_cur.tv_sec;
1485 nc_gettimespec_mono(&ts_cur);
1486 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001487 new_session->status = NC_STATUS_RUNNING;
1488 *session = new_session;
1489
1490 return msgtype;
1491}
1492
1493API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001494nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001495{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001496 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001497 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001498 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001499 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001500 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001501
Michal Vasko45e53ae2016-04-07 11:46:03 +02001502 if (!ps) {
1503 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001504 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001505 } else if (!session) {
1506 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001507 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001508 }
1509
Michal Vasko48a63ed2016-03-01 09:48:21 +01001510 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001511 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001512 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001513 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001514
Michal Vasko96164bf2016-01-21 15:41:58 +01001515 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001516 cur_session = ps->sessions[i]->session;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001517 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1518 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001519 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001520 for (new_session = cur_session->ti.libssh.next;
1521 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001522 new_session = new_session->ti.libssh.next) {
1523 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1524 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1525 /* we found our session */
1526 break;
1527 }
1528 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001529 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001530 break;
1531 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001532
Michal Vasko96164bf2016-01-21 15:41:58 +01001533 new_session = NULL;
1534 }
1535 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001536
Michal Vasko48a63ed2016-03-01 09:48:21 +01001537 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001538 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001539
Michal Vasko96164bf2016-01-21 15:41:58 +01001540 if (!new_session) {
1541 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001542 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001543 }
1544
1545 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01001546 new_session->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskofb89d772016-01-08 12:25:35 +01001547
Michal Vasko086311b2016-01-08 09:53:11 +01001548 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001549 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001550 if (msgtype != NC_MSG_HELLO) {
1551 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001552 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001553
Michal Vasko9f6275e2017-10-05 13:50:05 +02001554 nc_gettimespec_real(&ts_cur);
1555 new_session->opts.server.session_start = ts_cur.tv_sec;
1556 nc_gettimespec_mono(&ts_cur);
1557 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001558 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001559 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001560
Michal Vasko71090fc2016-05-24 16:37:28 +02001561 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001562}