blob: fb0dc85b0debc48115cf04382a878eda7613fc86 [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 Vasko857425d2019-12-10 09:55:03 +010027#include "config.h"
Michal Vasko11d142a2016-01-19 15:58:24 +010028#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010029#include "session_server_ch.h"
30#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010031
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +020032#if !defined(HAVE_CRYPT_R)
33pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER;
34#endif
35
Michal Vasko086311b2016-01-08 09:53:11 +010036extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010037
Michal Vasko4c1fb492017-01-30 14:31:07 +010038static char *
Michal Vaskoddce1212019-05-24 09:58:49 +020039base64der_key_to_tmp_file(const char *in, const char *key_str)
Michal Vasko086311b2016-01-08 09:53:11 +010040{
Michal Vasko4c1fb492017-01-30 14:31:07 +010041 char path[12] = "/tmp/XXXXXX";
42 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010043 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010044 FILE *file;
45
46 if (in == NULL) {
47 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010048 }
49
mekleob31878b2019-09-09 14:10:47 +020050 umode = umask(0177);
Michal Vasko4c1fb492017-01-30 14:31:07 +010051 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010052 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010053 if (fd == -1) {
54 return NULL;
55 }
56
Michal Vasko3964a832018-09-18 14:37:39 +020057 file = fdopen(fd, "w");
Michal Vasko4c1fb492017-01-30 14:31:07 +010058 if (!file) {
59 close(fd);
60 return NULL;
61 }
62
63 /* write the key into the file */
64 written = fwrite("-----BEGIN ", 1, 11, file);
Michal Vaskoddce1212019-05-24 09:58:49 +020065 written += fwrite(key_str, 1, strlen(key_str), file);
Michal Vasko4c1fb492017-01-30 14:31:07 +010066 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
67 written += fwrite(in, 1, strlen(in), file);
68 written += fwrite("\n-----END ", 1, 10, 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-----", 1, 17, file);
71
72 fclose(file);
73 if ((unsigned)written != 62 + strlen(in)) {
74 unlink(path);
75 return NULL;
76 }
77
78 return strdup(path);
79}
80
81static int
Michal Vasko7d255882017-02-09 13:35:08 +010082nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +010083{
Michal Vaskofbfe8b62017-02-14 10:22:30 +010084 uint8_t i;
85
Michal Vasko4c1fb492017-01-30 14:31:07 +010086 if (!name) {
87 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +010088 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +010089 } else if (idx > opts->hostkey_count) {
90 ERRARG("idx");
91 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010092 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010093
Michal Vaskofbfe8b62017-02-14 10:22:30 +010094 for (i = 0; i < opts->hostkey_count; ++i) {
95 if (!strcmp(opts->hostkeys[i], name)) {
96 ERRARG("name");
97 return -1;
98 }
99 }
100
Michal Vaskoe2713da2016-08-22 16:06:40 +0200101 ++opts->hostkey_count;
102 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
103 if (!opts->hostkeys) {
104 ERRMEM;
105 return -1;
106 }
Michal Vasko7d255882017-02-09 13:35:08 +0100107
108 if (idx < 0) {
109 idx = opts->hostkey_count - 1;
110 }
111 if (idx != opts->hostkey_count - 1) {
112 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
113 }
114 opts->hostkeys[idx] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200115
Michal Vasko5fcc7142016-02-02 12:21:10 +0100116 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100117}
118
119API int
Michal Vasko7d255882017-02-09 13:35:08 +0100120nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100121{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100122 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100123 struct nc_endpt *endpt;
124
Michal Vasko51e514d2016-02-02 15:51:52 +0100125 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100126 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100127 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100128 return -1;
129 }
Michal Vasko7d255882017-02-09 13:35:08 +0100130 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100131 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100132 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100133
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100134 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100135}
136
Michal Vasko974410a2018-04-03 09:36:57 +0200137API void
138nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data),
139 void *user_data, void (*free_user_data)(void *user_data))
140{
141 server_opts.passwd_auth_clb = passwd_auth_clb;
142 server_opts.passwd_auth_data = user_data;
143 server_opts.passwd_auth_data_free = free_user_data;
144}
145
bhart1bb7cdb2018-07-02 15:03:30 -0500146API void
147nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data),
148 void *user_data, void (*free_user_data)(void *user_data))
149{
150 server_opts.interactive_auth_clb = interactive_auth_clb;
151 server_opts.interactive_auth_data = user_data;
152 server_opts.interactive_auth_data_free = free_user_data;
153}
Michal Vasko733c0bd2018-07-03 13:14:40 +0200154
bhart1bb7cdb2018-07-02 15:03:30 -0500155API void
156nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data),
157 void *user_data, void (*free_user_data)(void *user_data))
158{
159 server_opts.pubkey_auth_clb = pubkey_auth_clb;
160 server_opts.pubkey_auth_data = user_data;
161 server_opts.pubkey_auth_data_free = free_user_data;
162}
163
Michal Vaskob05053d2016-01-22 16:12:06 +0100164API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200165nc_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 +0100166{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100167 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200168 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200169 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100170
Michal Vasko2e6defd2016-10-07 15:48:15 +0200171 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200172 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
173 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200174 return -1;
175 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200176 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200177 /* UNLOCK */
178 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200179
180 return ret;
181}
182
Michal Vasko4c1fb492017-01-30 14:31:07 +0100183API void
184nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
Michal Vaskoe49a15f2019-05-27 14:18:36 +0200185 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 +0100186{
187 if (!hostkey_clb) {
188 ERRARG("hostkey_clb");
189 return;
190 }
191
192 server_opts.hostkey_clb = hostkey_clb;
193 server_opts.hostkey_data = user_data;
194 server_opts.hostkey_data_free = free_user_data;
195}
196
Michal Vaskoe2713da2016-08-22 16:06:40 +0200197static int
Michal Vasko7d255882017-02-09 13:35:08 +0100198nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200199{
200 uint8_t i;
201
Michal Vasko7d255882017-02-09 13:35:08 +0100202 if (name && (idx > -1)) {
203 ERRARG("name and idx");
204 return -1;
205 } else if (idx >= opts->hostkey_count) {
206 ERRARG("idx");
207 }
208
209 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200210 for (i = 0; i < opts->hostkey_count; ++i) {
211 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
212 }
213 free(opts->hostkeys);
214 opts->hostkeys = NULL;
215 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100216 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200217 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100218 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100219 idx = i;
220 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200221 }
222 }
223
Michal Vasko7d255882017-02-09 13:35:08 +0100224 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200225 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100226 } else {
227remove_idx:
228 --opts->hostkey_count;
229 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
230 if (idx < opts->hostkey_count - 1) {
231 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
232 }
233 if (!opts->hostkey_count) {
234 free(opts->hostkeys);
235 opts->hostkeys = NULL;
236 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200237 }
238
239 return 0;
240}
241
242API int
Michal Vasko7d255882017-02-09 13:35:08 +0100243nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200244{
245 int ret;
246 struct nc_endpt *endpt;
247
248 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100249 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200250 if (!endpt) {
251 return -1;
252 }
Michal Vasko7d255882017-02-09 13:35:08 +0100253 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200254 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100255 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200256
257 return ret;
258}
259
260API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200261nc_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 +0200262{
263 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200264 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200265 struct nc_ch_endpt *endpt;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200266
Michal Vasko2e6defd2016-10-07 15:48:15 +0200267 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200268 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
269 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200270 return -1;
271 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200272 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200273 /* UNLOCK */
274 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100275
276 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100277}
278
279static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100280nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
281{
282 uint8_t i;
283 int16_t mov_idx = -1, after_idx = -1;
284 const char *bckup;
285
286 if (!key_mov) {
287 ERRARG("key_mov");
288 return -1;
289 }
290
291 for (i = 0; i < opts->hostkey_count; ++i) {
292 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
293 after_idx = i;
294 }
295 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
296 mov_idx = i;
297 }
298
299 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
300 break;
301 }
302 }
303
304 if (key_after && (after_idx == -1)) {
305 ERRARG("key_after");
306 return -1;
307 }
308 if (mov_idx == -1) {
309 ERRARG("key_mov");
310 return -1;
311 }
312 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
313 /* nothing to do */
314 return 0;
315 }
316
317 /* finally move the key */
318 bckup = opts->hostkeys[mov_idx];
319 if (mov_idx > after_idx) {
320 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
321 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
322 opts->hostkeys[after_idx + 1] = bckup;
323 } else {
324 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
325 opts->hostkeys[after_idx] = bckup;
326 }
327
328 return 0;
329}
330
331API int
332nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
333{
334 int ret;
335 struct nc_endpt *endpt;
336
337 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100338 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100339 if (!endpt) {
340 return -1;
341 }
342 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
343 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100344 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100345
346 return ret;
347}
348
349API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200350nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
351 const char *key_after)
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100352{
353 int ret;
354 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200355 struct nc_ch_endpt *endpt;
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100356
357 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200358 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100359 if (!endpt) {
360 return -1;
361 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200362 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100363 /* UNLOCK */
364 nc_server_ch_client_unlock(client);
365
366 return ret;
367}
368
369static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100370nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100371{
Michal Vaskob05053d2016-01-22 16:12:06 +0100372 opts->auth_methods = auth_methods;
373 return 0;
374}
375
376API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100377nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100378{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100379 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100380 struct nc_endpt *endpt;
381
Michal Vasko51e514d2016-02-02 15:51:52 +0100382 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100383 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100384 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100385 return -1;
386 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200387 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100388 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100389 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100390
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100391 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100392}
393
394API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200395nc_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 +0100396{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100397 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200398 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200399 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100400
Michal Vasko2e6defd2016-10-07 15:48:15 +0200401 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200402 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
403 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200404 return -1;
405 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200406 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200407 /* UNLOCK */
408 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100409
410 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100411}
412
Michal Vaskoddce1212019-05-24 09:58:49 +0200413API int
414nc_server_ssh_endpt_get_auth_methods(const char *endpt_name)
415{
416 int ret;
417 struct nc_endpt *endpt;
418
419 /* LOCK */
420 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
421 if (!endpt) {
422 return -1;
423 }
424 ret = endpt->opts.ssh->auth_methods;
425 /* UNLOCK */
426 pthread_rwlock_unlock(&server_opts.endpt_lock);
427
428 return ret;
429}
430
431API int
Michal Vaskoadf30f02019-06-24 09:34:47 +0200432nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name)
Michal Vaskoddce1212019-05-24 09:58:49 +0200433{
434 int ret;
435 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200436 struct nc_ch_endpt *endpt;
Michal Vaskoddce1212019-05-24 09:58:49 +0200437
438 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200439 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
440 if (!endpt) {
Michal Vaskoddce1212019-05-24 09:58:49 +0200441 return -1;
442 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200443 ret = endpt->opts.ssh->auth_methods;
Michal Vaskoddce1212019-05-24 09:58:49 +0200444 /* UNLOCK */
445 nc_server_ch_client_unlock(client);
446
447 return ret;
448}
449
Michal Vaskob05053d2016-01-22 16:12:06 +0100450static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100451nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100452{
Michal Vaskob05053d2016-01-22 16:12:06 +0100453 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200454 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100455 return -1;
456 }
457
Michal Vaskob05053d2016-01-22 16:12:06 +0100458 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100459 return 0;
460}
461
462API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100463nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100464{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100465 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100466 struct nc_endpt *endpt;
467
Michal Vasko51e514d2016-02-02 15:51:52 +0100468 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100469 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100470 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100471 return -1;
472 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200473 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100474 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100475 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100476
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100477 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100478}
479
480API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200481nc_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 +0100482{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100483 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200484 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200485 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100486
Michal Vasko2e6defd2016-10-07 15:48:15 +0200487 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200488 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
489 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200490 return -1;
491 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200492 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200493 /* UNLOCK */
494 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100495
496 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100497}
498
499static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100500nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100501{
Michal Vaskob05053d2016-01-22 16:12:06 +0100502 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200503 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100504 return -1;
505 }
506
Michal Vaskob05053d2016-01-22 16:12:06 +0100507 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100508 return 0;
509}
510
511API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100512nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100513{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100514 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100515 struct nc_endpt *endpt;
516
Michal Vasko51e514d2016-02-02 15:51:52 +0100517 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100518 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100519 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100520 return -1;
521 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200522 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100523 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100524 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100525
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100526 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100527}
528
529API int
Michal Vaskocbad4c52019-06-27 16:30:35 +0200530nc_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 +0100531{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100532 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200533 struct nc_ch_client *client;
Michal Vaskoadf30f02019-06-24 09:34:47 +0200534 struct nc_ch_endpt *endpt;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100535
Michal Vasko2e6defd2016-10-07 15:48:15 +0200536 /* LOCK */
Michal Vaskoadf30f02019-06-24 09:34:47 +0200537 endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client);
538 if (!endpt) {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200539 return -1;
540 }
Michal Vaskoadf30f02019-06-24 09:34:47 +0200541 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200542 /* UNLOCK */
543 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100544
545 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100546}
547
548static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100549_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
550 const char *username)
551{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100552 /* LOCK */
553 pthread_mutex_lock(&server_opts.authkey_lock);
554
Michal Vasko17dfda92016-12-01 14:06:16 +0100555 ++server_opts.authkey_count;
556 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
557 if (!server_opts.authkeys) {
558 ERRMEM;
559 return -1;
560 }
561 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
562 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
563 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
564 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
565
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100566 /* UNLOCK */
567 pthread_mutex_unlock(&server_opts.authkey_lock);
568
Michal Vasko17dfda92016-12-01 14:06:16 +0100569 return 0;
570}
571
572API int
573nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100574{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200575 if (!pubkey_path) {
576 ERRARG("pubkey_path");
577 return -1;
578 } else if (!username) {
579 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100580 return -1;
581 }
582
Michal Vasko17dfda92016-12-01 14:06:16 +0100583 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100584}
585
586API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100587nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100588{
Michal Vasko17dfda92016-12-01 14:06:16 +0100589 if (!pubkey_base64) {
590 ERRARG("pubkey_base64");
591 return -1;
592 } else if (!type) {
593 ERRARG("type");
594 return -1;
595 } else if (!username) {
596 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100597 return -1;
598 }
599
Michal Vasko17dfda92016-12-01 14:06:16 +0100600 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100601}
602
603API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100604nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
605 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100606{
Michal Vasko086311b2016-01-08 09:53:11 +0100607 uint32_t i;
608 int ret = -1;
609
Michal Vasko17dfda92016-12-01 14:06:16 +0100610 /* LOCK */
611 pthread_mutex_lock(&server_opts.authkey_lock);
612
613 if (!pubkey_path && !pubkey_base64 && !type && !username) {
614 for (i = 0; i < server_opts.authkey_count; ++i) {
615 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
616 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
617 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100618
Michal Vasko086311b2016-01-08 09:53:11 +0100619 ret = 0;
620 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100621 free(server_opts.authkeys);
622 server_opts.authkeys = NULL;
623 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100624 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100625 for (i = 0; i < server_opts.authkey_count; ++i) {
626 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
Michal Vaskoe846aee2019-03-28 08:38:41 +0100627 && (!pubkey_base64 || !strcmp(server_opts.authkeys[i].base64, pubkey_base64))
Michal Vasko17dfda92016-12-01 14:06:16 +0100628 && (!type || (server_opts.authkeys[i].type == type))
629 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
630 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
631 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
632 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100633
Michal Vasko17dfda92016-12-01 14:06:16 +0100634 --server_opts.authkey_count;
635 if (i < server_opts.authkey_count) {
636 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
637 sizeof *server_opts.authkeys);
638 } else if (!server_opts.authkey_count) {
639 free(server_opts.authkeys);
640 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100641 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100642
643 ret = 0;
644 }
645 }
Michal Vasko086311b2016-01-08 09:53:11 +0100646 }
647
Michal Vasko51e514d2016-02-02 15:51:52 +0100648 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100649 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100650
651 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100652}
653
654void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100655nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100656{
Michal Vasko7d255882017-02-09 13:35:08 +0100657 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100658}
659
Michal Vasko086311b2016-01-08 09:53:11 +0100660static char *
661auth_password_get_pwd_hash(const char *username)
662{
663 struct passwd *pwd, pwd_buf;
664 struct spwd *spwd, spwd_buf;
665 char *pass_hash = NULL, buf[256];
666
667 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
668 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100669 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100670 return NULL;
671 }
672
673 if (!strcmp(pwd->pw_passwd, "x")) {
674 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
675 if (!spwd) {
676 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
677 return NULL;
678 }
679
680 pass_hash = spwd->sp_pwdp;
681 } else {
682 pass_hash = pwd->pw_passwd;
683 }
684
685 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100686 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100687 return NULL;
688 }
689
690 /* check the hash structure for special meaning */
691 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
692 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
693 return NULL;
694 }
695 if (!strcmp(pass_hash, "*NP*")) {
696 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
697 return NULL;
698 }
699
700 return strdup(pass_hash);
701}
702
703static int
704auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
705{
706 char *new_pass_hash;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200707#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100708 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200709#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100710
711 if (!pass_hash[0]) {
712 if (!pass_clear[0]) {
713 WRN("User authentication successful with an empty password!");
714 return 0;
715 } else {
716 /* the user did now know he does not need any password,
717 * (which should not be used) so deny authentication */
718 return 1;
719 }
720 }
721
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200722#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100723 cdata.initialized = 0;
724 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200725#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200726 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200727 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200728 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200729#endif
Andrew Langefeld158d6fd2018-06-11 18:51:44 -0500730
731 if (!new_pass_hash) {
732 return 1;
733 }
734
Michal Vasko086311b2016-01-08 09:53:11 +0100735 return strcmp(new_pass_hash, pass_hash);
736}
737
738static void
739nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
740{
741 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100742 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100743
Michal Vaskoebba7602018-03-23 13:14:08 +0100744 if (server_opts.passwd_auth_clb) {
745 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
746 } else {
747 pass_hash = auth_password_get_pwd_hash(session->username);
748 if (pass_hash) {
749 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
750 free(pass_hash);
751 }
Michal Vasko086311b2016-01-08 09:53:11 +0100752 }
753
Michal Vaskoebba7602018-03-23 13:14:08 +0100754 if (!auth_ret) {
755 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
756 VRB("User \"%s\" authenticated.", session->username);
757 ssh_message_auth_reply_success(msg, 0);
758 } else {
759 ++session->opts.server.ssh_auth_attempts;
760 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
761 ssh_message_reply_default(msg);
762 }
Michal Vasko086311b2016-01-08 09:53:11 +0100763}
764
765static void
766nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
767{
bhart3bc2f582018-06-05 12:40:32 -0500768 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100769 char *pass_hash;
770
bhart1bb7cdb2018-07-02 15:03:30 -0500771 if (server_opts.interactive_auth_clb) {
Michal Vasko733c0bd2018-07-03 13:14:40 +0200772 auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100773 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500774 if (!ssh_message_auth_kbdint_is_response(msg)) {
775 const char *prompts[] = {"Password: "};
776 char echo[] = {0};
777
778 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
Robert Vargaad7a5532018-08-10 20:40:54 +0200779 auth_ret = -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100780 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500781 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {// failed session
782 ssh_message_reply_default(msg);
783 return;
784 }
bhart3bc2f582018-06-05 12:40:32 -0500785 pass_hash = auth_password_get_pwd_hash(session->username);// get hashed password
786 if (pass_hash) {
Robert Vargaad7a5532018-08-10 20:40:54 +0200787 /* Normalize auth_password_compare_pwd result to 0 or 1 */
788 auth_ret = !!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0));
bhart3bc2f582018-06-05 12:40:32 -0500789 free(pass_hash);// free hashed password
790 }
Michal Vasko086311b2016-01-08 09:53:11 +0100791 }
bhart1bb7cdb2018-07-02 15:03:30 -0500792 }
793
Robert Vargaad7a5532018-08-10 20:40:54 +0200794 /* We have already sent a reply */
795 if (auth_ret == -1) {
796 return;
797 }
798
bhart1bb7cdb2018-07-02 15:03:30 -0500799 /* Authenticate message based on outcome */
800 if (!auth_ret) {
801 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
802 VRB("User \"%s\" authenticated.", session->username);
803 ssh_message_auth_reply_success(msg, 0);
804 } else {
805 ++session->opts.server.ssh_auth_attempts;
806 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
807 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100808 }
809}
810
811static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100812auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100813{
814 uint32_t i;
815 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100816 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100817 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100818
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100819 /* LOCK */
820 pthread_mutex_lock(&server_opts.authkey_lock);
821
Michal Vasko17dfda92016-12-01 14:06:16 +0100822 for (i = 0; i < server_opts.authkey_count; ++i) {
823 switch (server_opts.authkeys[i].type) {
824 case NC_SSH_KEY_UNKNOWN:
825 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
826 break;
827 case NC_SSH_KEY_DSA:
828 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
829 break;
830 case NC_SSH_KEY_RSA:
831 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
832 break;
833 case NC_SSH_KEY_ECDSA:
834 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
835 break;
836 }
837
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200838 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100839 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200840 continue;
841 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100842 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100843 continue;
844 }
845
846 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
847 ssh_key_free(pub_key);
848 break;
849 }
850
851 ssh_key_free(pub_key);
852 }
853
Michal Vasko17dfda92016-12-01 14:06:16 +0100854 if (i < server_opts.authkey_count) {
855 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100856 }
857
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100858 /* UNLOCK */
859 pthread_mutex_unlock(&server_opts.authkey_lock);
860
Michal Vasko086311b2016-01-08 09:53:11 +0100861 return username;
862}
863
864static void
865nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
866{
867 const char *username;
868 int signature_state;
869
Michal Vasko733c0bd2018-07-03 13:14:40 +0200870 if (server_opts.pubkey_auth_clb) {
871 if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) {
bhart3bc2f582018-06-05 12:40:32 -0500872 goto fail;
873 }
Michal Vasko733c0bd2018-07-03 13:14:40 +0200874 } else {
bhart3bc2f582018-06-05 12:40:32 -0500875 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
876 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
877 goto fail;
878 } else if (strcmp(session->username, username)) {
879 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
880 goto fail;
881 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200882 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200883
Michal Vasko086311b2016-01-08 09:53:11 +0100884 signature_state = ssh_message_auth_publickey_state(msg);
885 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
886 VRB("User \"%s\" authenticated.", session->username);
887 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
888 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100889 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200890 /* accepting only the use of a public key */
891 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100892 }
893
Michal Vaskobd13a932016-09-14 09:00:35 +0200894 return;
895
896fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200897 ++session->opts.server.ssh_auth_attempts;
898 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100899 ssh_message_reply_default(msg);
900}
901
902static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100903nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100904{
Michal Vasko96164bf2016-01-21 15:41:58 +0100905 ssh_channel chan;
906
907 /* first channel request */
908 if (!session->ti.libssh.channel) {
909 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100910 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100911 return -1;
912 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100913 chan = ssh_message_channel_request_open_reply_accept(msg);
914 if (!chan) {
915 ERR("Failed to create a new SSH channel.");
916 return -1;
917 }
918 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100919
Michal Vasko96164bf2016-01-21 15:41:58 +0100920 /* additional channel request */
921 } else {
922 chan = ssh_message_channel_request_open_reply_accept(msg);
923 if (!chan) {
924 ERR("Session %u: failed to create a new SSH channel.", session->id);
925 return -1;
926 }
927 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100928 }
929
Michal Vasko086311b2016-01-08 09:53:11 +0100930 return 0;
931}
932
933static int
934nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
935{
Michal Vasko96164bf2016-01-21 15:41:58 +0100936 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100937
Michal Vasko96164bf2016-01-21 15:41:58 +0100938 if (strcmp(subsystem, "netconf")) {
939 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100940 return -1;
941 }
942
Michal Vasko96164bf2016-01-21 15:41:58 +0100943 if (session->ti.libssh.channel == channel) {
944 /* first channel requested */
945 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
946 ERRINT;
947 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100948 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100949 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
950 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
951 return -1;
952 }
953
954 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100955 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100956 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +0200957 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100958 if (!new_session) {
959 ERRMEM;
960 return -1;
961 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100962
963 /* insert the new session */
964 if (!session->ti.libssh.next) {
965 new_session->ti.libssh.next = session;
966 } else {
967 new_session->ti.libssh.next = session->ti.libssh.next;
968 }
969 session->ti.libssh.next = new_session;
970
971 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +0100972 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +0200973 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +0100974 new_session->ti.libssh.channel = channel;
975 new_session->ti.libssh.session = session->ti.libssh.session;
976 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
977 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
978 new_session->port = session->port;
979 new_session->ctx = server_opts.ctx;
Michal Vasko83d15322018-09-27 09:44:02 +0200980 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
Michal Vasko086311b2016-01-08 09:53:11 +0100981 }
982
983 return 0;
984}
985
Michal Vasko96164bf2016-01-21 15:41:58 +0100986int
Michal Vaskob48aa812016-01-18 14:13:09 +0100987nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100988{
989 const char *str_type, *str_subtype = NULL, *username;
990 int subtype, type;
991 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100992
993 type = ssh_message_type(msg);
994 subtype = ssh_message_subtype(msg);
995
996 switch (type) {
997 case SSH_REQUEST_AUTH:
998 str_type = "request-auth";
999 switch (subtype) {
1000 case SSH_AUTH_METHOD_NONE:
1001 str_subtype = "none";
1002 break;
1003 case SSH_AUTH_METHOD_PASSWORD:
1004 str_subtype = "password";
1005 break;
1006 case SSH_AUTH_METHOD_PUBLICKEY:
1007 str_subtype = "publickey";
1008 break;
1009 case SSH_AUTH_METHOD_HOSTBASED:
1010 str_subtype = "hostbased";
1011 break;
1012 case SSH_AUTH_METHOD_INTERACTIVE:
1013 str_subtype = "interactive";
1014 break;
1015 case SSH_AUTH_METHOD_GSSAPI_MIC:
1016 str_subtype = "gssapi-mic";
1017 break;
1018 }
1019 break;
1020
1021 case SSH_REQUEST_CHANNEL_OPEN:
1022 str_type = "request-channel-open";
1023 switch (subtype) {
1024 case SSH_CHANNEL_SESSION:
1025 str_subtype = "session";
1026 break;
1027 case SSH_CHANNEL_DIRECT_TCPIP:
1028 str_subtype = "direct-tcpip";
1029 break;
1030 case SSH_CHANNEL_FORWARDED_TCPIP:
1031 str_subtype = "forwarded-tcpip";
1032 break;
1033 case (int)SSH_CHANNEL_X11:
1034 str_subtype = "channel-x11";
1035 break;
1036 case SSH_CHANNEL_UNKNOWN:
1037 /* fallthrough */
1038 default:
1039 str_subtype = "unknown";
1040 break;
1041 }
1042 break;
1043
1044 case SSH_REQUEST_CHANNEL:
1045 str_type = "request-channel";
1046 switch (subtype) {
1047 case SSH_CHANNEL_REQUEST_PTY:
1048 str_subtype = "pty";
1049 break;
1050 case SSH_CHANNEL_REQUEST_EXEC:
1051 str_subtype = "exec";
1052 break;
1053 case SSH_CHANNEL_REQUEST_SHELL:
1054 str_subtype = "shell";
1055 break;
1056 case SSH_CHANNEL_REQUEST_ENV:
1057 str_subtype = "env";
1058 break;
1059 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1060 str_subtype = "subsystem";
1061 break;
1062 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1063 str_subtype = "window-change";
1064 break;
1065 case SSH_CHANNEL_REQUEST_X11:
1066 str_subtype = "x11";
1067 break;
1068 case SSH_CHANNEL_REQUEST_UNKNOWN:
1069 /* fallthrough */
1070 default:
1071 str_subtype = "unknown";
1072 break;
1073 }
1074 break;
1075
1076 case SSH_REQUEST_SERVICE:
1077 str_type = "request-service";
1078 str_subtype = ssh_message_service_service(msg);
1079 break;
1080
1081 case SSH_REQUEST_GLOBAL:
1082 str_type = "request-global";
1083 switch (subtype) {
1084 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1085 str_subtype = "tcpip-forward";
1086 break;
1087 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1088 str_subtype = "cancel-tcpip-forward";
1089 break;
1090 case SSH_GLOBAL_REQUEST_UNKNOWN:
1091 /* fallthrough */
1092 default:
1093 str_subtype = "unknown";
1094 break;
1095 }
1096 break;
1097
1098 default:
1099 str_type = "unknown";
1100 str_subtype = "unknown";
1101 break;
1102 }
1103
1104 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001105 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1106 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1107 * but we got it now, during session free */
1108 VRB("SSH message arrived on a %s session, the request will be denied.",
1109 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1110 ssh_message_reply_default(msg);
1111 return 0;
1112 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001113 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001114
1115 /*
1116 * process known messages
1117 */
1118 if (type == SSH_REQUEST_AUTH) {
1119 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1120 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1121 ssh_message_reply_default(msg);
1122 return 0;
1123 }
1124
Michal Vasko086311b2016-01-08 09:53:11 +01001125 /* save the username, do not let the client change it */
1126 username = ssh_message_auth_user(msg);
1127 if (!session->username) {
1128 if (!username) {
1129 ERR("Denying an auth request without a username.");
1130 return 1;
1131 }
1132
Michal Vasko05ba9df2016-01-13 14:40:27 +01001133 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001134 } else if (username) {
1135 if (strcmp(username, session->username)) {
1136 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1137 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001138 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001139 return 1;
1140 }
1141 }
1142
1143 if (subtype == SSH_AUTH_METHOD_NONE) {
1144 /* libssh will return the supported auth methods */
1145 return 1;
1146 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1147 nc_sshcb_auth_password(session, msg);
1148 return 0;
1149 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1150 nc_sshcb_auth_pubkey(session, msg);
1151 return 0;
1152 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1153 nc_sshcb_auth_kbdint(session, msg);
1154 return 0;
1155 }
1156 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001157 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001158 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001159 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001160 }
Michal Vasko086311b2016-01-08 09:53:11 +01001161 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001162
Michal Vasko0df67562016-01-21 15:50:11 +01001163 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001164 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1165 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001166 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001167 } else {
1168 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001169 }
1170 return 0;
1171 }
1172 }
1173
1174 /* we did not process it */
1175 return 1;
1176}
1177
Michal Vasko1a38c862016-01-15 15:50:07 +01001178/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001179static int
1180nc_open_netconf_channel(struct nc_session *session, int timeout)
1181{
Michal Vasko36c7be82017-02-22 13:37:59 +01001182 int ret;
1183 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001184
1185 /* message callback is executed twice to give chance for the channel to be
1186 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001187 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001188 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001189 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001190 return -1;
1191 }
1192
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001193 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1194 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001195 ERR("Failed to receive SSH messages on a session (%s).",
1196 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001197 return -1;
1198 }
1199
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001200 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001201 return 0;
1202 }
1203
1204 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1205 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001206 ERR("Failed to receive SSH messages on a session (%s).",
1207 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001208 return -1;
1209 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001210
1211 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1212 /* we did not receive subsystem-request, timeout */
1213 return 0;
1214 }
1215
1216 return 1;
1217 }
1218
Michal Vasko36c7be82017-02-22 13:37:59 +01001219 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001220 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001221 nc_addtimespec(&ts_timeout, timeout);
1222 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001223 while (1) {
1224 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001225 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001226 return -1;
1227 }
1228
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001229 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1230 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001231 ERR("Failed to receive SSH messages on a session (%s).",
1232 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001233 return -1;
1234 }
1235
Michal Vasko086311b2016-01-08 09:53:11 +01001236 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001237 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001238 }
1239
Michal Vasko086311b2016-01-08 09:53:11 +01001240 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001241 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001242 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001243 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1244 /* timeout */
1245 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1246 break;
1247 }
1248 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001249 }
Michal Vasko086311b2016-01-08 09:53:11 +01001250
Michal Vasko1a38c862016-01-15 15:50:07 +01001251 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001252}
1253
Michal Vasko4c1fb492017-01-30 14:31:07 +01001254static int
1255nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1256{
1257 uint8_t i;
1258 char *privkey_path, *privkey_data;
Michal Vaskoddce1212019-05-24 09:58:49 +02001259 int ret;
Michal Vaskoe49a15f2019-05-27 14:18:36 +02001260 NC_SSH_KEY_TYPE privkey_type;
Michal Vasko4c1fb492017-01-30 14:31:07 +01001261
1262 if (!server_opts.hostkey_clb) {
1263 ERR("Callback for retrieving SSH host keys not set.");
1264 return -1;
1265 }
1266
1267 for (i = 0; i < hostkey_count; ++i) {
1268 privkey_path = privkey_data = NULL;
Michal Vaskoddce1212019-05-24 09:58:49 +02001269 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_type)) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001270 ERR("Host key callback failed.");
1271 return -1;
1272 }
1273
1274 if (privkey_data) {
Michal Vaskoddce1212019-05-24 09:58:49 +02001275 privkey_path = base64der_key_to_tmp_file(privkey_data, nc_keytype2str(privkey_type));
Michal Vasko4c1fb492017-01-30 14:31:07 +01001276 if (!privkey_path) {
1277 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1278 free(privkey_data);
1279 return -1;
1280 }
1281 }
1282
1283 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1284
1285 /* cleanup */
1286 if (privkey_data && unlink(privkey_path)) {
1287 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1288 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001289 free(privkey_data);
1290
1291 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001292 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1293 }
1294 free(privkey_path);
1295
1296 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001297 return -1;
1298 }
1299 }
1300
1301 return 0;
1302}
1303
Michal Vasko96164bf2016-01-21 15:41:58 +01001304int
Michal Vasko0190bc32016-03-02 15:47:49 +01001305nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001306{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001307 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001308 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001309 int libssh_auth_methods = 0, ret;
1310 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001311
Michal Vasko2cc4c682016-03-01 09:16:48 +01001312 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001313
Michal Vasko086311b2016-01-08 09:53:11 +01001314 /* other transport-specific data */
1315 session->ti_type = NC_TI_LIBSSH;
1316 session->ti.libssh.session = ssh_new();
1317 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001318 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001319 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001320 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001321 }
1322
Michal Vaskoc61c4492016-01-25 11:13:34 +01001323 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001324 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1325 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001326 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001327 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1328 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001329 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001330 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1331 }
1332 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1333
Michal Vaskoe2713da2016-08-22 16:06:40 +02001334 sbind = ssh_bind_new();
1335 if (!sbind) {
1336 ERR("Failed to create an SSH bind.");
1337 close(sock);
1338 return -1;
1339 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001340
1341 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1342 close(sock);
1343 ssh_bind_free(sbind);
1344 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001345 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001346
Michal Vasko086311b2016-01-08 09:53:11 +01001347 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001348 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001349 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001350
Michal Vaskoe2713da2016-08-22 16:06:40 +02001351 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1352 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001353 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001354 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001355 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001356 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001357 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001358
Michal Vasko0190bc32016-03-02 15:47:49 +01001359 ssh_set_blocking(session->ti.libssh.session, 0);
1360
Michal Vasko36c7be82017-02-22 13:37:59 +01001361 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001362 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001363 nc_addtimespec(&ts_timeout, timeout);
1364 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001365 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001366 /* this tends to take longer */
1367 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001368 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001369 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001370 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1371 break;
1372 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001373 }
1374 }
1375 if (ret == SSH_AGAIN) {
1376 ERR("SSH key exchange timeout.");
1377 return 0;
1378 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001379 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001380 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001381 }
1382
1383 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001384 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001385 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001386 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1387 }
1388 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001389 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001390 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001391 return -1;
1392 }
1393
Michal Vasko086311b2016-01-08 09:53:11 +01001394 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001395 ERR("Failed to receive SSH messages on a session (%s).",
1396 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001397 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001398 }
1399
Michal Vasko36c7be82017-02-22 13:37:59 +01001400 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1401 break;
1402 }
1403
Michal Vasko145ae672017-02-07 10:57:27 +01001404 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1405 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1406 return -1;
1407 }
1408
Michal Vasko086311b2016-01-08 09:53:11 +01001409 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001410 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001411 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001412 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1413 /* timeout */
1414 break;
1415 }
1416 }
1417 }
Michal Vasko086311b2016-01-08 09:53:11 +01001418
1419 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1420 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001421 if (session->username) {
1422 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1423 } else {
1424 ERR("User failed to authenticate for too long, disconnecting.");
1425 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001426 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001427 }
1428
Michal Vasko086311b2016-01-08 09:53:11 +01001429 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001430 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001431 if (ret < 1) {
1432 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001433 }
1434
Michal Vasko96164bf2016-01-21 15:41:58 +01001435 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001436 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001437}
1438
Michal Vasko71090fc2016-05-24 16:37:28 +02001439API NC_MSG_TYPE
1440nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1441{
1442 NC_MSG_TYPE msgtype;
1443 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001444 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001445
1446 if (!orig_session) {
1447 ERRARG("orig_session");
1448 return NC_MSG_ERROR;
1449 } else if (!session) {
1450 ERRARG("session");
1451 return NC_MSG_ERROR;
1452 }
1453
1454 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1455 && orig_session->ti.libssh.next) {
1456 for (new_session = orig_session->ti.libssh.next;
1457 new_session != orig_session;
1458 new_session = new_session->ti.libssh.next) {
1459 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1460 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1461 /* we found our session */
1462 break;
1463 }
1464 }
1465 if (new_session == orig_session) {
1466 new_session = NULL;
1467 }
1468 }
1469
1470 if (!new_session) {
1471 ERR("Session does not have a NETCONF SSH channel ready.");
1472 return NC_MSG_ERROR;
1473 }
1474
1475 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01001476 new_session->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001477
1478 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001479 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001480 if (msgtype != NC_MSG_HELLO) {
1481 return msgtype;
1482 }
1483
Michal Vasko9f6275e2017-10-05 13:50:05 +02001484 nc_gettimespec_real(&ts_cur);
1485 new_session->opts.server.session_start = ts_cur.tv_sec;
1486 nc_gettimespec_mono(&ts_cur);
1487 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001488 new_session->status = NC_STATUS_RUNNING;
1489 *session = new_session;
1490
1491 return msgtype;
1492}
1493
1494API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001495nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001496{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001497 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001498 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001499 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001500 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001501 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001502
Michal Vasko45e53ae2016-04-07 11:46:03 +02001503 if (!ps) {
1504 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001505 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001506 } else if (!session) {
1507 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001508 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001509 }
1510
Michal Vasko48a63ed2016-03-01 09:48:21 +01001511 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001512 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001513 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001514 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001515
Michal Vasko96164bf2016-01-21 15:41:58 +01001516 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001517 cur_session = ps->sessions[i]->session;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001518 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1519 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001520 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001521 for (new_session = cur_session->ti.libssh.next;
1522 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001523 new_session = new_session->ti.libssh.next) {
1524 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1525 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1526 /* we found our session */
1527 break;
1528 }
1529 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001530 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001531 break;
1532 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001533
Michal Vasko96164bf2016-01-21 15:41:58 +01001534 new_session = NULL;
1535 }
1536 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001537
Michal Vasko48a63ed2016-03-01 09:48:21 +01001538 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001539 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001540
Michal Vasko96164bf2016-01-21 15:41:58 +01001541 if (!new_session) {
1542 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001543 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001544 }
1545
1546 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01001547 new_session->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskofb89d772016-01-08 12:25:35 +01001548
Michal Vasko086311b2016-01-08 09:53:11 +01001549 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001550 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001551 if (msgtype != NC_MSG_HELLO) {
1552 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001553 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001554
Michal Vasko9f6275e2017-10-05 13:50:05 +02001555 nc_gettimespec_real(&ts_cur);
1556 new_session->opts.server.session_start = ts_cur.tv_sec;
1557 nc_gettimespec_mono(&ts_cur);
1558 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001559 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001560 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001561
Michal Vasko71090fc2016-05-24 16:37:28 +02001562 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001563}