blob: 31abce2a5c6691fec0fedf7be4cd43dd91f16d99 [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;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200754#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100755 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200756#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100757
758 if (!pass_hash[0]) {
759 if (!pass_clear[0]) {
760 WRN("User authentication successful with an empty password!");
761 return 0;
762 } else {
763 /* the user did now know he does not need any password,
764 * (which should not be used) so deny authentication */
765 return 1;
766 }
767 }
768
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200769#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100770 cdata.initialized = 0;
771 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200772#else
773 new_pass_hash = crypt(pass_clear, pass_hash);
774#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100775 return strcmp(new_pass_hash, pass_hash);
776}
777
778static void
779nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
780{
781 char *pass_hash;
782
783 pass_hash = auth_password_get_pwd_hash(session->username);
784 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100785 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100786 ssh_message_auth_reply_success(msg, 0);
787 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
788 free(pass_hash);
789 return;
790 }
791
792 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200793 ++session->opts.server.ssh_auth_attempts;
794 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100795 ssh_message_reply_default(msg);
796}
797
798static void
799nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
800{
801 char *pass_hash;
802
803 if (!ssh_message_auth_kbdint_is_response(msg)) {
804 const char *prompts[] = {"Password: "};
805 char echo[] = {0};
806
807 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
808 } else {
809 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
810 ssh_message_reply_default(msg);
811 return;
812 }
813 pass_hash = auth_password_get_pwd_hash(session->username);
814 if (!pass_hash) {
815 ssh_message_reply_default(msg);
816 return;
817 }
818 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
819 VRB("User \"%s\" authenticated.", session->username);
820 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
821 ssh_message_auth_reply_success(msg, 0);
822 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200823 ++session->opts.server.ssh_auth_attempts;
824 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100825 ssh_message_reply_default(msg);
826 }
Radek Krejcifb533742016-03-04 15:12:54 +0100827 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100828 }
829}
830
831static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100832auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100833{
834 uint32_t i;
835 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100836 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100837 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100838
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100839 /* LOCK */
840 pthread_mutex_lock(&server_opts.authkey_lock);
841
Michal Vasko17dfda92016-12-01 14:06:16 +0100842 for (i = 0; i < server_opts.authkey_count; ++i) {
843 switch (server_opts.authkeys[i].type) {
844 case NC_SSH_KEY_UNKNOWN:
845 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
846 break;
847 case NC_SSH_KEY_DSA:
848 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
849 break;
850 case NC_SSH_KEY_RSA:
851 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
852 break;
853 case NC_SSH_KEY_ECDSA:
854 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
855 break;
856 }
857
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200858 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100859 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200860 continue;
861 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100862 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100863 continue;
864 }
865
866 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
867 ssh_key_free(pub_key);
868 break;
869 }
870
871 ssh_key_free(pub_key);
872 }
873
Michal Vasko17dfda92016-12-01 14:06:16 +0100874 if (i < server_opts.authkey_count) {
875 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100876 }
877
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100878 /* UNLOCK */
879 pthread_mutex_unlock(&server_opts.authkey_lock);
880
Michal Vasko086311b2016-01-08 09:53:11 +0100881 return username;
882}
883
884static void
885nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
886{
887 const char *username;
888 int signature_state;
889
Michal Vasko17dfda92016-12-01 14:06:16 +0100890 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200891 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
892 goto fail;
893 } else if (strcmp(session->username, username)) {
894 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200895 goto fail;
896 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200897
Michal Vasko086311b2016-01-08 09:53:11 +0100898 signature_state = ssh_message_auth_publickey_state(msg);
899 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
900 VRB("User \"%s\" authenticated.", session->username);
901 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
902 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100903 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200904 /* accepting only the use of a public key */
905 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100906 }
907
Michal Vaskobd13a932016-09-14 09:00:35 +0200908 return;
909
910fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200911 ++session->opts.server.ssh_auth_attempts;
912 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100913 ssh_message_reply_default(msg);
914}
915
916static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100917nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100918{
Michal Vasko96164bf2016-01-21 15:41:58 +0100919 ssh_channel chan;
920
921 /* first channel request */
922 if (!session->ti.libssh.channel) {
923 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100924 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100925 return -1;
926 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100927 chan = ssh_message_channel_request_open_reply_accept(msg);
928 if (!chan) {
929 ERR("Failed to create a new SSH channel.");
930 return -1;
931 }
932 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100933
Michal Vasko96164bf2016-01-21 15:41:58 +0100934 /* additional channel request */
935 } else {
936 chan = ssh_message_channel_request_open_reply_accept(msg);
937 if (!chan) {
938 ERR("Session %u: failed to create a new SSH channel.", session->id);
939 return -1;
940 }
941 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100942 }
943
Michal Vasko086311b2016-01-08 09:53:11 +0100944 return 0;
945}
946
947static int
948nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
949{
Michal Vasko96164bf2016-01-21 15:41:58 +0100950 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100951
Michal Vasko96164bf2016-01-21 15:41:58 +0100952 if (strcmp(subsystem, "netconf")) {
953 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100954 return -1;
955 }
956
Michal Vasko96164bf2016-01-21 15:41:58 +0100957 if (session->ti.libssh.channel == channel) {
958 /* first channel requested */
959 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
960 ERRINT;
961 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100962 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100963 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
964 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
965 return -1;
966 }
967
968 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100969 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100970 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vaskoade892d2017-02-22 13:40:35 +0100971 new_session = nc_new_session(1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100972 if (!new_session) {
973 ERRMEM;
974 return -1;
975 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100976
977 /* insert the new session */
978 if (!session->ti.libssh.next) {
979 new_session->ti.libssh.next = session;
980 } else {
981 new_session->ti.libssh.next = session->ti.libssh.next;
982 }
983 session->ti.libssh.next = new_session;
984
985 new_session->status = NC_STATUS_STARTING;
986 new_session->side = NC_SERVER;
987 new_session->ti_type = NC_TI_LIBSSH;
988 new_session->ti_lock = session->ti_lock;
Michal Vaskoade892d2017-02-22 13:40:35 +0100989 new_session->ti_cond = session->ti_cond;
990 new_session->ti_inuse = session->ti_inuse;
Michal Vasko96164bf2016-01-21 15:41:58 +0100991 new_session->ti.libssh.channel = channel;
992 new_session->ti.libssh.session = session->ti.libssh.session;
993 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
994 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
995 new_session->port = session->port;
996 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100997 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
998 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100999 }
1000
1001 return 0;
1002}
1003
Michal Vasko96164bf2016-01-21 15:41:58 +01001004int
Michal Vaskob48aa812016-01-18 14:13:09 +01001005nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001006{
1007 const char *str_type, *str_subtype = NULL, *username;
1008 int subtype, type;
1009 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001010
1011 type = ssh_message_type(msg);
1012 subtype = ssh_message_subtype(msg);
1013
1014 switch (type) {
1015 case SSH_REQUEST_AUTH:
1016 str_type = "request-auth";
1017 switch (subtype) {
1018 case SSH_AUTH_METHOD_NONE:
1019 str_subtype = "none";
1020 break;
1021 case SSH_AUTH_METHOD_PASSWORD:
1022 str_subtype = "password";
1023 break;
1024 case SSH_AUTH_METHOD_PUBLICKEY:
1025 str_subtype = "publickey";
1026 break;
1027 case SSH_AUTH_METHOD_HOSTBASED:
1028 str_subtype = "hostbased";
1029 break;
1030 case SSH_AUTH_METHOD_INTERACTIVE:
1031 str_subtype = "interactive";
1032 break;
1033 case SSH_AUTH_METHOD_GSSAPI_MIC:
1034 str_subtype = "gssapi-mic";
1035 break;
1036 }
1037 break;
1038
1039 case SSH_REQUEST_CHANNEL_OPEN:
1040 str_type = "request-channel-open";
1041 switch (subtype) {
1042 case SSH_CHANNEL_SESSION:
1043 str_subtype = "session";
1044 break;
1045 case SSH_CHANNEL_DIRECT_TCPIP:
1046 str_subtype = "direct-tcpip";
1047 break;
1048 case SSH_CHANNEL_FORWARDED_TCPIP:
1049 str_subtype = "forwarded-tcpip";
1050 break;
1051 case (int)SSH_CHANNEL_X11:
1052 str_subtype = "channel-x11";
1053 break;
1054 case SSH_CHANNEL_UNKNOWN:
1055 /* fallthrough */
1056 default:
1057 str_subtype = "unknown";
1058 break;
1059 }
1060 break;
1061
1062 case SSH_REQUEST_CHANNEL:
1063 str_type = "request-channel";
1064 switch (subtype) {
1065 case SSH_CHANNEL_REQUEST_PTY:
1066 str_subtype = "pty";
1067 break;
1068 case SSH_CHANNEL_REQUEST_EXEC:
1069 str_subtype = "exec";
1070 break;
1071 case SSH_CHANNEL_REQUEST_SHELL:
1072 str_subtype = "shell";
1073 break;
1074 case SSH_CHANNEL_REQUEST_ENV:
1075 str_subtype = "env";
1076 break;
1077 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1078 str_subtype = "subsystem";
1079 break;
1080 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1081 str_subtype = "window-change";
1082 break;
1083 case SSH_CHANNEL_REQUEST_X11:
1084 str_subtype = "x11";
1085 break;
1086 case SSH_CHANNEL_REQUEST_UNKNOWN:
1087 /* fallthrough */
1088 default:
1089 str_subtype = "unknown";
1090 break;
1091 }
1092 break;
1093
1094 case SSH_REQUEST_SERVICE:
1095 str_type = "request-service";
1096 str_subtype = ssh_message_service_service(msg);
1097 break;
1098
1099 case SSH_REQUEST_GLOBAL:
1100 str_type = "request-global";
1101 switch (subtype) {
1102 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1103 str_subtype = "tcpip-forward";
1104 break;
1105 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1106 str_subtype = "cancel-tcpip-forward";
1107 break;
1108 case SSH_GLOBAL_REQUEST_UNKNOWN:
1109 /* fallthrough */
1110 default:
1111 str_subtype = "unknown";
1112 break;
1113 }
1114 break;
1115
1116 default:
1117 str_type = "unknown";
1118 str_subtype = "unknown";
1119 break;
1120 }
1121
1122 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001123 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1124 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1125 * but we got it now, during session free */
1126 VRB("SSH message arrived on a %s session, the request will be denied.",
1127 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1128 ssh_message_reply_default(msg);
1129 return 0;
1130 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001131 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001132
1133 /*
1134 * process known messages
1135 */
1136 if (type == SSH_REQUEST_AUTH) {
1137 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1138 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1139 ssh_message_reply_default(msg);
1140 return 0;
1141 }
1142
Michal Vasko086311b2016-01-08 09:53:11 +01001143 /* save the username, do not let the client change it */
1144 username = ssh_message_auth_user(msg);
1145 if (!session->username) {
1146 if (!username) {
1147 ERR("Denying an auth request without a username.");
1148 return 1;
1149 }
1150
Michal Vasko05ba9df2016-01-13 14:40:27 +01001151 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001152 } else if (username) {
1153 if (strcmp(username, session->username)) {
1154 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1155 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001156 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001157 return 1;
1158 }
1159 }
1160
1161 if (subtype == SSH_AUTH_METHOD_NONE) {
1162 /* libssh will return the supported auth methods */
1163 return 1;
1164 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1165 nc_sshcb_auth_password(session, msg);
1166 return 0;
1167 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1168 nc_sshcb_auth_pubkey(session, msg);
1169 return 0;
1170 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1171 nc_sshcb_auth_kbdint(session, msg);
1172 return 0;
1173 }
1174 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001175 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001176 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001177 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001178 }
Michal Vasko086311b2016-01-08 09:53:11 +01001179 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001180
Michal Vasko0df67562016-01-21 15:50:11 +01001181 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001182 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1183 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001184 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001185 } else {
1186 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001187 }
1188 return 0;
1189 }
1190 }
1191
1192 /* we did not process it */
1193 return 1;
1194}
1195
Michal Vasko1a38c862016-01-15 15:50:07 +01001196/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001197static int
1198nc_open_netconf_channel(struct nc_session *session, int timeout)
1199{
Michal Vasko36c7be82017-02-22 13:37:59 +01001200 int ret;
1201 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001202
1203 /* message callback is executed twice to give chance for the channel to be
1204 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001205 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001206 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001207 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001208 return -1;
1209 }
1210
Michal Vaskoade892d2017-02-22 13:40:35 +01001211 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001212 if (ret != 1) {
1213 return ret;
1214 }
1215
1216 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1217 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001218 ERR("Failed to receive SSH messages on a session (%s).",
1219 ssh_get_error(session->ti.libssh.session));
Michal Vaskoade892d2017-02-22 13:40:35 +01001220 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +01001221 return -1;
1222 }
1223
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001224 if (!session->ti.libssh.channel) {
1225 /* we did not receive channel-open, timeout */
Michal Vaskoade892d2017-02-22 13:40:35 +01001226 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001227 return 0;
1228 }
1229
1230 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001231 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001232 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001233 ERR("Failed to receive SSH messages on a session (%s).",
1234 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001235 return -1;
1236 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001237
1238 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1239 /* we did not receive subsystem-request, timeout */
1240 return 0;
1241 }
1242
1243 return 1;
1244 }
1245
Michal Vasko36c7be82017-02-22 13:37:59 +01001246 if (timeout > -1) {
1247 nc_gettimespec(&ts_timeout);
1248 nc_addtimespec(&ts_timeout, timeout);
1249 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001250 while (1) {
1251 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001252 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001253 return -1;
1254 }
1255
Michal Vaskoade892d2017-02-22 13:40:35 +01001256 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001257 if (ret != 1) {
1258 return ret;
1259 }
1260
1261 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001262 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001263 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001264 ERR("Failed to receive SSH messages on a session (%s).",
1265 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001266 return -1;
1267 }
1268
Michal Vasko086311b2016-01-08 09:53:11 +01001269 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001270 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001271 }
1272
Michal Vasko086311b2016-01-08 09:53:11 +01001273 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001274 if (timeout > -1) {
1275 nc_gettimespec(&ts_cur);
1276 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1277 /* timeout */
1278 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1279 break;
1280 }
1281 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001282 }
Michal Vasko086311b2016-01-08 09:53:11 +01001283
Michal Vasko1a38c862016-01-15 15:50:07 +01001284 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001285}
1286
Michal Vasko4c1fb492017-01-30 14:31:07 +01001287static int
1288nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1289{
1290 uint8_t i;
1291 char *privkey_path, *privkey_data;
1292 int privkey_data_rsa, ret;
1293
1294 if (!server_opts.hostkey_clb) {
1295 ERR("Callback for retrieving SSH host keys not set.");
1296 return -1;
1297 }
1298
1299 for (i = 0; i < hostkey_count; ++i) {
1300 privkey_path = privkey_data = NULL;
1301 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1302 ERR("Host key callback failed.");
1303 return -1;
1304 }
1305
1306 if (privkey_data) {
1307 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1308 if (!privkey_path) {
1309 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1310 free(privkey_data);
1311 return -1;
1312 }
1313 }
1314
1315 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1316
1317 /* cleanup */
1318 if (privkey_data && unlink(privkey_path)) {
1319 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1320 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001321 free(privkey_data);
1322
1323 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001324 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1325 }
1326 free(privkey_path);
1327
1328 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001329 return -1;
1330 }
1331 }
1332
1333 return 0;
1334}
1335
Michal Vasko96164bf2016-01-21 15:41:58 +01001336int
Michal Vasko0190bc32016-03-02 15:47:49 +01001337nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001338{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001339 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001340 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001341 int libssh_auth_methods = 0, ret;
1342 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001343
Michal Vasko2cc4c682016-03-01 09:16:48 +01001344 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001345
Michal Vasko086311b2016-01-08 09:53:11 +01001346 /* other transport-specific data */
1347 session->ti_type = NC_TI_LIBSSH;
1348 session->ti.libssh.session = ssh_new();
1349 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001350 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001351 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001352 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001353 }
1354
Michal Vaskoc61c4492016-01-25 11:13:34 +01001355 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001356 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1357 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001358 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001359 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1360 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001361 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001362 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1363 }
1364 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1365
Michal Vaskoe2713da2016-08-22 16:06:40 +02001366 sbind = ssh_bind_new();
1367 if (!sbind) {
1368 ERR("Failed to create an SSH bind.");
1369 close(sock);
1370 return -1;
1371 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001372
1373 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1374 close(sock);
1375 ssh_bind_free(sbind);
1376 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001377 }
1378 if (opts->banner) {
1379 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1380 }
1381
Michal Vasko086311b2016-01-08 09:53:11 +01001382 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001383 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001384 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001385
Michal Vaskoe2713da2016-08-22 16:06:40 +02001386 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1387 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001388 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001389 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001390 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001391 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001392 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001393
Michal Vasko0190bc32016-03-02 15:47:49 +01001394 ssh_set_blocking(session->ti.libssh.session, 0);
1395
Michal Vasko36c7be82017-02-22 13:37:59 +01001396 if (timeout > -1) {
1397 nc_gettimespec(&ts_timeout);
1398 nc_addtimespec(&ts_timeout, timeout);
1399 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001400 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001401 /* this tends to take longer */
1402 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001403 if (timeout > -1) {
1404 nc_gettimespec(&ts_cur);
1405 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1406 break;
1407 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001408 }
1409 }
1410 if (ret == SSH_AGAIN) {
1411 ERR("SSH key exchange timeout.");
1412 return 0;
1413 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001414 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001415 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001416 }
1417
1418 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001419 if (opts->auth_timeout) {
1420 nc_gettimespec(&ts_timeout);
1421 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1422 }
1423 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001424 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001425 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001426 return -1;
1427 }
1428
Michal Vasko086311b2016-01-08 09:53:11 +01001429 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001430 ERR("Failed to receive SSH messages on a session (%s).",
1431 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001432 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001433 }
1434
Michal Vasko36c7be82017-02-22 13:37:59 +01001435 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1436 break;
1437 }
1438
Michal Vasko145ae672017-02-07 10:57:27 +01001439 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1440 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1441 return -1;
1442 }
1443
Michal Vasko086311b2016-01-08 09:53:11 +01001444 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001445 if (opts->auth_timeout) {
1446 nc_gettimespec(&ts_cur);
1447 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1448 /* timeout */
1449 break;
1450 }
1451 }
1452 }
Michal Vasko086311b2016-01-08 09:53:11 +01001453
1454 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1455 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001456 if (session->username) {
1457 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1458 } else {
1459 ERR("User failed to authenticate for too long, disconnecting.");
1460 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001461 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001462 }
1463
Michal Vasko086311b2016-01-08 09:53:11 +01001464 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001465 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001466 if (ret < 1) {
1467 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001468 }
1469
Michal Vasko96164bf2016-01-21 15:41:58 +01001470 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001471 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001472}
1473
Michal Vasko71090fc2016-05-24 16:37:28 +02001474API NC_MSG_TYPE
1475nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1476{
1477 NC_MSG_TYPE msgtype;
1478 struct nc_session *new_session = NULL;
1479
1480 if (!orig_session) {
1481 ERRARG("orig_session");
1482 return NC_MSG_ERROR;
1483 } else if (!session) {
1484 ERRARG("session");
1485 return NC_MSG_ERROR;
1486 }
1487
1488 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1489 && orig_session->ti.libssh.next) {
1490 for (new_session = orig_session->ti.libssh.next;
1491 new_session != orig_session;
1492 new_session = new_session->ti.libssh.next) {
1493 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1494 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1495 /* we found our session */
1496 break;
1497 }
1498 }
1499 if (new_session == orig_session) {
1500 new_session = NULL;
1501 }
1502 }
1503
1504 if (!new_session) {
1505 ERR("Session does not have a NETCONF SSH channel ready.");
1506 return NC_MSG_ERROR;
1507 }
1508
1509 /* assign new SID atomically */
1510 pthread_spin_lock(&server_opts.sid_lock);
1511 new_session->id = server_opts.new_session_id++;
1512 pthread_spin_unlock(&server_opts.sid_lock);
1513
1514 /* NETCONF handshake */
1515 msgtype = nc_handshake(new_session);
1516 if (msgtype != NC_MSG_HELLO) {
1517 return msgtype;
1518 }
1519
Michal Vasko2e6defd2016-10-07 15:48:15 +02001520 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001521 new_session->status = NC_STATUS_RUNNING;
1522 *session = new_session;
1523
1524 return msgtype;
1525}
1526
1527API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001528nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001529{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001530 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001531 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001532 struct nc_session *new_session = NULL, *cur_session;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001533 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001534
Michal Vasko45e53ae2016-04-07 11:46:03 +02001535 if (!ps) {
1536 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001537 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001538 } else if (!session) {
1539 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001540 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001541 }
1542
Michal Vasko48a63ed2016-03-01 09:48:21 +01001543 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001544 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001545 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001546 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001547
Michal Vasko96164bf2016-01-21 15:41:58 +01001548 for (i = 0; i < ps->session_count; ++i) {
Michal Vaskoe4300a82017-05-24 10:35:42 +02001549 cur_session = ps->sessions[i].session;
1550 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1551 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001552 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001553 for (new_session = cur_session->ti.libssh.next;
1554 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001555 new_session = new_session->ti.libssh.next) {
1556 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1557 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1558 /* we found our session */
1559 break;
1560 }
1561 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001562 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001563 break;
1564 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001565
Michal Vasko96164bf2016-01-21 15:41:58 +01001566 new_session = NULL;
1567 }
1568 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001569
Michal Vasko48a63ed2016-03-01 09:48:21 +01001570 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001571 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001572
Michal Vasko96164bf2016-01-21 15:41:58 +01001573 if (!new_session) {
1574 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001575 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001576 }
1577
1578 /* assign new SID atomically */
1579 pthread_spin_lock(&server_opts.sid_lock);
1580 new_session->id = server_opts.new_session_id++;
1581 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001582
Michal Vasko086311b2016-01-08 09:53:11 +01001583 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001584 msgtype = nc_handshake(new_session);
1585 if (msgtype != NC_MSG_HELLO) {
1586 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001587 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001588
Michal Vasko2e6defd2016-10-07 15:48:15 +02001589 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001590 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001591 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001592
Michal Vasko71090fc2016-05-24 16:37:28 +02001593 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001594}