blob: e98453a3eede454183b2d20feae0367353573684 [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>
21#include <pwd.h>
22#include <shadow.h>
23#include <crypt.h>
24#include <errno.h>
25
Michal Vasko11d142a2016-01-19 15:58:24 +010026#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010027#include "session_server_ch.h"
28#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010029
30extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010031
Michal Vasko4c1fb492017-01-30 14:31:07 +010032static char *
33base64der_key_to_tmp_file(const char *in, int rsa)
Michal Vasko086311b2016-01-08 09:53:11 +010034{
Michal Vasko4c1fb492017-01-30 14:31:07 +010035 char path[12] = "/tmp/XXXXXX";
36 int fd, written;
37 FILE *file;
38
39 if (in == NULL) {
40 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010041 }
42
Michal Vasko4c1fb492017-01-30 14:31:07 +010043 fd = mkstemp(path);
44 if (fd == -1) {
45 return NULL;
46 }
47
48 file = fdopen(fd, "r");
49 if (!file) {
50 close(fd);
51 return NULL;
52 }
53
54 /* write the key into the file */
55 written = fwrite("-----BEGIN ", 1, 11, file);
56 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
57 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
58 written += fwrite(in, 1, strlen(in), file);
59 written += fwrite("\n-----END ", 1, 10, file);
60 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
61 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
62
63 fclose(file);
64 if ((unsigned)written != 62 + strlen(in)) {
65 unlink(path);
66 return NULL;
67 }
68
69 return strdup(path);
70}
71
72static int
Michal Vasko7d255882017-02-09 13:35:08 +010073nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +010074{
Michal Vaskofbfe8b62017-02-14 10:22:30 +010075 uint8_t i;
76
Michal Vasko4c1fb492017-01-30 14:31:07 +010077 if (!name) {
78 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +010079 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +010080 } else if (idx > opts->hostkey_count) {
81 ERRARG("idx");
82 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010083 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010084
Michal Vaskofbfe8b62017-02-14 10:22:30 +010085 for (i = 0; i < opts->hostkey_count; ++i) {
86 if (!strcmp(opts->hostkeys[i], name)) {
87 ERRARG("name");
88 return -1;
89 }
90 }
91
Michal Vaskoe2713da2016-08-22 16:06:40 +020092 ++opts->hostkey_count;
93 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
94 if (!opts->hostkeys) {
95 ERRMEM;
96 return -1;
97 }
Michal Vasko7d255882017-02-09 13:35:08 +010098
99 if (idx < 0) {
100 idx = opts->hostkey_count - 1;
101 }
102 if (idx != opts->hostkey_count - 1) {
103 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
104 }
105 opts->hostkeys[idx] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200106
Michal Vasko5fcc7142016-02-02 12:21:10 +0100107 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100108}
109
110API int
Michal Vasko7d255882017-02-09 13:35:08 +0100111nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100112{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100113 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100114 struct nc_endpt *endpt;
115
Michal Vasko51e514d2016-02-02 15:51:52 +0100116 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100117 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100118 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100119 return -1;
120 }
Michal Vasko7d255882017-02-09 13:35:08 +0100121 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100122 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100123 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100124
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100125 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100126}
127
128API int
Michal Vasko7d255882017-02-09 13:35:08 +0100129nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100130{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100131 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200132 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100133
Michal Vasko2e6defd2016-10-07 15:48:15 +0200134 /* LOCK */
135 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
136 if (!client) {
137 return -1;
138 }
Michal Vasko7d255882017-02-09 13:35:08 +0100139 ret = nc_server_ssh_add_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200140 /* UNLOCK */
141 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200142
143 return ret;
144}
145
Michal Vasko4c1fb492017-01-30 14:31:07 +0100146API void
147nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
148 char **privkey_data, int *privkey_data_rsa),
149 void *user_data, void (*free_user_data)(void *user_data))
150{
151 if (!hostkey_clb) {
152 ERRARG("hostkey_clb");
153 return;
154 }
155
156 server_opts.hostkey_clb = hostkey_clb;
157 server_opts.hostkey_data = user_data;
158 server_opts.hostkey_data_free = free_user_data;
159}
160
Michal Vaskoe2713da2016-08-22 16:06:40 +0200161static int
Michal Vasko7d255882017-02-09 13:35:08 +0100162nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200163{
164 uint8_t i;
165
Michal Vasko7d255882017-02-09 13:35:08 +0100166 if (name && (idx > -1)) {
167 ERRARG("name and idx");
168 return -1;
169 } else if (idx >= opts->hostkey_count) {
170 ERRARG("idx");
171 }
172
173 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200174 for (i = 0; i < opts->hostkey_count; ++i) {
175 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
176 }
177 free(opts->hostkeys);
178 opts->hostkeys = NULL;
179 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100180 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200181 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100182 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100183 idx = i;
184 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200185 }
186 }
187
Michal Vasko7d255882017-02-09 13:35:08 +0100188 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200189 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100190 } else {
191remove_idx:
192 --opts->hostkey_count;
193 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
194 if (idx < opts->hostkey_count - 1) {
195 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
196 }
197 if (!opts->hostkey_count) {
198 free(opts->hostkeys);
199 opts->hostkeys = NULL;
200 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200201 }
202
203 return 0;
204}
205
206API int
Michal Vasko7d255882017-02-09 13:35:08 +0100207nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200208{
209 int ret;
210 struct nc_endpt *endpt;
211
212 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100213 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200214 if (!endpt) {
215 return -1;
216 }
Michal Vasko7d255882017-02-09 13:35:08 +0100217 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200218 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100219 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200220
221 return ret;
222}
223
224API int
Michal Vasko7d255882017-02-09 13:35:08 +0100225nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200226{
227 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200228 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200229
Michal Vasko2e6defd2016-10-07 15:48:15 +0200230 /* LOCK */
231 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
232 if (!client) {
233 return -1;
234 }
Michal Vasko7d255882017-02-09 13:35:08 +0100235 ret = nc_server_ssh_del_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200236 /* UNLOCK */
237 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100238
239 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100240}
241
242static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100243nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
244{
245 uint8_t i;
246 int16_t mov_idx = -1, after_idx = -1;
247 const char *bckup;
248
249 if (!key_mov) {
250 ERRARG("key_mov");
251 return -1;
252 }
253
254 for (i = 0; i < opts->hostkey_count; ++i) {
255 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
256 after_idx = i;
257 }
258 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
259 mov_idx = i;
260 }
261
262 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
263 break;
264 }
265 }
266
267 if (key_after && (after_idx == -1)) {
268 ERRARG("key_after");
269 return -1;
270 }
271 if (mov_idx == -1) {
272 ERRARG("key_mov");
273 return -1;
274 }
275 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
276 /* nothing to do */
277 return 0;
278 }
279
280 /* finally move the key */
281 bckup = opts->hostkeys[mov_idx];
282 if (mov_idx > after_idx) {
283 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
284 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
285 opts->hostkeys[after_idx + 1] = bckup;
286 } else {
287 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
288 opts->hostkeys[after_idx] = bckup;
289 }
290
291 return 0;
292}
293
294API int
295nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
296{
297 int ret;
298 struct nc_endpt *endpt;
299
300 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100301 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100302 if (!endpt) {
303 return -1;
304 }
305 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
306 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100307 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100308
309 return ret;
310}
311
312API int
313nc_server_ssh_ch_client_mov_hostkey(const char *client_name, const char *key_mov, const char *key_after)
314{
315 int ret;
316 struct nc_ch_client *client;
317
318 /* LOCK */
319 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
320 if (!client) {
321 return -1;
322 }
323 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, client->opts.ssh);
324 /* UNLOCK */
325 nc_server_ch_client_unlock(client);
326
327 return ret;
328}
329
330static int
331nc_server_ssh_mod_hostkey(const char *name, const char *new_name, struct nc_server_ssh_opts *opts)
332{
333 uint8_t i;
334
335 if (!name) {
336 ERRARG("name");
337 return -1;
338 } else if (!new_name) {
339 ERRARG("new_name");
340 return -1;
341 }
342
343 for (i = 0; i < opts->hostkey_count; ++i) {
344 if (!strcmp(opts->hostkeys[i], name)) {
345 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
346 opts->hostkeys[i] = lydict_insert(server_opts.ctx, new_name, 0);
347 return 0;
348 }
349 }
350
351 ERRARG("name");
352 return -1;
353}
354
355API int
356nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name)
357{
358 int ret;
359 struct nc_endpt *endpt;
360
361 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100362 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100363 if (!endpt) {
364 return -1;
365 }
366 ret = nc_server_ssh_mov_hostkey(name, new_name, endpt->opts.ssh);
367 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100368 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100369
370 return ret;
371}
372
373API int
374nc_server_ssh_ch_client_mod_hostkey(const char *client_name, const char *name, const char *new_name)
375{
376 int ret;
377 struct nc_ch_client *client;
378
379 /* LOCK */
380 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
381 if (!client) {
382 return -1;
383 }
384 ret = nc_server_ssh_mod_hostkey(name, new_name, client->opts.ssh);
385 /* UNLOCK */
386 nc_server_ch_client_unlock(client);
387
388 return ret;
389}
390
391static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100392nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100393{
Michal Vaskob05053d2016-01-22 16:12:06 +0100394 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200395 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100396 return -1;
397 }
398
Michal Vaskoe2713da2016-08-22 16:06:40 +0200399 if (opts->banner) {
400 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100401 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200402 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100403 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100404}
405
406API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100407nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100408{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100409 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100410 struct nc_endpt *endpt;
411
Michal Vasko51e514d2016-02-02 15:51:52 +0100412 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100413 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100414 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100415 return -1;
416 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200417 ret = nc_server_ssh_set_banner(banner, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100418 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100419 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100420
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100421 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100422}
423
424API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200425nc_server_ssh_ch_client_set_banner(const char *client_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100426{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100427 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200428 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100429
Michal Vasko2e6defd2016-10-07 15:48:15 +0200430 /* LOCK */
431 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
432 if (!client) {
433 return -1;
434 }
435 ret = nc_server_ssh_set_banner(banner, client->opts.ssh);
436 /* UNLOCK */
437 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100438
439 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100440}
441
442static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100443nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100444{
Michal Vasko086311b2016-01-08 09:53:11 +0100445 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
446 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200447 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100448 return -1;
449 }
450
Michal Vaskob05053d2016-01-22 16:12:06 +0100451 opts->auth_methods = auth_methods;
452 return 0;
453}
454
455API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100456nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100457{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100458 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100459 struct nc_endpt *endpt;
460
Michal Vasko51e514d2016-02-02 15:51:52 +0100461 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100462 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100463 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100464 return -1;
465 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200466 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100467 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100468 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100469
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100470 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100471}
472
473API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200474nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100475{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100476 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200477 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100478
Michal Vasko2e6defd2016-10-07 15:48:15 +0200479 /* LOCK */
480 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
481 if (!client) {
482 return -1;
483 }
484 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
485 /* UNLOCK */
486 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100487
488 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100489}
490
491static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100492nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100493{
Michal Vaskob05053d2016-01-22 16:12:06 +0100494 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200495 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100496 return -1;
497 }
498
Michal Vaskob05053d2016-01-22 16:12:06 +0100499 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100500 return 0;
501}
502
503API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100504nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100505{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100506 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100507 struct nc_endpt *endpt;
508
Michal Vasko51e514d2016-02-02 15:51:52 +0100509 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100510 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100511 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100512 return -1;
513 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200514 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100515 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100516 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100517
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100518 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100519}
520
521API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200522nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100523{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100524 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200525 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100526
Michal Vasko2e6defd2016-10-07 15:48:15 +0200527 /* LOCK */
528 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
529 if (!client) {
530 return -1;
531 }
532 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
533 /* UNLOCK */
534 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100535
536 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100537}
538
539static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100540nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100541{
Michal Vaskob05053d2016-01-22 16:12:06 +0100542 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200543 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100544 return -1;
545 }
546
Michal Vaskob05053d2016-01-22 16:12:06 +0100547 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100548 return 0;
549}
550
551API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100552nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100553{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100554 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100555 struct nc_endpt *endpt;
556
Michal Vasko51e514d2016-02-02 15:51:52 +0100557 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100558 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100559 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100560 return -1;
561 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200562 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100563 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100564 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100565
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100566 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100567}
568
569API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200570nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100571{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100572 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200573 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100574
Michal Vasko2e6defd2016-10-07 15:48:15 +0200575 /* LOCK */
576 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
577 if (!client) {
578 return -1;
579 }
580 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
581 /* UNLOCK */
582 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100583
584 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100585}
586
587static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100588_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
589 const char *username)
590{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100591 /* LOCK */
592 pthread_mutex_lock(&server_opts.authkey_lock);
593
Michal Vasko17dfda92016-12-01 14:06:16 +0100594 ++server_opts.authkey_count;
595 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
596 if (!server_opts.authkeys) {
597 ERRMEM;
598 return -1;
599 }
600 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
601 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
602 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
603 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
604
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100605 /* UNLOCK */
606 pthread_mutex_unlock(&server_opts.authkey_lock);
607
Michal Vasko17dfda92016-12-01 14:06:16 +0100608 return 0;
609}
610
611API int
612nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100613{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200614 if (!pubkey_path) {
615 ERRARG("pubkey_path");
616 return -1;
617 } else if (!username) {
618 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100619 return -1;
620 }
621
Michal Vasko17dfda92016-12-01 14:06:16 +0100622 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100623}
624
625API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100626nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100627{
Michal Vasko17dfda92016-12-01 14:06:16 +0100628 if (!pubkey_base64) {
629 ERRARG("pubkey_base64");
630 return -1;
631 } else if (!type) {
632 ERRARG("type");
633 return -1;
634 } else if (!username) {
635 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100636 return -1;
637 }
638
Michal Vasko17dfda92016-12-01 14:06:16 +0100639 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100640}
641
642API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100643nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
644 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100645{
Michal Vasko086311b2016-01-08 09:53:11 +0100646 uint32_t i;
647 int ret = -1;
648
Michal Vasko17dfda92016-12-01 14:06:16 +0100649 /* LOCK */
650 pthread_mutex_lock(&server_opts.authkey_lock);
651
652 if (!pubkey_path && !pubkey_base64 && !type && !username) {
653 for (i = 0; i < server_opts.authkey_count; ++i) {
654 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
655 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
656 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100657
Michal Vasko086311b2016-01-08 09:53:11 +0100658 ret = 0;
659 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100660 free(server_opts.authkeys);
661 server_opts.authkeys = NULL;
662 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100663 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100664 for (i = 0; i < server_opts.authkey_count; ++i) {
665 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
666 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
667 && (!type || (server_opts.authkeys[i].type == type))
668 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
669 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
670 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
671 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100672
Michal Vasko17dfda92016-12-01 14:06:16 +0100673 --server_opts.authkey_count;
674 if (i < server_opts.authkey_count) {
675 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
676 sizeof *server_opts.authkeys);
677 } else if (!server_opts.authkey_count) {
678 free(server_opts.authkeys);
679 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100680 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100681
682 ret = 0;
683 }
684 }
Michal Vasko086311b2016-01-08 09:53:11 +0100685 }
686
Michal Vasko51e514d2016-02-02 15:51:52 +0100687 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100688 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100689
690 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100691}
692
693void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100694nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100695{
Michal Vasko7d255882017-02-09 13:35:08 +0100696 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200697 if (opts->banner) {
698 lydict_remove(server_opts.ctx, opts->banner);
699 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100700 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100701}
702
Michal Vasko086311b2016-01-08 09:53:11 +0100703static char *
704auth_password_get_pwd_hash(const char *username)
705{
706 struct passwd *pwd, pwd_buf;
707 struct spwd *spwd, spwd_buf;
708 char *pass_hash = NULL, buf[256];
709
710 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
711 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100712 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100713 return NULL;
714 }
715
716 if (!strcmp(pwd->pw_passwd, "x")) {
717 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
718 if (!spwd) {
719 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
720 return NULL;
721 }
722
723 pass_hash = spwd->sp_pwdp;
724 } else {
725 pass_hash = pwd->pw_passwd;
726 }
727
728 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100729 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100730 return NULL;
731 }
732
733 /* check the hash structure for special meaning */
734 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
735 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
736 return NULL;
737 }
738 if (!strcmp(pass_hash, "*NP*")) {
739 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
740 return NULL;
741 }
742
743 return strdup(pass_hash);
744}
745
746static int
747auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
748{
749 char *new_pass_hash;
750 struct crypt_data cdata;
751
752 if (!pass_hash[0]) {
753 if (!pass_clear[0]) {
754 WRN("User authentication successful with an empty password!");
755 return 0;
756 } else {
757 /* the user did now know he does not need any password,
758 * (which should not be used) so deny authentication */
759 return 1;
760 }
761 }
762
763 cdata.initialized = 0;
764 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
765 return strcmp(new_pass_hash, pass_hash);
766}
767
768static void
769nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
770{
771 char *pass_hash;
772
773 pass_hash = auth_password_get_pwd_hash(session->username);
774 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100775 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100776 ssh_message_auth_reply_success(msg, 0);
777 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
778 free(pass_hash);
779 return;
780 }
781
782 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200783 ++session->opts.server.ssh_auth_attempts;
784 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100785 ssh_message_reply_default(msg);
786}
787
788static void
789nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
790{
791 char *pass_hash;
792
793 if (!ssh_message_auth_kbdint_is_response(msg)) {
794 const char *prompts[] = {"Password: "};
795 char echo[] = {0};
796
797 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
798 } else {
799 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
800 ssh_message_reply_default(msg);
801 return;
802 }
803 pass_hash = auth_password_get_pwd_hash(session->username);
804 if (!pass_hash) {
805 ssh_message_reply_default(msg);
806 return;
807 }
808 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
809 VRB("User \"%s\" authenticated.", session->username);
810 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
811 ssh_message_auth_reply_success(msg, 0);
812 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200813 ++session->opts.server.ssh_auth_attempts;
814 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100815 ssh_message_reply_default(msg);
816 }
Radek Krejcifb533742016-03-04 15:12:54 +0100817 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100818 }
819}
820
821static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100822auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100823{
824 uint32_t i;
825 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100826 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100827 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100828
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100829 /* LOCK */
830 pthread_mutex_lock(&server_opts.authkey_lock);
831
Michal Vasko17dfda92016-12-01 14:06:16 +0100832 for (i = 0; i < server_opts.authkey_count; ++i) {
833 switch (server_opts.authkeys[i].type) {
834 case NC_SSH_KEY_UNKNOWN:
835 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
836 break;
837 case NC_SSH_KEY_DSA:
838 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
839 break;
840 case NC_SSH_KEY_RSA:
841 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
842 break;
843 case NC_SSH_KEY_ECDSA:
844 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
845 break;
846 }
847
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200848 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100849 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200850 continue;
851 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100852 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100853 continue;
854 }
855
856 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
857 ssh_key_free(pub_key);
858 break;
859 }
860
861 ssh_key_free(pub_key);
862 }
863
Michal Vasko17dfda92016-12-01 14:06:16 +0100864 if (i < server_opts.authkey_count) {
865 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100866 }
867
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100868 /* UNLOCK */
869 pthread_mutex_unlock(&server_opts.authkey_lock);
870
Michal Vasko086311b2016-01-08 09:53:11 +0100871 return username;
872}
873
874static void
875nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
876{
877 const char *username;
878 int signature_state;
879
Michal Vasko17dfda92016-12-01 14:06:16 +0100880 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200881 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
882 goto fail;
883 } else if (strcmp(session->username, username)) {
884 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200885 goto fail;
886 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200887
Michal Vasko086311b2016-01-08 09:53:11 +0100888 signature_state = ssh_message_auth_publickey_state(msg);
889 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
890 VRB("User \"%s\" authenticated.", session->username);
891 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
892 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100893 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200894 /* accepting only the use of a public key */
895 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100896 }
897
Michal Vaskobd13a932016-09-14 09:00:35 +0200898 return;
899
900fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200901 ++session->opts.server.ssh_auth_attempts;
902 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100903 ssh_message_reply_default(msg);
904}
905
906static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100907nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100908{
Michal Vasko96164bf2016-01-21 15:41:58 +0100909 ssh_channel chan;
910
911 /* first channel request */
912 if (!session->ti.libssh.channel) {
913 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100914 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100915 return -1;
916 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100917 chan = ssh_message_channel_request_open_reply_accept(msg);
918 if (!chan) {
919 ERR("Failed to create a new SSH channel.");
920 return -1;
921 }
922 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100923
Michal Vasko96164bf2016-01-21 15:41:58 +0100924 /* additional channel request */
925 } else {
926 chan = ssh_message_channel_request_open_reply_accept(msg);
927 if (!chan) {
928 ERR("Session %u: failed to create a new SSH channel.", session->id);
929 return -1;
930 }
931 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100932 }
933
Michal Vasko086311b2016-01-08 09:53:11 +0100934 return 0;
935}
936
937static int
938nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
939{
Michal Vasko96164bf2016-01-21 15:41:58 +0100940 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100941
Michal Vasko96164bf2016-01-21 15:41:58 +0100942 if (strcmp(subsystem, "netconf")) {
943 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100944 return -1;
945 }
946
Michal Vasko96164bf2016-01-21 15:41:58 +0100947 if (session->ti.libssh.channel == channel) {
948 /* first channel requested */
949 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
950 ERRINT;
951 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100952 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100953 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
954 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
955 return -1;
956 }
957
958 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100959 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100960 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vaskoade892d2017-02-22 13:40:35 +0100961 new_session = nc_new_session(1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100962 if (!new_session) {
963 ERRMEM;
964 return -1;
965 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100966
967 /* insert the new session */
968 if (!session->ti.libssh.next) {
969 new_session->ti.libssh.next = session;
970 } else {
971 new_session->ti.libssh.next = session->ti.libssh.next;
972 }
973 session->ti.libssh.next = new_session;
974
975 new_session->status = NC_STATUS_STARTING;
976 new_session->side = NC_SERVER;
977 new_session->ti_type = NC_TI_LIBSSH;
978 new_session->ti_lock = session->ti_lock;
Michal Vaskoade892d2017-02-22 13:40:35 +0100979 new_session->ti_cond = session->ti_cond;
980 new_session->ti_inuse = session->ti_inuse;
Michal Vasko96164bf2016-01-21 15:41:58 +0100981 new_session->ti.libssh.channel = channel;
982 new_session->ti.libssh.session = session->ti.libssh.session;
983 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
984 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
985 new_session->port = session->port;
986 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100987 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
988 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100989 }
990
991 return 0;
992}
993
Michal Vasko96164bf2016-01-21 15:41:58 +0100994int
Michal Vaskob48aa812016-01-18 14:13:09 +0100995nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100996{
997 const char *str_type, *str_subtype = NULL, *username;
998 int subtype, type;
999 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001000
1001 type = ssh_message_type(msg);
1002 subtype = ssh_message_subtype(msg);
1003
1004 switch (type) {
1005 case SSH_REQUEST_AUTH:
1006 str_type = "request-auth";
1007 switch (subtype) {
1008 case SSH_AUTH_METHOD_NONE:
1009 str_subtype = "none";
1010 break;
1011 case SSH_AUTH_METHOD_PASSWORD:
1012 str_subtype = "password";
1013 break;
1014 case SSH_AUTH_METHOD_PUBLICKEY:
1015 str_subtype = "publickey";
1016 break;
1017 case SSH_AUTH_METHOD_HOSTBASED:
1018 str_subtype = "hostbased";
1019 break;
1020 case SSH_AUTH_METHOD_INTERACTIVE:
1021 str_subtype = "interactive";
1022 break;
1023 case SSH_AUTH_METHOD_GSSAPI_MIC:
1024 str_subtype = "gssapi-mic";
1025 break;
1026 }
1027 break;
1028
1029 case SSH_REQUEST_CHANNEL_OPEN:
1030 str_type = "request-channel-open";
1031 switch (subtype) {
1032 case SSH_CHANNEL_SESSION:
1033 str_subtype = "session";
1034 break;
1035 case SSH_CHANNEL_DIRECT_TCPIP:
1036 str_subtype = "direct-tcpip";
1037 break;
1038 case SSH_CHANNEL_FORWARDED_TCPIP:
1039 str_subtype = "forwarded-tcpip";
1040 break;
1041 case (int)SSH_CHANNEL_X11:
1042 str_subtype = "channel-x11";
1043 break;
1044 case SSH_CHANNEL_UNKNOWN:
1045 /* fallthrough */
1046 default:
1047 str_subtype = "unknown";
1048 break;
1049 }
1050 break;
1051
1052 case SSH_REQUEST_CHANNEL:
1053 str_type = "request-channel";
1054 switch (subtype) {
1055 case SSH_CHANNEL_REQUEST_PTY:
1056 str_subtype = "pty";
1057 break;
1058 case SSH_CHANNEL_REQUEST_EXEC:
1059 str_subtype = "exec";
1060 break;
1061 case SSH_CHANNEL_REQUEST_SHELL:
1062 str_subtype = "shell";
1063 break;
1064 case SSH_CHANNEL_REQUEST_ENV:
1065 str_subtype = "env";
1066 break;
1067 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1068 str_subtype = "subsystem";
1069 break;
1070 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1071 str_subtype = "window-change";
1072 break;
1073 case SSH_CHANNEL_REQUEST_X11:
1074 str_subtype = "x11";
1075 break;
1076 case SSH_CHANNEL_REQUEST_UNKNOWN:
1077 /* fallthrough */
1078 default:
1079 str_subtype = "unknown";
1080 break;
1081 }
1082 break;
1083
1084 case SSH_REQUEST_SERVICE:
1085 str_type = "request-service";
1086 str_subtype = ssh_message_service_service(msg);
1087 break;
1088
1089 case SSH_REQUEST_GLOBAL:
1090 str_type = "request-global";
1091 switch (subtype) {
1092 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1093 str_subtype = "tcpip-forward";
1094 break;
1095 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1096 str_subtype = "cancel-tcpip-forward";
1097 break;
1098 case SSH_GLOBAL_REQUEST_UNKNOWN:
1099 /* fallthrough */
1100 default:
1101 str_subtype = "unknown";
1102 break;
1103 }
1104 break;
1105
1106 default:
1107 str_type = "unknown";
1108 str_subtype = "unknown";
1109 break;
1110 }
1111
1112 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001113 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1114 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1115 * but we got it now, during session free */
1116 VRB("SSH message arrived on a %s session, the request will be denied.",
1117 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1118 ssh_message_reply_default(msg);
1119 return 0;
1120 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001121 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001122
1123 /*
1124 * process known messages
1125 */
1126 if (type == SSH_REQUEST_AUTH) {
1127 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1128 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1129 ssh_message_reply_default(msg);
1130 return 0;
1131 }
1132
Michal Vasko086311b2016-01-08 09:53:11 +01001133 /* save the username, do not let the client change it */
1134 username = ssh_message_auth_user(msg);
1135 if (!session->username) {
1136 if (!username) {
1137 ERR("Denying an auth request without a username.");
1138 return 1;
1139 }
1140
Michal Vasko05ba9df2016-01-13 14:40:27 +01001141 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001142 } else if (username) {
1143 if (strcmp(username, session->username)) {
1144 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1145 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001146 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001147 return 1;
1148 }
1149 }
1150
1151 if (subtype == SSH_AUTH_METHOD_NONE) {
1152 /* libssh will return the supported auth methods */
1153 return 1;
1154 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1155 nc_sshcb_auth_password(session, msg);
1156 return 0;
1157 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1158 nc_sshcb_auth_pubkey(session, msg);
1159 return 0;
1160 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1161 nc_sshcb_auth_kbdint(session, msg);
1162 return 0;
1163 }
1164 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001165 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001166 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001167 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001168 }
Michal Vasko086311b2016-01-08 09:53:11 +01001169 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001170
Michal Vasko0df67562016-01-21 15:50:11 +01001171 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001172 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1173 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001174 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001175 } else {
1176 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001177 }
1178 return 0;
1179 }
1180 }
1181
1182 /* we did not process it */
1183 return 1;
1184}
1185
Michal Vasko1a38c862016-01-15 15:50:07 +01001186/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001187static int
1188nc_open_netconf_channel(struct nc_session *session, int timeout)
1189{
Michal Vasko36c7be82017-02-22 13:37:59 +01001190 int ret;
1191 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001192
1193 /* message callback is executed twice to give chance for the channel to be
1194 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001195 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001196 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001197 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001198 return -1;
1199 }
1200
Michal Vaskoade892d2017-02-22 13:40:35 +01001201 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001202 if (ret != 1) {
1203 return ret;
1204 }
1205
1206 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1207 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001208 ERR("Failed to receive SSH messages on a session (%s).",
1209 ssh_get_error(session->ti.libssh.session));
Michal Vaskoade892d2017-02-22 13:40:35 +01001210 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +01001211 return -1;
1212 }
1213
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001214 if (!session->ti.libssh.channel) {
1215 /* we did not receive channel-open, timeout */
Michal Vaskoade892d2017-02-22 13:40:35 +01001216 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001217 return 0;
1218 }
1219
1220 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001221 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001222 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001223 ERR("Failed to receive SSH messages on a session (%s).",
1224 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001225 return -1;
1226 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001227
1228 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1229 /* we did not receive subsystem-request, timeout */
1230 return 0;
1231 }
1232
1233 return 1;
1234 }
1235
Michal Vasko36c7be82017-02-22 13:37:59 +01001236 if (timeout > -1) {
1237 nc_gettimespec(&ts_timeout);
1238 nc_addtimespec(&ts_timeout, timeout);
1239 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001240 while (1) {
1241 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001242 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001243 return -1;
1244 }
1245
Michal Vaskoade892d2017-02-22 13:40:35 +01001246 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001247 if (ret != 1) {
1248 return ret;
1249 }
1250
1251 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001252 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001253 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001254 ERR("Failed to receive SSH messages on a session (%s).",
1255 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001256 return -1;
1257 }
1258
Michal Vasko086311b2016-01-08 09:53:11 +01001259 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001260 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001261 }
1262
Michal Vasko086311b2016-01-08 09:53:11 +01001263 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001264 if (timeout > -1) {
1265 nc_gettimespec(&ts_cur);
1266 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1267 /* timeout */
1268 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1269 break;
1270 }
1271 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001272 }
Michal Vasko086311b2016-01-08 09:53:11 +01001273
Michal Vasko1a38c862016-01-15 15:50:07 +01001274 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001275}
1276
Michal Vasko4c1fb492017-01-30 14:31:07 +01001277static int
1278nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1279{
1280 uint8_t i;
1281 char *privkey_path, *privkey_data;
1282 int privkey_data_rsa, ret;
1283
1284 if (!server_opts.hostkey_clb) {
1285 ERR("Callback for retrieving SSH host keys not set.");
1286 return -1;
1287 }
1288
1289 for (i = 0; i < hostkey_count; ++i) {
1290 privkey_path = privkey_data = NULL;
1291 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1292 ERR("Host key callback failed.");
1293 return -1;
1294 }
1295
1296 if (privkey_data) {
1297 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1298 if (!privkey_path) {
1299 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1300 free(privkey_data);
1301 return -1;
1302 }
1303 }
1304
1305 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1306
1307 /* cleanup */
1308 if (privkey_data && unlink(privkey_path)) {
1309 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1310 }
1311 free(privkey_path);
1312 free(privkey_data);
1313
1314 if (ret != SSH_OK) {
1315 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], ssh_get_error(sbind));
1316 return -1;
1317 }
1318 }
1319
1320 return 0;
1321}
1322
Michal Vasko96164bf2016-01-21 15:41:58 +01001323int
Michal Vasko0190bc32016-03-02 15:47:49 +01001324nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001325{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001326 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001327 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001328 int libssh_auth_methods = 0, ret;
1329 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001330
Michal Vasko2cc4c682016-03-01 09:16:48 +01001331 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001332
Michal Vasko086311b2016-01-08 09:53:11 +01001333 /* other transport-specific data */
1334 session->ti_type = NC_TI_LIBSSH;
1335 session->ti.libssh.session = ssh_new();
1336 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001337 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001338 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001339 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001340 }
1341
Michal Vaskoc61c4492016-01-25 11:13:34 +01001342 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001343 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1344 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001345 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001346 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1347 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001348 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001349 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1350 }
1351 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1352
Michal Vaskoe2713da2016-08-22 16:06:40 +02001353 sbind = ssh_bind_new();
1354 if (!sbind) {
1355 ERR("Failed to create an SSH bind.");
1356 close(sock);
1357 return -1;
1358 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001359
1360 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1361 close(sock);
1362 ssh_bind_free(sbind);
1363 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001364 }
1365 if (opts->banner) {
1366 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1367 }
1368
Michal Vasko086311b2016-01-08 09:53:11 +01001369 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001370 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001371 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001372
Michal Vaskoe2713da2016-08-22 16:06:40 +02001373 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1374 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001375 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001376 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001377 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001378 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001379 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001380
Michal Vasko0190bc32016-03-02 15:47:49 +01001381 ssh_set_blocking(session->ti.libssh.session, 0);
1382
Michal Vasko36c7be82017-02-22 13:37:59 +01001383 if (timeout > -1) {
1384 nc_gettimespec(&ts_timeout);
1385 nc_addtimespec(&ts_timeout, timeout);
1386 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001387 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001388 /* this tends to take longer */
1389 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001390 if (timeout > -1) {
1391 nc_gettimespec(&ts_cur);
1392 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1393 break;
1394 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001395 }
1396 }
1397 if (ret == SSH_AGAIN) {
1398 ERR("SSH key exchange timeout.");
1399 return 0;
1400 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001401 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001402 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001403 }
1404
1405 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001406 if (opts->auth_timeout) {
1407 nc_gettimespec(&ts_timeout);
1408 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1409 }
1410 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001411 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001412 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001413 return -1;
1414 }
1415
Michal Vasko086311b2016-01-08 09:53:11 +01001416 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001417 ERR("Failed to receive SSH messages on a session (%s).",
1418 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001419 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001420 }
1421
Michal Vasko36c7be82017-02-22 13:37:59 +01001422 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1423 break;
1424 }
1425
Michal Vasko145ae672017-02-07 10:57:27 +01001426 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1427 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1428 return -1;
1429 }
1430
Michal Vasko086311b2016-01-08 09:53:11 +01001431 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001432 if (opts->auth_timeout) {
1433 nc_gettimespec(&ts_cur);
1434 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1435 /* timeout */
1436 break;
1437 }
1438 }
1439 }
Michal Vasko086311b2016-01-08 09:53:11 +01001440
1441 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1442 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001443 if (session->username) {
1444 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1445 } else {
1446 ERR("User failed to authenticate for too long, disconnecting.");
1447 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001448 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001449 }
1450
Michal Vasko086311b2016-01-08 09:53:11 +01001451 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001452 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001453 if (ret < 1) {
1454 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001455 }
1456
Michal Vasko96164bf2016-01-21 15:41:58 +01001457 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001458 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001459}
1460
Michal Vasko71090fc2016-05-24 16:37:28 +02001461API NC_MSG_TYPE
1462nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1463{
1464 NC_MSG_TYPE msgtype;
1465 struct nc_session *new_session = NULL;
1466
1467 if (!orig_session) {
1468 ERRARG("orig_session");
1469 return NC_MSG_ERROR;
1470 } else if (!session) {
1471 ERRARG("session");
1472 return NC_MSG_ERROR;
1473 }
1474
1475 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1476 && orig_session->ti.libssh.next) {
1477 for (new_session = orig_session->ti.libssh.next;
1478 new_session != orig_session;
1479 new_session = new_session->ti.libssh.next) {
1480 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1481 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1482 /* we found our session */
1483 break;
1484 }
1485 }
1486 if (new_session == orig_session) {
1487 new_session = NULL;
1488 }
1489 }
1490
1491 if (!new_session) {
1492 ERR("Session does not have a NETCONF SSH channel ready.");
1493 return NC_MSG_ERROR;
1494 }
1495
1496 /* assign new SID atomically */
1497 pthread_spin_lock(&server_opts.sid_lock);
1498 new_session->id = server_opts.new_session_id++;
1499 pthread_spin_unlock(&server_opts.sid_lock);
1500
1501 /* NETCONF handshake */
1502 msgtype = nc_handshake(new_session);
1503 if (msgtype != NC_MSG_HELLO) {
1504 return msgtype;
1505 }
1506
Michal Vasko2e6defd2016-10-07 15:48:15 +02001507 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001508 new_session->status = NC_STATUS_RUNNING;
1509 *session = new_session;
1510
1511 return msgtype;
1512}
1513
1514API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001515nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001516{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001517 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001518 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001519 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001520 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001521
Michal Vasko45e53ae2016-04-07 11:46:03 +02001522 if (!ps) {
1523 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001524 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001525 } else if (!session) {
1526 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001527 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001528 }
1529
Michal Vasko48a63ed2016-03-01 09:48:21 +01001530 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001531 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001532 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001533 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001534
Michal Vasko96164bf2016-01-21 15:41:58 +01001535 for (i = 0; i < ps->session_count; ++i) {
1536 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1537 && ps->sessions[i]->ti.libssh.next) {
1538 /* an SSH session with more channels */
1539 for (new_session = ps->sessions[i]->ti.libssh.next;
1540 new_session != ps->sessions[i];
1541 new_session = new_session->ti.libssh.next) {
1542 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1543 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1544 /* we found our session */
1545 break;
1546 }
1547 }
1548 if (new_session != ps->sessions[i]) {
1549 break;
1550 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001551
Michal Vasko96164bf2016-01-21 15:41:58 +01001552 new_session = NULL;
1553 }
1554 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001555
Michal Vasko48a63ed2016-03-01 09:48:21 +01001556 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001557 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001558
Michal Vasko96164bf2016-01-21 15:41:58 +01001559 if (!new_session) {
1560 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001561 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001562 }
1563
1564 /* assign new SID atomically */
1565 pthread_spin_lock(&server_opts.sid_lock);
1566 new_session->id = server_opts.new_session_id++;
1567 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001568
Michal Vasko086311b2016-01-08 09:53:11 +01001569 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001570 msgtype = nc_handshake(new_session);
1571 if (msgtype != NC_MSG_HELLO) {
1572 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001573 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001574
Michal Vasko2e6defd2016-10-07 15:48:15 +02001575 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001576 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001577 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001578
Michal Vasko71090fc2016-05-24 16:37:28 +02001579 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001580}