blob: ed48c291ed796a468986dc2c5a1215b0c6e00540 [file] [log] [blame]
Michal Vasko086311b2016-01-08 09:53:11 +01001/**
2 * \file session_server_ssh.c
3 * \author Michal Vasko <mvasko@cesnet.cz>
4 * \brief libnetconf2 SSH server session manipulation functions
5 *
Michal Vasko4c1fb492017-01-30 14:31:07 +01006 * Copyright (c) 2017 CESNET, z.s.p.o.
Michal Vasko086311b2016-01-08 09:53:11 +01007 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
15#define _GNU_SOURCE
Michal Vasko4c1fb492017-01-30 14:31:07 +010016#define _POSIX_SOURCE
Michal Vasko086311b2016-01-08 09:53:11 +010017
18#include <stdlib.h>
19#include <string.h>
20#include <sys/types.h>
21#include <pwd.h>
22#include <shadow.h>
23#include <crypt.h>
24#include <errno.h>
25
Michal Vasko11d142a2016-01-19 15:58:24 +010026#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010027#include "session_server_ch.h"
28#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010029
30extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010031
Michal Vasko4c1fb492017-01-30 14:31:07 +010032static char *
33base64der_key_to_tmp_file(const char *in, int rsa)
Michal Vasko086311b2016-01-08 09:53:11 +010034{
Michal Vasko4c1fb492017-01-30 14:31:07 +010035 char path[12] = "/tmp/XXXXXX";
36 int fd, written;
37 FILE *file;
38
39 if (in == NULL) {
40 return NULL;
Michal Vasko086311b2016-01-08 09:53:11 +010041 }
42
Michal Vasko4c1fb492017-01-30 14:31:07 +010043 fd = mkstemp(path);
44 if (fd == -1) {
45 return NULL;
46 }
47
48 file = fdopen(fd, "r");
49 if (!file) {
50 close(fd);
51 return NULL;
52 }
53
54 /* write the key into the file */
55 written = fwrite("-----BEGIN ", 1, 11, file);
56 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
57 written += fwrite(" PRIVATE KEY-----\n", 1, 18, file);
58 written += fwrite(in, 1, strlen(in), file);
59 written += fwrite("\n-----END ", 1, 10, file);
60 written += fwrite((rsa ? "RSA" : "DSA"), 1, 3, file);
61 written += fwrite(" PRIVATE KEY-----", 1, 17, file);
62
63 fclose(file);
64 if ((unsigned)written != 62 + strlen(in)) {
65 unlink(path);
66 return NULL;
67 }
68
69 return strdup(path);
70}
71
72static int
73nc_server_ssh_add_hostkey(const char *name, struct nc_server_ssh_opts *opts)
74{
75 if (!name) {
76 ERRARG("name");
Michal Vasko5fcc7142016-02-02 12:21:10 +010077 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010078 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010079
Michal Vaskoe2713da2016-08-22 16:06:40 +020080 ++opts->hostkey_count;
81 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
82 if (!opts->hostkeys) {
83 ERRMEM;
84 return -1;
85 }
Michal Vasko4c1fb492017-01-30 14:31:07 +010086 opts->hostkeys[opts->hostkey_count - 1] = lydict_insert(server_opts.ctx, name, 0);
Michal Vaskoe2713da2016-08-22 16:06:40 +020087
Michal Vasko5fcc7142016-02-02 12:21:10 +010088 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +010089}
90
91API int
Michal Vasko4c1fb492017-01-30 14:31:07 +010092nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name)
Michal Vaskob05053d2016-01-22 16:12:06 +010093{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010094 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +010095 struct nc_endpt *endpt;
96
Michal Vasko51e514d2016-02-02 15:51:52 +010097 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020098 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +010099 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100100 return -1;
101 }
Michal Vasko4c1fb492017-01-30 14:31:07 +0100102 ret = nc_server_ssh_add_hostkey(name, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100103 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100104 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100105
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100106 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100107}
108
109API int
Michal Vasko4c1fb492017-01-30 14:31:07 +0100110nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *name)
Michal Vaskob05053d2016-01-22 16:12:06 +0100111{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100112 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200113 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100114
Michal Vasko2e6defd2016-10-07 15:48:15 +0200115 /* LOCK */
116 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
117 if (!client) {
118 return -1;
119 }
Michal Vasko4c1fb492017-01-30 14:31:07 +0100120 ret = nc_server_ssh_add_hostkey(name, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200121 /* UNLOCK */
122 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200123
124 return ret;
125}
126
Michal Vasko4c1fb492017-01-30 14:31:07 +0100127API void
128nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
129 char **privkey_data, int *privkey_data_rsa),
130 void *user_data, void (*free_user_data)(void *user_data))
131{
132 if (!hostkey_clb) {
133 ERRARG("hostkey_clb");
134 return;
135 }
136
137 server_opts.hostkey_clb = hostkey_clb;
138 server_opts.hostkey_data = user_data;
139 server_opts.hostkey_data_free = free_user_data;
140}
141
Michal Vaskoe2713da2016-08-22 16:06:40 +0200142static int
Michal Vasko4c1fb492017-01-30 14:31:07 +0100143nc_server_ssh_del_hostkey(const char *name, struct nc_server_ssh_opts *opts)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200144{
145 uint8_t i;
146
Michal Vasko4c1fb492017-01-30 14:31:07 +0100147 if (!name) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200148 for (i = 0; i < opts->hostkey_count; ++i) {
149 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
150 }
151 free(opts->hostkeys);
152 opts->hostkeys = NULL;
153 opts->hostkey_count = 0;
154 } else {
155 for (i = 0; i < opts->hostkey_count; ++i) {
Michal Vasko4c1fb492017-01-30 14:31:07 +0100156 if (!strcmp(opts->hostkeys[i], name)) {
Michal Vaskoe2713da2016-08-22 16:06:40 +0200157 --opts->hostkey_count;
158 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
159 if (i < opts->hostkey_count - 1) {
160 memmove(opts->hostkeys + i, opts->hostkeys + i + 1, (opts->hostkey_count - i) * sizeof *opts->hostkeys);
161 }
162 return 0;
163 }
164 }
165
Michal Vasko4c1fb492017-01-30 14:31:07 +0100166 ERR("Host key \"%s\" not found.", name);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200167 return -1;
168 }
169
170 return 0;
171}
172
173API int
Michal Vasko4c1fb492017-01-30 14:31:07 +0100174nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200175{
176 int ret;
177 struct nc_endpt *endpt;
178
179 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200180 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200181 if (!endpt) {
182 return -1;
183 }
Michal Vasko4c1fb492017-01-30 14:31:07 +0100184 ret = nc_server_ssh_del_hostkey(name, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200185 /* UNLOCK */
186 nc_server_endpt_unlock(endpt);
187
188 return ret;
189}
190
191API int
Michal Vasko4c1fb492017-01-30 14:31:07 +0100192nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *name)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200193{
194 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200195 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200196
Michal Vasko2e6defd2016-10-07 15:48:15 +0200197 /* LOCK */
198 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
199 if (!client) {
200 return -1;
201 }
Michal Vasko4c1fb492017-01-30 14:31:07 +0100202 ret = nc_server_ssh_del_hostkey(name, client->opts.ssh);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200203 /* UNLOCK */
204 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100205
206 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100207}
208
209static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100210nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100211{
Michal Vaskob05053d2016-01-22 16:12:06 +0100212 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200213 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100214 return -1;
215 }
216
Michal Vaskoe2713da2016-08-22 16:06:40 +0200217 if (opts->banner) {
218 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100219 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200220 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100221 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100222}
223
224API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100225nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100226{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100227 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100228 struct nc_endpt *endpt;
229
Michal Vasko51e514d2016-02-02 15:51:52 +0100230 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200231 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100232 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100233 return -1;
234 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200235 ret = nc_server_ssh_set_banner(banner, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100236 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100237 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100238
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100239 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100240}
241
242API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200243nc_server_ssh_ch_client_set_banner(const char *client_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100244{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100245 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200246 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100247
Michal Vasko2e6defd2016-10-07 15:48:15 +0200248 /* LOCK */
249 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
250 if (!client) {
251 return -1;
252 }
253 ret = nc_server_ssh_set_banner(banner, client->opts.ssh);
254 /* UNLOCK */
255 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100256
257 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100258}
259
260static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100261nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100262{
Michal Vasko086311b2016-01-08 09:53:11 +0100263 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
264 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200265 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100266 return -1;
267 }
268
Michal Vaskob05053d2016-01-22 16:12:06 +0100269 opts->auth_methods = auth_methods;
270 return 0;
271}
272
273API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100274nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100275{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100276 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100277 struct nc_endpt *endpt;
278
Michal Vasko51e514d2016-02-02 15:51:52 +0100279 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200280 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100281 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100282 return -1;
283 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200284 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100285 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100286 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100287
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100288 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100289}
290
291API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200292nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100293{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100294 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200295 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100296
Michal Vasko2e6defd2016-10-07 15:48:15 +0200297 /* LOCK */
298 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
299 if (!client) {
300 return -1;
301 }
302 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
303 /* UNLOCK */
304 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100305
306 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100307}
308
309static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100310nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100311{
Michal Vaskob05053d2016-01-22 16:12:06 +0100312 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200313 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100314 return -1;
315 }
316
Michal Vaskob05053d2016-01-22 16:12:06 +0100317 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100318 return 0;
319}
320
321API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100322nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100323{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100324 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100325 struct nc_endpt *endpt;
326
Michal Vasko51e514d2016-02-02 15:51:52 +0100327 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200328 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100329 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100330 return -1;
331 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200332 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100333 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100334 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100335
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100336 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100337}
338
339API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200340nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100341{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100342 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200343 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100344
Michal Vasko2e6defd2016-10-07 15:48:15 +0200345 /* LOCK */
346 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
347 if (!client) {
348 return -1;
349 }
350 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
351 /* UNLOCK */
352 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100353
354 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100355}
356
357static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100358nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100359{
Michal Vaskob05053d2016-01-22 16:12:06 +0100360 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200361 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100362 return -1;
363 }
364
Michal Vaskob05053d2016-01-22 16:12:06 +0100365 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100366 return 0;
367}
368
369API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100370nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100371{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100372 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100373 struct nc_endpt *endpt;
374
Michal Vasko51e514d2016-02-02 15:51:52 +0100375 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200376 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100377 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100378 return -1;
379 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200380 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100381 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100382 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100383
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100384 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100385}
386
387API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200388nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100389{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100390 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200391 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100392
Michal Vasko2e6defd2016-10-07 15:48:15 +0200393 /* LOCK */
394 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
395 if (!client) {
396 return -1;
397 }
398 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
399 /* UNLOCK */
400 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100401
402 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100403}
404
405static int
Michal Vasko17dfda92016-12-01 14:06:16 +0100406_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
407 const char *username)
408{
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100409 /* LOCK */
410 pthread_mutex_lock(&server_opts.authkey_lock);
411
Michal Vasko17dfda92016-12-01 14:06:16 +0100412 ++server_opts.authkey_count;
413 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
414 if (!server_opts.authkeys) {
415 ERRMEM;
416 return -1;
417 }
418 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
419 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
420 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
421 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
422
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100423 /* UNLOCK */
424 pthread_mutex_unlock(&server_opts.authkey_lock);
425
Michal Vasko17dfda92016-12-01 14:06:16 +0100426 return 0;
427}
428
429API int
430nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100431{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200432 if (!pubkey_path) {
433 ERRARG("pubkey_path");
434 return -1;
435 } else if (!username) {
436 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100437 return -1;
438 }
439
Michal Vasko17dfda92016-12-01 14:06:16 +0100440 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100441}
442
443API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100444nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100445{
Michal Vasko17dfda92016-12-01 14:06:16 +0100446 if (!pubkey_base64) {
447 ERRARG("pubkey_base64");
448 return -1;
449 } else if (!type) {
450 ERRARG("type");
451 return -1;
452 } else if (!username) {
453 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100454 return -1;
455 }
456
Michal Vasko17dfda92016-12-01 14:06:16 +0100457 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100458}
459
460API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100461nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
462 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100463{
Michal Vasko086311b2016-01-08 09:53:11 +0100464 uint32_t i;
465 int ret = -1;
466
Michal Vasko17dfda92016-12-01 14:06:16 +0100467 /* LOCK */
468 pthread_mutex_lock(&server_opts.authkey_lock);
469
470 if (!pubkey_path && !pubkey_base64 && !type && !username) {
471 for (i = 0; i < server_opts.authkey_count; ++i) {
472 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
473 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
474 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100475
Michal Vasko086311b2016-01-08 09:53:11 +0100476 ret = 0;
477 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100478 free(server_opts.authkeys);
479 server_opts.authkeys = NULL;
480 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100481 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100482 for (i = 0; i < server_opts.authkey_count; ++i) {
483 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
484 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
485 && (!type || (server_opts.authkeys[i].type == type))
486 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
487 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
488 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
489 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100490
Michal Vasko17dfda92016-12-01 14:06:16 +0100491 --server_opts.authkey_count;
492 if (i < server_opts.authkey_count) {
493 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
494 sizeof *server_opts.authkeys);
495 } else if (!server_opts.authkey_count) {
496 free(server_opts.authkeys);
497 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100498 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100499
500 ret = 0;
501 }
502 }
Michal Vasko086311b2016-01-08 09:53:11 +0100503 }
504
Michal Vasko51e514d2016-02-02 15:51:52 +0100505 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100506 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100507
508 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100509}
510
511void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100512nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100513{
Michal Vaskoe2713da2016-08-22 16:06:40 +0200514 nc_server_ssh_del_hostkey(NULL, opts);
515 if (opts->banner) {
516 lydict_remove(server_opts.ctx, opts->banner);
517 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100518 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100519}
520
Michal Vasko086311b2016-01-08 09:53:11 +0100521static char *
522auth_password_get_pwd_hash(const char *username)
523{
524 struct passwd *pwd, pwd_buf;
525 struct spwd *spwd, spwd_buf;
526 char *pass_hash = NULL, buf[256];
527
528 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
529 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100530 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100531 return NULL;
532 }
533
534 if (!strcmp(pwd->pw_passwd, "x")) {
535 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
536 if (!spwd) {
537 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
538 return NULL;
539 }
540
541 pass_hash = spwd->sp_pwdp;
542 } else {
543 pass_hash = pwd->pw_passwd;
544 }
545
546 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100547 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100548 return NULL;
549 }
550
551 /* check the hash structure for special meaning */
552 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
553 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
554 return NULL;
555 }
556 if (!strcmp(pass_hash, "*NP*")) {
557 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
558 return NULL;
559 }
560
561 return strdup(pass_hash);
562}
563
564static int
565auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
566{
567 char *new_pass_hash;
568 struct crypt_data cdata;
569
570 if (!pass_hash[0]) {
571 if (!pass_clear[0]) {
572 WRN("User authentication successful with an empty password!");
573 return 0;
574 } else {
575 /* the user did now know he does not need any password,
576 * (which should not be used) so deny authentication */
577 return 1;
578 }
579 }
580
581 cdata.initialized = 0;
582 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
583 return strcmp(new_pass_hash, pass_hash);
584}
585
586static void
587nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
588{
589 char *pass_hash;
590
591 pass_hash = auth_password_get_pwd_hash(session->username);
592 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100593 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100594 ssh_message_auth_reply_success(msg, 0);
595 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
596 free(pass_hash);
597 return;
598 }
599
600 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200601 ++session->opts.server.ssh_auth_attempts;
602 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100603 ssh_message_reply_default(msg);
604}
605
606static void
607nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
608{
609 char *pass_hash;
610
611 if (!ssh_message_auth_kbdint_is_response(msg)) {
612 const char *prompts[] = {"Password: "};
613 char echo[] = {0};
614
615 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
616 } else {
617 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
618 ssh_message_reply_default(msg);
619 return;
620 }
621 pass_hash = auth_password_get_pwd_hash(session->username);
622 if (!pass_hash) {
623 ssh_message_reply_default(msg);
624 return;
625 }
626 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
627 VRB("User \"%s\" authenticated.", session->username);
628 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
629 ssh_message_auth_reply_success(msg, 0);
630 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200631 ++session->opts.server.ssh_auth_attempts;
632 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100633 ssh_message_reply_default(msg);
634 }
Radek Krejcifb533742016-03-04 15:12:54 +0100635 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100636 }
637}
638
639static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100640auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100641{
642 uint32_t i;
643 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100644 const char *username = NULL;
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200645 int ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100646
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100647 /* LOCK */
648 pthread_mutex_lock(&server_opts.authkey_lock);
649
Michal Vasko17dfda92016-12-01 14:06:16 +0100650 for (i = 0; i < server_opts.authkey_count; ++i) {
651 switch (server_opts.authkeys[i].type) {
652 case NC_SSH_KEY_UNKNOWN:
653 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
654 break;
655 case NC_SSH_KEY_DSA:
656 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
657 break;
658 case NC_SSH_KEY_RSA:
659 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
660 break;
661 case NC_SSH_KEY_ECDSA:
662 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
663 break;
664 }
665
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200666 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100667 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200668 continue;
669 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100670 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100671 continue;
672 }
673
674 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
675 ssh_key_free(pub_key);
676 break;
677 }
678
679 ssh_key_free(pub_key);
680 }
681
Michal Vasko17dfda92016-12-01 14:06:16 +0100682 if (i < server_opts.authkey_count) {
683 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100684 }
685
Michal Vaskoa05c7b12017-01-30 14:33:08 +0100686 /* UNLOCK */
687 pthread_mutex_unlock(&server_opts.authkey_lock);
688
Michal Vasko086311b2016-01-08 09:53:11 +0100689 return username;
690}
691
692static void
693nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
694{
695 const char *username;
696 int signature_state;
697
Michal Vasko17dfda92016-12-01 14:06:16 +0100698 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200699 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
700 goto fail;
701 } else if (strcmp(session->username, username)) {
702 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200703 goto fail;
704 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200705
Michal Vasko086311b2016-01-08 09:53:11 +0100706 signature_state = ssh_message_auth_publickey_state(msg);
707 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
708 VRB("User \"%s\" authenticated.", session->username);
709 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
710 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100711 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200712 /* accepting only the use of a public key */
713 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100714 }
715
Michal Vaskobd13a932016-09-14 09:00:35 +0200716 return;
717
718fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200719 ++session->opts.server.ssh_auth_attempts;
720 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100721 ssh_message_reply_default(msg);
722}
723
724static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100725nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100726{
Michal Vasko96164bf2016-01-21 15:41:58 +0100727 ssh_channel chan;
728
729 /* first channel request */
730 if (!session->ti.libssh.channel) {
731 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100732 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100733 return -1;
734 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100735 chan = ssh_message_channel_request_open_reply_accept(msg);
736 if (!chan) {
737 ERR("Failed to create a new SSH channel.");
738 return -1;
739 }
740 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100741
Michal Vasko96164bf2016-01-21 15:41:58 +0100742 /* additional channel request */
743 } else {
744 chan = ssh_message_channel_request_open_reply_accept(msg);
745 if (!chan) {
746 ERR("Session %u: failed to create a new SSH channel.", session->id);
747 return -1;
748 }
749 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100750 }
751
Michal Vasko086311b2016-01-08 09:53:11 +0100752 return 0;
753}
754
755static int
756nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
757{
Michal Vasko96164bf2016-01-21 15:41:58 +0100758 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100759
Michal Vasko96164bf2016-01-21 15:41:58 +0100760 if (strcmp(subsystem, "netconf")) {
761 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100762 return -1;
763 }
764
Michal Vasko96164bf2016-01-21 15:41:58 +0100765 if (session->ti.libssh.channel == channel) {
766 /* first channel requested */
767 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
768 ERRINT;
769 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100770 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100771 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
772 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
773 return -1;
774 }
775
776 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100777 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100778 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
779 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100780 if (!new_session) {
781 ERRMEM;
782 return -1;
783 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100784
785 /* insert the new session */
786 if (!session->ti.libssh.next) {
787 new_session->ti.libssh.next = session;
788 } else {
789 new_session->ti.libssh.next = session->ti.libssh.next;
790 }
791 session->ti.libssh.next = new_session;
792
793 new_session->status = NC_STATUS_STARTING;
794 new_session->side = NC_SERVER;
795 new_session->ti_type = NC_TI_LIBSSH;
796 new_session->ti_lock = session->ti_lock;
797 new_session->ti.libssh.channel = channel;
798 new_session->ti.libssh.session = session->ti.libssh.session;
799 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
800 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
801 new_session->port = session->port;
802 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100803 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
804 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100805 }
806
807 return 0;
808}
809
Michal Vasko96164bf2016-01-21 15:41:58 +0100810int
Michal Vaskob48aa812016-01-18 14:13:09 +0100811nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100812{
813 const char *str_type, *str_subtype = NULL, *username;
814 int subtype, type;
815 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100816
817 type = ssh_message_type(msg);
818 subtype = ssh_message_subtype(msg);
819
820 switch (type) {
821 case SSH_REQUEST_AUTH:
822 str_type = "request-auth";
823 switch (subtype) {
824 case SSH_AUTH_METHOD_NONE:
825 str_subtype = "none";
826 break;
827 case SSH_AUTH_METHOD_PASSWORD:
828 str_subtype = "password";
829 break;
830 case SSH_AUTH_METHOD_PUBLICKEY:
831 str_subtype = "publickey";
832 break;
833 case SSH_AUTH_METHOD_HOSTBASED:
834 str_subtype = "hostbased";
835 break;
836 case SSH_AUTH_METHOD_INTERACTIVE:
837 str_subtype = "interactive";
838 break;
839 case SSH_AUTH_METHOD_GSSAPI_MIC:
840 str_subtype = "gssapi-mic";
841 break;
842 }
843 break;
844
845 case SSH_REQUEST_CHANNEL_OPEN:
846 str_type = "request-channel-open";
847 switch (subtype) {
848 case SSH_CHANNEL_SESSION:
849 str_subtype = "session";
850 break;
851 case SSH_CHANNEL_DIRECT_TCPIP:
852 str_subtype = "direct-tcpip";
853 break;
854 case SSH_CHANNEL_FORWARDED_TCPIP:
855 str_subtype = "forwarded-tcpip";
856 break;
857 case (int)SSH_CHANNEL_X11:
858 str_subtype = "channel-x11";
859 break;
860 case SSH_CHANNEL_UNKNOWN:
861 /* fallthrough */
862 default:
863 str_subtype = "unknown";
864 break;
865 }
866 break;
867
868 case SSH_REQUEST_CHANNEL:
869 str_type = "request-channel";
870 switch (subtype) {
871 case SSH_CHANNEL_REQUEST_PTY:
872 str_subtype = "pty";
873 break;
874 case SSH_CHANNEL_REQUEST_EXEC:
875 str_subtype = "exec";
876 break;
877 case SSH_CHANNEL_REQUEST_SHELL:
878 str_subtype = "shell";
879 break;
880 case SSH_CHANNEL_REQUEST_ENV:
881 str_subtype = "env";
882 break;
883 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
884 str_subtype = "subsystem";
885 break;
886 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
887 str_subtype = "window-change";
888 break;
889 case SSH_CHANNEL_REQUEST_X11:
890 str_subtype = "x11";
891 break;
892 case SSH_CHANNEL_REQUEST_UNKNOWN:
893 /* fallthrough */
894 default:
895 str_subtype = "unknown";
896 break;
897 }
898 break;
899
900 case SSH_REQUEST_SERVICE:
901 str_type = "request-service";
902 str_subtype = ssh_message_service_service(msg);
903 break;
904
905 case SSH_REQUEST_GLOBAL:
906 str_type = "request-global";
907 switch (subtype) {
908 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
909 str_subtype = "tcpip-forward";
910 break;
911 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
912 str_subtype = "cancel-tcpip-forward";
913 break;
914 case SSH_GLOBAL_REQUEST_UNKNOWN:
915 /* fallthrough */
916 default:
917 str_subtype = "unknown";
918 break;
919 }
920 break;
921
922 default:
923 str_type = "unknown";
924 str_subtype = "unknown";
925 break;
926 }
927
928 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100929 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
930 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
931 * but we got it now, during session free */
932 VRB("SSH message arrived on a %s session, the request will be denied.",
933 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
934 ssh_message_reply_default(msg);
935 return 0;
936 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100937 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100938
939 /*
940 * process known messages
941 */
942 if (type == SSH_REQUEST_AUTH) {
943 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
944 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
945 ssh_message_reply_default(msg);
946 return 0;
947 }
948
Michal Vasko2e6defd2016-10-07 15:48:15 +0200949 if (session->opts.server.ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100950 /* too many failed attempts */
951 ssh_message_reply_default(msg);
952 return 0;
953 }
954
955 /* save the username, do not let the client change it */
956 username = ssh_message_auth_user(msg);
957 if (!session->username) {
958 if (!username) {
959 ERR("Denying an auth request without a username.");
960 return 1;
961 }
962
Michal Vasko05ba9df2016-01-13 14:40:27 +0100963 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100964 } else if (username) {
965 if (strcmp(username, session->username)) {
966 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
967 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100968 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100969 return 1;
970 }
971 }
972
973 if (subtype == SSH_AUTH_METHOD_NONE) {
974 /* libssh will return the supported auth methods */
975 return 1;
976 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
977 nc_sshcb_auth_password(session, msg);
978 return 0;
979 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
980 nc_sshcb_auth_pubkey(session, msg);
981 return 0;
982 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
983 nc_sshcb_auth_kbdint(session, msg);
984 return 0;
985 }
986 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100987 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100988 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100989 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100990 }
Michal Vasko086311b2016-01-08 09:53:11 +0100991 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100992
Michal Vasko0df67562016-01-21 15:50:11 +0100993 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100994 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
995 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100996 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100997 } else {
998 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100999 }
1000 return 0;
1001 }
1002 }
1003
1004 /* we did not process it */
1005 return 1;
1006}
1007
Michal Vasko1a38c862016-01-15 15:50:07 +01001008/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +01001009static int
1010nc_open_netconf_channel(struct nc_session *session, int timeout)
1011{
Michal Vasko62be1ce2016-03-03 13:24:52 +01001012 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001013
1014 /* message callback is executed twice to give chance for the channel to be
1015 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001016 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001017 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001018 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001019 return -1;
1020 }
1021
Michal vasko50cc94f2016-10-04 13:46:20 +02001022 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001023 if (ret != 1) {
1024 return ret;
1025 }
1026
1027 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1028 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001029 ERR("Failed to receive SSH messages on a session (%s).",
1030 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +01001031 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +01001032 return -1;
1033 }
1034
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001035 if (!session->ti.libssh.channel) {
1036 /* we did not receive channel-open, timeout */
1037 pthread_mutex_unlock(session->ti_lock);
1038 return 0;
1039 }
1040
1041 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1042 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001043 ERR("Failed to receive SSH messages on a session (%s).",
1044 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001045 pthread_mutex_unlock(session->ti_lock);
1046 return -1;
1047 }
1048 pthread_mutex_unlock(session->ti_lock);
1049
1050 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1051 /* we did not receive subsystem-request, timeout */
1052 return 0;
1053 }
1054
1055 return 1;
1056 }
1057
1058 while (1) {
1059 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001060 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001061 return -1;
1062 }
1063
Michal vasko50cc94f2016-10-04 13:46:20 +02001064 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001065 if (ret != 1) {
1066 return ret;
1067 }
1068
1069 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1070 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001071 ERR("Failed to receive SSH messages on a session (%s).",
1072 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001073 pthread_mutex_unlock(session->ti_lock);
1074 return -1;
1075 }
1076
1077 pthread_mutex_unlock(session->ti_lock);
1078
Michal Vasko086311b2016-01-08 09:53:11 +01001079 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001080 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001081 }
1082
Michal Vasko105bf272016-02-03 15:34:35 +01001083 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001084 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001085 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +01001086 break;
1087 }
1088
Michal Vasko086311b2016-01-08 09:53:11 +01001089 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001090 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001091 }
Michal Vasko086311b2016-01-08 09:53:11 +01001092
Michal Vasko1a38c862016-01-15 15:50:07 +01001093 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001094}
1095
Michal Vasko4c1fb492017-01-30 14:31:07 +01001096static int
1097nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1098{
1099 uint8_t i;
1100 char *privkey_path, *privkey_data;
1101 int privkey_data_rsa, ret;
1102
1103 if (!server_opts.hostkey_clb) {
1104 ERR("Callback for retrieving SSH host keys not set.");
1105 return -1;
1106 }
1107
1108 for (i = 0; i < hostkey_count; ++i) {
1109 privkey_path = privkey_data = NULL;
1110 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1111 ERR("Host key callback failed.");
1112 return -1;
1113 }
1114
1115 if (privkey_data) {
1116 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1117 if (!privkey_path) {
1118 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1119 free(privkey_data);
1120 return -1;
1121 }
1122 }
1123
1124 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1125
1126 /* cleanup */
1127 if (privkey_data && unlink(privkey_path)) {
1128 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1129 }
1130 free(privkey_path);
1131 free(privkey_data);
1132
1133 if (ret != SSH_OK) {
1134 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], ssh_get_error(sbind));
1135 return -1;
1136 }
1137 }
1138
1139 return 0;
1140}
1141
Michal Vasko96164bf2016-01-21 15:41:58 +01001142int
Michal Vasko0190bc32016-03-02 15:47:49 +01001143nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001144{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001145 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001146 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001147 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001148
Michal Vasko2cc4c682016-03-01 09:16:48 +01001149 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001150
Michal Vasko086311b2016-01-08 09:53:11 +01001151 /* other transport-specific data */
1152 session->ti_type = NC_TI_LIBSSH;
1153 session->ti.libssh.session = ssh_new();
1154 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001155 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001156 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001157 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001158 }
1159
Michal Vaskoc61c4492016-01-25 11:13:34 +01001160 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001161 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1162 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001163 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001164 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1165 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001166 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001167 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1168 }
1169 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1170
Michal Vaskoe2713da2016-08-22 16:06:40 +02001171 sbind = ssh_bind_new();
1172 if (!sbind) {
1173 ERR("Failed to create an SSH bind.");
1174 close(sock);
1175 return -1;
1176 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001177
1178 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1179 close(sock);
1180 ssh_bind_free(sbind);
1181 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001182 }
1183 if (opts->banner) {
1184 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1185 }
1186
Michal Vasko086311b2016-01-08 09:53:11 +01001187 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001188 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001189 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001190
Michal Vaskoe2713da2016-08-22 16:06:40 +02001191 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1192 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001193 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001194 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001195 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001196 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001197 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001198
Michal Vasko0190bc32016-03-02 15:47:49 +01001199 ssh_set_blocking(session->ti.libssh.session, 0);
1200
1201 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001202 /* this tends to take longer */
1203 usleep(NC_TIMEOUT_STEP * 20);
1204 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001205 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1206 break;
1207 }
1208 }
1209 if (ret == SSH_AGAIN) {
1210 ERR("SSH key exchange timeout.");
1211 return 0;
1212 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001213 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001214 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001215 }
1216
1217 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001218 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001219 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001220 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001221 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001222 return -1;
1223 }
1224
Michal Vasko086311b2016-01-08 09:53:11 +01001225 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001226 ERR("Failed to receive SSH messages on a session (%s).",
1227 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001228 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001229 }
1230
1231 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1232 break;
1233 }
1234
1235 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001236 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001237 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001238
1239 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1240 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001241 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001242 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001243 }
1244
Michal Vasko086311b2016-01-08 09:53:11 +01001245 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001246 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001247 if (ret < 1) {
1248 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001249 }
1250
Michal Vasko96164bf2016-01-21 15:41:58 +01001251 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001252 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001253}
1254
Michal Vasko71090fc2016-05-24 16:37:28 +02001255API NC_MSG_TYPE
1256nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1257{
1258 NC_MSG_TYPE msgtype;
1259 struct nc_session *new_session = NULL;
1260
1261 if (!orig_session) {
1262 ERRARG("orig_session");
1263 return NC_MSG_ERROR;
1264 } else if (!session) {
1265 ERRARG("session");
1266 return NC_MSG_ERROR;
1267 }
1268
1269 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1270 && orig_session->ti.libssh.next) {
1271 for (new_session = orig_session->ti.libssh.next;
1272 new_session != orig_session;
1273 new_session = new_session->ti.libssh.next) {
1274 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1275 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1276 /* we found our session */
1277 break;
1278 }
1279 }
1280 if (new_session == orig_session) {
1281 new_session = NULL;
1282 }
1283 }
1284
1285 if (!new_session) {
1286 ERR("Session does not have a NETCONF SSH channel ready.");
1287 return NC_MSG_ERROR;
1288 }
1289
1290 /* assign new SID atomically */
1291 pthread_spin_lock(&server_opts.sid_lock);
1292 new_session->id = server_opts.new_session_id++;
1293 pthread_spin_unlock(&server_opts.sid_lock);
1294
1295 /* NETCONF handshake */
1296 msgtype = nc_handshake(new_session);
1297 if (msgtype != NC_MSG_HELLO) {
1298 return msgtype;
1299 }
1300
Michal Vasko2e6defd2016-10-07 15:48:15 +02001301 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001302 new_session->status = NC_STATUS_RUNNING;
1303 *session = new_session;
1304
1305 return msgtype;
1306}
1307
1308API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001309nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001310{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001311 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001312 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001313 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001314 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001315
Michal Vasko45e53ae2016-04-07 11:46:03 +02001316 if (!ps) {
1317 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001318 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001319 } else if (!session) {
1320 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001321 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001322 }
1323
Michal Vasko48a63ed2016-03-01 09:48:21 +01001324 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001325 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001326 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001327 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001328
Michal Vasko96164bf2016-01-21 15:41:58 +01001329 for (i = 0; i < ps->session_count; ++i) {
1330 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1331 && ps->sessions[i]->ti.libssh.next) {
1332 /* an SSH session with more channels */
1333 for (new_session = ps->sessions[i]->ti.libssh.next;
1334 new_session != ps->sessions[i];
1335 new_session = new_session->ti.libssh.next) {
1336 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1337 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1338 /* we found our session */
1339 break;
1340 }
1341 }
1342 if (new_session != ps->sessions[i]) {
1343 break;
1344 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001345
Michal Vasko96164bf2016-01-21 15:41:58 +01001346 new_session = NULL;
1347 }
1348 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001349
Michal Vasko48a63ed2016-03-01 09:48:21 +01001350 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001351 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001352
Michal Vasko96164bf2016-01-21 15:41:58 +01001353 if (!new_session) {
1354 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001355 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001356 }
1357
1358 /* assign new SID atomically */
1359 pthread_spin_lock(&server_opts.sid_lock);
1360 new_session->id = server_opts.new_session_id++;
1361 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001362
Michal Vasko086311b2016-01-08 09:53:11 +01001363 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001364 msgtype = nc_handshake(new_session);
1365 if (msgtype != NC_MSG_HELLO) {
1366 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001367 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001368
Michal Vasko2e6defd2016-10-07 15:48:15 +02001369 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001370 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001371 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001372
Michal Vasko71090fc2016-05-24 16:37:28 +02001373 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001374}