blob: b46bf567e42b3765576eb33d33c0eff945942ca9 [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>
Michal Vasko9f6275e2017-10-05 13:50:05 +020025#include <time.h>
Michal Vasko086311b2016-01-08 09:53:11 +010026
Michal Vasko11d142a2016-01-19 15:58:24 +010027#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010028#include "session_server_ch.h"
29#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010030
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +020031#if !defined(HAVE_CRYPT_R)
32pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER;
33#endif
34
Michal Vasko086311b2016-01-08 09:53:11 +010035extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010036
Michal Vasko4c1fb492017-01-30 14:31:07 +010037static char *
38base64der_key_to_tmp_file(const char *in, int rsa)
Michal Vasko086311b2016-01-08 09:53:11 +010039{
Michal Vasko4c1fb492017-01-30 14:31:07 +010040 char path[12] = "/tmp/XXXXXX";
41 int fd, written;
Michal Vasko27252692017-03-21 15:34:13 +010042 mode_t umode;
Michal Vasko4c1fb492017-01-30 14:31:07 +010043 FILE *file;
44
45 if (in == NULL) {
46 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010047 }
48
Michal Vasko27252692017-03-21 15:34:13 +010049 umode = umask(0600);
Michal Vasko4c1fb492017-01-30 14:31:07 +010050 fd = mkstemp(path);
Michal Vasko27252692017-03-21 15:34:13 +010051 umask(umode);
Michal Vasko4c1fb492017-01-30 14:31:07 +010052 if (fd == -1) {
53 return NULL;
54 }
55
56 file = fdopen(fd, "r");
57 if (!file) {
58 close(fd);
59 return NULL;
60 }
61
62 /* write the key into the file */
63 written = fwrite("-----BEGIN ", 1, 11, file);
64 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
65 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
66 written += fwrite(in, 1, strlen(in), file);
67 written += fwrite("\n-----END ", 1, 10, file);
68 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
69 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
70
71 fclose(file);
72 if ((unsigned)written != 62 + strlen(in)) {
73 unlink(path);
74 return NULL;
75 }
76
77 return strdup(path);
78}
79
80static int
Michal Vasko7d255882017-02-09 13:35:08 +010081nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vasko4c1fb492017-01-30 14:31:07 +010082{
Michal Vaskofbfe8b62017-02-14 10:22:30 +010083 uint8_t i;
84
Michal Vasko4c1fb492017-01-30 14:31:07 +010085 if (!name) {
86 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +010087 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +010088 } else if (idx > opts->hostkey_count) {
89 ERRARG("idx");
90 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010091 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010092
Michal Vaskofbfe8b62017-02-14 10:22:30 +010093 for (i = 0; i < opts->hostkey_count; ++i) {
94 if (!strcmp(opts->hostkeys[i], name)) {
95 ERRARG("name");
96 return -1;
97 }
98 }
99
Michal Vaskoe2713da2016-08-22 16:06:40 +0200100 ++opts->hostkey_count;
101 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
102 if (!opts->hostkeys) {
103 ERRMEM;
104 return -1;
105 }
Michal Vasko7d255882017-02-09 13:35:08 +0100106
107 if (idx < 0) {
108 idx = opts->hostkey_count - 1;
109 }
110 if (idx != opts->hostkey_count - 1) {
111 memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx);
112 }
113 opts->hostkeys[idx] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200114
Michal Vasko5fcc7142016-02-02 12:21:10 +0100115 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +0100116}
117
118API int
Michal Vasko7d255882017-02-09 13:35:08 +0100119nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100120{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100121 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100122 struct nc_endpt *endpt;
123
Michal Vasko51e514d2016-02-02 15:51:52 +0100124 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100125 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100126 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100127 return -1;
128 }
Michal Vasko7d255882017-02-09 13:35:08 +0100129 ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100130 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100131 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100132
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100133 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100134}
135
136API int
Michal Vasko7d255882017-02-09 13:35:08 +0100137nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100138{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100139 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200140 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100141
Michal Vasko2e6defd2016-10-07 15:48:15 +0200142 /* LOCK */
143 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
144 if (!client) {
145 return -1;
146 }
Michal Vasko7d255882017-02-09 13:35:08 +0100147 ret = nc_server_ssh_add_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200148 /* UNLOCK */
149 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200150
151 return ret;
152}
153
Michal Vasko4c1fb492017-01-30 14:31:07 +0100154API void
155nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
156 char **privkey_data, int *privkey_data_rsa),
157 void *user_data, void (*free_user_data)(void *user_data))
158{
159 if (!hostkey_clb) {
160 ERRARG("hostkey_clb");
161 return;
162 }
163
164 server_opts.hostkey_clb = hostkey_clb;
165 server_opts.hostkey_data = user_data;
166 server_opts.hostkey_data_free = free_user_data;
167}
168
Michal Vaskoe2713da2016-08-22 16:06:40 +0200169static int
Michal Vasko7d255882017-02-09 13:35:08 +0100170nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200171{
172 uint8_t i;
173
Michal Vasko7d255882017-02-09 13:35:08 +0100174 if (name && (idx > -1)) {
175 ERRARG("name and idx");
176 return -1;
177 } else if (idx >= opts->hostkey_count) {
178 ERRARG("idx");
179 }
180
181 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200182 for (i = 0; i < opts->hostkey_count; ++i) {
183 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
184 }
185 free(opts->hostkeys);
186 opts->hostkeys = NULL;
187 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100188 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200189 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100190 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100191 idx = i;
192 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200193 }
194 }
195
Michal Vasko7d255882017-02-09 13:35:08 +0100196 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200197 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100198 } else {
199remove_idx:
200 --opts->hostkey_count;
201 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
202 if (idx < opts->hostkey_count - 1) {
203 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
204 }
205 if (!opts->hostkey_count) {
206 free(opts->hostkeys);
207 opts->hostkeys = NULL;
208 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200209 }
210
211 return 0;
212}
213
214API int
Michal Vasko7d255882017-02-09 13:35:08 +0100215nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200216{
217 int ret;
218 struct nc_endpt *endpt;
219
220 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100221 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200222 if (!endpt) {
223 return -1;
224 }
Michal Vasko7d255882017-02-09 13:35:08 +0100225 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200226 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100227 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200228
229 return ret;
230}
231
232API int
Michal Vasko7d255882017-02-09 13:35:08 +0100233nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200234{
235 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200236 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200237
Michal Vasko2e6defd2016-10-07 15:48:15 +0200238 /* LOCK */
239 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
240 if (!client) {
241 return -1;
242 }
Michal Vasko7d255882017-02-09 13:35:08 +0100243 ret = nc_server_ssh_del_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200244 /* UNLOCK */
245 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100246
247 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100248}
249
250static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100251nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
252{
253 uint8_t i;
254 int16_t mov_idx = -1, after_idx = -1;
255 const char *bckup;
256
257 if (!key_mov) {
258 ERRARG("key_mov");
259 return -1;
260 }
261
262 for (i = 0; i < opts->hostkey_count; ++i) {
263 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
264 after_idx = i;
265 }
266 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
267 mov_idx = i;
268 }
269
270 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
271 break;
272 }
273 }
274
275 if (key_after && (after_idx == -1)) {
276 ERRARG("key_after");
277 return -1;
278 }
279 if (mov_idx == -1) {
280 ERRARG("key_mov");
281 return -1;
282 }
283 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
284 /* nothing to do */
285 return 0;
286 }
287
288 /* finally move the key */
289 bckup = opts->hostkeys[mov_idx];
290 if (mov_idx > after_idx) {
291 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
292 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
293 opts->hostkeys[after_idx + 1] = bckup;
294 } else {
295 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
296 opts->hostkeys[after_idx] = bckup;
297 }
298
299 return 0;
300}
301
302API int
303nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
304{
305 int ret;
306 struct nc_endpt *endpt;
307
308 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100309 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100310 if (!endpt) {
311 return -1;
312 }
313 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
314 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100315 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100316
317 return ret;
318}
319
320API int
321nc_server_ssh_ch_client_mov_hostkey(const char *client_name, const char *key_mov, const char *key_after)
322{
323 int ret;
324 struct nc_ch_client *client;
325
326 /* LOCK */
327 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
328 if (!client) {
329 return -1;
330 }
331 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, client->opts.ssh);
332 /* UNLOCK */
333 nc_server_ch_client_unlock(client);
334
335 return ret;
336}
337
338static int
339nc_server_ssh_mod_hostkey(const char *name, const char *new_name, struct nc_server_ssh_opts *opts)
340{
341 uint8_t i;
342
343 if (!name) {
344 ERRARG("name");
345 return -1;
346 } else if (!new_name) {
347 ERRARG("new_name");
348 return -1;
349 }
350
351 for (i = 0; i < opts->hostkey_count; ++i) {
352 if (!strcmp(opts->hostkeys[i], name)) {
353 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
354 opts->hostkeys[i] = lydict_insert(server_opts.ctx, new_name, 0);
355 return 0;
356 }
357 }
358
359 ERRARG("name");
360 return -1;
361}
362
363API int
364nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name)
365{
366 int ret;
367 struct nc_endpt *endpt;
368
369 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100370 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100371 if (!endpt) {
372 return -1;
373 }
374 ret = nc_server_ssh_mov_hostkey(name, new_name, endpt->opts.ssh);
375 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100376 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100377
378 return ret;
379}
380
381API int
382nc_server_ssh_ch_client_mod_hostkey(const char *client_name, const char *name, const char *new_name)
383{
384 int ret;
385 struct nc_ch_client *client;
386
387 /* LOCK */
388 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
389 if (!client) {
390 return -1;
391 }
392 ret = nc_server_ssh_mod_hostkey(name, new_name, client->opts.ssh);
393 /* UNLOCK */
394 nc_server_ch_client_unlock(client);
395
396 return ret;
397}
398
399static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100400nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100401{
Michal Vaskob05053d2016-01-22 16:12:06 +0100402 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200403 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100404 return -1;
405 }
406
Michal Vaskoe2713da2016-08-22 16:06:40 +0200407 if (opts->banner) {
408 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100409 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200410 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100411 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100412}
413
414API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100415nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100416{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100417 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100418 struct nc_endpt *endpt;
419
Michal Vasko51e514d2016-02-02 15:51:52 +0100420 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100421 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100422 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100423 return -1;
424 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200425 ret = nc_server_ssh_set_banner(banner, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100426 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100427 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100428
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100429 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100430}
431
432API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200433nc_server_ssh_ch_client_set_banner(const char *client_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100434{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100435 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200436 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100437
Michal Vasko2e6defd2016-10-07 15:48:15 +0200438 /* LOCK */
439 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
440 if (!client) {
441 return -1;
442 }
443 ret = nc_server_ssh_set_banner(banner, client->opts.ssh);
444 /* UNLOCK */
445 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100446
447 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100448}
449
450static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100451nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100452{
Michal Vasko086311b2016-01-08 09:53:11 +0100453 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
454 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200455 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100456 return -1;
457 }
458
Michal Vaskob05053d2016-01-22 16:12:06 +0100459 opts->auth_methods = auth_methods;
460 return 0;
461}
462
463API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100464nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100465{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100466 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100467 struct nc_endpt *endpt;
468
Michal Vasko51e514d2016-02-02 15:51:52 +0100469 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100470 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100471 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100472 return -1;
473 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200474 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100475 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100476 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100477
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100478 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100479}
480
481API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200482nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100483{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100484 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200485 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100486
Michal Vasko2e6defd2016-10-07 15:48:15 +0200487 /* LOCK */
488 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
489 if (!client) {
490 return -1;
491 }
492 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
493 /* UNLOCK */
494 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100495
496 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100497}
498
499static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100500nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100501{
Michal Vaskob05053d2016-01-22 16:12:06 +0100502 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200503 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100504 return -1;
505 }
506
Michal Vaskob05053d2016-01-22 16:12:06 +0100507 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100508 return 0;
509}
510
511API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100512nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100513{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100514 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100515 struct nc_endpt *endpt;
516
Michal Vasko51e514d2016-02-02 15:51:52 +0100517 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100518 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100519 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100520 return -1;
521 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200522 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100523 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100524 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100525
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100526 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100527}
528
529API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200530nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100531{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100532 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200533 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100534
Michal Vasko2e6defd2016-10-07 15:48:15 +0200535 /* LOCK */
536 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
537 if (!client) {
538 return -1;
539 }
540 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
541 /* UNLOCK */
542 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100543
544 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100545}
546
547static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100548nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100549{
Michal Vaskob05053d2016-01-22 16:12:06 +0100550 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200551 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100552 return -1;
553 }
554
Michal Vaskob05053d2016-01-22 16:12:06 +0100555 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100556 return 0;
557}
558
559API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100560nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100561{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100562 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100563 struct nc_endpt *endpt;
564
Michal Vasko51e514d2016-02-02 15:51:52 +0100565 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100566 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100567 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100568 return -1;
569 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200570 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100571 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100572 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100573
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100574 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100575}
576
577API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200578nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100579{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100580 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200581 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100582
Michal Vasko2e6defd2016-10-07 15:48:15 +0200583 /* LOCK */
584 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
585 if (!client) {
586 return -1;
587 }
588 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
589 /* UNLOCK */
590 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100591
592 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100593}
594
595static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100596_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
597 const char *username)
598{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100599 /* LOCK */
600 pthread_mutex_lock(&server_opts.authkey_lock);
601
Michal Vasko17dfda92016-12-01 14:06:16 +0100602 ++server_opts.authkey_count;
603 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
604 if (!server_opts.authkeys) {
605 ERRMEM;
606 return -1;
607 }
608 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
609 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
610 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
611 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
612
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100613 /* UNLOCK */
614 pthread_mutex_unlock(&server_opts.authkey_lock);
615
Michal Vasko17dfda92016-12-01 14:06:16 +0100616 return 0;
617}
618
619API int
620nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100621{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200622 if (!pubkey_path) {
623 ERRARG("pubkey_path");
624 return -1;
625 } else if (!username) {
626 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100627 return -1;
628 }
629
Michal Vasko17dfda92016-12-01 14:06:16 +0100630 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100631}
632
633API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100634nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100635{
Michal Vasko17dfda92016-12-01 14:06:16 +0100636 if (!pubkey_base64) {
637 ERRARG("pubkey_base64");
638 return -1;
639 } else if (!type) {
640 ERRARG("type");
641 return -1;
642 } else if (!username) {
643 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100644 return -1;
645 }
646
Michal Vasko17dfda92016-12-01 14:06:16 +0100647 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100648}
649
650API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100651nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
652 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100653{
Michal Vasko086311b2016-01-08 09:53:11 +0100654 uint32_t i;
655 int ret = -1;
656
Michal Vasko17dfda92016-12-01 14:06:16 +0100657 /* LOCK */
658 pthread_mutex_lock(&server_opts.authkey_lock);
659
660 if (!pubkey_path && !pubkey_base64 && !type && !username) {
661 for (i = 0; i < server_opts.authkey_count; ++i) {
662 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
663 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
664 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100665
Michal Vasko086311b2016-01-08 09:53:11 +0100666 ret = 0;
667 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100668 free(server_opts.authkeys);
669 server_opts.authkeys = NULL;
670 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100671 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100672 for (i = 0; i < server_opts.authkey_count; ++i) {
673 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
674 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
675 && (!type || (server_opts.authkeys[i].type == type))
676 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
677 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
678 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
679 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100680
Michal Vasko17dfda92016-12-01 14:06:16 +0100681 --server_opts.authkey_count;
682 if (i < server_opts.authkey_count) {
683 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
684 sizeof *server_opts.authkeys);
685 } else if (!server_opts.authkey_count) {
686 free(server_opts.authkeys);
687 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100688 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100689
690 ret = 0;
691 }
692 }
Michal Vasko086311b2016-01-08 09:53:11 +0100693 }
694
Michal Vasko51e514d2016-02-02 15:51:52 +0100695 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100696 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100697
698 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100699}
700
701void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100702nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100703{
Michal Vasko7d255882017-02-09 13:35:08 +0100704 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200705 if (opts->banner) {
706 lydict_remove(server_opts.ctx, opts->banner);
707 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100708 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100709}
710
Michal Vasko086311b2016-01-08 09:53:11 +0100711static char *
712auth_password_get_pwd_hash(const char *username)
713{
714 struct passwd *pwd, pwd_buf;
715 struct spwd *spwd, spwd_buf;
716 char *pass_hash = NULL, buf[256];
717
718 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
719 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100720 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100721 return NULL;
722 }
723
724 if (!strcmp(pwd->pw_passwd, "x")) {
725 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
726 if (!spwd) {
727 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
728 return NULL;
729 }
730
731 pass_hash = spwd->sp_pwdp;
732 } else {
733 pass_hash = pwd->pw_passwd;
734 }
735
736 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100737 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100738 return NULL;
739 }
740
741 /* check the hash structure for special meaning */
742 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
743 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
744 return NULL;
745 }
746 if (!strcmp(pass_hash, "*NP*")) {
747 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
748 return NULL;
749 }
750
751 return strdup(pass_hash);
752}
753
754static int
755auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
756{
757 char *new_pass_hash;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200758#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100759 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200760#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100761
762 if (!pass_hash[0]) {
763 if (!pass_clear[0]) {
764 WRN("User authentication successful with an empty password!");
765 return 0;
766 } else {
767 /* the user did now know he does not need any password,
768 * (which should not be used) so deny authentication */
769 return 1;
770 }
771 }
772
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200773#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100774 cdata.initialized = 0;
775 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200776#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200777 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200778 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200779 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200780#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100781 return strcmp(new_pass_hash, pass_hash);
782}
783
784static void
785nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
786{
787 char *pass_hash;
788
789 pass_hash = auth_password_get_pwd_hash(session->username);
790 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100791 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100792 ssh_message_auth_reply_success(msg, 0);
793 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
794 free(pass_hash);
795 return;
796 }
797
798 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200799 ++session->opts.server.ssh_auth_attempts;
800 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100801 ssh_message_reply_default(msg);
802}
803
804static void
805nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
806{
807 char *pass_hash;
808
809 if (!ssh_message_auth_kbdint_is_response(msg)) {
810 const char *prompts[] = {"Password: "};
811 char echo[] = {0};
812
813 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
814 } else {
815 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
816 ssh_message_reply_default(msg);
817 return;
818 }
819 pass_hash = auth_password_get_pwd_hash(session->username);
820 if (!pass_hash) {
821 ssh_message_reply_default(msg);
822 return;
823 }
824 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
825 VRB("User \"%s\" authenticated.", session->username);
826 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
827 ssh_message_auth_reply_success(msg, 0);
828 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200829 ++session->opts.server.ssh_auth_attempts;
830 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100831 ssh_message_reply_default(msg);
832 }
Radek Krejcifb533742016-03-04 15:12:54 +0100833 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100834 }
835}
836
837static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100838auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100839{
840 uint32_t i;
841 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100842 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100843 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100844
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100845 /* LOCK */
846 pthread_mutex_lock(&server_opts.authkey_lock);
847
Michal Vasko17dfda92016-12-01 14:06:16 +0100848 for (i = 0; i < server_opts.authkey_count; ++i) {
849 switch (server_opts.authkeys[i].type) {
850 case NC_SSH_KEY_UNKNOWN:
851 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
852 break;
853 case NC_SSH_KEY_DSA:
854 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
855 break;
856 case NC_SSH_KEY_RSA:
857 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
858 break;
859 case NC_SSH_KEY_ECDSA:
860 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
861 break;
862 }
863
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200864 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100865 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200866 continue;
867 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100868 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100869 continue;
870 }
871
872 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
873 ssh_key_free(pub_key);
874 break;
875 }
876
877 ssh_key_free(pub_key);
878 }
879
Michal Vasko17dfda92016-12-01 14:06:16 +0100880 if (i < server_opts.authkey_count) {
881 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100882 }
883
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100884 /* UNLOCK */
885 pthread_mutex_unlock(&server_opts.authkey_lock);
886
Michal Vasko086311b2016-01-08 09:53:11 +0100887 return username;
888}
889
890static void
891nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
892{
893 const char *username;
894 int signature_state;
895
Michal Vasko17dfda92016-12-01 14:06:16 +0100896 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200897 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
898 goto fail;
899 } else if (strcmp(session->username, username)) {
900 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200901 goto fail;
902 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200903
Michal Vasko086311b2016-01-08 09:53:11 +0100904 signature_state = ssh_message_auth_publickey_state(msg);
905 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
906 VRB("User \"%s\" authenticated.", session->username);
907 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
908 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100909 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200910 /* accepting only the use of a public key */
911 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100912 }
913
Michal Vaskobd13a932016-09-14 09:00:35 +0200914 return;
915
916fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200917 ++session->opts.server.ssh_auth_attempts;
918 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100919 ssh_message_reply_default(msg);
920}
921
922static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100923nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100924{
Michal Vasko96164bf2016-01-21 15:41:58 +0100925 ssh_channel chan;
926
927 /* first channel request */
928 if (!session->ti.libssh.channel) {
929 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100930 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100931 return -1;
932 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100933 chan = ssh_message_channel_request_open_reply_accept(msg);
934 if (!chan) {
935 ERR("Failed to create a new SSH channel.");
936 return -1;
937 }
938 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100939
Michal Vasko96164bf2016-01-21 15:41:58 +0100940 /* additional channel request */
941 } else {
942 chan = ssh_message_channel_request_open_reply_accept(msg);
943 if (!chan) {
944 ERR("Session %u: failed to create a new SSH channel.", session->id);
945 return -1;
946 }
947 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100948 }
949
Michal Vasko086311b2016-01-08 09:53:11 +0100950 return 0;
951}
952
953static int
954nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
955{
Michal Vasko96164bf2016-01-21 15:41:58 +0100956 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100957
Michal Vasko96164bf2016-01-21 15:41:58 +0100958 if (strcmp(subsystem, "netconf")) {
959 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100960 return -1;
961 }
962
Michal Vasko96164bf2016-01-21 15:41:58 +0100963 if (session->ti.libssh.channel == channel) {
964 /* first channel requested */
965 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
966 ERRINT;
967 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100968 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100969 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
970 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
971 return -1;
972 }
973
974 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100975 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100976 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vaskoade892d2017-02-22 13:40:35 +0100977 new_session = nc_new_session(1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100978 if (!new_session) {
979 ERRMEM;
980 return -1;
981 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100982
983 /* insert the new session */
984 if (!session->ti.libssh.next) {
985 new_session->ti.libssh.next = session;
986 } else {
987 new_session->ti.libssh.next = session->ti.libssh.next;
988 }
989 session->ti.libssh.next = new_session;
990
991 new_session->status = NC_STATUS_STARTING;
992 new_session->side = NC_SERVER;
993 new_session->ti_type = NC_TI_LIBSSH;
994 new_session->ti_lock = session->ti_lock;
Michal Vaskoade892d2017-02-22 13:40:35 +0100995 new_session->ti_cond = session->ti_cond;
996 new_session->ti_inuse = session->ti_inuse;
Michal Vasko96164bf2016-01-21 15:41:58 +0100997 new_session->ti.libssh.channel = channel;
998 new_session->ti.libssh.session = session->ti.libssh.session;
999 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
1000 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
1001 new_session->port = session->port;
1002 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001003 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
1004 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001005 }
1006
1007 return 0;
1008}
1009
Michal Vasko96164bf2016-01-21 15:41:58 +01001010int
Michal Vaskob48aa812016-01-18 14:13:09 +01001011nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +01001012{
1013 const char *str_type, *str_subtype = NULL, *username;
1014 int subtype, type;
1015 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +01001016
1017 type = ssh_message_type(msg);
1018 subtype = ssh_message_subtype(msg);
1019
1020 switch (type) {
1021 case SSH_REQUEST_AUTH:
1022 str_type = "request-auth";
1023 switch (subtype) {
1024 case SSH_AUTH_METHOD_NONE:
1025 str_subtype = "none";
1026 break;
1027 case SSH_AUTH_METHOD_PASSWORD:
1028 str_subtype = "password";
1029 break;
1030 case SSH_AUTH_METHOD_PUBLICKEY:
1031 str_subtype = "publickey";
1032 break;
1033 case SSH_AUTH_METHOD_HOSTBASED:
1034 str_subtype = "hostbased";
1035 break;
1036 case SSH_AUTH_METHOD_INTERACTIVE:
1037 str_subtype = "interactive";
1038 break;
1039 case SSH_AUTH_METHOD_GSSAPI_MIC:
1040 str_subtype = "gssapi-mic";
1041 break;
1042 }
1043 break;
1044
1045 case SSH_REQUEST_CHANNEL_OPEN:
1046 str_type = "request-channel-open";
1047 switch (subtype) {
1048 case SSH_CHANNEL_SESSION:
1049 str_subtype = "session";
1050 break;
1051 case SSH_CHANNEL_DIRECT_TCPIP:
1052 str_subtype = "direct-tcpip";
1053 break;
1054 case SSH_CHANNEL_FORWARDED_TCPIP:
1055 str_subtype = "forwarded-tcpip";
1056 break;
1057 case (int)SSH_CHANNEL_X11:
1058 str_subtype = "channel-x11";
1059 break;
1060 case SSH_CHANNEL_UNKNOWN:
1061 /* fallthrough */
1062 default:
1063 str_subtype = "unknown";
1064 break;
1065 }
1066 break;
1067
1068 case SSH_REQUEST_CHANNEL:
1069 str_type = "request-channel";
1070 switch (subtype) {
1071 case SSH_CHANNEL_REQUEST_PTY:
1072 str_subtype = "pty";
1073 break;
1074 case SSH_CHANNEL_REQUEST_EXEC:
1075 str_subtype = "exec";
1076 break;
1077 case SSH_CHANNEL_REQUEST_SHELL:
1078 str_subtype = "shell";
1079 break;
1080 case SSH_CHANNEL_REQUEST_ENV:
1081 str_subtype = "env";
1082 break;
1083 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
1084 str_subtype = "subsystem";
1085 break;
1086 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
1087 str_subtype = "window-change";
1088 break;
1089 case SSH_CHANNEL_REQUEST_X11:
1090 str_subtype = "x11";
1091 break;
1092 case SSH_CHANNEL_REQUEST_UNKNOWN:
1093 /* fallthrough */
1094 default:
1095 str_subtype = "unknown";
1096 break;
1097 }
1098 break;
1099
1100 case SSH_REQUEST_SERVICE:
1101 str_type = "request-service";
1102 str_subtype = ssh_message_service_service(msg);
1103 break;
1104
1105 case SSH_REQUEST_GLOBAL:
1106 str_type = "request-global";
1107 switch (subtype) {
1108 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
1109 str_subtype = "tcpip-forward";
1110 break;
1111 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
1112 str_subtype = "cancel-tcpip-forward";
1113 break;
1114 case SSH_GLOBAL_REQUEST_UNKNOWN:
1115 /* fallthrough */
1116 default:
1117 str_subtype = "unknown";
1118 break;
1119 }
1120 break;
1121
1122 default:
1123 str_type = "unknown";
1124 str_subtype = "unknown";
1125 break;
1126 }
1127
1128 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +01001129 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
1130 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
1131 * but we got it now, during session free */
1132 VRB("SSH message arrived on a %s session, the request will be denied.",
1133 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
1134 ssh_message_reply_default(msg);
1135 return 0;
1136 }
Michal Vasko96164bf2016-01-21 15:41:58 +01001137 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +01001138
1139 /*
1140 * process known messages
1141 */
1142 if (type == SSH_REQUEST_AUTH) {
1143 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1144 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
1145 ssh_message_reply_default(msg);
1146 return 0;
1147 }
1148
Michal Vasko086311b2016-01-08 09:53:11 +01001149 /* save the username, do not let the client change it */
1150 username = ssh_message_auth_user(msg);
1151 if (!session->username) {
1152 if (!username) {
1153 ERR("Denying an auth request without a username.");
1154 return 1;
1155 }
1156
Michal Vasko05ba9df2016-01-13 14:40:27 +01001157 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +01001158 } else if (username) {
1159 if (strcmp(username, session->username)) {
1160 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
1161 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +01001162 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +01001163 return 1;
1164 }
1165 }
1166
1167 if (subtype == SSH_AUTH_METHOD_NONE) {
1168 /* libssh will return the supported auth methods */
1169 return 1;
1170 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
1171 nc_sshcb_auth_password(session, msg);
1172 return 0;
1173 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
1174 nc_sshcb_auth_pubkey(session, msg);
1175 return 0;
1176 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
1177 nc_sshcb_auth_kbdint(session, msg);
1178 return 0;
1179 }
1180 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +01001181 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001182 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +01001183 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001184 }
Michal Vasko086311b2016-01-08 09:53:11 +01001185 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +01001186
Michal Vasko0df67562016-01-21 15:50:11 +01001187 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001188 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
1189 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +01001190 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +01001191 } else {
1192 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +01001193 }
1194 return 0;
1195 }
1196 }
1197
1198 /* we did not process it */
1199 return 1;
1200}
1201
Michal Vasko1a38c862016-01-15 15:50:07 +01001202/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001203static int
1204nc_open_netconf_channel(struct nc_session *session, int timeout)
1205{
Michal Vasko36c7be82017-02-22 13:37:59 +01001206 int ret;
1207 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001208
1209 /* message callback is executed twice to give chance for the channel to be
1210 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001211 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001212 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001213 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001214 return -1;
1215 }
1216
Michal Vaskoade892d2017-02-22 13:40:35 +01001217 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001218 if (ret != 1) {
1219 return ret;
1220 }
1221
1222 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1223 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001224 ERR("Failed to receive SSH messages on a session (%s).",
1225 ssh_get_error(session->ti.libssh.session));
Michal Vaskoade892d2017-02-22 13:40:35 +01001226 nc_session_unlock(session, timeout, __func__);
Michal Vasko086311b2016-01-08 09:53:11 +01001227 return -1;
1228 }
1229
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001230 if (!session->ti.libssh.channel) {
1231 /* we did not receive channel-open, timeout */
Michal Vaskoade892d2017-02-22 13:40:35 +01001232 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001233 return 0;
1234 }
1235
1236 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001237 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001238 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001239 ERR("Failed to receive SSH messages on a session (%s).",
1240 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001241 return -1;
1242 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001243
1244 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1245 /* we did not receive subsystem-request, timeout */
1246 return 0;
1247 }
1248
1249 return 1;
1250 }
1251
Michal Vasko36c7be82017-02-22 13:37:59 +01001252 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001253 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001254 nc_addtimespec(&ts_timeout, timeout);
1255 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001256 while (1) {
1257 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001258 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001259 return -1;
1260 }
1261
Michal Vaskoade892d2017-02-22 13:40:35 +01001262 ret = nc_session_lock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001263 if (ret != 1) {
1264 return ret;
1265 }
1266
1267 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
Michal Vaskoade892d2017-02-22 13:40:35 +01001268 nc_session_unlock(session, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001269 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001270 ERR("Failed to receive SSH messages on a session (%s).",
1271 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001272 return -1;
1273 }
1274
Michal Vasko086311b2016-01-08 09:53:11 +01001275 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001276 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001277 }
1278
Michal Vasko086311b2016-01-08 09:53:11 +01001279 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001280 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001281 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001282 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1283 /* timeout */
1284 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1285 break;
1286 }
1287 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001288 }
Michal Vasko086311b2016-01-08 09:53:11 +01001289
Michal Vasko1a38c862016-01-15 15:50:07 +01001290 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001291}
1292
Michal Vasko4c1fb492017-01-30 14:31:07 +01001293static int
1294nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1295{
1296 uint8_t i;
1297 char *privkey_path, *privkey_data;
1298 int privkey_data_rsa, ret;
1299
1300 if (!server_opts.hostkey_clb) {
1301 ERR("Callback for retrieving SSH host keys not set.");
1302 return -1;
1303 }
1304
1305 for (i = 0; i < hostkey_count; ++i) {
1306 privkey_path = privkey_data = NULL;
1307 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1308 ERR("Host key callback failed.");
1309 return -1;
1310 }
1311
1312 if (privkey_data) {
1313 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1314 if (!privkey_path) {
1315 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1316 free(privkey_data);
1317 return -1;
1318 }
1319 }
1320
1321 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1322
1323 /* cleanup */
1324 if (privkey_data && unlink(privkey_path)) {
1325 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1326 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001327 free(privkey_data);
1328
1329 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001330 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1331 }
1332 free(privkey_path);
1333
1334 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001335 return -1;
1336 }
1337 }
1338
1339 return 0;
1340}
1341
Michal Vasko96164bf2016-01-21 15:41:58 +01001342int
Michal Vasko0190bc32016-03-02 15:47:49 +01001343nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001344{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001345 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001346 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001347 int libssh_auth_methods = 0, ret;
1348 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001349
Michal Vasko2cc4c682016-03-01 09:16:48 +01001350 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001351
Michal Vasko086311b2016-01-08 09:53:11 +01001352 /* other transport-specific data */
1353 session->ti_type = NC_TI_LIBSSH;
1354 session->ti.libssh.session = ssh_new();
1355 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001356 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001357 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001358 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001359 }
1360
Michal Vaskoc61c4492016-01-25 11:13:34 +01001361 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001362 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1363 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001364 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001365 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1366 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001367 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001368 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1369 }
1370 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1371
Michal Vaskoe2713da2016-08-22 16:06:40 +02001372 sbind = ssh_bind_new();
1373 if (!sbind) {
1374 ERR("Failed to create an SSH bind.");
1375 close(sock);
1376 return -1;
1377 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001378
1379 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1380 close(sock);
1381 ssh_bind_free(sbind);
1382 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001383 }
1384 if (opts->banner) {
1385 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1386 }
1387
Michal Vasko086311b2016-01-08 09:53:11 +01001388 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001389 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001390 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001391
Michal Vaskoe2713da2016-08-22 16:06:40 +02001392 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1393 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001394 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001395 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001396 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001397 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001398 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001399
Michal Vasko0190bc32016-03-02 15:47:49 +01001400 ssh_set_blocking(session->ti.libssh.session, 0);
1401
Michal Vasko36c7be82017-02-22 13:37:59 +01001402 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001403 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001404 nc_addtimespec(&ts_timeout, timeout);
1405 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001406 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001407 /* this tends to take longer */
1408 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001409 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001410 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001411 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1412 break;
1413 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001414 }
1415 }
1416 if (ret == SSH_AGAIN) {
1417 ERR("SSH key exchange timeout.");
1418 return 0;
1419 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001420 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001421 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001422 }
1423
1424 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001425 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001426 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001427 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1428 }
1429 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001430 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001431 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001432 return -1;
1433 }
1434
Michal Vasko086311b2016-01-08 09:53:11 +01001435 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001436 ERR("Failed to receive SSH messages on a session (%s).",
1437 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001438 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001439 }
1440
Michal Vasko36c7be82017-02-22 13:37:59 +01001441 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1442 break;
1443 }
1444
Michal Vasko145ae672017-02-07 10:57:27 +01001445 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1446 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1447 return -1;
1448 }
1449
Michal Vasko086311b2016-01-08 09:53:11 +01001450 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001451 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001452 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001453 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1454 /* timeout */
1455 break;
1456 }
1457 }
1458 }
Michal Vasko086311b2016-01-08 09:53:11 +01001459
1460 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1461 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001462 if (session->username) {
1463 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1464 } else {
1465 ERR("User failed to authenticate for too long, disconnecting.");
1466 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001467 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001468 }
1469
Michal Vasko086311b2016-01-08 09:53:11 +01001470 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001471 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001472 if (ret < 1) {
1473 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001474 }
1475
Michal Vasko96164bf2016-01-21 15:41:58 +01001476 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001477 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001478}
1479
Michal Vasko71090fc2016-05-24 16:37:28 +02001480API NC_MSG_TYPE
1481nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1482{
1483 NC_MSG_TYPE msgtype;
1484 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001485 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001486
1487 if (!orig_session) {
1488 ERRARG("orig_session");
1489 return NC_MSG_ERROR;
1490 } else if (!session) {
1491 ERRARG("session");
1492 return NC_MSG_ERROR;
1493 }
1494
1495 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1496 && orig_session->ti.libssh.next) {
1497 for (new_session = orig_session->ti.libssh.next;
1498 new_session != orig_session;
1499 new_session = new_session->ti.libssh.next) {
1500 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1501 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1502 /* we found our session */
1503 break;
1504 }
1505 }
1506 if (new_session == orig_session) {
1507 new_session = NULL;
1508 }
1509 }
1510
1511 if (!new_session) {
1512 ERR("Session does not have a NETCONF SSH channel ready.");
1513 return NC_MSG_ERROR;
1514 }
1515
1516 /* assign new SID atomically */
1517 pthread_spin_lock(&server_opts.sid_lock);
1518 new_session->id = server_opts.new_session_id++;
1519 pthread_spin_unlock(&server_opts.sid_lock);
1520
1521 /* NETCONF handshake */
1522 msgtype = nc_handshake(new_session);
1523 if (msgtype != NC_MSG_HELLO) {
1524 return msgtype;
1525 }
1526
Michal Vasko9f6275e2017-10-05 13:50:05 +02001527 nc_gettimespec_real(&ts_cur);
1528 new_session->opts.server.session_start = ts_cur.tv_sec;
1529 nc_gettimespec_mono(&ts_cur);
1530 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001531 new_session->status = NC_STATUS_RUNNING;
1532 *session = new_session;
1533
1534 return msgtype;
1535}
1536
1537API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001538nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001539{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001540 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001541 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001542 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001543 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001544 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001545
Michal Vasko45e53ae2016-04-07 11:46:03 +02001546 if (!ps) {
1547 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001548 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001549 } else if (!session) {
1550 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001551 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001552 }
1553
Michal Vasko48a63ed2016-03-01 09:48:21 +01001554 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001555 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001556 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001557 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001558
Michal Vasko96164bf2016-01-21 15:41:58 +01001559 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001560 cur_session = ps->sessions[i]->session;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001561 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1562 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001563 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001564 for (new_session = cur_session->ti.libssh.next;
1565 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001566 new_session = new_session->ti.libssh.next) {
1567 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1568 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1569 /* we found our session */
1570 break;
1571 }
1572 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001573 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001574 break;
1575 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001576
Michal Vasko96164bf2016-01-21 15:41:58 +01001577 new_session = NULL;
1578 }
1579 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001580
Michal Vasko48a63ed2016-03-01 09:48:21 +01001581 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001582 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001583
Michal Vasko96164bf2016-01-21 15:41:58 +01001584 if (!new_session) {
1585 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001586 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001587 }
1588
1589 /* assign new SID atomically */
1590 pthread_spin_lock(&server_opts.sid_lock);
1591 new_session->id = server_opts.new_session_id++;
1592 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001593
Michal Vasko086311b2016-01-08 09:53:11 +01001594 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001595 msgtype = nc_handshake(new_session);
1596 if (msgtype != NC_MSG_HELLO) {
1597 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001598 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001599
Michal Vasko9f6275e2017-10-05 13:50:05 +02001600 nc_gettimespec_real(&ts_cur);
1601 new_session->opts.server.session_start = ts_cur.tv_sec;
1602 nc_gettimespec_mono(&ts_cur);
1603 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001604 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001605 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001606
Michal Vasko71090fc2016-05-24 16:37:28 +02001607 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001608}