blob: 2cc924fedf35c0df76d817356dfed651f6312d05 [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 Vasko2e6defd2016-10-07 15:48:15 +0200117 endpt = nc_server_endpt_lock(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 Vaskoc6b9c7b2016-01-28 11:10:08 +0100123 nc_server_endpt_unlock(endpt);
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 Vasko2e6defd2016-10-07 15:48:15 +0200213 endpt = nc_server_endpt_lock(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 */
219 nc_server_endpt_unlock(endpt);
220
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 */
301 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
302 if (!endpt) {
303 return -1;
304 }
305 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
306 /* UNLOCK */
307 nc_server_endpt_unlock(endpt);
308
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 */
362 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
363 if (!endpt) {
364 return -1;
365 }
366 ret = nc_server_ssh_mov_hostkey(name, new_name, endpt->opts.ssh);
367 /* UNLOCK */
368 nc_server_endpt_unlock(endpt);
369
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 Vasko2e6defd2016-10-07 15:48:15 +0200413 endpt = nc_server_endpt_lock(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 Vaskoc6b9c7b2016-01-28 11:10:08 +0100419 nc_server_endpt_unlock(endpt);
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 Vasko2e6defd2016-10-07 15:48:15 +0200462 endpt = nc_server_endpt_lock(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 Vaskoc6b9c7b2016-01-28 11:10:08 +0100468 nc_server_endpt_unlock(endpt);
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 Vasko2e6defd2016-10-07 15:48:15 +0200510 endpt = nc_server_endpt_lock(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 Vaskoc6b9c7b2016-01-28 11:10:08 +0100516 nc_server_endpt_unlock(endpt);
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 Vasko2e6defd2016-10-07 15:48:15 +0200558 endpt = nc_server_endpt_lock(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 Vaskoc6b9c7b2016-01-28 11:10:08 +0100564 nc_server_endpt_unlock(endpt);
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 Vasko7abcdeb2016-05-30 15:27:00 +0200827 int ret;
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 */
961 new_session = calloc(1, sizeof *new_session);
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;
979 new_session->ti.libssh.channel = channel;
980 new_session->ti.libssh.session = session->ti.libssh.session;
981 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
982 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
983 new_session->port = session->port;
984 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100985 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
986 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100987 }
988
989 return 0;
990}
991
Michal Vasko96164bf2016-01-21 15:41:58 +0100992int
Michal Vaskob48aa812016-01-18 14:13:09 +0100993nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100994{
995 const char *str_type, *str_subtype = NULL, *username;
996 int subtype, type;
997 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100998
999 type = ssh_message_type(msg);
1000 subtype = ssh_message_subtype(msg);
1001
1002 switch (type) {
1003 case SSH_REQUEST_AUTH:
1004 str_type = "request-auth";
1005 switch (subtype) {
1006 case SSH_AUTH_METHOD_NONE:
1007 str_subtype = "none";
1008 break;
1009 case SSH_AUTH_METHOD_PASSWORD:
1010 str_subtype = "password";
1011 break;
1012 case SSH_AUTH_METHOD_PUBLICKEY:
1013 str_subtype = "publickey";
1014 break;
1015 case SSH_AUTH_METHOD_HOSTBASED:
1016 str_subtype = "hostbased";
1017 break;
1018 case SSH_AUTH_METHOD_INTERACTIVE:
1019 str_subtype = "interactive";
1020 break;
1021 case SSH_AUTH_METHOD_GSSAPI_MIC:
1022 str_subtype = "gssapi-mic";
1023 break;
1024 }
1025 break;
1026
1027 case SSH_REQUEST_CHANNEL_OPEN:
1028 str_type = "request-channel-open";
1029 switch (subtype) {
1030 case SSH_CHANNEL_SESSION:
1031 str_subtype = "session";
1032 break;
1033 case SSH_CHANNEL_DIRECT_TCPIP:
1034 str_subtype = "direct-tcpip";
1035 break;
1036 case SSH_CHANNEL_FORWARDED_TCPIP:
1037 str_subtype = "forwarded-tcpip";
1038 break;
1039 case (int)SSH_CHANNEL_X11:
1040 str_subtype = "channel-x11";
1041 break;
1042 case SSH_CHANNEL_UNKNOWN:
1043 /* fallthrough */
1044 default:
1045 str_subtype = "unknown";
1046 break;
1047 }
1048 break;
1049
1050 case SSH_REQUEST_CHANNEL:
1051 str_type = "request-channel";
1052 switch (subtype) {
1053 case SSH_CHANNEL_REQUEST_PTY:
1054 str_subtype = "pty";
1055 break;
1056 case SSH_CHANNEL_REQUEST_EXEC:
1057 str_subtype = "exec";
1058 break;
1059 case SSH_CHANNEL_REQUEST_SHELL:
1060 str_subtype = "shell";
1061 break;
1062 case SSH_CHANNEL_REQUEST_ENV:
1063 str_subtype = "env";
1064 break;
1065 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1066 str_subtype = "subsystem";
1067 break;
1068 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1069 str_subtype = "window-change";
1070 break;
1071 case SSH_CHANNEL_REQUEST_X11:
1072 str_subtype = "x11";
1073 break;
1074 case SSH_CHANNEL_REQUEST_UNKNOWN:
1075 /* fallthrough */
1076 default:
1077 str_subtype = "unknown";
1078 break;
1079 }
1080 break;
1081
1082 case SSH_REQUEST_SERVICE:
1083 str_type = "request-service";
1084 str_subtype = ssh_message_service_service(msg);
1085 break;
1086
1087 case SSH_REQUEST_GLOBAL:
1088 str_type = "request-global";
1089 switch (subtype) {
1090 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1091 str_subtype = "tcpip-forward";
1092 break;
1093 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1094 str_subtype = "cancel-tcpip-forward";
1095 break;
1096 case SSH_GLOBAL_REQUEST_UNKNOWN:
1097 /* fallthrough */
1098 default:
1099 str_subtype = "unknown";
1100 break;
1101 }
1102 break;
1103
1104 default:
1105 str_type = "unknown";
1106 str_subtype = "unknown";
1107 break;
1108 }
1109
1110 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001111 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1112 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1113 * but we got it now, during session free */
1114 VRB("SSH message arrived on a %s session, the request will be denied.",
1115 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1116 ssh_message_reply_default(msg);
1117 return 0;
1118 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001119 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001120
1121 /*
1122 * process known messages
1123 */
1124 if (type == SSH_REQUEST_AUTH) {
1125 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1126 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1127 ssh_message_reply_default(msg);
1128 return 0;
1129 }
1130
Michal Vasko086311b2016-01-08 09:53:11 +01001131 /* save the username, do not let the client change it */
1132 username = ssh_message_auth_user(msg);
1133 if (!session->username) {
1134 if (!username) {
1135 ERR("Denying an auth request without a username.");
1136 return 1;
1137 }
1138
Michal Vasko05ba9df2016-01-13 14:40:27 +01001139 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001140 } else if (username) {
1141 if (strcmp(username, session->username)) {
1142 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1143 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001144 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001145 return 1;
1146 }
1147 }
1148
1149 if (subtype == SSH_AUTH_METHOD_NONE) {
1150 /* libssh will return the supported auth methods */
1151 return 1;
1152 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1153 nc_sshcb_auth_password(session, msg);
1154 return 0;
1155 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1156 nc_sshcb_auth_pubkey(session, msg);
1157 return 0;
1158 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1159 nc_sshcb_auth_kbdint(session, msg);
1160 return 0;
1161 }
1162 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001163 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001164 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001165 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001166 }
Michal Vasko086311b2016-01-08 09:53:11 +01001167 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001168
Michal Vasko0df67562016-01-21 15:50:11 +01001169 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001170 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1171 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001172 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001173 } else {
1174 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001175 }
1176 return 0;
1177 }
1178 }
1179
1180 /* we did not process it */
1181 return 1;
1182}
1183
Michal Vasko1a38c862016-01-15 15:50:07 +01001184/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001185static int
1186nc_open_netconf_channel(struct nc_session *session, int timeout)
1187{
Michal Vasko36c7be82017-02-22 13:37:59 +01001188 int ret;
1189 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001190
1191 /* message callback is executed twice to give chance for the channel to be
1192 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001193 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001194 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001195 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001196 return -1;
1197 }
1198
Michal vasko50cc94f2016-10-04 13:46:20 +02001199 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001200 if (ret != 1) {
1201 return ret;
1202 }
1203
1204 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1205 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001206 ERR("Failed to receive SSH messages on a session (%s).",
1207 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +01001208 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +01001209 return -1;
1210 }
1211
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001212 if (!session->ti.libssh.channel) {
1213 /* we did not receive channel-open, timeout */
1214 pthread_mutex_unlock(session->ti_lock);
1215 return 0;
1216 }
1217
1218 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1219 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001220 ERR("Failed to receive SSH messages on a session (%s).",
1221 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001222 pthread_mutex_unlock(session->ti_lock);
1223 return -1;
1224 }
1225 pthread_mutex_unlock(session->ti_lock);
1226
1227 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1228 /* we did not receive subsystem-request, timeout */
1229 return 0;
1230 }
1231
1232 return 1;
1233 }
1234
Michal Vasko36c7be82017-02-22 13:37:59 +01001235 if (timeout > -1) {
1236 nc_gettimespec(&ts_timeout);
1237 nc_addtimespec(&ts_timeout, timeout);
1238 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001239 while (1) {
1240 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001241 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001242 return -1;
1243 }
1244
Michal vasko50cc94f2016-10-04 13:46:20 +02001245 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001246 if (ret != 1) {
1247 return ret;
1248 }
1249
1250 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1251 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001252 ERR("Failed to receive SSH messages on a session (%s).",
1253 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001254 pthread_mutex_unlock(session->ti_lock);
1255 return -1;
1256 }
1257
1258 pthread_mutex_unlock(session->ti_lock);
1259
Michal Vasko086311b2016-01-08 09:53:11 +01001260 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001261 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001262 }
1263
Michal Vasko086311b2016-01-08 09:53:11 +01001264 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001265 if (timeout > -1) {
1266 nc_gettimespec(&ts_cur);
1267 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1268 /* timeout */
1269 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1270 break;
1271 }
1272 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001273 }
Michal Vasko086311b2016-01-08 09:53:11 +01001274
Michal Vasko1a38c862016-01-15 15:50:07 +01001275 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001276}
1277
Michal Vasko4c1fb492017-01-30 14:31:07 +01001278static int
1279nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1280{
1281 uint8_t i;
1282 char *privkey_path, *privkey_data;
1283 int privkey_data_rsa, ret;
1284
1285 if (!server_opts.hostkey_clb) {
1286 ERR("Callback for retrieving SSH host keys not set.");
1287 return -1;
1288 }
1289
1290 for (i = 0; i < hostkey_count; ++i) {
1291 privkey_path = privkey_data = NULL;
1292 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1293 ERR("Host key callback failed.");
1294 return -1;
1295 }
1296
1297 if (privkey_data) {
1298 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1299 if (!privkey_path) {
1300 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1301 free(privkey_data);
1302 return -1;
1303 }
1304 }
1305
1306 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1307
1308 /* cleanup */
1309 if (privkey_data && unlink(privkey_path)) {
1310 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1311 }
1312 free(privkey_path);
1313 free(privkey_data);
1314
1315 if (ret != SSH_OK) {
1316 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], ssh_get_error(sbind));
1317 return -1;
1318 }
1319 }
1320
1321 return 0;
1322}
1323
Michal Vasko96164bf2016-01-21 15:41:58 +01001324int
Michal Vasko0190bc32016-03-02 15:47:49 +01001325nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001326{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001327 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001328 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001329 int libssh_auth_methods = 0, ret;
1330 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001331
Michal Vasko2cc4c682016-03-01 09:16:48 +01001332 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001333
Michal Vasko086311b2016-01-08 09:53:11 +01001334 /* other transport-specific data */
1335 session->ti_type = NC_TI_LIBSSH;
1336 session->ti.libssh.session = ssh_new();
1337 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001338 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001339 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001340 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001341 }
1342
Michal Vaskoc61c4492016-01-25 11:13:34 +01001343 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001344 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1345 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001346 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001347 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1348 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001349 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001350 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1351 }
1352 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1353
Michal Vaskoe2713da2016-08-22 16:06:40 +02001354 sbind = ssh_bind_new();
1355 if (!sbind) {
1356 ERR("Failed to create an SSH bind.");
1357 close(sock);
1358 return -1;
1359 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001360
1361 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1362 close(sock);
1363 ssh_bind_free(sbind);
1364 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001365 }
1366 if (opts->banner) {
1367 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1368 }
1369
Michal Vasko086311b2016-01-08 09:53:11 +01001370 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001371 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001372 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001373
Michal Vaskoe2713da2016-08-22 16:06:40 +02001374 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1375 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001376 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001377 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001378 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001379 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001380 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001381
Michal Vasko0190bc32016-03-02 15:47:49 +01001382 ssh_set_blocking(session->ti.libssh.session, 0);
1383
Michal Vasko36c7be82017-02-22 13:37:59 +01001384 if (timeout > -1) {
1385 nc_gettimespec(&ts_timeout);
1386 nc_addtimespec(&ts_timeout, timeout);
1387 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001388 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001389 /* this tends to take longer */
1390 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001391 if (timeout > -1) {
1392 nc_gettimespec(&ts_cur);
1393 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1394 break;
1395 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001396 }
1397 }
1398 if (ret == SSH_AGAIN) {
1399 ERR("SSH key exchange timeout.");
1400 return 0;
1401 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001402 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001403 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001404 }
1405
1406 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001407 if (opts->auth_timeout) {
1408 nc_gettimespec(&ts_timeout);
1409 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1410 }
1411 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001412 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001413 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001414 return -1;
1415 }
1416
Michal Vasko086311b2016-01-08 09:53:11 +01001417 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001418 ERR("Failed to receive SSH messages on a session (%s).",
1419 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001420 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001421 }
1422
Michal Vasko36c7be82017-02-22 13:37:59 +01001423 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1424 break;
1425 }
1426
Michal Vasko145ae672017-02-07 10:57:27 +01001427 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1428 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1429 return -1;
1430 }
1431
Michal Vasko086311b2016-01-08 09:53:11 +01001432 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001433 if (opts->auth_timeout) {
1434 nc_gettimespec(&ts_cur);
1435 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1436 /* timeout */
1437 break;
1438 }
1439 }
1440 }
Michal Vasko086311b2016-01-08 09:53:11 +01001441
1442 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1443 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001444 if (session->username) {
1445 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1446 } else {
1447 ERR("User failed to authenticate for too long, disconnecting.");
1448 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001449 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001450 }
1451
Michal Vasko086311b2016-01-08 09:53:11 +01001452 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001453 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001454 if (ret < 1) {
1455 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001456 }
1457
Michal Vasko96164bf2016-01-21 15:41:58 +01001458 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001459 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001460}
1461
Michal Vasko71090fc2016-05-24 16:37:28 +02001462API NC_MSG_TYPE
1463nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1464{
1465 NC_MSG_TYPE msgtype;
1466 struct nc_session *new_session = NULL;
1467
1468 if (!orig_session) {
1469 ERRARG("orig_session");
1470 return NC_MSG_ERROR;
1471 } else if (!session) {
1472 ERRARG("session");
1473 return NC_MSG_ERROR;
1474 }
1475
1476 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1477 && orig_session->ti.libssh.next) {
1478 for (new_session = orig_session->ti.libssh.next;
1479 new_session != orig_session;
1480 new_session = new_session->ti.libssh.next) {
1481 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1482 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1483 /* we found our session */
1484 break;
1485 }
1486 }
1487 if (new_session == orig_session) {
1488 new_session = NULL;
1489 }
1490 }
1491
1492 if (!new_session) {
1493 ERR("Session does not have a NETCONF SSH channel ready.");
1494 return NC_MSG_ERROR;
1495 }
1496
1497 /* assign new SID atomically */
1498 pthread_spin_lock(&server_opts.sid_lock);
1499 new_session->id = server_opts.new_session_id++;
1500 pthread_spin_unlock(&server_opts.sid_lock);
1501
1502 /* NETCONF handshake */
1503 msgtype = nc_handshake(new_session);
1504 if (msgtype != NC_MSG_HELLO) {
1505 return msgtype;
1506 }
1507
Michal Vasko2e6defd2016-10-07 15:48:15 +02001508 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001509 new_session->status = NC_STATUS_RUNNING;
1510 *session = new_session;
1511
1512 return msgtype;
1513}
1514
1515API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001516nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001517{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001518 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001519 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001520 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001521 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001522
Michal Vasko45e53ae2016-04-07 11:46:03 +02001523 if (!ps) {
1524 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001525 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001526 } else if (!session) {
1527 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001528 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001529 }
1530
Michal Vasko48a63ed2016-03-01 09:48:21 +01001531 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001532 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001533 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001534 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001535
Michal Vasko96164bf2016-01-21 15:41:58 +01001536 for (i = 0; i < ps->session_count; ++i) {
1537 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1538 && ps->sessions[i]->ti.libssh.next) {
1539 /* an SSH session with more channels */
1540 for (new_session = ps->sessions[i]->ti.libssh.next;
1541 new_session != ps->sessions[i];
1542 new_session = new_session->ti.libssh.next) {
1543 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1544 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1545 /* we found our session */
1546 break;
1547 }
1548 }
1549 if (new_session != ps->sessions[i]) {
1550 break;
1551 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001552
Michal Vasko96164bf2016-01-21 15:41:58 +01001553 new_session = NULL;
1554 }
1555 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001556
Michal Vasko48a63ed2016-03-01 09:48:21 +01001557 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001558 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001559
Michal Vasko96164bf2016-01-21 15:41:58 +01001560 if (!new_session) {
1561 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001562 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001563 }
1564
1565 /* assign new SID atomically */
1566 pthread_spin_lock(&server_opts.sid_lock);
1567 new_session->id = server_opts.new_session_id++;
1568 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001569
Michal Vasko086311b2016-01-08 09:53:11 +01001570 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001571 msgtype = nc_handshake(new_session);
1572 if (msgtype != NC_MSG_HELLO) {
1573 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001574 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001575
Michal Vasko2e6defd2016-10-07 15:48:15 +02001576 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001577 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001578 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001579
Michal Vasko71090fc2016-05-24 16:37:28 +02001580 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001581}