blob: 88aa0a31e6e2ce33b1e7538cf077ee7d6c8a63cd [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 *
38base64der_key_to_tmp_file(const char *in, int rsa)
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
56 file = fdopen(fd, "r");
57 if (!file) {
58 close(fd);
59 return NULL;
60 }
61
62 /* write the key into the file */
63 written = fwrite("-----BEGIN ", 1, 11, file);
64 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
65 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
66 written += fwrite(in, 1, strlen(in), file);
67 written += fwrite("\n-----END ", 1, 10, file);
68 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
69 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
Michal Vaskob05053d2016-01-22 16:12:06 +0100145API int
Michal Vasko7d255882017-02-09 13:35:08 +0100146nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100147{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100148 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200149 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100150
Michal Vasko2e6defd2016-10-07 15:48:15 +0200151 /* LOCK */
152 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
153 if (!client) {
154 return -1;
155 }
Michal Vasko7d255882017-02-09 13:35:08 +0100156 ret = nc_server_ssh_add_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200157 /* UNLOCK */
158 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200159
160 return ret;
161}
162
Michal Vasko4c1fb492017-01-30 14:31:07 +0100163API void
164nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
165 char **privkey_data, int *privkey_data_rsa),
166 void *user_data, void (*free_user_data)(void *user_data))
167{
168 if (!hostkey_clb) {
169 ERRARG("hostkey_clb");
170 return;
171 }
172
173 server_opts.hostkey_clb = hostkey_clb;
174 server_opts.hostkey_data = user_data;
175 server_opts.hostkey_data_free = free_user_data;
176}
177
Michal Vaskoe2713da2016-08-22 16:06:40 +0200178static int
Michal Vasko7d255882017-02-09 13:35:08 +0100179nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200180{
181 uint8_t i;
182
Michal Vasko7d255882017-02-09 13:35:08 +0100183 if (name && (idx > -1)) {
184 ERRARG("name and idx");
185 return -1;
186 } else if (idx >= opts->hostkey_count) {
187 ERRARG("idx");
188 }
189
190 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200191 for (i = 0; i < opts->hostkey_count; ++i) {
192 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
193 }
194 free(opts->hostkeys);
195 opts->hostkeys = NULL;
196 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100197 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200198 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100199 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100200 idx = i;
201 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200202 }
203 }
204
Michal Vasko7d255882017-02-09 13:35:08 +0100205 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200206 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100207 } else {
208remove_idx:
209 --opts->hostkey_count;
210 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
211 if (idx < opts->hostkey_count - 1) {
212 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
213 }
214 if (!opts->hostkey_count) {
215 free(opts->hostkeys);
216 opts->hostkeys = NULL;
217 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200218 }
219
220 return 0;
221}
222
223API int
Michal Vasko7d255882017-02-09 13:35:08 +0100224nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200225{
226 int ret;
227 struct nc_endpt *endpt;
228
229 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100230 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200231 if (!endpt) {
232 return -1;
233 }
Michal Vasko7d255882017-02-09 13:35:08 +0100234 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200235 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100236 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200237
238 return ret;
239}
240
241API int
Michal Vasko7d255882017-02-09 13:35:08 +0100242nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200243{
244 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200245 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200246
Michal Vasko2e6defd2016-10-07 15:48:15 +0200247 /* LOCK */
248 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
249 if (!client) {
250 return -1;
251 }
Michal Vasko7d255882017-02-09 13:35:08 +0100252 ret = nc_server_ssh_del_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200253 /* UNLOCK */
254 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100255
256 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100257}
258
259static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100260nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
261{
262 uint8_t i;
263 int16_t mov_idx = -1, after_idx = -1;
264 const char *bckup;
265
266 if (!key_mov) {
267 ERRARG("key_mov");
268 return -1;
269 }
270
271 for (i = 0; i < opts->hostkey_count; ++i) {
272 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
273 after_idx = i;
274 }
275 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
276 mov_idx = i;
277 }
278
279 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
280 break;
281 }
282 }
283
284 if (key_after && (after_idx == -1)) {
285 ERRARG("key_after");
286 return -1;
287 }
288 if (mov_idx == -1) {
289 ERRARG("key_mov");
290 return -1;
291 }
292 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
293 /* nothing to do */
294 return 0;
295 }
296
297 /* finally move the key */
298 bckup = opts->hostkeys[mov_idx];
299 if (mov_idx > after_idx) {
300 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
301 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
302 opts->hostkeys[after_idx + 1] = bckup;
303 } else {
304 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
305 opts->hostkeys[after_idx] = bckup;
306 }
307
308 return 0;
309}
310
311API int
312nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
313{
314 int ret;
315 struct nc_endpt *endpt;
316
317 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100318 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100319 if (!endpt) {
320 return -1;
321 }
322 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
323 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100324 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100325
326 return ret;
327}
328
329API int
330nc_server_ssh_ch_client_mov_hostkey(const char *client_name, const char *key_mov, const char *key_after)
331{
332 int ret;
333 struct nc_ch_client *client;
334
335 /* LOCK */
336 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
337 if (!client) {
338 return -1;
339 }
340 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, client->opts.ssh);
341 /* UNLOCK */
342 nc_server_ch_client_unlock(client);
343
344 return ret;
345}
346
347static int
348nc_server_ssh_mod_hostkey(const char *name, const char *new_name, struct nc_server_ssh_opts *opts)
349{
350 uint8_t i;
351
352 if (!name) {
353 ERRARG("name");
354 return -1;
355 } else if (!new_name) {
356 ERRARG("new_name");
357 return -1;
358 }
359
360 for (i = 0; i < opts->hostkey_count; ++i) {
361 if (!strcmp(opts->hostkeys[i], name)) {
362 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
363 opts->hostkeys[i] = lydict_insert(server_opts.ctx, new_name, 0);
364 return 0;
365 }
366 }
367
368 ERRARG("name");
369 return -1;
370}
371
372API int
373nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name)
374{
375 int ret;
376 struct nc_endpt *endpt;
377
378 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100379 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100380 if (!endpt) {
381 return -1;
382 }
383 ret = nc_server_ssh_mov_hostkey(name, new_name, endpt->opts.ssh);
384 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100385 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100386
387 return ret;
388}
389
390API int
391nc_server_ssh_ch_client_mod_hostkey(const char *client_name, const char *name, const char *new_name)
392{
393 int ret;
394 struct nc_ch_client *client;
395
396 /* LOCK */
397 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
398 if (!client) {
399 return -1;
400 }
401 ret = nc_server_ssh_mod_hostkey(name, new_name, client->opts.ssh);
402 /* UNLOCK */
403 nc_server_ch_client_unlock(client);
404
405 return ret;
406}
407
408static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100409nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100410{
Michal Vaskob05053d2016-01-22 16:12:06 +0100411 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200412 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100413 return -1;
414 }
415
Michal Vaskoe2713da2016-08-22 16:06:40 +0200416 if (opts->banner) {
417 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100418 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200419 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100420 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100421}
422
423API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100424nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100425{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100426 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100427 struct nc_endpt *endpt;
428
Michal Vasko51e514d2016-02-02 15:51:52 +0100429 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100430 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100431 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100432 return -1;
433 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200434 ret = nc_server_ssh_set_banner(banner, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100435 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100436 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100437
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100438 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100439}
440
441API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200442nc_server_ssh_ch_client_set_banner(const char *client_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100443{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100444 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200445 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100446
Michal Vasko2e6defd2016-10-07 15:48:15 +0200447 /* LOCK */
448 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
449 if (!client) {
450 return -1;
451 }
452 ret = nc_server_ssh_set_banner(banner, client->opts.ssh);
453 /* UNLOCK */
454 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100455
456 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100457}
458
459static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100460nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100461{
Michal Vasko086311b2016-01-08 09:53:11 +0100462 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
463 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200464 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100465 return -1;
466 }
467
Michal Vaskob05053d2016-01-22 16:12:06 +0100468 opts->auth_methods = auth_methods;
469 return 0;
470}
471
472API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100473nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100474{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100475 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100476 struct nc_endpt *endpt;
477
Michal Vasko51e514d2016-02-02 15:51:52 +0100478 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100479 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100480 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100481 return -1;
482 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200483 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100484 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100485 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100486
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100487 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100488}
489
490API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200491nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100492{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100493 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200494 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100495
Michal Vasko2e6defd2016-10-07 15:48:15 +0200496 /* LOCK */
497 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
498 if (!client) {
499 return -1;
500 }
501 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
502 /* UNLOCK */
503 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100504
505 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100506}
507
508static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100509nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100510{
Michal Vaskob05053d2016-01-22 16:12:06 +0100511 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200512 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100513 return -1;
514 }
515
Michal Vaskob05053d2016-01-22 16:12:06 +0100516 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100517 return 0;
518}
519
520API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100521nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100522{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100523 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100524 struct nc_endpt *endpt;
525
Michal Vasko51e514d2016-02-02 15:51:52 +0100526 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100527 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100528 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100529 return -1;
530 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200531 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100532 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100533 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100534
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100535 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100536}
537
538API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200539nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100540{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100541 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200542 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100543
Michal Vasko2e6defd2016-10-07 15:48:15 +0200544 /* LOCK */
545 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
546 if (!client) {
547 return -1;
548 }
549 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
550 /* UNLOCK */
551 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100552
553 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100554}
555
556static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100557nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100558{
Michal Vaskob05053d2016-01-22 16:12:06 +0100559 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200560 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100561 return -1;
562 }
563
Michal Vaskob05053d2016-01-22 16:12:06 +0100564 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100565 return 0;
566}
567
568API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100569nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100570{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100571 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100572 struct nc_endpt *endpt;
573
Michal Vasko51e514d2016-02-02 15:51:52 +0100574 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100575 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100576 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100577 return -1;
578 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200579 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100580 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100581 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100582
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100583 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100584}
585
586API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200587nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100588{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100589 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200590 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100591
Michal Vasko2e6defd2016-10-07 15:48:15 +0200592 /* LOCK */
593 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
594 if (!client) {
595 return -1;
596 }
597 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
598 /* UNLOCK */
599 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100600
601 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100602}
603
604static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100605_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
606 const char *username)
607{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100608 /* LOCK */
609 pthread_mutex_lock(&server_opts.authkey_lock);
610
Michal Vasko17dfda92016-12-01 14:06:16 +0100611 ++server_opts.authkey_count;
612 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
613 if (!server_opts.authkeys) {
614 ERRMEM;
615 return -1;
616 }
617 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
618 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
619 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
620 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
621
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100622 /* UNLOCK */
623 pthread_mutex_unlock(&server_opts.authkey_lock);
624
Michal Vasko17dfda92016-12-01 14:06:16 +0100625 return 0;
626}
627
628API int
629nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100630{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200631 if (!pubkey_path) {
632 ERRARG("pubkey_path");
633 return -1;
634 } else if (!username) {
635 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100636 return -1;
637 }
638
Michal Vasko17dfda92016-12-01 14:06:16 +0100639 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100640}
641
642API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100643nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100644{
Michal Vasko17dfda92016-12-01 14:06:16 +0100645 if (!pubkey_base64) {
646 ERRARG("pubkey_base64");
647 return -1;
648 } else if (!type) {
649 ERRARG("type");
650 return -1;
651 } else if (!username) {
652 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100653 return -1;
654 }
655
Michal Vasko17dfda92016-12-01 14:06:16 +0100656 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100657}
658
659API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100660nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
661 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100662{
Michal Vasko086311b2016-01-08 09:53:11 +0100663 uint32_t i;
664 int ret = -1;
665
Michal Vasko17dfda92016-12-01 14:06:16 +0100666 /* LOCK */
667 pthread_mutex_lock(&server_opts.authkey_lock);
668
669 if (!pubkey_path && !pubkey_base64 && !type && !username) {
670 for (i = 0; i < server_opts.authkey_count; ++i) {
671 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
672 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
673 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100674
Michal Vasko086311b2016-01-08 09:53:11 +0100675 ret = 0;
676 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100677 free(server_opts.authkeys);
678 server_opts.authkeys = NULL;
679 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100680 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100681 for (i = 0; i < server_opts.authkey_count; ++i) {
682 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
683 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
684 && (!type || (server_opts.authkeys[i].type == type))
685 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
686 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
687 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
688 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100689
Michal Vasko17dfda92016-12-01 14:06:16 +0100690 --server_opts.authkey_count;
691 if (i < server_opts.authkey_count) {
692 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
693 sizeof *server_opts.authkeys);
694 } else if (!server_opts.authkey_count) {
695 free(server_opts.authkeys);
696 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100697 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100698
699 ret = 0;
700 }
701 }
Michal Vasko086311b2016-01-08 09:53:11 +0100702 }
703
Michal Vasko51e514d2016-02-02 15:51:52 +0100704 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100705 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100706
707 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100708}
709
710void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100711nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100712{
Michal Vasko7d255882017-02-09 13:35:08 +0100713 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200714 if (opts->banner) {
715 lydict_remove(server_opts.ctx, opts->banner);
716 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100717 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100718}
719
Michal Vasko086311b2016-01-08 09:53:11 +0100720static char *
721auth_password_get_pwd_hash(const char *username)
722{
723 struct passwd *pwd, pwd_buf;
724 struct spwd *spwd, spwd_buf;
725 char *pass_hash = NULL, buf[256];
726
727 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
728 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100729 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100730 return NULL;
731 }
732
733 if (!strcmp(pwd->pw_passwd, "x")) {
734 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
735 if (!spwd) {
736 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
737 return NULL;
738 }
739
740 pass_hash = spwd->sp_pwdp;
741 } else {
742 pass_hash = pwd->pw_passwd;
743 }
744
745 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100746 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100747 return NULL;
748 }
749
750 /* check the hash structure for special meaning */
751 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
752 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
753 return NULL;
754 }
755 if (!strcmp(pass_hash, "*NP*")) {
756 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
757 return NULL;
758 }
759
760 return strdup(pass_hash);
761}
762
763static int
764auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
765{
766 char *new_pass_hash;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200767#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100768 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200769#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100770
771 if (!pass_hash[0]) {
772 if (!pass_clear[0]) {
773 WRN("User authentication successful with an empty password!");
774 return 0;
775 } else {
776 /* the user did now know he does not need any password,
777 * (which should not be used) so deny authentication */
778 return 1;
779 }
780 }
781
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200782#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100783 cdata.initialized = 0;
784 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200785#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200786 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200787 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200788 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200789#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100790 return strcmp(new_pass_hash, pass_hash);
791}
792
793static void
794nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
795{
796 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100797 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100798
Michal Vaskoebba7602018-03-23 13:14:08 +0100799 if (server_opts.passwd_auth_clb) {
800 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
801 } else {
802 pass_hash = auth_password_get_pwd_hash(session->username);
803 if (pass_hash) {
804 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
805 free(pass_hash);
806 }
Michal Vasko086311b2016-01-08 09:53:11 +0100807 }
808
Michal Vaskoebba7602018-03-23 13:14:08 +0100809 if (!auth_ret) {
810 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
811 VRB("User \"%s\" authenticated.", session->username);
812 ssh_message_auth_reply_success(msg, 0);
813 } else {
814 ++session->opts.server.ssh_auth_attempts;
815 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
816 ssh_message_reply_default(msg);
817 }
Michal Vasko086311b2016-01-08 09:53:11 +0100818}
819
820static void
821nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
822{
823 char *pass_hash;
824
825 if (!ssh_message_auth_kbdint_is_response(msg)) {
826 const char *prompts[] = {"Password: "};
827 char echo[] = {0};
828
829 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
830 } else {
831 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
832 ssh_message_reply_default(msg);
833 return;
834 }
835 pass_hash = auth_password_get_pwd_hash(session->username);
836 if (!pass_hash) {
837 ssh_message_reply_default(msg);
838 return;
839 }
840 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
841 VRB("User \"%s\" authenticated.", session->username);
842 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
843 ssh_message_auth_reply_success(msg, 0);
844 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200845 ++session->opts.server.ssh_auth_attempts;
846 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100847 ssh_message_reply_default(msg);
848 }
Radek Krejcifb533742016-03-04 15:12:54 +0100849 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100850 }
851}
852
853static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100854auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100855{
856 uint32_t i;
857 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100858 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100859 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100860
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100861 /* LOCK */
862 pthread_mutex_lock(&server_opts.authkey_lock);
863
Michal Vasko17dfda92016-12-01 14:06:16 +0100864 for (i = 0; i < server_opts.authkey_count; ++i) {
865 switch (server_opts.authkeys[i].type) {
866 case NC_SSH_KEY_UNKNOWN:
867 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
868 break;
869 case NC_SSH_KEY_DSA:
870 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
871 break;
872 case NC_SSH_KEY_RSA:
873 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
874 break;
875 case NC_SSH_KEY_ECDSA:
876 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
877 break;
878 }
879
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200880 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100881 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200882 continue;
883 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100884 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100885 continue;
886 }
887
888 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
889 ssh_key_free(pub_key);
890 break;
891 }
892
893 ssh_key_free(pub_key);
894 }
895
Michal Vasko17dfda92016-12-01 14:06:16 +0100896 if (i < server_opts.authkey_count) {
897 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100898 }
899
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100900 /* UNLOCK */
901 pthread_mutex_unlock(&server_opts.authkey_lock);
902
Michal Vasko086311b2016-01-08 09:53:11 +0100903 return username;
904}
905
906static void
907nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
908{
909 const char *username;
910 int signature_state;
911
Michal Vasko17dfda92016-12-01 14:06:16 +0100912 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200913 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
914 goto fail;
915 } else if (strcmp(session->username, username)) {
916 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200917 goto fail;
918 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200919
Michal Vasko086311b2016-01-08 09:53:11 +0100920 signature_state = ssh_message_auth_publickey_state(msg);
921 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
922 VRB("User \"%s\" authenticated.", session->username);
923 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
924 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100925 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200926 /* accepting only the use of a public key */
927 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100928 }
929
Michal Vaskobd13a932016-09-14 09:00:35 +0200930 return;
931
932fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200933 ++session->opts.server.ssh_auth_attempts;
934 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100935 ssh_message_reply_default(msg);
936}
937
938static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100939nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100940{
Michal Vasko96164bf2016-01-21 15:41:58 +0100941 ssh_channel chan;
942
943 /* first channel request */
944 if (!session->ti.libssh.channel) {
945 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100946 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100947 return -1;
948 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100949 chan = ssh_message_channel_request_open_reply_accept(msg);
950 if (!chan) {
951 ERR("Failed to create a new SSH channel.");
952 return -1;
953 }
954 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100955
Michal Vasko96164bf2016-01-21 15:41:58 +0100956 /* additional channel request */
957 } else {
958 chan = ssh_message_channel_request_open_reply_accept(msg);
959 if (!chan) {
960 ERR("Session %u: failed to create a new SSH channel.", session->id);
961 return -1;
962 }
963 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100964 }
965
Michal Vasko086311b2016-01-08 09:53:11 +0100966 return 0;
967}
968
969static int
970nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
971{
Michal Vasko96164bf2016-01-21 15:41:58 +0100972 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100973
Michal Vasko96164bf2016-01-21 15:41:58 +0100974 if (strcmp(subsystem, "netconf")) {
975 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100976 return -1;
977 }
978
Michal Vasko96164bf2016-01-21 15:41:58 +0100979 if (session->ti.libssh.channel == channel) {
980 /* first channel requested */
981 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
982 ERRINT;
983 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100984 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100985 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
986 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
987 return -1;
988 }
989
990 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100991 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100992 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +0200993 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100994 if (!new_session) {
995 ERRMEM;
996 return -1;
997 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100998
999 /* insert the new session */
1000 if (!session->ti.libssh.next) {
1001 new_session->ti.libssh.next = session;
1002 } else {
1003 new_session->ti.libssh.next = session->ti.libssh.next;
1004 }
1005 session->ti.libssh.next = new_session;
1006
1007 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001008 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +02001009 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +01001010 new_session->ti.libssh.channel = channel;
1011 new_session->ti.libssh.session = session->ti.libssh.session;
1012 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
1013 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
1014 new_session->port = session->port;
1015 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001016 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
1017 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001018 }
1019
1020 return 0;
1021}
1022
Michal Vasko96164bf2016-01-21 15:41:58 +01001023int
Michal Vaskob48aa812016-01-18 14:13:09 +01001024nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001025{
1026 const char *str_type, *str_subtype = NULL, *username;
1027 int subtype, type;
1028 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001029
1030 type = ssh_message_type(msg);
1031 subtype = ssh_message_subtype(msg);
1032
1033 switch (type) {
1034 case SSH_REQUEST_AUTH:
1035 str_type = "request-auth";
1036 switch (subtype) {
1037 case SSH_AUTH_METHOD_NONE:
1038 str_subtype = "none";
1039 break;
1040 case SSH_AUTH_METHOD_PASSWORD:
1041 str_subtype = "password";
1042 break;
1043 case SSH_AUTH_METHOD_PUBLICKEY:
1044 str_subtype = "publickey";
1045 break;
1046 case SSH_AUTH_METHOD_HOSTBASED:
1047 str_subtype = "hostbased";
1048 break;
1049 case SSH_AUTH_METHOD_INTERACTIVE:
1050 str_subtype = "interactive";
1051 break;
1052 case SSH_AUTH_METHOD_GSSAPI_MIC:
1053 str_subtype = "gssapi-mic";
1054 break;
1055 }
1056 break;
1057
1058 case SSH_REQUEST_CHANNEL_OPEN:
1059 str_type = "request-channel-open";
1060 switch (subtype) {
1061 case SSH_CHANNEL_SESSION:
1062 str_subtype = "session";
1063 break;
1064 case SSH_CHANNEL_DIRECT_TCPIP:
1065 str_subtype = "direct-tcpip";
1066 break;
1067 case SSH_CHANNEL_FORWARDED_TCPIP:
1068 str_subtype = "forwarded-tcpip";
1069 break;
1070 case (int)SSH_CHANNEL_X11:
1071 str_subtype = "channel-x11";
1072 break;
1073 case SSH_CHANNEL_UNKNOWN:
1074 /* fallthrough */
1075 default:
1076 str_subtype = "unknown";
1077 break;
1078 }
1079 break;
1080
1081 case SSH_REQUEST_CHANNEL:
1082 str_type = "request-channel";
1083 switch (subtype) {
1084 case SSH_CHANNEL_REQUEST_PTY:
1085 str_subtype = "pty";
1086 break;
1087 case SSH_CHANNEL_REQUEST_EXEC:
1088 str_subtype = "exec";
1089 break;
1090 case SSH_CHANNEL_REQUEST_SHELL:
1091 str_subtype = "shell";
1092 break;
1093 case SSH_CHANNEL_REQUEST_ENV:
1094 str_subtype = "env";
1095 break;
1096 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1097 str_subtype = "subsystem";
1098 break;
1099 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1100 str_subtype = "window-change";
1101 break;
1102 case SSH_CHANNEL_REQUEST_X11:
1103 str_subtype = "x11";
1104 break;
1105 case SSH_CHANNEL_REQUEST_UNKNOWN:
1106 /* fallthrough */
1107 default:
1108 str_subtype = "unknown";
1109 break;
1110 }
1111 break;
1112
1113 case SSH_REQUEST_SERVICE:
1114 str_type = "request-service";
1115 str_subtype = ssh_message_service_service(msg);
1116 break;
1117
1118 case SSH_REQUEST_GLOBAL:
1119 str_type = "request-global";
1120 switch (subtype) {
1121 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1122 str_subtype = "tcpip-forward";
1123 break;
1124 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1125 str_subtype = "cancel-tcpip-forward";
1126 break;
1127 case SSH_GLOBAL_REQUEST_UNKNOWN:
1128 /* fallthrough */
1129 default:
1130 str_subtype = "unknown";
1131 break;
1132 }
1133 break;
1134
1135 default:
1136 str_type = "unknown";
1137 str_subtype = "unknown";
1138 break;
1139 }
1140
1141 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001142 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1143 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1144 * but we got it now, during session free */
1145 VRB("SSH message arrived on a %s session, the request will be denied.",
1146 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1147 ssh_message_reply_default(msg);
1148 return 0;
1149 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001150 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001151
1152 /*
1153 * process known messages
1154 */
1155 if (type == SSH_REQUEST_AUTH) {
1156 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1157 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1158 ssh_message_reply_default(msg);
1159 return 0;
1160 }
1161
Michal Vasko086311b2016-01-08 09:53:11 +01001162 /* save the username, do not let the client change it */
1163 username = ssh_message_auth_user(msg);
1164 if (!session->username) {
1165 if (!username) {
1166 ERR("Denying an auth request without a username.");
1167 return 1;
1168 }
1169
Michal Vasko05ba9df2016-01-13 14:40:27 +01001170 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001171 } else if (username) {
1172 if (strcmp(username, session->username)) {
1173 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1174 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001175 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001176 return 1;
1177 }
1178 }
1179
1180 if (subtype == SSH_AUTH_METHOD_NONE) {
1181 /* libssh will return the supported auth methods */
1182 return 1;
1183 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1184 nc_sshcb_auth_password(session, msg);
1185 return 0;
1186 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1187 nc_sshcb_auth_pubkey(session, msg);
1188 return 0;
1189 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1190 nc_sshcb_auth_kbdint(session, msg);
1191 return 0;
1192 }
1193 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001194 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001195 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001196 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001197 }
Michal Vasko086311b2016-01-08 09:53:11 +01001198 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001199
Michal Vasko0df67562016-01-21 15:50:11 +01001200 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001201 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1202 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001203 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001204 } else {
1205 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001206 }
1207 return 0;
1208 }
1209 }
1210
1211 /* we did not process it */
1212 return 1;
1213}
1214
Michal Vasko1a38c862016-01-15 15:50:07 +01001215/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001216static int
1217nc_open_netconf_channel(struct nc_session *session, int timeout)
1218{
Michal Vasko36c7be82017-02-22 13:37:59 +01001219 int ret;
1220 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001221
1222 /* message callback is executed twice to give chance for the channel to be
1223 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001224 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001225 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001226 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001227 return -1;
1228 }
1229
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001230 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1231 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001232 ERR("Failed to receive SSH messages on a session (%s).",
1233 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001234 return -1;
1235 }
1236
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001237 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001238 return 0;
1239 }
1240
1241 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1242 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001243 ERR("Failed to receive SSH messages on a session (%s).",
1244 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001245 return -1;
1246 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001247
1248 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1249 /* we did not receive subsystem-request, timeout */
1250 return 0;
1251 }
1252
1253 return 1;
1254 }
1255
Michal Vasko36c7be82017-02-22 13:37:59 +01001256 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001257 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001258 nc_addtimespec(&ts_timeout, timeout);
1259 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001260 while (1) {
1261 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001262 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001263 return -1;
1264 }
1265
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001266 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1267 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001268 ERR("Failed to receive SSH messages on a session (%s).",
1269 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001270 return -1;
1271 }
1272
Michal Vasko086311b2016-01-08 09:53:11 +01001273 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001274 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001275 }
1276
Michal Vasko086311b2016-01-08 09:53:11 +01001277 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001278 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001279 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001280 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1281 /* timeout */
1282 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1283 break;
1284 }
1285 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001286 }
Michal Vasko086311b2016-01-08 09:53:11 +01001287
Michal Vasko1a38c862016-01-15 15:50:07 +01001288 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001289}
1290
Michal Vasko4c1fb492017-01-30 14:31:07 +01001291static int
1292nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1293{
1294 uint8_t i;
1295 char *privkey_path, *privkey_data;
1296 int privkey_data_rsa, ret;
1297
1298 if (!server_opts.hostkey_clb) {
1299 ERR("Callback for retrieving SSH host keys not set.");
1300 return -1;
1301 }
1302
1303 for (i = 0; i < hostkey_count; ++i) {
1304 privkey_path = privkey_data = NULL;
1305 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1306 ERR("Host key callback failed.");
1307 return -1;
1308 }
1309
1310 if (privkey_data) {
1311 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1312 if (!privkey_path) {
1313 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1314 free(privkey_data);
1315 return -1;
1316 }
1317 }
1318
1319 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1320
1321 /* cleanup */
1322 if (privkey_data && unlink(privkey_path)) {
1323 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1324 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001325 free(privkey_data);
1326
1327 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001328 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1329 }
1330 free(privkey_path);
1331
1332 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001333 return -1;
1334 }
1335 }
1336
1337 return 0;
1338}
1339
Michal Vasko96164bf2016-01-21 15:41:58 +01001340int
Michal Vasko0190bc32016-03-02 15:47:49 +01001341nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001342{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001343 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001344 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001345 int libssh_auth_methods = 0, ret;
1346 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001347
Michal Vasko2cc4c682016-03-01 09:16:48 +01001348 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001349
Michal Vasko086311b2016-01-08 09:53:11 +01001350 /* other transport-specific data */
1351 session->ti_type = NC_TI_LIBSSH;
1352 session->ti.libssh.session = ssh_new();
1353 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001354 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001355 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001356 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001357 }
1358
Michal Vaskoc61c4492016-01-25 11:13:34 +01001359 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001360 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1361 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001362 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001363 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1364 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001365 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001366 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1367 }
1368 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1369
Michal Vaskoe2713da2016-08-22 16:06:40 +02001370 sbind = ssh_bind_new();
1371 if (!sbind) {
1372 ERR("Failed to create an SSH bind.");
1373 close(sock);
1374 return -1;
1375 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001376
1377 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1378 close(sock);
1379 ssh_bind_free(sbind);
1380 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001381 }
1382 if (opts->banner) {
1383 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1384 }
1385
Michal Vasko086311b2016-01-08 09:53:11 +01001386 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001387 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001388 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001389
Michal Vaskoe2713da2016-08-22 16:06:40 +02001390 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1391 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001392 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001393 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001394 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001395 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001396 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001397
Michal Vasko0190bc32016-03-02 15:47:49 +01001398 ssh_set_blocking(session->ti.libssh.session, 0);
1399
Michal Vasko36c7be82017-02-22 13:37:59 +01001400 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001401 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001402 nc_addtimespec(&ts_timeout, timeout);
1403 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001404 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001405 /* this tends to take longer */
1406 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001407 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001408 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001409 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1410 break;
1411 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001412 }
1413 }
1414 if (ret == SSH_AGAIN) {
1415 ERR("SSH key exchange timeout.");
1416 return 0;
1417 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001418 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001419 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001420 }
1421
1422 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001423 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001424 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001425 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1426 }
1427 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001428 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001429 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001430 return -1;
1431 }
1432
Michal Vasko086311b2016-01-08 09:53:11 +01001433 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001434 ERR("Failed to receive SSH messages on a session (%s).",
1435 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001436 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001437 }
1438
Michal Vasko36c7be82017-02-22 13:37:59 +01001439 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1440 break;
1441 }
1442
Michal Vasko145ae672017-02-07 10:57:27 +01001443 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1444 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1445 return -1;
1446 }
1447
Michal Vasko086311b2016-01-08 09:53:11 +01001448 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001449 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001450 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001451 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1452 /* timeout */
1453 break;
1454 }
1455 }
1456 }
Michal Vasko086311b2016-01-08 09:53:11 +01001457
1458 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1459 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001460 if (session->username) {
1461 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1462 } else {
1463 ERR("User failed to authenticate for too long, disconnecting.");
1464 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001465 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001466 }
1467
Michal Vasko086311b2016-01-08 09:53:11 +01001468 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001469 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001470 if (ret < 1) {
1471 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001472 }
1473
Michal Vasko96164bf2016-01-21 15:41:58 +01001474 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001475 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001476}
1477
Michal Vasko71090fc2016-05-24 16:37:28 +02001478API NC_MSG_TYPE
1479nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1480{
1481 NC_MSG_TYPE msgtype;
1482 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001483 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001484
1485 if (!orig_session) {
1486 ERRARG("orig_session");
1487 return NC_MSG_ERROR;
1488 } else if (!session) {
1489 ERRARG("session");
1490 return NC_MSG_ERROR;
1491 }
1492
1493 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1494 && orig_session->ti.libssh.next) {
1495 for (new_session = orig_session->ti.libssh.next;
1496 new_session != orig_session;
1497 new_session = new_session->ti.libssh.next) {
1498 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1499 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1500 /* we found our session */
1501 break;
1502 }
1503 }
1504 if (new_session == orig_session) {
1505 new_session = NULL;
1506 }
1507 }
1508
1509 if (!new_session) {
1510 ERR("Session does not have a NETCONF SSH channel ready.");
1511 return NC_MSG_ERROR;
1512 }
1513
1514 /* assign new SID atomically */
1515 pthread_spin_lock(&server_opts.sid_lock);
1516 new_session->id = server_opts.new_session_id++;
1517 pthread_spin_unlock(&server_opts.sid_lock);
1518
1519 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001520 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001521 if (msgtype != NC_MSG_HELLO) {
1522 return msgtype;
1523 }
1524
Michal Vasko9f6275e2017-10-05 13:50:05 +02001525 nc_gettimespec_real(&ts_cur);
1526 new_session->opts.server.session_start = ts_cur.tv_sec;
1527 nc_gettimespec_mono(&ts_cur);
1528 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001529 new_session->status = NC_STATUS_RUNNING;
1530 *session = new_session;
1531
1532 return msgtype;
1533}
1534
1535API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001536nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001537{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001538 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001539 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001540 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001541 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001542 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001543
Michal Vasko45e53ae2016-04-07 11:46:03 +02001544 if (!ps) {
1545 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001546 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001547 } else if (!session) {
1548 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001549 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001550 }
1551
Michal Vasko48a63ed2016-03-01 09:48:21 +01001552 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001553 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001554 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001555 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001556
Michal Vasko96164bf2016-01-21 15:41:58 +01001557 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001558 cur_session = ps->sessions[i]->session;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001559 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1560 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001561 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001562 for (new_session = cur_session->ti.libssh.next;
1563 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001564 new_session = new_session->ti.libssh.next) {
1565 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1566 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1567 /* we found our session */
1568 break;
1569 }
1570 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001571 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001572 break;
1573 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001574
Michal Vasko96164bf2016-01-21 15:41:58 +01001575 new_session = NULL;
1576 }
1577 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001578
Michal Vasko48a63ed2016-03-01 09:48:21 +01001579 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001580 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001581
Michal Vasko96164bf2016-01-21 15:41:58 +01001582 if (!new_session) {
1583 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001584 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001585 }
1586
1587 /* assign new SID atomically */
1588 pthread_spin_lock(&server_opts.sid_lock);
1589 new_session->id = server_opts.new_session_id++;
1590 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001591
Michal Vasko086311b2016-01-08 09:53:11 +01001592 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001593 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001594 if (msgtype != NC_MSG_HELLO) {
1595 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001596 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001597
Michal Vasko9f6275e2017-10-05 13:50:05 +02001598 nc_gettimespec_real(&ts_cur);
1599 new_session->opts.server.session_start = ts_cur.tv_sec;
1600 nc_gettimespec_mono(&ts_cur);
1601 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001602 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001603 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001604
Michal Vasko71090fc2016-05-24 16:37:28 +02001605 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001606}