blob: 4a6f20109a134bf17d4145c2cb91c803fd0432fc [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
Michal Vasko3964a832018-09-18 14:37:39 +020056 file = fdopen(fd, "w");
Michal Vasko4c1fb492017-01-30 14:31:07 +010057 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
Michal Vasko974410a2018-04-03 09:36:57 +0200136API void
137nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data),
138 void *user_data, void (*free_user_data)(void *user_data))
139{
140 server_opts.passwd_auth_clb = passwd_auth_clb;
141 server_opts.passwd_auth_data = user_data;
142 server_opts.passwd_auth_data_free = free_user_data;
143}
144
bhart1bb7cdb2018-07-02 15:03:30 -0500145API void
146nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data),
147 void *user_data, void (*free_user_data)(void *user_data))
148{
149 server_opts.interactive_auth_clb = interactive_auth_clb;
150 server_opts.interactive_auth_data = user_data;
151 server_opts.interactive_auth_data_free = free_user_data;
152}
Michal Vasko733c0bd2018-07-03 13:14:40 +0200153
bhart1bb7cdb2018-07-02 15:03:30 -0500154API void
155nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data),
156 void *user_data, void (*free_user_data)(void *user_data))
157{
158 server_opts.pubkey_auth_clb = pubkey_auth_clb;
159 server_opts.pubkey_auth_data = user_data;
160 server_opts.pubkey_auth_data_free = free_user_data;
161}
162
163
Michal Vaskob05053d2016-01-22 16:12:06 +0100164API int
Michal Vasko7d255882017-02-09 13:35:08 +0100165nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskob05053d2016-01-22 16:12:06 +0100166{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100167 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200168 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100169
Michal Vasko2e6defd2016-10-07 15:48:15 +0200170 /* LOCK */
171 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
172 if (!client) {
173 return -1;
174 }
Michal Vasko7d255882017-02-09 13:35:08 +0100175 ret = nc_server_ssh_add_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200176 /* UNLOCK */
177 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200178
179 return ret;
180}
181
Michal Vasko4c1fb492017-01-30 14:31:07 +0100182API void
183nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
184 char **privkey_data, int *privkey_data_rsa),
185 void *user_data, void (*free_user_data)(void *user_data))
186{
187 if (!hostkey_clb) {
188 ERRARG("hostkey_clb");
189 return;
190 }
191
192 server_opts.hostkey_clb = hostkey_clb;
193 server_opts.hostkey_data = user_data;
194 server_opts.hostkey_data_free = free_user_data;
195}
196
Michal Vaskoe2713da2016-08-22 16:06:40 +0200197static int
Michal Vasko7d255882017-02-09 13:35:08 +0100198nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200199{
200 uint8_t i;
201
Michal Vasko7d255882017-02-09 13:35:08 +0100202 if (name && (idx > -1)) {
203 ERRARG("name and idx");
204 return -1;
205 } else if (idx >= opts->hostkey_count) {
206 ERRARG("idx");
207 }
208
209 if (!name && (idx < 0)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200210 for (i = 0; i < opts->hostkey_count; ++i) {
211 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
212 }
213 free(opts->hostkeys);
214 opts->hostkeys = NULL;
215 opts->hostkey_count = 0;
Michal Vasko7d255882017-02-09 13:35:08 +0100216 } else if (name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200217 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100218 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vasko7d255882017-02-09 13:35:08 +0100219 idx = i;
220 goto remove_idx;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200221 }
222 }
223
Michal Vasko7d255882017-02-09 13:35:08 +0100224 ERRARG("name");
Michal Vaskoe2713da2016-08-22 16:06:40 +0200225 return -1;
Michal Vasko7d255882017-02-09 13:35:08 +0100226 } else {
227remove_idx:
228 --opts->hostkey_count;
229 lydict_remove(server_opts.ctx, opts->hostkeys[idx]);
230 if (idx < opts->hostkey_count - 1) {
231 memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys);
232 }
233 if (!opts->hostkey_count) {
234 free(opts->hostkeys);
235 opts->hostkeys = NULL;
236 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200237 }
238
239 return 0;
240}
241
242API int
Michal Vasko7d255882017-02-09 13:35:08 +0100243nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200244{
245 int ret;
246 struct nc_endpt *endpt;
247
248 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100249 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200250 if (!endpt) {
251 return -1;
252 }
Michal Vasko7d255882017-02-09 13:35:08 +0100253 ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200254 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100255 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200256
257 return ret;
258}
259
260API int
Michal Vasko7d255882017-02-09 13:35:08 +0100261nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *name, int16_t idx)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200262{
263 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200264 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200265
Michal Vasko2e6defd2016-10-07 15:48:15 +0200266 /* LOCK */
267 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
268 if (!client) {
269 return -1;
270 }
Michal Vasko7d255882017-02-09 13:35:08 +0100271 ret = nc_server_ssh_del_hostkey(name, idx, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200272 /* UNLOCK */
273 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100274
275 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100276}
277
278static int
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100279nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts)
280{
281 uint8_t i;
282 int16_t mov_idx = -1, after_idx = -1;
283 const char *bckup;
284
285 if (!key_mov) {
286 ERRARG("key_mov");
287 return -1;
288 }
289
290 for (i = 0; i < opts->hostkey_count; ++i) {
291 if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) {
292 after_idx = i;
293 }
294 if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) {
295 mov_idx = i;
296 }
297
298 if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) {
299 break;
300 }
301 }
302
303 if (key_after && (after_idx == -1)) {
304 ERRARG("key_after");
305 return -1;
306 }
307 if (mov_idx == -1) {
308 ERRARG("key_mov");
309 return -1;
310 }
311 if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) {
312 /* nothing to do */
313 return 0;
314 }
315
316 /* finally move the key */
317 bckup = opts->hostkeys[mov_idx];
318 if (mov_idx > after_idx) {
319 memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1,
320 ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys);
321 opts->hostkeys[after_idx + 1] = bckup;
322 } else {
323 memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys);
324 opts->hostkeys[after_idx] = bckup;
325 }
326
327 return 0;
328}
329
330API int
331nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after)
332{
333 int ret;
334 struct nc_endpt *endpt;
335
336 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100337 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100338 if (!endpt) {
339 return -1;
340 }
341 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh);
342 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100343 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100344
345 return ret;
346}
347
348API int
349nc_server_ssh_ch_client_mov_hostkey(const char *client_name, const char *key_mov, const char *key_after)
350{
351 int ret;
352 struct nc_ch_client *client;
353
354 /* LOCK */
355 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
356 if (!client) {
357 return -1;
358 }
359 ret = nc_server_ssh_mov_hostkey(key_mov, key_after, client->opts.ssh);
360 /* UNLOCK */
361 nc_server_ch_client_unlock(client);
362
363 return ret;
364}
365
366static int
367nc_server_ssh_mod_hostkey(const char *name, const char *new_name, struct nc_server_ssh_opts *opts)
368{
369 uint8_t i;
370
371 if (!name) {
372 ERRARG("name");
373 return -1;
374 } else if (!new_name) {
375 ERRARG("new_name");
376 return -1;
377 }
378
379 for (i = 0; i < opts->hostkey_count; ++i) {
380 if (!strcmp(opts->hostkeys[i], name)) {
381 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
382 opts->hostkeys[i] = lydict_insert(server_opts.ctx, new_name, 0);
383 return 0;
384 }
385 }
386
387 ERRARG("name");
388 return -1;
389}
390
391API int
392nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name)
393{
394 int ret;
395 struct nc_endpt *endpt;
396
397 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100398 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100399 if (!endpt) {
400 return -1;
401 }
402 ret = nc_server_ssh_mov_hostkey(name, new_name, endpt->opts.ssh);
403 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100404 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vaskofbfe8b62017-02-14 10:22:30 +0100405
406 return ret;
407}
408
409API int
410nc_server_ssh_ch_client_mod_hostkey(const char *client_name, const char *name, const char *new_name)
411{
412 int ret;
413 struct nc_ch_client *client;
414
415 /* LOCK */
416 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
417 if (!client) {
418 return -1;
419 }
420 ret = nc_server_ssh_mod_hostkey(name, new_name, client->opts.ssh);
421 /* UNLOCK */
422 nc_server_ch_client_unlock(client);
423
424 return ret;
425}
426
427static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100428nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100429{
Michal Vasko086311b2016-01-08 09:53:11 +0100430 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
431 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200432 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100433 return -1;
434 }
435
Michal Vaskob05053d2016-01-22 16:12:06 +0100436 opts->auth_methods = auth_methods;
437 return 0;
438}
439
440API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100441nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100442{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100443 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100444 struct nc_endpt *endpt;
445
Michal Vasko51e514d2016-02-02 15:51:52 +0100446 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100447 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100448 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100449 return -1;
450 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200451 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100452 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100453 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100454
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100455 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100456}
457
458API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200459nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100460{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100461 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200462 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100463
Michal Vasko2e6defd2016-10-07 15:48:15 +0200464 /* LOCK */
465 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
466 if (!client) {
467 return -1;
468 }
469 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
470 /* UNLOCK */
471 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100472
473 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100474}
475
476static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100477nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100478{
Michal Vaskob05053d2016-01-22 16:12:06 +0100479 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200480 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100481 return -1;
482 }
483
Michal Vaskob05053d2016-01-22 16:12:06 +0100484 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100485 return 0;
486}
487
488API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100489nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100490{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100491 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100492 struct nc_endpt *endpt;
493
Michal Vasko51e514d2016-02-02 15:51:52 +0100494 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100495 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100496 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100497 return -1;
498 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200499 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100500 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100501 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100502
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100503 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100504}
505
506API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200507nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100508{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100509 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200510 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100511
Michal Vasko2e6defd2016-10-07 15:48:15 +0200512 /* LOCK */
513 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
514 if (!client) {
515 return -1;
516 }
517 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
518 /* UNLOCK */
519 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100520
521 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100522}
523
524static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100525nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100526{
Michal Vaskob05053d2016-01-22 16:12:06 +0100527 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200528 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100529 return -1;
530 }
531
Michal Vaskob05053d2016-01-22 16:12:06 +0100532 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100533 return 0;
534}
535
536API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100537nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100538{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100539 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100540 struct nc_endpt *endpt;
541
Michal Vasko51e514d2016-02-02 15:51:52 +0100542 /* LOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100543 endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100544 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100545 return -1;
546 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200547 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100548 /* UNLOCK */
Michal Vaskoade892d2017-02-22 13:40:35 +0100549 pthread_rwlock_unlock(&server_opts.endpt_lock);
Michal Vasko3031aae2016-01-27 16:07:18 +0100550
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100551 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100552}
553
554API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200555nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100556{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100557 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200558 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100559
Michal Vasko2e6defd2016-10-07 15:48:15 +0200560 /* LOCK */
561 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
562 if (!client) {
563 return -1;
564 }
565 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
566 /* UNLOCK */
567 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100568
569 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100570}
571
572static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100573_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
574 const char *username)
575{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100576 /* LOCK */
577 pthread_mutex_lock(&server_opts.authkey_lock);
578
Michal Vasko17dfda92016-12-01 14:06:16 +0100579 ++server_opts.authkey_count;
580 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
581 if (!server_opts.authkeys) {
582 ERRMEM;
583 return -1;
584 }
585 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
586 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
587 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
588 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
589
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100590 /* UNLOCK */
591 pthread_mutex_unlock(&server_opts.authkey_lock);
592
Michal Vasko17dfda92016-12-01 14:06:16 +0100593 return 0;
594}
595
596API int
597nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100598{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200599 if (!pubkey_path) {
600 ERRARG("pubkey_path");
601 return -1;
602 } else if (!username) {
603 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100604 return -1;
605 }
606
Michal Vasko17dfda92016-12-01 14:06:16 +0100607 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100608}
609
610API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100611nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100612{
Michal Vasko17dfda92016-12-01 14:06:16 +0100613 if (!pubkey_base64) {
614 ERRARG("pubkey_base64");
615 return -1;
616 } else if (!type) {
617 ERRARG("type");
618 return -1;
619 } else if (!username) {
620 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100621 return -1;
622 }
623
Michal Vasko17dfda92016-12-01 14:06:16 +0100624 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100625}
626
627API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100628nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
629 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100630{
Michal Vasko086311b2016-01-08 09:53:11 +0100631 uint32_t i;
632 int ret = -1;
633
Michal Vasko17dfda92016-12-01 14:06:16 +0100634 /* LOCK */
635 pthread_mutex_lock(&server_opts.authkey_lock);
636
637 if (!pubkey_path && !pubkey_base64 && !type && !username) {
638 for (i = 0; i < server_opts.authkey_count; ++i) {
639 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
640 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
641 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100642
Michal Vasko086311b2016-01-08 09:53:11 +0100643 ret = 0;
644 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100645 free(server_opts.authkeys);
646 server_opts.authkeys = NULL;
647 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100648 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100649 for (i = 0; i < server_opts.authkey_count; ++i) {
650 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
651 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
652 && (!type || (server_opts.authkeys[i].type == type))
653 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
654 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
655 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
656 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100657
Michal Vasko17dfda92016-12-01 14:06:16 +0100658 --server_opts.authkey_count;
659 if (i < server_opts.authkey_count) {
660 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
661 sizeof *server_opts.authkeys);
662 } else if (!server_opts.authkey_count) {
663 free(server_opts.authkeys);
664 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100665 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100666
667 ret = 0;
668 }
669 }
Michal Vasko086311b2016-01-08 09:53:11 +0100670 }
671
Michal Vasko51e514d2016-02-02 15:51:52 +0100672 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100673 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100674
675 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100676}
677
678void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100679nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100680{
Michal Vasko7d255882017-02-09 13:35:08 +0100681 nc_server_ssh_del_hostkey(NULL, -1, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100682}
683
Michal Vasko086311b2016-01-08 09:53:11 +0100684static char *
685auth_password_get_pwd_hash(const char *username)
686{
687 struct passwd *pwd, pwd_buf;
688 struct spwd *spwd, spwd_buf;
689 char *pass_hash = NULL, buf[256];
690
691 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
692 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100693 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100694 return NULL;
695 }
696
697 if (!strcmp(pwd->pw_passwd, "x")) {
698 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
699 if (!spwd) {
700 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
701 return NULL;
702 }
703
704 pass_hash = spwd->sp_pwdp;
705 } else {
706 pass_hash = pwd->pw_passwd;
707 }
708
709 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100710 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100711 return NULL;
712 }
713
714 /* check the hash structure for special meaning */
715 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
716 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
717 return NULL;
718 }
719 if (!strcmp(pass_hash, "*NP*")) {
720 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
721 return NULL;
722 }
723
724 return strdup(pass_hash);
725}
726
727static int
728auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
729{
730 char *new_pass_hash;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200731#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100732 struct crypt_data cdata;
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200733#endif
Michal Vasko086311b2016-01-08 09:53:11 +0100734
735 if (!pass_hash[0]) {
736 if (!pass_clear[0]) {
737 WRN("User authentication successful with an empty password!");
738 return 0;
739 } else {
740 /* the user did now know he does not need any password,
741 * (which should not be used) so deny authentication */
742 return 1;
743 }
744 }
745
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200746#if defined(HAVE_CRYPT_R)
Michal Vasko086311b2016-01-08 09:53:11 +0100747 cdata.initialized = 0;
748 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200749#else
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200750 pthread_mutex_lock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200751 new_pass_hash = crypt(pass_clear, pass_hash);
Mislav Novakovicce9a7ef2017-08-08 13:45:52 +0200752 pthread_mutex_unlock(&crypt_lock);
Mislav Novakovicebf4bd72017-08-02 10:43:41 +0200753#endif
Andrew Langefeld158d6fd2018-06-11 18:51:44 -0500754
755 if (!new_pass_hash) {
756 return 1;
757 }
758
Michal Vasko086311b2016-01-08 09:53:11 +0100759 return strcmp(new_pass_hash, pass_hash);
760}
761
762static void
763nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
764{
765 char *pass_hash;
Michal Vaskoebba7602018-03-23 13:14:08 +0100766 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100767
Michal Vaskoebba7602018-03-23 13:14:08 +0100768 if (server_opts.passwd_auth_clb) {
769 auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data);
770 } else {
771 pass_hash = auth_password_get_pwd_hash(session->username);
772 if (pass_hash) {
773 auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg));
774 free(pass_hash);
775 }
Michal Vasko086311b2016-01-08 09:53:11 +0100776 }
777
Michal Vaskoebba7602018-03-23 13:14:08 +0100778 if (!auth_ret) {
779 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
780 VRB("User \"%s\" authenticated.", session->username);
781 ssh_message_auth_reply_success(msg, 0);
782 } else {
783 ++session->opts.server.ssh_auth_attempts;
784 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
785 ssh_message_reply_default(msg);
786 }
Michal Vasko086311b2016-01-08 09:53:11 +0100787}
788
789static void
790nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
791{
bhart3bc2f582018-06-05 12:40:32 -0500792 int auth_ret = 1;
Michal Vasko086311b2016-01-08 09:53:11 +0100793 char *pass_hash;
794
bhart1bb7cdb2018-07-02 15:03:30 -0500795 if (server_opts.interactive_auth_clb) {
Michal Vasko733c0bd2018-07-03 13:14:40 +0200796 auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data);
Michal Vasko086311b2016-01-08 09:53:11 +0100797 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500798 if (!ssh_message_auth_kbdint_is_response(msg)) {
799 const char *prompts[] = {"Password: "};
800 char echo[] = {0};
801
802 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
Robert Vargaad7a5532018-08-10 20:40:54 +0200803 auth_ret = -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100804 } else {
bhart1bb7cdb2018-07-02 15:03:30 -0500805 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {// failed session
806 ssh_message_reply_default(msg);
807 return;
808 }
bhart3bc2f582018-06-05 12:40:32 -0500809 pass_hash = auth_password_get_pwd_hash(session->username);// get hashed password
810 if (pass_hash) {
Robert Vargaad7a5532018-08-10 20:40:54 +0200811 /* Normalize auth_password_compare_pwd result to 0 or 1 */
812 auth_ret = !!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0));
bhart3bc2f582018-06-05 12:40:32 -0500813 free(pass_hash);// free hashed password
814 }
Michal Vasko086311b2016-01-08 09:53:11 +0100815 }
bhart1bb7cdb2018-07-02 15:03:30 -0500816 }
817
Robert Vargaad7a5532018-08-10 20:40:54 +0200818 /* We have already sent a reply */
819 if (auth_ret == -1) {
820 return;
821 }
822
bhart1bb7cdb2018-07-02 15:03:30 -0500823 /* Authenticate message based on outcome */
824 if (!auth_ret) {
825 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
826 VRB("User \"%s\" authenticated.", session->username);
827 ssh_message_auth_reply_success(msg, 0);
828 } else {
829 ++session->opts.server.ssh_auth_attempts;
830 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
831 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100832 }
833}
834
835static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100836auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100837{
838 uint32_t i;
839 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100840 const char *username = NULL;
Michal Vasko3e9d1682017-02-24 09:50:15 +0100841 int ret = 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100842
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100843 /* LOCK */
844 pthread_mutex_lock(&server_opts.authkey_lock);
845
Michal Vasko17dfda92016-12-01 14:06:16 +0100846 for (i = 0; i < server_opts.authkey_count; ++i) {
847 switch (server_opts.authkeys[i].type) {
848 case NC_SSH_KEY_UNKNOWN:
849 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
850 break;
851 case NC_SSH_KEY_DSA:
852 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
853 break;
854 case NC_SSH_KEY_RSA:
855 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
856 break;
857 case NC_SSH_KEY_ECDSA:
858 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
859 break;
860 }
861
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200862 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100863 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200864 continue;
865 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100866 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100867 continue;
868 }
869
870 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
871 ssh_key_free(pub_key);
872 break;
873 }
874
875 ssh_key_free(pub_key);
876 }
877
Michal Vasko17dfda92016-12-01 14:06:16 +0100878 if (i < server_opts.authkey_count) {
879 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100880 }
881
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100882 /* UNLOCK */
883 pthread_mutex_unlock(&server_opts.authkey_lock);
884
Michal Vasko086311b2016-01-08 09:53:11 +0100885 return username;
886}
887
888static void
889nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
890{
891 const char *username;
892 int signature_state;
893
Michal Vasko733c0bd2018-07-03 13:14:40 +0200894 if (server_opts.pubkey_auth_clb) {
895 if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) {
bhart3bc2f582018-06-05 12:40:32 -0500896 goto fail;
897 }
Michal Vasko733c0bd2018-07-03 13:14:40 +0200898 } else {
bhart3bc2f582018-06-05 12:40:32 -0500899 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
900 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
901 goto fail;
902 } else if (strcmp(session->username, username)) {
903 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
904 goto fail;
905 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200906 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200907
Michal Vasko086311b2016-01-08 09:53:11 +0100908 signature_state = ssh_message_auth_publickey_state(msg);
909 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
910 VRB("User \"%s\" authenticated.", session->username);
911 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
912 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100913 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200914 /* accepting only the use of a public key */
915 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100916 }
917
Michal Vaskobd13a932016-09-14 09:00:35 +0200918 return;
919
920fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200921 ++session->opts.server.ssh_auth_attempts;
922 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100923 ssh_message_reply_default(msg);
924}
925
926static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100927nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100928{
Michal Vasko96164bf2016-01-21 15:41:58 +0100929 ssh_channel chan;
930
931 /* first channel request */
932 if (!session->ti.libssh.channel) {
933 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100934 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100935 return -1;
936 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100937 chan = ssh_message_channel_request_open_reply_accept(msg);
938 if (!chan) {
939 ERR("Failed to create a new SSH channel.");
940 return -1;
941 }
942 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100943
Michal Vasko96164bf2016-01-21 15:41:58 +0100944 /* additional channel request */
945 } else {
946 chan = ssh_message_channel_request_open_reply_accept(msg);
947 if (!chan) {
948 ERR("Session %u: failed to create a new SSH channel.", session->id);
949 return -1;
950 }
951 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100952 }
953
Michal Vasko086311b2016-01-08 09:53:11 +0100954 return 0;
955}
956
957static int
958nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
959{
Michal Vasko96164bf2016-01-21 15:41:58 +0100960 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100961
Michal Vasko96164bf2016-01-21 15:41:58 +0100962 if (strcmp(subsystem, "netconf")) {
963 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100964 return -1;
965 }
966
Michal Vasko96164bf2016-01-21 15:41:58 +0100967 if (session->ti.libssh.channel == channel) {
968 /* first channel requested */
969 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
970 ERRINT;
971 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100972 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100973 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
974 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
975 return -1;
976 }
977
978 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100979 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100980 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
Michal Vasko131120a2018-05-29 15:44:02 +0200981 new_session = nc_new_session(NC_SERVER, 1);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100982 if (!new_session) {
983 ERRMEM;
984 return -1;
985 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100986
987 /* insert the new session */
988 if (!session->ti.libssh.next) {
989 new_session->ti.libssh.next = session;
990 } else {
991 new_session->ti.libssh.next = session->ti.libssh.next;
992 }
993 session->ti.libssh.next = new_session;
994
995 new_session->status = NC_STATUS_STARTING;
Michal Vasko96164bf2016-01-21 15:41:58 +0100996 new_session->ti_type = NC_TI_LIBSSH;
Michal Vasko131120a2018-05-29 15:44:02 +0200997 new_session->io_lock = session->io_lock;
Michal Vasko96164bf2016-01-21 15:41:58 +0100998 new_session->ti.libssh.channel = channel;
999 new_session->ti.libssh.session = session->ti.libssh.session;
1000 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
1001 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
1002 new_session->port = session->port;
1003 new_session->ctx = server_opts.ctx;
Michal Vasko83d15322018-09-27 09:44:02 +02001004 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX;
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 Vasko7f1c78b2016-01-19 09:52:14 +01001217 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1218 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001219 ERR("Failed to receive SSH messages on a session (%s).",
1220 ssh_get_error(session->ti.libssh.session));
Michal Vasko086311b2016-01-08 09:53:11 +01001221 return -1;
1222 }
1223
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001224 if (!session->ti.libssh.channel) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001225 return 0;
1226 }
1227
1228 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1229 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001230 ERR("Failed to receive SSH messages on a session (%s).",
1231 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001232 return -1;
1233 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001234
1235 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1236 /* we did not receive subsystem-request, timeout */
1237 return 0;
1238 }
1239
1240 return 1;
1241 }
1242
Michal Vasko36c7be82017-02-22 13:37:59 +01001243 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001244 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001245 nc_addtimespec(&ts_timeout, timeout);
1246 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001247 while (1) {
1248 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001249 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001250 return -1;
1251 }
1252
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001253 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1254 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001255 ERR("Failed to receive SSH messages on a session (%s).",
1256 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001257 return -1;
1258 }
1259
Michal Vasko086311b2016-01-08 09:53:11 +01001260 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001261 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001262 }
1263
Michal Vasko086311b2016-01-08 09:53:11 +01001264 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001265 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001266 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001267 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1268 /* timeout */
1269 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
1270 break;
1271 }
1272 }
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001273 }
Michal Vasko086311b2016-01-08 09:53:11 +01001274
Michal Vasko1a38c862016-01-15 15:50:07 +01001275 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001276}
1277
Michal Vasko4c1fb492017-01-30 14:31:07 +01001278static int
1279nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1280{
1281 uint8_t i;
1282 char *privkey_path, *privkey_data;
1283 int privkey_data_rsa, ret;
1284
1285 if (!server_opts.hostkey_clb) {
1286 ERR("Callback for retrieving SSH host keys not set.");
1287 return -1;
1288 }
1289
1290 for (i = 0; i < hostkey_count; ++i) {
1291 privkey_path = privkey_data = NULL;
1292 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1293 ERR("Host key callback failed.");
1294 return -1;
1295 }
1296
1297 if (privkey_data) {
1298 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1299 if (!privkey_path) {
1300 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1301 free(privkey_data);
1302 return -1;
1303 }
1304 }
1305
1306 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1307
1308 /* cleanup */
1309 if (privkey_data && unlink(privkey_path)) {
1310 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1311 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001312 free(privkey_data);
1313
1314 if (ret != SSH_OK) {
Michal Vasko80075de2017-07-10 11:38:52 +02001315 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path);
1316 }
1317 free(privkey_path);
1318
1319 if (ret != SSH_OK) {
Michal Vasko4c1fb492017-01-30 14:31:07 +01001320 return -1;
1321 }
1322 }
1323
1324 return 0;
1325}
1326
Michal Vasko96164bf2016-01-21 15:41:58 +01001327int
Michal Vasko0190bc32016-03-02 15:47:49 +01001328nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001329{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001330 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001331 struct nc_server_ssh_opts *opts;
Michal Vasko36c7be82017-02-22 13:37:59 +01001332 int libssh_auth_methods = 0, ret;
1333 struct timespec ts_timeout, ts_cur;
Michal Vasko086311b2016-01-08 09:53:11 +01001334
Michal Vasko2cc4c682016-03-01 09:16:48 +01001335 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001336
Michal Vasko086311b2016-01-08 09:53:11 +01001337 /* other transport-specific data */
1338 session->ti_type = NC_TI_LIBSSH;
1339 session->ti.libssh.session = ssh_new();
1340 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001341 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001342 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001343 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001344 }
1345
Michal Vaskoc61c4492016-01-25 11:13:34 +01001346 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001347 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1348 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001349 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001350 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1351 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001352 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001353 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1354 }
1355 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1356
Michal Vaskoe2713da2016-08-22 16:06:40 +02001357 sbind = ssh_bind_new();
1358 if (!sbind) {
1359 ERR("Failed to create an SSH bind.");
1360 close(sock);
1361 return -1;
1362 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001363
1364 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1365 close(sock);
1366 ssh_bind_free(sbind);
1367 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001368 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001369
Michal Vasko086311b2016-01-08 09:53:11 +01001370 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001371 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001372 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001373
Michal Vaskoe2713da2016-08-22 16:06:40 +02001374 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1375 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001376 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001377 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001378 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001379 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001380 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001381
Michal Vasko0190bc32016-03-02 15:47:49 +01001382 ssh_set_blocking(session->ti.libssh.session, 0);
1383
Michal Vasko36c7be82017-02-22 13:37:59 +01001384 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001385 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001386 nc_addtimespec(&ts_timeout, timeout);
1387 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001388 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001389 /* this tends to take longer */
1390 usleep(NC_TIMEOUT_STEP * 20);
Michal Vasko36c7be82017-02-22 13:37:59 +01001391 if (timeout > -1) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001392 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001393 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1394 break;
1395 }
Michal Vasko0190bc32016-03-02 15:47:49 +01001396 }
1397 }
1398 if (ret == SSH_AGAIN) {
1399 ERR("SSH key exchange timeout.");
1400 return 0;
1401 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001402 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001403 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001404 }
1405
1406 /* authenticate */
Michal Vasko36c7be82017-02-22 13:37:59 +01001407 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001408 nc_gettimespec_mono(&ts_timeout);
Michal Vasko36c7be82017-02-22 13:37:59 +01001409 nc_addtimespec(&ts_timeout, opts->auth_timeout * 1000);
1410 }
1411 while (1) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001412 if (!nc_session_is_connected(session)) {
Michal Vaskoc13da702017-02-07 10:57:57 +01001413 ERR("Communication SSH socket unexpectedly closed.");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001414 return -1;
1415 }
1416
Michal Vasko086311b2016-01-08 09:53:11 +01001417 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001418 ERR("Failed to receive SSH messages on a session (%s).",
1419 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001420 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001421 }
1422
Michal Vasko36c7be82017-02-22 13:37:59 +01001423 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1424 break;
1425 }
1426
Michal Vasko145ae672017-02-07 10:57:27 +01001427 if (session->opts.server.ssh_auth_attempts >= opts->auth_attempts) {
1428 ERR("Too many failed authentication attempts of user \"%s\".", session->username);
1429 return -1;
1430 }
1431
Michal Vasko086311b2016-01-08 09:53:11 +01001432 usleep(NC_TIMEOUT_STEP);
Michal Vasko36c7be82017-02-22 13:37:59 +01001433 if (opts->auth_timeout) {
Michal Vaskoe852c8b2017-10-05 10:02:20 +02001434 nc_gettimespec_mono(&ts_cur);
Michal Vasko36c7be82017-02-22 13:37:59 +01001435 if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
1436 /* timeout */
1437 break;
1438 }
1439 }
1440 }
Michal Vasko086311b2016-01-08 09:53:11 +01001441
1442 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1443 /* timeout */
Michal Vaskoc13da702017-02-07 10:57:57 +01001444 if (session->username) {
1445 ERR("User \"%s\" failed to authenticate for too long, disconnecting.", session->username);
1446 } else {
1447 ERR("User failed to authenticate for too long, disconnecting.");
1448 }
Michal Vasko1a38c862016-01-15 15:50:07 +01001449 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001450 }
1451
Michal Vasko086311b2016-01-08 09:53:11 +01001452 /* open channel */
Michal Vasko36c7be82017-02-22 13:37:59 +01001453 ret = nc_open_netconf_channel(session, timeout);
Michal Vasko1a38c862016-01-15 15:50:07 +01001454 if (ret < 1) {
1455 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001456 }
1457
Michal Vasko96164bf2016-01-21 15:41:58 +01001458 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001459 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001460}
1461
Michal Vasko71090fc2016-05-24 16:37:28 +02001462API NC_MSG_TYPE
1463nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1464{
1465 NC_MSG_TYPE msgtype;
1466 struct nc_session *new_session = NULL;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001467 struct timespec ts_cur;
Michal Vasko71090fc2016-05-24 16:37:28 +02001468
1469 if (!orig_session) {
1470 ERRARG("orig_session");
1471 return NC_MSG_ERROR;
1472 } else if (!session) {
1473 ERRARG("session");
1474 return NC_MSG_ERROR;
1475 }
1476
1477 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1478 && orig_session->ti.libssh.next) {
1479 for (new_session = orig_session->ti.libssh.next;
1480 new_session != orig_session;
1481 new_session = new_session->ti.libssh.next) {
1482 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1483 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1484 /* we found our session */
1485 break;
1486 }
1487 }
1488 if (new_session == orig_session) {
1489 new_session = NULL;
1490 }
1491 }
1492
1493 if (!new_session) {
1494 ERR("Session does not have a NETCONF SSH channel ready.");
1495 return NC_MSG_ERROR;
1496 }
1497
1498 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01001499 new_session->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vasko71090fc2016-05-24 16:37:28 +02001500
1501 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001502 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001503 if (msgtype != NC_MSG_HELLO) {
1504 return msgtype;
1505 }
1506
Michal Vasko9f6275e2017-10-05 13:50:05 +02001507 nc_gettimespec_real(&ts_cur);
1508 new_session->opts.server.session_start = ts_cur.tv_sec;
1509 nc_gettimespec_mono(&ts_cur);
1510 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko71090fc2016-05-24 16:37:28 +02001511 new_session->status = NC_STATUS_RUNNING;
1512 *session = new_session;
1513
1514 return msgtype;
1515}
1516
1517API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001518nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001519{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001520 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001521 NC_MSG_TYPE msgtype;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001522 struct nc_session *new_session = NULL, *cur_session;
Michal Vasko9f6275e2017-10-05 13:50:05 +02001523 struct timespec ts_cur;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001524 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001525
Michal Vasko45e53ae2016-04-07 11:46:03 +02001526 if (!ps) {
1527 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001528 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001529 } else if (!session) {
1530 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001531 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001532 }
1533
Michal Vasko48a63ed2016-03-01 09:48:21 +01001534 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001535 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001536 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001537 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001538
Michal Vasko96164bf2016-01-21 15:41:58 +01001539 for (i = 0; i < ps->session_count; ++i) {
fanchanghu3d4e7212017-08-09 09:42:30 +08001540 cur_session = ps->sessions[i]->session;
Michal Vaskoe4300a82017-05-24 10:35:42 +02001541 if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH)
1542 && cur_session->ti.libssh.next) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001543 /* an SSH session with more channels */
Michal Vaskoe4300a82017-05-24 10:35:42 +02001544 for (new_session = cur_session->ti.libssh.next;
1545 new_session != cur_session;
Michal Vasko96164bf2016-01-21 15:41:58 +01001546 new_session = new_session->ti.libssh.next) {
1547 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1548 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1549 /* we found our session */
1550 break;
1551 }
1552 }
Michal Vaskoe4300a82017-05-24 10:35:42 +02001553 if (new_session != cur_session) {
Michal Vasko96164bf2016-01-21 15:41:58 +01001554 break;
1555 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001556
Michal Vasko96164bf2016-01-21 15:41:58 +01001557 new_session = NULL;
1558 }
1559 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001560
Michal Vasko48a63ed2016-03-01 09:48:21 +01001561 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001562 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001563
Michal Vasko96164bf2016-01-21 15:41:58 +01001564 if (!new_session) {
1565 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001566 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001567 }
1568
1569 /* assign new SID atomically */
Michal Vasko69a3ff62018-11-09 09:31:48 +01001570 new_session->id = ATOMIC_INC(&server_opts.new_session_id);
Michal Vaskofb89d772016-01-08 12:25:35 +01001571
Michal Vasko086311b2016-01-08 09:53:11 +01001572 /* NETCONF handshake */
Michal Vasko131120a2018-05-29 15:44:02 +02001573 msgtype = nc_handshake_io(new_session);
Michal Vasko71090fc2016-05-24 16:37:28 +02001574 if (msgtype != NC_MSG_HELLO) {
1575 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001576 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001577
Michal Vasko9f6275e2017-10-05 13:50:05 +02001578 nc_gettimespec_real(&ts_cur);
1579 new_session->opts.server.session_start = ts_cur.tv_sec;
1580 nc_gettimespec_mono(&ts_cur);
1581 new_session->opts.server.last_rpc = ts_cur.tv_sec;
Michal Vasko086311b2016-01-08 09:53:11 +01001582 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001583 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001584
Michal Vasko71090fc2016-05-24 16:37:28 +02001585 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001586}