blob: 86637e6e2a7274736cf84709868f92330d5baef0 [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
Michal Vasko4c1fb492017-01-30 14:31:07 +010016#define _POSIX_SOURCE
Michal Vasko086311b2016-01-08 09:53:11 +010017
18#include <stdlib.h>
19#include <string.h>
20#include <sys/types.h>
Michal Vasko27252692017-03-21 15:34:13 +010021#include <sys/stat.h>
Michal Vasko086311b2016-01-08 09:53:11 +010022#include <pwd.h>
23#include <shadow.h>
24#include <crypt.h>
25#include <errno.h>
26
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
31extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010032
Michal Vasko4c1fb492017-01-30 14:31:07 +010033static char *
34base64der_key_to_tmp_file(const char *in, int rsa)
Michal Vasko086311b2016-01-08 09:53:11 +010035{
Michal Vasko4c1fb492017-01-30 14:31:07 +010036 char path[12] = "/tmp/XXXXXX";
37 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010038 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010039 FILE *file;
40
41 if (in == NULL) {
42 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010043 }
44
Michal Vasko27252692017-03-21 15:34:13 +010045 umode = umask(0600);
Michal Vasko4c1fb492017-01-30 14:31:07 +010046 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010047 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010048 if (fd == -1) {
49 return NULL;
50 }
51
52 file = fdopen(fd, "r");
53 if (!file) {
54 close(fd);
55 return NULL;
56 }
57
58 /* write the key into the file */
59 written = fwrite("-----BEGIN ", 1, 11, file);
60 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
61 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
62 written += fwrite(in, 1, strlen(in), file);
63 written += fwrite("\n-----END ", 1, 10, file);
64 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
65 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
66
67 fclose(file);
68 if ((unsigned)written != 62 + strlen(in)) {
69 unlink(path);
70 return NULL;
71 }
72
73 return strdup(path);
74}
75
76static int
Michal Vasko7d255882017-02-09 13:35:08 +010077nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +010078{
Michal Vaskofbfe8b62017-02-14 10:22:30 +010079 uint8_t i;
80
Michal Vasko4c1fb492017-01-30 14:31:07 +010081 if (!name) {
82 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +010083 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +010084 } else if (idx > opts->hostkey_count) {
85 ERRARG("idx");
86 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010087 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010088
Michal Vaskofbfe8b62017-02-14 10:22:30 +010089 for (i = 0; i < opts->hostkey_count; ++i) {
90 if (!strcmp(opts->hostkeys[i], name)) {
91 ERRARG("name");
92 return -1;
93 }
94 }
95
Michal Vaskoe2713da2016-08-22 16:06:40 +020096 ++opts->hostkey_count;
97 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
98 if (!opts->hostkeys) {
99 ERRMEM;
100 return -1;
101 }
Michal Vasko7d255882017-02-09 13:35:08 +0100102
103 if (idx < 0) {
104 idx = opts->hostkey_count - 1;
105 }
106 if (idx != opts->hostkey_count - 1) {
107 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
108 }
109 opts->hostkeys[idx] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200110
Michal Vasko5fcc7142016-02-02 12:21:10 +0100111 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100112}
113
114API int
Michal Vasko7d255882017-02-09 13:35:08 +0100115nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100116{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100117 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100118 struct nc_endpt *endpt;
119
Michal Vasko51e514d2016-02-02 15:51:52 +0100120 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100121 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100122 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100123 return -1;
124 }
Michal Vasko7d255882017-02-09 13:35:08 +0100125 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100126 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100127 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100128
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100129 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100130}
131
132API int
Michal Vasko7d255882017-02-09 13:35:08 +0100133nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100134{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100135 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200136 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100137
Michal Vasko2e6defd2016-10-07 15:48:15 +0200138 /* LOCK */
139 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
140 if (!client) {
141 return -1;
142 }
Michal Vasko7d255882017-02-09 13:35:08 +0100143 ret = nc_server_ssh_add_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200144 /* UNLOCK */
145 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200146
147 return ret;
148}
149
Michal Vasko4c1fb492017-01-30 14:31:07 +0100150API void
151nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
152 char **privkey_data, int *privkey_data_rsa),
153 void *user_data, void (*free_user_data)(void *user_data))
154{
155 if (!hostkey_clb) {
156 ERRARG("hostkey_clb");
157 return;
158 }
159
160 server_opts.hostkey_clb = hostkey_clb;
161 server_opts.hostkey_data = user_data;
162 server_opts.hostkey_data_free = free_user_data;
163}
164
Michal Vaskoe2713da2016-08-22 16:06:40 +0200165static int
Michal Vasko7d255882017-02-09 13:35:08 +0100166nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200167{
168 uint8_t i;
169
Michal Vasko7d255882017-02-09 13:35:08 +0100170 if (name && (idx > -1)) {
171 ERRARG("name and idx");
172 return -1;
173 } else if (idx >= opts->hostkey_count) {
174 ERRARG("idx");
175 }
176
177 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200178 for (i = 0; i < opts->hostkey_count; ++i) {
179 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
180 }
181 free(opts->hostkeys);
182 opts->hostkeys = NULL;
183 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100184 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200185 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100186 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100187 idx = i;
188 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200189 }
190 }
191
Michal Vasko7d255882017-02-09 13:35:08 +0100192 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200193 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100194 } else {
195remove_idx:
196 --opts->hostkey_count;
197 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
198 if (idx < opts->hostkey_count - 1) {
199 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
200 }
201 if (!opts->hostkey_count) {
202 free(opts->hostkeys);
203 opts->hostkeys = NULL;
204 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200205 }
206
207 return 0;
208}
209
210API int
Michal Vasko7d255882017-02-09 13:35:08 +0100211nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200212{
213 int ret;
214 struct nc_endpt *endpt;
215
216 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100217 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200218 if (!endpt) {
219 return -1;
220 }
Michal Vasko7d255882017-02-09 13:35:08 +0100221 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200222 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100223 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200224
225 return ret;
226}
227
228API int
Michal Vasko7d255882017-02-09 13:35:08 +0100229nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200230{
231 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200232 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200233
Michal Vasko2e6defd2016-10-07 15:48:15 +0200234 /* LOCK */
235 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
236 if (!client) {
237 return -1;
238 }
Michal Vasko7d255882017-02-09 13:35:08 +0100239 ret = nc_server_ssh_del_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200240 /* UNLOCK */
241 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100242
243 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100244}
245
246static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100247nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
248{
249 uint8_t i;
250 int16_t mov_idx = -1, after_idx = -1;
251 const char *bckup;
252
253 if (!key_mov) {
254 ERRARG("key_mov");
255 return -1;
256 }
257
258 for (i = 0; i < opts->hostkey_count; ++i) {
259 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
260 after_idx = i;
261 }
262 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
263 mov_idx = i;
264 }
265
266 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
267 break;
268 }
269 }
270
271 if (key_after && (after_idx == -1)) {
272 ERRARG("key_after");
273 return -1;
274 }
275 if (mov_idx == -1) {
276 ERRARG("key_mov");
277 return -1;
278 }
279 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
280 /* nothing to do */
281 return 0;
282 }
283
284 /* finally move the key */
285 bckup = opts->hostkeys[mov_idx];
286 if (mov_idx > after_idx) {
287 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
288 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
289 opts->hostkeys[after_idx + 1] = bckup;
290 } else {
291 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
292 opts->hostkeys[after_idx] = bckup;
293 }
294
295 return 0;
296}
297
298API int
299nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
300{
301 int ret;
302 struct nc_endpt *endpt;
303
304 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100305 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100306 if (!endpt) {
307 return -1;
308 }
309 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
310 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100311 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100312
313 return ret;
314}
315
316API int
317nc_server_ssh_ch_client_mov_hostkey(const char *client_name, const char *key_mov, const char *key_after)
318{
319 int ret;
320 struct nc_ch_client *client;
321
322 /* LOCK */
323 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
324 if (!client) {
325 return -1;
326 }
327 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, client->opts.ssh);
328 /* UNLOCK */
329 nc_server_ch_client_unlock(client);
330
331 return ret;
332}
333
334static int
335nc_server_ssh_mod_hostkey(const char *name, const char *new_name, struct nc_server_ssh_opts *opts)
336{
337 uint8_t i;
338
339 if (!name) {
340 ERRARG("name");
341 return -1;
342 } else if (!new_name) {
343 ERRARG("new_name");
344 return -1;
345 }
346
347 for (i = 0; i < opts->hostkey_count; ++i) {
348 if (!strcmp(opts->hostkeys[i], name)) {
349 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
350 opts->hostkeys[i] = lydict_insert(server_opts.ctx, new_name, 0);
351 return 0;
352 }
353 }
354
355 ERRARG("name");
356 return -1;
357}
358
359API int
360nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name)
361{
362 int ret;
363 struct nc_endpt *endpt;
364
365 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100366 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100367 if (!endpt) {
368 return -1;
369 }
370 ret = nc_server_ssh_mov_hostkey(name, new_name, endpt->opts.ssh);
371 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100372 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100373
374 return ret;
375}
376
377API int
378nc_server_ssh_ch_client_mod_hostkey(const char *client_name, const char *name, const char *new_name)
379{
380 int ret;
381 struct nc_ch_client *client;
382
383 /* LOCK */
384 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
385 if (!client) {
386 return -1;
387 }
388 ret = nc_server_ssh_mod_hostkey(name, new_name, client->opts.ssh);
389 /* UNLOCK */
390 nc_server_ch_client_unlock(client);
391
392 return ret;
393}
394
395static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100396nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100397{
Michal Vaskob05053d2016-01-22 16:12:06 +0100398 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200399 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100400 return -1;
401 }
402
Michal Vaskoe2713da2016-08-22 16:06:40 +0200403 if (opts->banner) {
404 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100405 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200406 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100407 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100408}
409
410API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100411nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100412{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100413 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100414 struct nc_endpt *endpt;
415
Michal Vasko51e514d2016-02-02 15:51:52 +0100416 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100417 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100418 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100419 return -1;
420 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200421 ret = nc_server_ssh_set_banner(banner, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100422 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100423 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100424
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100425 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100426}
427
428API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200429nc_server_ssh_ch_client_set_banner(const char *client_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100430{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100431 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200432 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100433
Michal Vasko2e6defd2016-10-07 15:48:15 +0200434 /* LOCK */
435 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
436 if (!client) {
437 return -1;
438 }
439 ret = nc_server_ssh_set_banner(banner, client->opts.ssh);
440 /* UNLOCK */
441 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100442
443 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100444}
445
446static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100447nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100448{
Michal Vasko086311b2016-01-08 09:53:11 +0100449 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
450 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200451 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100452 return -1;
453 }
454
Michal Vaskob05053d2016-01-22 16:12:06 +0100455 opts->auth_methods = auth_methods;
456 return 0;
457}
458
459API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100460nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100461{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100462 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100463 struct nc_endpt *endpt;
464
Michal Vasko51e514d2016-02-02 15:51:52 +0100465 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100466 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100467 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100468 return -1;
469 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200470 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100471 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100472 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100473
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100474 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100475}
476
477API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200478nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100479{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100480 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200481 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100482
Michal Vasko2e6defd2016-10-07 15:48:15 +0200483 /* LOCK */
484 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
485 if (!client) {
486 return -1;
487 }
488 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
489 /* UNLOCK */
490 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100491
492 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100493}
494
495static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100496nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100497{
Michal Vaskob05053d2016-01-22 16:12:06 +0100498 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200499 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100500 return -1;
501 }
502
Michal Vaskob05053d2016-01-22 16:12:06 +0100503 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100504 return 0;
505}
506
507API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100508nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100509{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100510 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100511 struct nc_endpt *endpt;
512
Michal Vasko51e514d2016-02-02 15:51:52 +0100513 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100514 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100515 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100516 return -1;
517 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200518 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100519 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100520 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100521
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100522 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100523}
524
525API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200526nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100527{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100528 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200529 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100530
Michal Vasko2e6defd2016-10-07 15:48:15 +0200531 /* LOCK */
532 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
533 if (!client) {
534 return -1;
535 }
536 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
537 /* UNLOCK */
538 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100539
540 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100541}
542
543static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100544nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100545{
Michal Vaskob05053d2016-01-22 16:12:06 +0100546 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200547 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100548 return -1;
549 }
550
Michal Vaskob05053d2016-01-22 16:12:06 +0100551 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100552 return 0;
553}
554
555API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100556nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100557{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100558 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100559 struct nc_endpt *endpt;
560
Michal Vasko51e514d2016-02-02 15:51:52 +0100561 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100562 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100563 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100564 return -1;
565 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200566 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100567 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100568 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100569
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100570 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100571}
572
573API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200574nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100575{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100576 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200577 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100578
Michal Vasko2e6defd2016-10-07 15:48:15 +0200579 /* LOCK */
580 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
581 if (!client) {
582 return -1;
583 }
584 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
585 /* UNLOCK */
586 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100587
588 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100589}
590
591static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100592_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
593 const char *username)
594{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100595 /* LOCK */
596 pthread_mutex_lock(&server_opts.authkey_lock);
597
Michal Vasko17dfda92016-12-01 14:06:16 +0100598 ++server_opts.authkey_count;
599 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
600 if (!server_opts.authkeys) {
601 ERRMEM;
602 return -1;
603 }
604 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
605 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
606 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
607 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
608
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100609 /* UNLOCK */
610 pthread_mutex_unlock(&server_opts.authkey_lock);
611
Michal Vasko17dfda92016-12-01 14:06:16 +0100612 return 0;
613}
614
615API int
616nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100617{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200618 if (!pubkey_path) {
619 ERRARG("pubkey_path");
620 return -1;
621 } else if (!username) {
622 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100623 return -1;
624 }
625
Michal Vasko17dfda92016-12-01 14:06:16 +0100626 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100627}
628
629API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100630nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100631{
Michal Vasko17dfda92016-12-01 14:06:16 +0100632 if (!pubkey_base64) {
633 ERRARG("pubkey_base64");
634 return -1;
635 } else if (!type) {
636 ERRARG("type");
637 return -1;
638 } else if (!username) {
639 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100640 return -1;
641 }
642
Michal Vasko17dfda92016-12-01 14:06:16 +0100643 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100644}
645
646API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100647nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
648 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100649{
Michal Vasko086311b2016-01-08 09:53:11 +0100650 uint32_t i;
651 int ret = -1;
652
Michal Vasko17dfda92016-12-01 14:06:16 +0100653 /* LOCK */
654 pthread_mutex_lock(&server_opts.authkey_lock);
655
656 if (!pubkey_path && !pubkey_base64 && !type && !username) {
657 for (i = 0; i < server_opts.authkey_count; ++i) {
658 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
659 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
660 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100661
Michal Vasko086311b2016-01-08 09:53:11 +0100662 ret = 0;
663 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100664 free(server_opts.authkeys);
665 server_opts.authkeys = NULL;
666 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100667 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100668 for (i = 0; i < server_opts.authkey_count; ++i) {
669 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
670 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
671 && (!type || (server_opts.authkeys[i].type == type))
672 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
673 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
674 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
675 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100676
Michal Vasko17dfda92016-12-01 14:06:16 +0100677 --server_opts.authkey_count;
678 if (i < server_opts.authkey_count) {
679 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
680 sizeof *server_opts.authkeys);
681 } else if (!server_opts.authkey_count) {
682 free(server_opts.authkeys);
683 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100684 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100685
686 ret = 0;
687 }
688 }
Michal Vasko086311b2016-01-08 09:53:11 +0100689 }
690
Michal Vasko51e514d2016-02-02 15:51:52 +0100691 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100692 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100693
694 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100695}
696
697void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100698nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100699{
Michal Vasko7d255882017-02-09 13:35:08 +0100700 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200701 if (opts->banner) {
702 lydict_remove(server_opts.ctx, opts->banner);
703 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100704 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100705}
706
Michal Vasko086311b2016-01-08 09:53:11 +0100707static char *
708auth_password_get_pwd_hash(const char *username)
709{
710 struct passwd *pwd, pwd_buf;
711 struct spwd *spwd, spwd_buf;
712 char *pass_hash = NULL, buf[256];
713
714 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
715 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100716 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100717 return NULL;
718 }
719
720 if (!strcmp(pwd->pw_passwd, "x")) {
721 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
722 if (!spwd) {
723 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
724 return NULL;
725 }
726
727 pass_hash = spwd->sp_pwdp;
728 } else {
729 pass_hash = pwd->pw_passwd;
730 }
731
732 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100733 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100734 return NULL;
735 }
736
737 /* check the hash structure for special meaning */
738 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
739 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
740 return NULL;
741 }
742 if (!strcmp(pass_hash, "*NP*")) {
743 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
744 return NULL;
745 }
746
747 return strdup(pass_hash);
748}
749
750static int
751auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
752{
753 char *new_pass_hash;
754 struct crypt_data cdata;
755
756 if (!pass_hash[0]) {
757 if (!pass_clear[0]) {
758 WRN("User authentication successful with an empty password!");
759 return 0;
760 } else {
761 /* the user did now know he does not need any password,
762 * (which should not be used) so deny authentication */
763 return 1;
764 }
765 }
766
767 cdata.initialized = 0;
768 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
769 return strcmp(new_pass_hash, pass_hash);
770}
771
772static void
773nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
774{
775 char *pass_hash;
776
777 pass_hash = auth_password_get_pwd_hash(session->username);
778 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100779 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100780 ssh_message_auth_reply_success(msg, 0);
781 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
782 free(pass_hash);
783 return;
784 }
785
786 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200787 ++session->opts.server.ssh_auth_attempts;
788 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100789 ssh_message_reply_default(msg);
790}
791
792static void
793nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
794{
795 char *pass_hash;
796
797 if (!ssh_message_auth_kbdint_is_response(msg)) {
798 const char *prompts[] = {"Password: "};
799 char echo[] = {0};
800
801 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
802 } else {
803 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
804 ssh_message_reply_default(msg);
805 return;
806 }
807 pass_hash = auth_password_get_pwd_hash(session->username);
808 if (!pass_hash) {
809 ssh_message_reply_default(msg);
810 return;
811 }
812 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
813 VRB("User \"%s\" authenticated.", session->username);
814 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
815 ssh_message_auth_reply_success(msg, 0);
816 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200817 ++session->opts.server.ssh_auth_attempts;
818 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100819 ssh_message_reply_default(msg);
820 }
Radek Krejcifb533742016-03-04 15:12:54 +0100821 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100822 }
823}
824
825static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100826auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100827{
828 uint32_t i;
829 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100830 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100831 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100832
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100833 /* LOCK */
834 pthread_mutex_lock(&server_opts.authkey_lock);
835
Michal Vasko17dfda92016-12-01 14:06:16 +0100836 for (i = 0; i < server_opts.authkey_count; ++i) {
837 switch (server_opts.authkeys[i].type) {
838 case NC_SSH_KEY_UNKNOWN:
839 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
840 break;
841 case NC_SSH_KEY_DSA:
842 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
843 break;
844 case NC_SSH_KEY_RSA:
845 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
846 break;
847 case NC_SSH_KEY_ECDSA:
848 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
849 break;
850 }
851
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200852 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100853 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200854 continue;
855 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100856 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100857 continue;
858 }
859
860 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
861 ssh_key_free(pub_key);
862 break;
863 }
864
865 ssh_key_free(pub_key);
866 }
867
Michal Vasko17dfda92016-12-01 14:06:16 +0100868 if (i < server_opts.authkey_count) {
869 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100870 }
871
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100872 /* UNLOCK */
873 pthread_mutex_unlock(&server_opts.authkey_lock);
874
Michal Vasko086311b2016-01-08 09:53:11 +0100875 return username;
876}
877
878static void
879nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
880{
881 const char *username;
882 int signature_state;
883
Michal Vasko17dfda92016-12-01 14:06:16 +0100884 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200885 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
886 goto fail;
887 } else if (strcmp(session->username, username)) {
888 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200889 goto fail;
890 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200891
Michal Vasko086311b2016-01-08 09:53:11 +0100892 signature_state = ssh_message_auth_publickey_state(msg);
893 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
894 VRB("User \"%s\" authenticated.", session->username);
895 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
896 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100897 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200898 /* accepting only the use of a public key */
899 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100900 }
901
Michal Vaskobd13a932016-09-14 09:00:35 +0200902 return;
903
904fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200905 ++session->opts.server.ssh_auth_attempts;
906 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100907 ssh_message_reply_default(msg);
908}
909
910static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100911nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100912{
Michal Vasko96164bf2016-01-21 15:41:58 +0100913 ssh_channel chan;
914
915 /* first channel request */
916 if (!session->ti.libssh.channel) {
917 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100918 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100919 return -1;
920 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100921 chan = ssh_message_channel_request_open_reply_accept(msg);
922 if (!chan) {
923 ERR("Failed to create a new SSH channel.");
924 return -1;
925 }
926 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100927
Michal Vasko96164bf2016-01-21 15:41:58 +0100928 /* additional channel request */
929 } else {
930 chan = ssh_message_channel_request_open_reply_accept(msg);
931 if (!chan) {
932 ERR("Session %u: failed to create a new SSH channel.", session->id);
933 return -1;
934 }
935 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100936 }
937
Michal Vasko086311b2016-01-08 09:53:11 +0100938 return 0;
939}
940
941static int
942nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
943{
Michal Vasko96164bf2016-01-21 15:41:58 +0100944 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100945
Michal Vasko96164bf2016-01-21 15:41:58 +0100946 if (strcmp(subsystem, "netconf")) {
947 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100948 return -1;
949 }
950
Michal Vasko96164bf2016-01-21 15:41:58 +0100951 if (session->ti.libssh.channel == channel) {
952 /* first channel requested */
953 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
954 ERRINT;
955 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100956 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100957 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
958 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
959 return -1;
960 }
961
962 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100963 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100964 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vaskoade892d2017-02-22 13:40:35 +0100965 new_session = nc_new_session(1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100966 if (!new_session) {
967 ERRMEM;
968 return -1;
969 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100970
971 /* insert the new session */
972 if (!session->ti.libssh.next) {
973 new_session->ti.libssh.next = session;
974 } else {
975 new_session->ti.libssh.next = session->ti.libssh.next;
976 }
977 session->ti.libssh.next = new_session;
978
979 new_session->status = NC_STATUS_STARTING;
980 new_session->side = NC_SERVER;
981 new_session->ti_type = NC_TI_LIBSSH;
982 new_session->ti_lock = session->ti_lock;
Michal Vaskoade892d2017-02-22 13:40:35 +0100983 new_session->ti_cond = session->ti_cond;
984 new_session->ti_inuse = session->ti_inuse;
Michal Vasko96164bf2016-01-21 15:41:58 +0100985 new_session->ti.libssh.channel = channel;
986 new_session->ti.libssh.session = session->ti.libssh.session;
987 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
988 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
989 new_session->port = session->port;
990 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100991 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
992 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100993 }
994
995 return 0;
996}
997
Michal Vasko96164bf2016-01-21 15:41:58 +0100998int
Michal Vaskob48aa812016-01-18 14:13:09 +0100999nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001000{
1001 const char *str_type, *str_subtype = NULL, *username;
1002 int subtype, type;
1003 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001004
1005 type = ssh_message_type(msg);
1006 subtype = ssh_message_subtype(msg);
1007
1008 switch (type) {
1009 case SSH_REQUEST_AUTH:
1010 str_type = "request-auth";
1011 switch (subtype) {
1012 case SSH_AUTH_METHOD_NONE:
1013 str_subtype = "none";
1014 break;
1015 case SSH_AUTH_METHOD_PASSWORD:
1016 str_subtype = "password";
1017 break;
1018 case SSH_AUTH_METHOD_PUBLICKEY:
1019 str_subtype = "publickey";
1020 break;
1021 case SSH_AUTH_METHOD_HOSTBASED:
1022 str_subtype = "hostbased";
1023 break;
1024 case SSH_AUTH_METHOD_INTERACTIVE:
1025 str_subtype = "interactive";
1026 break;
1027 case SSH_AUTH_METHOD_GSSAPI_MIC:
1028 str_subtype = "gssapi-mic";
1029 break;
1030 }
1031 break;
1032
1033 case SSH_REQUEST_CHANNEL_OPEN:
1034 str_type = "request-channel-open";
1035 switch (subtype) {
1036 case SSH_CHANNEL_SESSION:
1037 str_subtype = "session";
1038 break;
1039 case SSH_CHANNEL_DIRECT_TCPIP:
1040 str_subtype = "direct-tcpip";
1041 break;
1042 case SSH_CHANNEL_FORWARDED_TCPIP:
1043 str_subtype = "forwarded-tcpip";
1044 break;
1045 case (int)SSH_CHANNEL_X11:
1046 str_subtype = "channel-x11";
1047 break;
1048 case SSH_CHANNEL_UNKNOWN:
1049 /* fallthrough */
1050 default:
1051 str_subtype = "unknown";
1052 break;
1053 }
1054 break;
1055
1056 case SSH_REQUEST_CHANNEL:
1057 str_type = "request-channel";
1058 switch (subtype) {
1059 case SSH_CHANNEL_REQUEST_PTY:
1060 str_subtype = "pty";
1061 break;
1062 case SSH_CHANNEL_REQUEST_EXEC:
1063 str_subtype = "exec";
1064 break;
1065 case SSH_CHANNEL_REQUEST_SHELL:
1066 str_subtype = "shell";
1067 break;
1068 case SSH_CHANNEL_REQUEST_ENV:
1069 str_subtype = "env";
1070 break;
1071 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1072 str_subtype = "subsystem";
1073 break;
1074 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1075 str_subtype = "window-change";
1076 break;
1077 case SSH_CHANNEL_REQUEST_X11:
1078 str_subtype = "x11";
1079 break;
1080 case SSH_CHANNEL_REQUEST_UNKNOWN:
1081 /* fallthrough */
1082 default:
1083 str_subtype = "unknown";
1084 break;
1085 }
1086 break;
1087
1088 case SSH_REQUEST_SERVICE:
1089 str_type = "request-service";
1090 str_subtype = ssh_message_service_service(msg);
1091 break;
1092
1093 case SSH_REQUEST_GLOBAL:
1094 str_type = "request-global";
1095 switch (subtype) {
1096 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1097 str_subtype = "tcpip-forward";
1098 break;
1099 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1100 str_subtype = "cancel-tcpip-forward";
1101 break;
1102 case SSH_GLOBAL_REQUEST_UNKNOWN:
1103 /* fallthrough */
1104 default:
1105 str_subtype = "unknown";
1106 break;
1107 }
1108 break;
1109
1110 default:
1111 str_type = "unknown";
1112 str_subtype = "unknown";
1113 break;
1114 }
1115
1116 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001117 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1118 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1119 * but we got it now, during session free */
1120 VRB("SSH message arrived on a %s session, the request will be denied.",
1121 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1122 ssh_message_reply_default(msg);
1123 return 0;
1124 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001125 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001126
1127 /*
1128 * process known messages
1129 */
1130 if (type == SSH_REQUEST_AUTH) {
1131 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1132 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1133 ssh_message_reply_default(msg);
1134 return 0;
1135 }
1136
Michal Vasko086311b2016-01-08 09:53:11 +01001137 /* save the username, do not let the client change it */
1138 username = ssh_message_auth_user(msg);
1139 if (!session->username) {
1140 if (!username) {
1141 ERR("Denying an auth request without a username.");
1142 return 1;
1143 }
1144
Michal Vasko05ba9df2016-01-13 14:40:27 +01001145 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001146 } else if (username) {
1147 if (strcmp(username, session->username)) {
1148 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1149 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001150 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001151 return 1;
1152 }
1153 }
1154
1155 if (subtype == SSH_AUTH_METHOD_NONE) {
1156 /* libssh will return the supported auth methods */
1157 return 1;
1158 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1159 nc_sshcb_auth_password(session, msg);
1160 return 0;
1161 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1162 nc_sshcb_auth_pubkey(session, msg);
1163 return 0;
1164 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1165 nc_sshcb_auth_kbdint(session, msg);
1166 return 0;
1167 }
1168 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001169 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001170 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001171 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001172 }
Michal Vasko086311b2016-01-08 09:53:11 +01001173 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001174
Michal Vasko0df67562016-01-21 15:50:11 +01001175 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001176 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1177 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001178 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001179 } else {
1180 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001181 }
1182 return 0;
1183 }
1184 }
1185
1186 /* we did not process it */
1187 return 1;
1188}
1189
Michal Vasko1a38c862016-01-15 15:50:07 +01001190/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001191static int
1192nc_open_netconf_channel(struct nc_session *session, int timeout)
1193{
Michal Vasko36c7be82017-02-22 13:37:59 +01001194 int ret;
1195 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001196
1197 /* message callback is executed twice to give chance for the channel to be
1198 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001199 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001200 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001201 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001202 return -1;
1203 }
1204
Michal Vaskoade892d2017-02-22 13:40:35 +01001205 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001206 if (ret != 1) {
1207 return ret;
1208 }
1209
1210 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1211 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001212 ERR("Failed to receive SSH messages on a session (%s).",
1213 ssh_get_error(session->ti.libssh.session));
Michal Vaskoade892d2017-02-22 13:40:35 +01001214 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +01001215 return -1;
1216 }
1217
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001218 if (!session->ti.libssh.channel) {
1219 /* we did not receive channel-open, timeout */
Michal Vaskoade892d2017-02-22 13:40:35 +01001220 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001221 return 0;
1222 }
1223
1224 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001225 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001226 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001227 ERR("Failed to receive SSH messages on a session (%s).",
1228 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001229 return -1;
1230 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001231
1232 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1233 /* we did not receive subsystem-request, timeout */
1234 return 0;
1235 }
1236
1237 return 1;
1238 }
1239
Michal Vasko36c7be82017-02-22 13:37:59 +01001240 if (timeout > -1) {
1241 nc_gettimespec(&ts_timeout);
1242 nc_addtimespec(&ts_timeout, timeout);
1243 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001244 while (1) {
1245 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001246 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001247 return -1;
1248 }
1249
Michal Vaskoade892d2017-02-22 13:40:35 +01001250 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001251 if (ret != 1) {
1252 return ret;
1253 }
1254
1255 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001256 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001257 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001258 ERR("Failed to receive SSH messages on a session (%s).",
1259 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001260 return -1;
1261 }
1262
Michal Vasko086311b2016-01-08 09:53:11 +01001263 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001264 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001265 }
1266
Michal Vasko086311b2016-01-08 09:53:11 +01001267 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001268 if (timeout > -1) {
1269 nc_gettimespec(&ts_cur);
1270 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1271 /* timeout */
1272 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1273 break;
1274 }
1275 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001276 }
Michal Vasko086311b2016-01-08 09:53:11 +01001277
Michal Vasko1a38c862016-01-15 15:50:07 +01001278 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001279}
1280
Michal Vasko4c1fb492017-01-30 14:31:07 +01001281static int
1282nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1283{
1284 uint8_t i;
1285 char *privkey_path, *privkey_data;
1286 int privkey_data_rsa, ret;
1287
1288 if (!server_opts.hostkey_clb) {
1289 ERR("Callback for retrieving SSH host keys not set.");
1290 return -1;
1291 }
1292
1293 for (i = 0; i < hostkey_count; ++i) {
1294 privkey_path = privkey_data = NULL;
1295 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1296 ERR("Host key callback failed.");
1297 return -1;
1298 }
1299
1300 if (privkey_data) {
1301 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1302 if (!privkey_path) {
1303 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1304 free(privkey_data);
1305 return -1;
1306 }
1307 }
1308
1309 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1310
1311 /* cleanup */
1312 if (privkey_data && unlink(privkey_path)) {
1313 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1314 }
1315 free(privkey_path);
1316 free(privkey_data);
1317
1318 if (ret != SSH_OK) {
1319 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], ssh_get_error(sbind));
1320 return -1;
1321 }
1322 }
1323
1324 return 0;
1325}
1326
Michal Vasko96164bf2016-01-21 15:41:58 +01001327int
Michal Vasko0190bc32016-03-02 15:47:49 +01001328nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001329{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001330 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001331 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001332 int libssh_auth_methods = 0, ret;
1333 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001334
Michal Vasko2cc4c682016-03-01 09:16:48 +01001335 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001336
Michal Vasko086311b2016-01-08 09:53:11 +01001337 /* other transport-specific data */
1338 session->ti_type = NC_TI_LIBSSH;
1339 session->ti.libssh.session = ssh_new();
1340 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001341 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001342 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001343 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001344 }
1345
Michal Vaskoc61c4492016-01-25 11:13:34 +01001346 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001347 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1348 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001349 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001350 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1351 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001352 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001353 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1354 }
1355 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1356
Michal Vaskoe2713da2016-08-22 16:06:40 +02001357 sbind = ssh_bind_new();
1358 if (!sbind) {
1359 ERR("Failed to create an SSH bind.");
1360 close(sock);
1361 return -1;
1362 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001363
1364 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1365 close(sock);
1366 ssh_bind_free(sbind);
1367 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001368 }
1369 if (opts->banner) {
1370 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1371 }
1372
Michal Vasko086311b2016-01-08 09:53:11 +01001373 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001374 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001375 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001376
Michal Vaskoe2713da2016-08-22 16:06:40 +02001377 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1378 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001379 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001380 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001381 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001382 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001383 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001384
Michal Vasko0190bc32016-03-02 15:47:49 +01001385 ssh_set_blocking(session->ti.libssh.session, 0);
1386
Michal Vasko36c7be82017-02-22 13:37:59 +01001387 if (timeout > -1) {
1388 nc_gettimespec(&ts_timeout);
1389 nc_addtimespec(&ts_timeout, timeout);
1390 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001391 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001392 /* this tends to take longer */
1393 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001394 if (timeout > -1) {
1395 nc_gettimespec(&ts_cur);
1396 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1397 break;
1398 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001399 }
1400 }
1401 if (ret == SSH_AGAIN) {
1402 ERR("SSH key exchange timeout.");
1403 return 0;
1404 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001405 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001406 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001407 }
1408
1409 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001410 if (opts->auth_timeout) {
1411 nc_gettimespec(&ts_timeout);
1412 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1413 }
1414 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001415 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001416 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001417 return -1;
1418 }
1419
Michal Vasko086311b2016-01-08 09:53:11 +01001420 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001421 ERR("Failed to receive SSH messages on a session (%s).",
1422 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001423 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001424 }
1425
Michal Vasko36c7be82017-02-22 13:37:59 +01001426 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1427 break;
1428 }
1429
Michal Vasko145ae672017-02-07 10:57:27 +01001430 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1431 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1432 return -1;
1433 }
1434
Michal Vasko086311b2016-01-08 09:53:11 +01001435 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001436 if (opts->auth_timeout) {
1437 nc_gettimespec(&ts_cur);
1438 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1439 /* timeout */
1440 break;
1441 }
1442 }
1443 }
Michal Vasko086311b2016-01-08 09:53:11 +01001444
1445 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1446 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001447 if (session->username) {
1448 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1449 } else {
1450 ERR("User failed to authenticate for too long, disconnecting.");
1451 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001452 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001453 }
1454
Michal Vasko086311b2016-01-08 09:53:11 +01001455 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001456 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001457 if (ret < 1) {
1458 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001459 }
1460
Michal Vasko96164bf2016-01-21 15:41:58 +01001461 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001462 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001463}
1464
Michal Vasko71090fc2016-05-24 16:37:28 +02001465API NC_MSG_TYPE
1466nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1467{
1468 NC_MSG_TYPE msgtype;
1469 struct nc_session *new_session = NULL;
1470
1471 if (!orig_session) {
1472 ERRARG("orig_session");
1473 return NC_MSG_ERROR;
1474 } else if (!session) {
1475 ERRARG("session");
1476 return NC_MSG_ERROR;
1477 }
1478
1479 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1480 && orig_session->ti.libssh.next) {
1481 for (new_session = orig_session->ti.libssh.next;
1482 new_session != orig_session;
1483 new_session = new_session->ti.libssh.next) {
1484 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1485 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1486 /* we found our session */
1487 break;
1488 }
1489 }
1490 if (new_session == orig_session) {
1491 new_session = NULL;
1492 }
1493 }
1494
1495 if (!new_session) {
1496 ERR("Session does not have a NETCONF SSH channel ready.");
1497 return NC_MSG_ERROR;
1498 }
1499
1500 /* assign new SID atomically */
1501 pthread_spin_lock(&server_opts.sid_lock);
1502 new_session->id = server_opts.new_session_id++;
1503 pthread_spin_unlock(&server_opts.sid_lock);
1504
1505 /* NETCONF handshake */
1506 msgtype = nc_handshake(new_session);
1507 if (msgtype != NC_MSG_HELLO) {
1508 return msgtype;
1509 }
1510
Michal Vasko2e6defd2016-10-07 15:48:15 +02001511 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001512 new_session->status = NC_STATUS_RUNNING;
1513 *session = new_session;
1514
1515 return msgtype;
1516}
1517
1518API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001519nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001520{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001521 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001522 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001523 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001524 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001525
Michal Vasko45e53ae2016-04-07 11:46:03 +02001526 if (!ps) {
1527 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001528 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001529 } else if (!session) {
1530 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001531 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001532 }
1533
Michal Vasko48a63ed2016-03-01 09:48:21 +01001534 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001535 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001536 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001537 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001538
Michal Vasko96164bf2016-01-21 15:41:58 +01001539 for (i = 0; i < ps->session_count; ++i) {
1540 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1541 && ps->sessions[i]->ti.libssh.next) {
1542 /* an SSH session with more channels */
1543 for (new_session = ps->sessions[i]->ti.libssh.next;
1544 new_session != ps->sessions[i];
1545 new_session = new_session->ti.libssh.next) {
1546 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1547 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1548 /* we found our session */
1549 break;
1550 }
1551 }
1552 if (new_session != ps->sessions[i]) {
1553 break;
1554 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001555
Michal Vasko96164bf2016-01-21 15:41:58 +01001556 new_session = NULL;
1557 }
1558 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001559
Michal Vasko48a63ed2016-03-01 09:48:21 +01001560 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001561 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001562
Michal Vasko96164bf2016-01-21 15:41:58 +01001563 if (!new_session) {
1564 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001565 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001566 }
1567
1568 /* assign new SID atomically */
1569 pthread_spin_lock(&server_opts.sid_lock);
1570 new_session->id = server_opts.new_session_id++;
1571 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001572
Michal Vasko086311b2016-01-08 09:53:11 +01001573 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001574 msgtype = nc_handshake(new_session);
1575 if (msgtype != NC_MSG_HELLO) {
1576 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001577 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001578
Michal Vasko2e6defd2016-10-07 15:48:15 +02001579 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001580 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001581 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001582
Michal Vasko71090fc2016-05-24 16:37:28 +02001583 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001584}