blob: f48bee6869589befdf80fc78a470354defe9f863 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server_ssh.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 SSH server session manipulation functions
5 *
Michal Vasko4c1fb492017-01-30 14:31:07 +01006 * Copyright (c) 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
15#define _GNU_SOURCE
16
17#include <stdlib.h>
18#include <string.h>
19#include <sys/types.h>
Michal Vasko27252692017-03-21 15:34:13 +010020#include <sys/stat.h>
Michal Vasko086311b2016-01-08 09:53:11 +010021#include <pwd.h>
22#include <shadow.h>
23#include <crypt.h>
24#include <errno.h>
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
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +020030#if !defined(HAVE_CRYPT_R)
31pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER;
32#endif
33
Michal Vasko086311b2016-01-08 09:53:11 +010034extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010035
Michal Vasko4c1fb492017-01-30 14:31:07 +010036static char *
37base64der_key_to_tmp_file(const char *in, int rsa)
Michal Vasko086311b2016-01-08 09:53:11 +010038{
Michal Vasko4c1fb492017-01-30 14:31:07 +010039 char path[12] = "/tmp/XXXXXX";
40 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010041 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010042 FILE *file;
43
44 if (in == NULL) {
45 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010046 }
47
Michal Vasko27252692017-03-21 15:34:13 +010048 umode = umask(0600);
Michal Vasko4c1fb492017-01-30 14:31:07 +010049 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010050 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010051 if (fd == -1) {
52 return NULL;
53 }
54
55 file = fdopen(fd, "r");
56 if (!file) {
57 close(fd);
58 return NULL;
59 }
60
61 /* write the key into the file */
62 written = fwrite("-----BEGIN ", 1, 11, file);
63 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
64 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
65 written += fwrite(in, 1, strlen(in), file);
66 written += fwrite("\n-----END ", 1, 10, file);
67 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
68 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
69
70 fclose(file);
71 if ((unsigned)written != 62 + strlen(in)) {
72 unlink(path);
73 return NULL;
74 }
75
76 return strdup(path);
77}
78
79static int
Michal Vasko7d255882017-02-09 13:35:08 +010080nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +010081{
Michal Vaskofbfe8b62017-02-14 10:22:30 +010082 uint8_t i;
83
Michal Vasko4c1fb492017-01-30 14:31:07 +010084 if (!name) {
85 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +010086 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +010087 } else if (idx > opts->hostkey_count) {
88 ERRARG("idx");
89 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010090 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010091
Michal Vaskofbfe8b62017-02-14 10:22:30 +010092 for (i = 0; i < opts->hostkey_count; ++i) {
93 if (!strcmp(opts->hostkeys[i], name)) {
94 ERRARG("name");
95 return -1;
96 }
97 }
98
Michal Vaskoe2713da2016-08-22 16:06:40 +020099 ++opts->hostkey_count;
100 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
101 if (!opts->hostkeys) {
102 ERRMEM;
103 return -1;
104 }
Michal Vasko7d255882017-02-09 13:35:08 +0100105
106 if (idx < 0) {
107 idx = opts->hostkey_count - 1;
108 }
109 if (idx != opts->hostkey_count - 1) {
110 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
111 }
112 opts->hostkeys[idx] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200113
Michal Vasko5fcc7142016-02-02 12:21:10 +0100114 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100115}
116
117API int
Michal Vasko7d255882017-02-09 13:35:08 +0100118nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100119{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100120 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100121 struct nc_endpt *endpt;
122
Michal Vasko51e514d2016-02-02 15:51:52 +0100123 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100124 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100125 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100126 return -1;
127 }
Michal Vasko7d255882017-02-09 13:35:08 +0100128 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100129 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100130 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100131
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100132 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100133}
134
135API int
Michal Vasko7d255882017-02-09 13:35:08 +0100136nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100137{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100138 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200139 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100140
Michal Vasko2e6defd2016-10-07 15:48:15 +0200141 /* LOCK */
142 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
143 if (!client) {
144 return -1;
145 }
Michal Vasko7d255882017-02-09 13:35:08 +0100146 ret = nc_server_ssh_add_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200147 /* UNLOCK */
148 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200149
150 return ret;
151}
152
Michal Vasko4c1fb492017-01-30 14:31:07 +0100153API void
154nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
155 char **privkey_data, int *privkey_data_rsa),
156 void *user_data, void (*free_user_data)(void *user_data))
157{
158 if (!hostkey_clb) {
159 ERRARG("hostkey_clb");
160 return;
161 }
162
163 server_opts.hostkey_clb = hostkey_clb;
164 server_opts.hostkey_data = user_data;
165 server_opts.hostkey_data_free = free_user_data;
166}
167
Michal Vaskoe2713da2016-08-22 16:06:40 +0200168static int
Michal Vasko7d255882017-02-09 13:35:08 +0100169nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200170{
171 uint8_t i;
172
Michal Vasko7d255882017-02-09 13:35:08 +0100173 if (name && (idx > -1)) {
174 ERRARG("name and idx");
175 return -1;
176 } else if (idx >= opts->hostkey_count) {
177 ERRARG("idx");
178 }
179
180 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200181 for (i = 0; i < opts->hostkey_count; ++i) {
182 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
183 }
184 free(opts->hostkeys);
185 opts->hostkeys = NULL;
186 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100187 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200188 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100189 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100190 idx = i;
191 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200192 }
193 }
194
Michal Vasko7d255882017-02-09 13:35:08 +0100195 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200196 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100197 } else {
198remove_idx:
199 --opts->hostkey_count;
200 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
201 if (idx < opts->hostkey_count - 1) {
202 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
203 }
204 if (!opts->hostkey_count) {
205 free(opts->hostkeys);
206 opts->hostkeys = NULL;
207 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200208 }
209
210 return 0;
211}
212
213API int
Michal Vasko7d255882017-02-09 13:35:08 +0100214nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200215{
216 int ret;
217 struct nc_endpt *endpt;
218
219 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100220 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200221 if (!endpt) {
222 return -1;
223 }
Michal Vasko7d255882017-02-09 13:35:08 +0100224 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200225 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100226 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200227
228 return ret;
229}
230
231API int
Michal Vasko7d255882017-02-09 13:35:08 +0100232nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200233{
234 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200235 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200236
Michal Vasko2e6defd2016-10-07 15:48:15 +0200237 /* LOCK */
238 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
239 if (!client) {
240 return -1;
241 }
Michal Vasko7d255882017-02-09 13:35:08 +0100242 ret = nc_server_ssh_del_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200243 /* UNLOCK */
244 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100245
246 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100247}
248
249static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100250nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
251{
252 uint8_t i;
253 int16_t mov_idx = -1, after_idx = -1;
254 const char *bckup;
255
256 if (!key_mov) {
257 ERRARG("key_mov");
258 return -1;
259 }
260
261 for (i = 0; i < opts->hostkey_count; ++i) {
262 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
263 after_idx = i;
264 }
265 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
266 mov_idx = i;
267 }
268
269 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
270 break;
271 }
272 }
273
274 if (key_after && (after_idx == -1)) {
275 ERRARG("key_after");
276 return -1;
277 }
278 if (mov_idx == -1) {
279 ERRARG("key_mov");
280 return -1;
281 }
282 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
283 /* nothing to do */
284 return 0;
285 }
286
287 /* finally move the key */
288 bckup = opts->hostkeys[mov_idx];
289 if (mov_idx > after_idx) {
290 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
291 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
292 opts->hostkeys[after_idx + 1] = bckup;
293 } else {
294 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
295 opts->hostkeys[after_idx] = bckup;
296 }
297
298 return 0;
299}
300
301API int
302nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
303{
304 int ret;
305 struct nc_endpt *endpt;
306
307 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100308 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100309 if (!endpt) {
310 return -1;
311 }
312 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
313 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100314 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100315
316 return ret;
317}
318
319API int
320nc_server_ssh_ch_client_mov_hostkey(const char *client_name, const char *key_mov, const char *key_after)
321{
322 int ret;
323 struct nc_ch_client *client;
324
325 /* LOCK */
326 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
327 if (!client) {
328 return -1;
329 }
330 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, client->opts.ssh);
331 /* UNLOCK */
332 nc_server_ch_client_unlock(client);
333
334 return ret;
335}
336
337static int
338nc_server_ssh_mod_hostkey(const char *name, const char *new_name, struct nc_server_ssh_opts *opts)
339{
340 uint8_t i;
341
342 if (!name) {
343 ERRARG("name");
344 return -1;
345 } else if (!new_name) {
346 ERRARG("new_name");
347 return -1;
348 }
349
350 for (i = 0; i < opts->hostkey_count; ++i) {
351 if (!strcmp(opts->hostkeys[i], name)) {
352 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
353 opts->hostkeys[i] = lydict_insert(server_opts.ctx, new_name, 0);
354 return 0;
355 }
356 }
357
358 ERRARG("name");
359 return -1;
360}
361
362API int
363nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name)
364{
365 int ret;
366 struct nc_endpt *endpt;
367
368 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100369 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100370 if (!endpt) {
371 return -1;
372 }
373 ret = nc_server_ssh_mov_hostkey(name, new_name, endpt->opts.ssh);
374 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100375 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100376
377 return ret;
378}
379
380API int
381nc_server_ssh_ch_client_mod_hostkey(const char *client_name, const char *name, const char *new_name)
382{
383 int ret;
384 struct nc_ch_client *client;
385
386 /* LOCK */
387 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
388 if (!client) {
389 return -1;
390 }
391 ret = nc_server_ssh_mod_hostkey(name, new_name, client->opts.ssh);
392 /* UNLOCK */
393 nc_server_ch_client_unlock(client);
394
395 return ret;
396}
397
398static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100399nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100400{
Michal Vaskob05053d2016-01-22 16:12:06 +0100401 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200402 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100403 return -1;
404 }
405
Michal Vaskoe2713da2016-08-22 16:06:40 +0200406 if (opts->banner) {
407 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100408 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200409 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100410 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100411}
412
413API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100414nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100415{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100416 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100417 struct nc_endpt *endpt;
418
Michal Vasko51e514d2016-02-02 15:51:52 +0100419 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100420 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100421 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100422 return -1;
423 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200424 ret = nc_server_ssh_set_banner(banner, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100425 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100426 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100427
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100428 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100429}
430
431API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200432nc_server_ssh_ch_client_set_banner(const char *client_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100433{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100434 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200435 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100436
Michal Vasko2e6defd2016-10-07 15:48:15 +0200437 /* LOCK */
438 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
439 if (!client) {
440 return -1;
441 }
442 ret = nc_server_ssh_set_banner(banner, client->opts.ssh);
443 /* UNLOCK */
444 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100445
446 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100447}
448
449static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100450nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100451{
Michal Vasko086311b2016-01-08 09:53:11 +0100452 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
453 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200454 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100455 return -1;
456 }
457
Michal Vaskob05053d2016-01-22 16:12:06 +0100458 opts->auth_methods = auth_methods;
459 return 0;
460}
461
462API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100463nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100464{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100465 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100466 struct nc_endpt *endpt;
467
Michal Vasko51e514d2016-02-02 15:51:52 +0100468 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100469 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100470 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100471 return -1;
472 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200473 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100474 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100475 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100476
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100477 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100478}
479
480API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200481nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100482{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100483 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200484 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100485
Michal Vasko2e6defd2016-10-07 15:48:15 +0200486 /* LOCK */
487 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
488 if (!client) {
489 return -1;
490 }
491 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
492 /* UNLOCK */
493 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100494
495 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100496}
497
498static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100499nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100500{
Michal Vaskob05053d2016-01-22 16:12:06 +0100501 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200502 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100503 return -1;
504 }
505
Michal Vaskob05053d2016-01-22 16:12:06 +0100506 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100507 return 0;
508}
509
510API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100511nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100512{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100513 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100514 struct nc_endpt *endpt;
515
Michal Vasko51e514d2016-02-02 15:51:52 +0100516 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100517 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100518 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100519 return -1;
520 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200521 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100522 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100523 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100524
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100525 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100526}
527
528API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200529nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100530{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100531 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200532 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100533
Michal Vasko2e6defd2016-10-07 15:48:15 +0200534 /* LOCK */
535 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
536 if (!client) {
537 return -1;
538 }
539 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
540 /* UNLOCK */
541 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100542
543 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100544}
545
546static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100547nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100548{
Michal Vaskob05053d2016-01-22 16:12:06 +0100549 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200550 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100551 return -1;
552 }
553
Michal Vaskob05053d2016-01-22 16:12:06 +0100554 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100555 return 0;
556}
557
558API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100559nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100560{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100561 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100562 struct nc_endpt *endpt;
563
Michal Vasko51e514d2016-02-02 15:51:52 +0100564 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100565 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100566 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100567 return -1;
568 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200569 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100570 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100571 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100572
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100573 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100574}
575
576API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200577nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100578{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100579 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200580 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100581
Michal Vasko2e6defd2016-10-07 15:48:15 +0200582 /* LOCK */
583 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
584 if (!client) {
585 return -1;
586 }
587 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
588 /* UNLOCK */
589 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100590
591 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100592}
593
594static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100595_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
596 const char *username)
597{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100598 /* LOCK */
599 pthread_mutex_lock(&server_opts.authkey_lock);
600
Michal Vasko17dfda92016-12-01 14:06:16 +0100601 ++server_opts.authkey_count;
602 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
603 if (!server_opts.authkeys) {
604 ERRMEM;
605 return -1;
606 }
607 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
608 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
609 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
610 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
611
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100612 /* UNLOCK */
613 pthread_mutex_unlock(&server_opts.authkey_lock);
614
Michal Vasko17dfda92016-12-01 14:06:16 +0100615 return 0;
616}
617
618API int
619nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100620{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200621 if (!pubkey_path) {
622 ERRARG("pubkey_path");
623 return -1;
624 } else if (!username) {
625 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100626 return -1;
627 }
628
Michal Vasko17dfda92016-12-01 14:06:16 +0100629 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100630}
631
632API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100633nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100634{
Michal Vasko17dfda92016-12-01 14:06:16 +0100635 if (!pubkey_base64) {
636 ERRARG("pubkey_base64");
637 return -1;
638 } else if (!type) {
639 ERRARG("type");
640 return -1;
641 } else if (!username) {
642 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100643 return -1;
644 }
645
Michal Vasko17dfda92016-12-01 14:06:16 +0100646 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100647}
648
649API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100650nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
651 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100652{
Michal Vasko086311b2016-01-08 09:53:11 +0100653 uint32_t i;
654 int ret = -1;
655
Michal Vasko17dfda92016-12-01 14:06:16 +0100656 /* LOCK */
657 pthread_mutex_lock(&server_opts.authkey_lock);
658
659 if (!pubkey_path && !pubkey_base64 && !type && !username) {
660 for (i = 0; i < server_opts.authkey_count; ++i) {
661 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
662 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
663 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100664
Michal Vasko086311b2016-01-08 09:53:11 +0100665 ret = 0;
666 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100667 free(server_opts.authkeys);
668 server_opts.authkeys = NULL;
669 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100670 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100671 for (i = 0; i < server_opts.authkey_count; ++i) {
672 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
673 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
674 && (!type || (server_opts.authkeys[i].type == type))
675 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
676 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
677 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
678 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100679
Michal Vasko17dfda92016-12-01 14:06:16 +0100680 --server_opts.authkey_count;
681 if (i < server_opts.authkey_count) {
682 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
683 sizeof *server_opts.authkeys);
684 } else if (!server_opts.authkey_count) {
685 free(server_opts.authkeys);
686 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100687 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100688
689 ret = 0;
690 }
691 }
Michal Vasko086311b2016-01-08 09:53:11 +0100692 }
693
Michal Vasko51e514d2016-02-02 15:51:52 +0100694 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100695 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100696
697 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100698}
699
700void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100701nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100702{
Michal Vasko7d255882017-02-09 13:35:08 +0100703 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200704 if (opts->banner) {
705 lydict_remove(server_opts.ctx, opts->banner);
706 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100707 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100708}
709
Michal Vasko086311b2016-01-08 09:53:11 +0100710static char *
711auth_password_get_pwd_hash(const char *username)
712{
713 struct passwd *pwd, pwd_buf;
714 struct spwd *spwd, spwd_buf;
715 char *pass_hash = NULL, buf[256];
716
717 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
718 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100719 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100720 return NULL;
721 }
722
723 if (!strcmp(pwd->pw_passwd, "x")) {
724 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
725 if (!spwd) {
726 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
727 return NULL;
728 }
729
730 pass_hash = spwd->sp_pwdp;
731 } else {
732 pass_hash = pwd->pw_passwd;
733 }
734
735 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100736 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100737 return NULL;
738 }
739
740 /* check the hash structure for special meaning */
741 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
742 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
743 return NULL;
744 }
745 if (!strcmp(pass_hash, "*NP*")) {
746 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
747 return NULL;
748 }
749
750 return strdup(pass_hash);
751}
752
753static int
754auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
755{
756 char *new_pass_hash;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200757#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100758 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200759#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100760
761 if (!pass_hash[0]) {
762 if (!pass_clear[0]) {
763 WRN("User authentication successful with an empty password!");
764 return 0;
765 } else {
766 /* the user did now know he does not need any password,
767 * (which should not be used) so deny authentication */
768 return 1;
769 }
770 }
771
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200772#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100773 cdata.initialized = 0;
774 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200775#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200776 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200777 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200778 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200779#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100780 return strcmp(new_pass_hash, pass_hash);
781}
782
783static void
784nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
785{
786 char *pass_hash;
787
788 pass_hash = auth_password_get_pwd_hash(session->username);
789 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100790 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100791 ssh_message_auth_reply_success(msg, 0);
792 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
793 free(pass_hash);
794 return;
795 }
796
797 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200798 ++session->opts.server.ssh_auth_attempts;
799 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100800 ssh_message_reply_default(msg);
801}
802
803static void
804nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
805{
806 char *pass_hash;
807
808 if (!ssh_message_auth_kbdint_is_response(msg)) {
809 const char *prompts[] = {"Password: "};
810 char echo[] = {0};
811
812 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
813 } else {
814 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
815 ssh_message_reply_default(msg);
816 return;
817 }
818 pass_hash = auth_password_get_pwd_hash(session->username);
819 if (!pass_hash) {
820 ssh_message_reply_default(msg);
821 return;
822 }
823 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
824 VRB("User \"%s\" authenticated.", session->username);
825 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
826 ssh_message_auth_reply_success(msg, 0);
827 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200828 ++session->opts.server.ssh_auth_attempts;
829 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100830 ssh_message_reply_default(msg);
831 }
Radek Krejcifb533742016-03-04 15:12:54 +0100832 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100833 }
834}
835
836static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100837auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100838{
839 uint32_t i;
840 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100841 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100842 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100843
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100844 /* LOCK */
845 pthread_mutex_lock(&server_opts.authkey_lock);
846
Michal Vasko17dfda92016-12-01 14:06:16 +0100847 for (i = 0; i < server_opts.authkey_count; ++i) {
848 switch (server_opts.authkeys[i].type) {
849 case NC_SSH_KEY_UNKNOWN:
850 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
851 break;
852 case NC_SSH_KEY_DSA:
853 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
854 break;
855 case NC_SSH_KEY_RSA:
856 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
857 break;
858 case NC_SSH_KEY_ECDSA:
859 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
860 break;
861 }
862
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200863 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100864 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200865 continue;
866 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100867 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100868 continue;
869 }
870
871 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
872 ssh_key_free(pub_key);
873 break;
874 }
875
876 ssh_key_free(pub_key);
877 }
878
Michal Vasko17dfda92016-12-01 14:06:16 +0100879 if (i < server_opts.authkey_count) {
880 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100881 }
882
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100883 /* UNLOCK */
884 pthread_mutex_unlock(&server_opts.authkey_lock);
885
Michal Vasko086311b2016-01-08 09:53:11 +0100886 return username;
887}
888
889static void
890nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
891{
892 const char *username;
893 int signature_state;
894
Michal Vasko17dfda92016-12-01 14:06:16 +0100895 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200896 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
897 goto fail;
898 } else if (strcmp(session->username, username)) {
899 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200900 goto fail;
901 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200902
Michal Vasko086311b2016-01-08 09:53:11 +0100903 signature_state = ssh_message_auth_publickey_state(msg);
904 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
905 VRB("User \"%s\" authenticated.", session->username);
906 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
907 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100908 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200909 /* accepting only the use of a public key */
910 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100911 }
912
Michal Vaskobd13a932016-09-14 09:00:35 +0200913 return;
914
915fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200916 ++session->opts.server.ssh_auth_attempts;
917 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100918 ssh_message_reply_default(msg);
919}
920
921static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100922nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100923{
Michal Vasko96164bf2016-01-21 15:41:58 +0100924 ssh_channel chan;
925
926 /* first channel request */
927 if (!session->ti.libssh.channel) {
928 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100929 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100930 return -1;
931 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100932 chan = ssh_message_channel_request_open_reply_accept(msg);
933 if (!chan) {
934 ERR("Failed to create a new SSH channel.");
935 return -1;
936 }
937 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100938
Michal Vasko96164bf2016-01-21 15:41:58 +0100939 /* additional channel request */
940 } else {
941 chan = ssh_message_channel_request_open_reply_accept(msg);
942 if (!chan) {
943 ERR("Session %u: failed to create a new SSH channel.", session->id);
944 return -1;
945 }
946 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100947 }
948
Michal Vasko086311b2016-01-08 09:53:11 +0100949 return 0;
950}
951
952static int
953nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
954{
Michal Vasko96164bf2016-01-21 15:41:58 +0100955 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100956
Michal Vasko96164bf2016-01-21 15:41:58 +0100957 if (strcmp(subsystem, "netconf")) {
958 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100959 return -1;
960 }
961
Michal Vasko96164bf2016-01-21 15:41:58 +0100962 if (session->ti.libssh.channel == channel) {
963 /* first channel requested */
964 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
965 ERRINT;
966 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100967 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100968 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
969 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
970 return -1;
971 }
972
973 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100974 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100975 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vaskoade892d2017-02-22 13:40:35 +0100976 new_session = nc_new_session(1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100977 if (!new_session) {
978 ERRMEM;
979 return -1;
980 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100981
982 /* insert the new session */
983 if (!session->ti.libssh.next) {
984 new_session->ti.libssh.next = session;
985 } else {
986 new_session->ti.libssh.next = session->ti.libssh.next;
987 }
988 session->ti.libssh.next = new_session;
989
990 new_session->status = NC_STATUS_STARTING;
991 new_session->side = NC_SERVER;
992 new_session->ti_type = NC_TI_LIBSSH;
993 new_session->ti_lock = session->ti_lock;
Michal Vaskoade892d2017-02-22 13:40:35 +0100994 new_session->ti_cond = session->ti_cond;
995 new_session->ti_inuse = session->ti_inuse;
Michal Vasko96164bf2016-01-21 15:41:58 +0100996 new_session->ti.libssh.channel = channel;
997 new_session->ti.libssh.session = session->ti.libssh.session;
998 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
999 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
1000 new_session->port = session->port;
1001 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001002 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
1003 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001004 }
1005
1006 return 0;
1007}
1008
Michal Vasko96164bf2016-01-21 15:41:58 +01001009int
Michal Vaskob48aa812016-01-18 14:13:09 +01001010nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001011{
1012 const char *str_type, *str_subtype = NULL, *username;
1013 int subtype, type;
1014 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001015
1016 type = ssh_message_type(msg);
1017 subtype = ssh_message_subtype(msg);
1018
1019 switch (type) {
1020 case SSH_REQUEST_AUTH:
1021 str_type = "request-auth";
1022 switch (subtype) {
1023 case SSH_AUTH_METHOD_NONE:
1024 str_subtype = "none";
1025 break;
1026 case SSH_AUTH_METHOD_PASSWORD:
1027 str_subtype = "password";
1028 break;
1029 case SSH_AUTH_METHOD_PUBLICKEY:
1030 str_subtype = "publickey";
1031 break;
1032 case SSH_AUTH_METHOD_HOSTBASED:
1033 str_subtype = "hostbased";
1034 break;
1035 case SSH_AUTH_METHOD_INTERACTIVE:
1036 str_subtype = "interactive";
1037 break;
1038 case SSH_AUTH_METHOD_GSSAPI_MIC:
1039 str_subtype = "gssapi-mic";
1040 break;
1041 }
1042 break;
1043
1044 case SSH_REQUEST_CHANNEL_OPEN:
1045 str_type = "request-channel-open";
1046 switch (subtype) {
1047 case SSH_CHANNEL_SESSION:
1048 str_subtype = "session";
1049 break;
1050 case SSH_CHANNEL_DIRECT_TCPIP:
1051 str_subtype = "direct-tcpip";
1052 break;
1053 case SSH_CHANNEL_FORWARDED_TCPIP:
1054 str_subtype = "forwarded-tcpip";
1055 break;
1056 case (int)SSH_CHANNEL_X11:
1057 str_subtype = "channel-x11";
1058 break;
1059 case SSH_CHANNEL_UNKNOWN:
1060 /* fallthrough */
1061 default:
1062 str_subtype = "unknown";
1063 break;
1064 }
1065 break;
1066
1067 case SSH_REQUEST_CHANNEL:
1068 str_type = "request-channel";
1069 switch (subtype) {
1070 case SSH_CHANNEL_REQUEST_PTY:
1071 str_subtype = "pty";
1072 break;
1073 case SSH_CHANNEL_REQUEST_EXEC:
1074 str_subtype = "exec";
1075 break;
1076 case SSH_CHANNEL_REQUEST_SHELL:
1077 str_subtype = "shell";
1078 break;
1079 case SSH_CHANNEL_REQUEST_ENV:
1080 str_subtype = "env";
1081 break;
1082 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1083 str_subtype = "subsystem";
1084 break;
1085 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1086 str_subtype = "window-change";
1087 break;
1088 case SSH_CHANNEL_REQUEST_X11:
1089 str_subtype = "x11";
1090 break;
1091 case SSH_CHANNEL_REQUEST_UNKNOWN:
1092 /* fallthrough */
1093 default:
1094 str_subtype = "unknown";
1095 break;
1096 }
1097 break;
1098
1099 case SSH_REQUEST_SERVICE:
1100 str_type = "request-service";
1101 str_subtype = ssh_message_service_service(msg);
1102 break;
1103
1104 case SSH_REQUEST_GLOBAL:
1105 str_type = "request-global";
1106 switch (subtype) {
1107 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1108 str_subtype = "tcpip-forward";
1109 break;
1110 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1111 str_subtype = "cancel-tcpip-forward";
1112 break;
1113 case SSH_GLOBAL_REQUEST_UNKNOWN:
1114 /* fallthrough */
1115 default:
1116 str_subtype = "unknown";
1117 break;
1118 }
1119 break;
1120
1121 default:
1122 str_type = "unknown";
1123 str_subtype = "unknown";
1124 break;
1125 }
1126
1127 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001128 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1129 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1130 * but we got it now, during session free */
1131 VRB("SSH message arrived on a %s session, the request will be denied.",
1132 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1133 ssh_message_reply_default(msg);
1134 return 0;
1135 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001136 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001137
1138 /*
1139 * process known messages
1140 */
1141 if (type == SSH_REQUEST_AUTH) {
1142 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1143 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1144 ssh_message_reply_default(msg);
1145 return 0;
1146 }
1147
Michal Vasko086311b2016-01-08 09:53:11 +01001148 /* save the username, do not let the client change it */
1149 username = ssh_message_auth_user(msg);
1150 if (!session->username) {
1151 if (!username) {
1152 ERR("Denying an auth request without a username.");
1153 return 1;
1154 }
1155
Michal Vasko05ba9df2016-01-13 14:40:27 +01001156 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001157 } else if (username) {
1158 if (strcmp(username, session->username)) {
1159 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1160 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001161 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001162 return 1;
1163 }
1164 }
1165
1166 if (subtype == SSH_AUTH_METHOD_NONE) {
1167 /* libssh will return the supported auth methods */
1168 return 1;
1169 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1170 nc_sshcb_auth_password(session, msg);
1171 return 0;
1172 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1173 nc_sshcb_auth_pubkey(session, msg);
1174 return 0;
1175 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1176 nc_sshcb_auth_kbdint(session, msg);
1177 return 0;
1178 }
1179 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001180 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001181 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001182 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001183 }
Michal Vasko086311b2016-01-08 09:53:11 +01001184 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001185
Michal Vasko0df67562016-01-21 15:50:11 +01001186 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001187 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1188 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001189 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001190 } else {
1191 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001192 }
1193 return 0;
1194 }
1195 }
1196
1197 /* we did not process it */
1198 return 1;
1199}
1200
Michal Vasko1a38c862016-01-15 15:50:07 +01001201/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001202static int
1203nc_open_netconf_channel(struct nc_session *session, int timeout)
1204{
Michal Vasko36c7be82017-02-22 13:37:59 +01001205 int ret;
1206 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001207
1208 /* message callback is executed twice to give chance for the channel to be
1209 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001210 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001211 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001212 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001213 return -1;
1214 }
1215
Michal Vaskoade892d2017-02-22 13:40:35 +01001216 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001217 if (ret != 1) {
1218 return ret;
1219 }
1220
1221 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1222 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 Vaskoade892d2017-02-22 13:40:35 +01001225 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +01001226 return -1;
1227 }
1228
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001229 if (!session->ti.libssh.channel) {
1230 /* we did not receive channel-open, timeout */
Michal Vaskoade892d2017-02-22 13:40:35 +01001231 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001232 return 0;
1233 }
1234
1235 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001236 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001237 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001238 ERR("Failed to receive SSH messages on a session (%s).",
1239 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001240 return -1;
1241 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001242
1243 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1244 /* we did not receive subsystem-request, timeout */
1245 return 0;
1246 }
1247
1248 return 1;
1249 }
1250
Michal Vasko36c7be82017-02-22 13:37:59 +01001251 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001252 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001253 nc_addtimespec(&ts_timeout, timeout);
1254 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001255 while (1) {
1256 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001257 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001258 return -1;
1259 }
1260
Michal Vaskoade892d2017-02-22 13:40:35 +01001261 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001262 if (ret != 1) {
1263 return ret;
1264 }
1265
1266 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001267 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001268 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001269 ERR("Failed to receive SSH messages on a session (%s).",
1270 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001271 return -1;
1272 }
1273
Michal Vasko086311b2016-01-08 09:53:11 +01001274 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001275 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001276 }
1277
Michal Vasko086311b2016-01-08 09:53:11 +01001278 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001279 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001280 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001281 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1282 /* timeout */
1283 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1284 break;
1285 }
1286 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001287 }
Michal Vasko086311b2016-01-08 09:53:11 +01001288
Michal Vasko1a38c862016-01-15 15:50:07 +01001289 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001290}
1291
Michal Vasko4c1fb492017-01-30 14:31:07 +01001292static int
1293nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1294{
1295 uint8_t i;
1296 char *privkey_path, *privkey_data;
1297 int privkey_data_rsa, ret;
1298
1299 if (!server_opts.hostkey_clb) {
1300 ERR("Callback for retrieving SSH host keys not set.");
1301 return -1;
1302 }
1303
1304 for (i = 0; i < hostkey_count; ++i) {
1305 privkey_path = privkey_data = NULL;
1306 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1307 ERR("Host key callback failed.");
1308 return -1;
1309 }
1310
1311 if (privkey_data) {
1312 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1313 if (!privkey_path) {
1314 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1315 free(privkey_data);
1316 return -1;
1317 }
1318 }
1319
1320 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1321
1322 /* cleanup */
1323 if (privkey_data && unlink(privkey_path)) {
1324 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1325 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001326 free(privkey_data);
1327
1328 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001329 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1330 }
1331 free(privkey_path);
1332
1333 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001334 return -1;
1335 }
1336 }
1337
1338 return 0;
1339}
1340
Michal Vasko96164bf2016-01-21 15:41:58 +01001341int
Michal Vasko0190bc32016-03-02 15:47:49 +01001342nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001343{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001344 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001345 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001346 int libssh_auth_methods = 0, ret;
1347 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001348
Michal Vasko2cc4c682016-03-01 09:16:48 +01001349 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001350
Michal Vasko086311b2016-01-08 09:53:11 +01001351 /* other transport-specific data */
1352 session->ti_type = NC_TI_LIBSSH;
1353 session->ti.libssh.session = ssh_new();
1354 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001355 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001356 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001357 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001358 }
1359
Michal Vaskoc61c4492016-01-25 11:13:34 +01001360 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001361 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1362 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001363 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001364 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1365 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001366 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001367 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1368 }
1369 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1370
Michal Vaskoe2713da2016-08-22 16:06:40 +02001371 sbind = ssh_bind_new();
1372 if (!sbind) {
1373 ERR("Failed to create an SSH bind.");
1374 close(sock);
1375 return -1;
1376 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001377
1378 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1379 close(sock);
1380 ssh_bind_free(sbind);
1381 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001382 }
1383 if (opts->banner) {
1384 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1385 }
1386
Michal Vasko086311b2016-01-08 09:53:11 +01001387 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001388 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001389 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001390
Michal Vaskoe2713da2016-08-22 16:06:40 +02001391 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1392 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001393 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001394 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001395 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001396 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001397 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001398
Michal Vasko0190bc32016-03-02 15:47:49 +01001399 ssh_set_blocking(session->ti.libssh.session, 0);
1400
Michal Vasko36c7be82017-02-22 13:37:59 +01001401 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001402 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001403 nc_addtimespec(&ts_timeout, timeout);
1404 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001405 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001406 /* this tends to take longer */
1407 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001408 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001409 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001410 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1411 break;
1412 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001413 }
1414 }
1415 if (ret == SSH_AGAIN) {
1416 ERR("SSH key exchange timeout.");
1417 return 0;
1418 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001419 ERR("SSH key exchange error (%s).", 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
1423 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001424 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001425 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001426 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1427 }
1428 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001429 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001430 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001431 return -1;
1432 }
1433
Michal Vasko086311b2016-01-08 09:53:11 +01001434 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001435 ERR("Failed to receive SSH messages on a session (%s).",
1436 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001437 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001438 }
1439
Michal Vasko36c7be82017-02-22 13:37:59 +01001440 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1441 break;
1442 }
1443
Michal Vasko145ae672017-02-07 10:57:27 +01001444 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1445 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1446 return -1;
1447 }
1448
Michal Vasko086311b2016-01-08 09:53:11 +01001449 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001450 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001451 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001452 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1453 /* timeout */
1454 break;
1455 }
1456 }
1457 }
Michal Vasko086311b2016-01-08 09:53:11 +01001458
1459 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1460 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001461 if (session->username) {
1462 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1463 } else {
1464 ERR("User failed to authenticate for too long, disconnecting.");
1465 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001466 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001467 }
1468
Michal Vasko086311b2016-01-08 09:53:11 +01001469 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001470 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001471 if (ret < 1) {
1472 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001473 }
1474
Michal Vasko96164bf2016-01-21 15:41:58 +01001475 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001476 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001477}
1478
Michal Vasko71090fc2016-05-24 16:37:28 +02001479API NC_MSG_TYPE
1480nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1481{
1482 NC_MSG_TYPE msgtype;
1483 struct nc_session *new_session = NULL;
1484
1485 if (!orig_session) {
1486 ERRARG("orig_session");
1487 return NC_MSG_ERROR;
1488 } else if (!session) {
1489 ERRARG("session");
1490 return NC_MSG_ERROR;
1491 }
1492
1493 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1494 && orig_session->ti.libssh.next) {
1495 for (new_session = orig_session->ti.libssh.next;
1496 new_session != orig_session;
1497 new_session = new_session->ti.libssh.next) {
1498 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1499 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1500 /* we found our session */
1501 break;
1502 }
1503 }
1504 if (new_session == orig_session) {
1505 new_session = NULL;
1506 }
1507 }
1508
1509 if (!new_session) {
1510 ERR("Session does not have a NETCONF SSH channel ready.");
1511 return NC_MSG_ERROR;
1512 }
1513
1514 /* assign new SID atomically */
1515 pthread_spin_lock(&server_opts.sid_lock);
1516 new_session->id = server_opts.new_session_id++;
1517 pthread_spin_unlock(&server_opts.sid_lock);
1518
1519 /* NETCONF handshake */
1520 msgtype = nc_handshake(new_session);
1521 if (msgtype != NC_MSG_HELLO) {
1522 return msgtype;
1523 }
1524
Michal Vasko2e6defd2016-10-07 15:48:15 +02001525 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001526 new_session->status = NC_STATUS_RUNNING;
1527 *session = new_session;
1528
1529 return msgtype;
1530}
1531
1532API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001533nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001534{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001535 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001536 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001537 struct nc_session *new_session = NULL, *cur_session;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001538 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001539
Michal Vasko45e53ae2016-04-07 11:46:03 +02001540 if (!ps) {
1541 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001542 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001543 } else if (!session) {
1544 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001545 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001546 }
1547
Michal Vasko48a63ed2016-03-01 09:48:21 +01001548 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001549 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001550 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001551 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001552
Michal Vasko96164bf2016-01-21 15:41:58 +01001553 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001554 cur_session = ps->sessions[i]->session;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001555 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1556 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001557 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001558 for (new_session = cur_session->ti.libssh.next;
1559 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001560 new_session = new_session->ti.libssh.next) {
1561 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1562 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1563 /* we found our session */
1564 break;
1565 }
1566 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001567 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001568 break;
1569 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001570
Michal Vasko96164bf2016-01-21 15:41:58 +01001571 new_session = NULL;
1572 }
1573 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001574
Michal Vasko48a63ed2016-03-01 09:48:21 +01001575 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001576 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001577
Michal Vasko96164bf2016-01-21 15:41:58 +01001578 if (!new_session) {
1579 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001580 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001581 }
1582
1583 /* assign new SID atomically */
1584 pthread_spin_lock(&server_opts.sid_lock);
1585 new_session->id = server_opts.new_session_id++;
1586 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001587
Michal Vasko086311b2016-01-08 09:53:11 +01001588 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001589 msgtype = nc_handshake(new_session);
1590 if (msgtype != NC_MSG_HELLO) {
1591 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001592 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001593
Michal Vasko2e6defd2016-10-07 15:48:15 +02001594 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001595 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001596 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001597
Michal Vasko71090fc2016-05-24 16:37:28 +02001598 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001599}