blob: 4758c0db523504919cbd464a896b391e18ff60c4 [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{
409 ++server_opts.authkey_count;
410 server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys);
411 if (!server_opts.authkeys) {
412 ERRMEM;
413 return -1;
414 }
415 server_opts.authkeys[server_opts.authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
416 server_opts.authkeys[server_opts.authkey_count - 1].base64 = lydict_insert(server_opts.ctx, pubkey_base64, 0);
417 server_opts.authkeys[server_opts.authkey_count - 1].type = type;
418 server_opts.authkeys[server_opts.authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
419
420 return 0;
421}
422
423API int
424nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100425{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200426 if (!pubkey_path) {
427 ERRARG("pubkey_path");
428 return -1;
429 } else if (!username) {
430 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100431 return -1;
432 }
433
Michal Vasko17dfda92016-12-01 14:06:16 +0100434 return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100435}
436
437API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100438nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100439{
Michal Vasko17dfda92016-12-01 14:06:16 +0100440 if (!pubkey_base64) {
441 ERRARG("pubkey_base64");
442 return -1;
443 } else if (!type) {
444 ERRARG("type");
445 return -1;
446 } else if (!username) {
447 ERRARG("username");
Michal Vasko3031aae2016-01-27 16:07:18 +0100448 return -1;
449 }
450
Michal Vasko17dfda92016-12-01 14:06:16 +0100451 return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username);
Michal Vasko086311b2016-01-08 09:53:11 +0100452}
453
454API int
Michal Vasko17dfda92016-12-01 14:06:16 +0100455nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
456 const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100457{
Michal Vasko086311b2016-01-08 09:53:11 +0100458 uint32_t i;
459 int ret = -1;
460
Michal Vasko17dfda92016-12-01 14:06:16 +0100461 /* LOCK */
462 pthread_mutex_lock(&server_opts.authkey_lock);
463
464 if (!pubkey_path && !pubkey_base64 && !type && !username) {
465 for (i = 0; i < server_opts.authkey_count; ++i) {
466 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
467 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
468 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100469
Michal Vasko086311b2016-01-08 09:53:11 +0100470 ret = 0;
471 }
Michal Vasko17dfda92016-12-01 14:06:16 +0100472 free(server_opts.authkeys);
473 server_opts.authkeys = NULL;
474 server_opts.authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100475 } else {
Michal Vasko17dfda92016-12-01 14:06:16 +0100476 for (i = 0; i < server_opts.authkey_count; ++i) {
477 if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path))
478 && (!pubkey_base64 || strcmp(server_opts.authkeys[i].base64, pubkey_base64))
479 && (!type || (server_opts.authkeys[i].type == type))
480 && (!username || !strcmp(server_opts.authkeys[i].username, username))) {
481 lydict_remove(server_opts.ctx, server_opts.authkeys[i].path);
482 lydict_remove(server_opts.ctx, server_opts.authkeys[i].base64);
483 lydict_remove(server_opts.ctx, server_opts.authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100484
Michal Vasko17dfda92016-12-01 14:06:16 +0100485 --server_opts.authkey_count;
486 if (i < server_opts.authkey_count) {
487 memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count],
488 sizeof *server_opts.authkeys);
489 } else if (!server_opts.authkey_count) {
490 free(server_opts.authkeys);
491 server_opts.authkeys = NULL;
Michal Vaskoc0256492016-02-02 12:19:06 +0100492 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100493
494 ret = 0;
495 }
496 }
Michal Vasko086311b2016-01-08 09:53:11 +0100497 }
498
Michal Vasko51e514d2016-02-02 15:51:52 +0100499 /* UNLOCK */
Michal Vasko17dfda92016-12-01 14:06:16 +0100500 pthread_mutex_unlock(&server_opts.authkey_lock);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100501
502 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100503}
504
505void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100506nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100507{
Michal Vaskoe2713da2016-08-22 16:06:40 +0200508 nc_server_ssh_del_hostkey(NULL, opts);
509 if (opts->banner) {
510 lydict_remove(server_opts.ctx, opts->banner);
511 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100512 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100513}
514
Michal Vasko086311b2016-01-08 09:53:11 +0100515static char *
516auth_password_get_pwd_hash(const char *username)
517{
518 struct passwd *pwd, pwd_buf;
519 struct spwd *spwd, spwd_buf;
520 char *pass_hash = NULL, buf[256];
521
522 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
523 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100524 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100525 return NULL;
526 }
527
528 if (!strcmp(pwd->pw_passwd, "x")) {
529 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
530 if (!spwd) {
531 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
532 return NULL;
533 }
534
535 pass_hash = spwd->sp_pwdp;
536 } else {
537 pass_hash = pwd->pw_passwd;
538 }
539
540 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100541 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100542 return NULL;
543 }
544
545 /* check the hash structure for special meaning */
546 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
547 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
548 return NULL;
549 }
550 if (!strcmp(pass_hash, "*NP*")) {
551 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
552 return NULL;
553 }
554
555 return strdup(pass_hash);
556}
557
558static int
559auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
560{
561 char *new_pass_hash;
562 struct crypt_data cdata;
563
564 if (!pass_hash[0]) {
565 if (!pass_clear[0]) {
566 WRN("User authentication successful with an empty password!");
567 return 0;
568 } else {
569 /* the user did now know he does not need any password,
570 * (which should not be used) so deny authentication */
571 return 1;
572 }
573 }
574
575 cdata.initialized = 0;
576 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
577 return strcmp(new_pass_hash, pass_hash);
578}
579
580static void
581nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
582{
583 char *pass_hash;
584
585 pass_hash = auth_password_get_pwd_hash(session->username);
586 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100587 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100588 ssh_message_auth_reply_success(msg, 0);
589 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
590 free(pass_hash);
591 return;
592 }
593
594 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200595 ++session->opts.server.ssh_auth_attempts;
596 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100597 ssh_message_reply_default(msg);
598}
599
600static void
601nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
602{
603 char *pass_hash;
604
605 if (!ssh_message_auth_kbdint_is_response(msg)) {
606 const char *prompts[] = {"Password: "};
607 char echo[] = {0};
608
609 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
610 } else {
611 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
612 ssh_message_reply_default(msg);
613 return;
614 }
615 pass_hash = auth_password_get_pwd_hash(session->username);
616 if (!pass_hash) {
617 ssh_message_reply_default(msg);
618 return;
619 }
620 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
621 VRB("User \"%s\" authenticated.", session->username);
622 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
623 ssh_message_auth_reply_success(msg, 0);
624 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200625 ++session->opts.server.ssh_auth_attempts;
626 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100627 ssh_message_reply_default(msg);
628 }
Radek Krejcifb533742016-03-04 15:12:54 +0100629 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100630 }
631}
632
633static const char *
Michal Vasko17dfda92016-12-01 14:06:16 +0100634auth_pubkey_compare_key(ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100635{
636 uint32_t i;
637 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100638 const char *username = NULL;
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200639 int ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100640
Michal Vasko17dfda92016-12-01 14:06:16 +0100641 for (i = 0; i < server_opts.authkey_count; ++i) {
642 switch (server_opts.authkeys[i].type) {
643 case NC_SSH_KEY_UNKNOWN:
644 ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key);
645 break;
646 case NC_SSH_KEY_DSA:
647 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key);
648 break;
649 case NC_SSH_KEY_RSA:
650 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key);
651 break;
652 case NC_SSH_KEY_ECDSA:
653 ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key);
654 break;
655 }
656
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200657 if (ret == SSH_EOF) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100658 WRN("Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username);
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200659 continue;
660 } else if (ret == SSH_ERROR) {
Michal Vasko17dfda92016-12-01 14:06:16 +0100661 WRN("Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100662 continue;
663 }
664
665 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
666 ssh_key_free(pub_key);
667 break;
668 }
669
670 ssh_key_free(pub_key);
671 }
672
Michal Vasko17dfda92016-12-01 14:06:16 +0100673 if (i < server_opts.authkey_count) {
674 username = server_opts.authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100675 }
676
677 return username;
678}
679
680static void
681nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
682{
683 const char *username;
684 int signature_state;
685
Michal Vasko17dfda92016-12-01 14:06:16 +0100686 if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200687 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
688 goto fail;
689 } else if (strcmp(session->username, username)) {
690 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200691 goto fail;
692 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200693
Michal Vasko086311b2016-01-08 09:53:11 +0100694 signature_state = ssh_message_auth_publickey_state(msg);
695 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
696 VRB("User \"%s\" authenticated.", session->username);
697 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
698 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100699 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200700 /* accepting only the use of a public key */
701 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100702 }
703
Michal Vaskobd13a932016-09-14 09:00:35 +0200704 return;
705
706fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200707 ++session->opts.server.ssh_auth_attempts;
708 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100709 ssh_message_reply_default(msg);
710}
711
712static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100713nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100714{
Michal Vasko96164bf2016-01-21 15:41:58 +0100715 ssh_channel chan;
716
717 /* first channel request */
718 if (!session->ti.libssh.channel) {
719 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100720 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100721 return -1;
722 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100723 chan = ssh_message_channel_request_open_reply_accept(msg);
724 if (!chan) {
725 ERR("Failed to create a new SSH channel.");
726 return -1;
727 }
728 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100729
Michal Vasko96164bf2016-01-21 15:41:58 +0100730 /* additional channel request */
731 } else {
732 chan = ssh_message_channel_request_open_reply_accept(msg);
733 if (!chan) {
734 ERR("Session %u: failed to create a new SSH channel.", session->id);
735 return -1;
736 }
737 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100738 }
739
Michal Vasko086311b2016-01-08 09:53:11 +0100740 return 0;
741}
742
743static int
744nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
745{
Michal Vasko96164bf2016-01-21 15:41:58 +0100746 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100747
Michal Vasko96164bf2016-01-21 15:41:58 +0100748 if (strcmp(subsystem, "netconf")) {
749 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100750 return -1;
751 }
752
Michal Vasko96164bf2016-01-21 15:41:58 +0100753 if (session->ti.libssh.channel == channel) {
754 /* first channel requested */
755 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
756 ERRINT;
757 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100758 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100759 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
760 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
761 return -1;
762 }
763
764 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100765 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100766 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
767 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100768 if (!new_session) {
769 ERRMEM;
770 return -1;
771 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100772
773 /* insert the new session */
774 if (!session->ti.libssh.next) {
775 new_session->ti.libssh.next = session;
776 } else {
777 new_session->ti.libssh.next = session->ti.libssh.next;
778 }
779 session->ti.libssh.next = new_session;
780
781 new_session->status = NC_STATUS_STARTING;
782 new_session->side = NC_SERVER;
783 new_session->ti_type = NC_TI_LIBSSH;
784 new_session->ti_lock = session->ti_lock;
785 new_session->ti.libssh.channel = channel;
786 new_session->ti.libssh.session = session->ti.libssh.session;
787 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
788 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
789 new_session->port = session->port;
790 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100791 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
792 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100793 }
794
795 return 0;
796}
797
Michal Vasko96164bf2016-01-21 15:41:58 +0100798int
Michal Vaskob48aa812016-01-18 14:13:09 +0100799nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100800{
801 const char *str_type, *str_subtype = NULL, *username;
802 int subtype, type;
803 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100804
805 type = ssh_message_type(msg);
806 subtype = ssh_message_subtype(msg);
807
808 switch (type) {
809 case SSH_REQUEST_AUTH:
810 str_type = "request-auth";
811 switch (subtype) {
812 case SSH_AUTH_METHOD_NONE:
813 str_subtype = "none";
814 break;
815 case SSH_AUTH_METHOD_PASSWORD:
816 str_subtype = "password";
817 break;
818 case SSH_AUTH_METHOD_PUBLICKEY:
819 str_subtype = "publickey";
820 break;
821 case SSH_AUTH_METHOD_HOSTBASED:
822 str_subtype = "hostbased";
823 break;
824 case SSH_AUTH_METHOD_INTERACTIVE:
825 str_subtype = "interactive";
826 break;
827 case SSH_AUTH_METHOD_GSSAPI_MIC:
828 str_subtype = "gssapi-mic";
829 break;
830 }
831 break;
832
833 case SSH_REQUEST_CHANNEL_OPEN:
834 str_type = "request-channel-open";
835 switch (subtype) {
836 case SSH_CHANNEL_SESSION:
837 str_subtype = "session";
838 break;
839 case SSH_CHANNEL_DIRECT_TCPIP:
840 str_subtype = "direct-tcpip";
841 break;
842 case SSH_CHANNEL_FORWARDED_TCPIP:
843 str_subtype = "forwarded-tcpip";
844 break;
845 case (int)SSH_CHANNEL_X11:
846 str_subtype = "channel-x11";
847 break;
848 case SSH_CHANNEL_UNKNOWN:
849 /* fallthrough */
850 default:
851 str_subtype = "unknown";
852 break;
853 }
854 break;
855
856 case SSH_REQUEST_CHANNEL:
857 str_type = "request-channel";
858 switch (subtype) {
859 case SSH_CHANNEL_REQUEST_PTY:
860 str_subtype = "pty";
861 break;
862 case SSH_CHANNEL_REQUEST_EXEC:
863 str_subtype = "exec";
864 break;
865 case SSH_CHANNEL_REQUEST_SHELL:
866 str_subtype = "shell";
867 break;
868 case SSH_CHANNEL_REQUEST_ENV:
869 str_subtype = "env";
870 break;
871 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
872 str_subtype = "subsystem";
873 break;
874 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
875 str_subtype = "window-change";
876 break;
877 case SSH_CHANNEL_REQUEST_X11:
878 str_subtype = "x11";
879 break;
880 case SSH_CHANNEL_REQUEST_UNKNOWN:
881 /* fallthrough */
882 default:
883 str_subtype = "unknown";
884 break;
885 }
886 break;
887
888 case SSH_REQUEST_SERVICE:
889 str_type = "request-service";
890 str_subtype = ssh_message_service_service(msg);
891 break;
892
893 case SSH_REQUEST_GLOBAL:
894 str_type = "request-global";
895 switch (subtype) {
896 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
897 str_subtype = "tcpip-forward";
898 break;
899 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
900 str_subtype = "cancel-tcpip-forward";
901 break;
902 case SSH_GLOBAL_REQUEST_UNKNOWN:
903 /* fallthrough */
904 default:
905 str_subtype = "unknown";
906 break;
907 }
908 break;
909
910 default:
911 str_type = "unknown";
912 str_subtype = "unknown";
913 break;
914 }
915
916 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100917 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
918 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
919 * but we got it now, during session free */
920 VRB("SSH message arrived on a %s session, the request will be denied.",
921 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
922 ssh_message_reply_default(msg);
923 return 0;
924 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100925 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100926
927 /*
928 * process known messages
929 */
930 if (type == SSH_REQUEST_AUTH) {
931 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
932 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
933 ssh_message_reply_default(msg);
934 return 0;
935 }
936
Michal Vasko2e6defd2016-10-07 15:48:15 +0200937 if (session->opts.server.ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100938 /* too many failed attempts */
939 ssh_message_reply_default(msg);
940 return 0;
941 }
942
943 /* save the username, do not let the client change it */
944 username = ssh_message_auth_user(msg);
945 if (!session->username) {
946 if (!username) {
947 ERR("Denying an auth request without a username.");
948 return 1;
949 }
950
Michal Vasko05ba9df2016-01-13 14:40:27 +0100951 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100952 } else if (username) {
953 if (strcmp(username, session->username)) {
954 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
955 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100956 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100957 return 1;
958 }
959 }
960
961 if (subtype == SSH_AUTH_METHOD_NONE) {
962 /* libssh will return the supported auth methods */
963 return 1;
964 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
965 nc_sshcb_auth_password(session, msg);
966 return 0;
967 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
968 nc_sshcb_auth_pubkey(session, msg);
969 return 0;
970 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
971 nc_sshcb_auth_kbdint(session, msg);
972 return 0;
973 }
974 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100975 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100976 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100977 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100978 }
Michal Vasko086311b2016-01-08 09:53:11 +0100979 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100980
Michal Vasko0df67562016-01-21 15:50:11 +0100981 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100982 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
983 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100984 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100985 } else {
986 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100987 }
988 return 0;
989 }
990 }
991
992 /* we did not process it */
993 return 1;
994}
995
Michal Vasko1a38c862016-01-15 15:50:07 +0100996/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100997static int
998nc_open_netconf_channel(struct nc_session *session, int timeout)
999{
Michal Vasko62be1ce2016-03-03 13:24:52 +01001000 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001001
1002 /* message callback is executed twice to give chance for the channel to be
1003 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001004 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001005 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001006 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001007 return -1;
1008 }
1009
Michal vasko50cc94f2016-10-04 13:46:20 +02001010 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001011 if (ret != 1) {
1012 return ret;
1013 }
1014
1015 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1016 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001017 ERR("Failed to receive SSH messages on a session (%s).",
1018 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +01001019 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +01001020 return -1;
1021 }
1022
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001023 if (!session->ti.libssh.channel) {
1024 /* we did not receive channel-open, timeout */
1025 pthread_mutex_unlock(session->ti_lock);
1026 return 0;
1027 }
1028
1029 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1030 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001031 ERR("Failed to receive SSH messages on a session (%s).",
1032 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001033 pthread_mutex_unlock(session->ti_lock);
1034 return -1;
1035 }
1036 pthread_mutex_unlock(session->ti_lock);
1037
1038 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1039 /* we did not receive subsystem-request, timeout */
1040 return 0;
1041 }
1042
1043 return 1;
1044 }
1045
1046 while (1) {
1047 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001048 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001049 return -1;
1050 }
1051
Michal vasko50cc94f2016-10-04 13:46:20 +02001052 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001053 if (ret != 1) {
1054 return ret;
1055 }
1056
1057 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1058 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001059 ERR("Failed to receive SSH messages on a session (%s).",
1060 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001061 pthread_mutex_unlock(session->ti_lock);
1062 return -1;
1063 }
1064
1065 pthread_mutex_unlock(session->ti_lock);
1066
Michal Vasko086311b2016-01-08 09:53:11 +01001067 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001068 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001069 }
1070
Michal Vasko105bf272016-02-03 15:34:35 +01001071 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001072 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001073 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +01001074 break;
1075 }
1076
Michal Vasko086311b2016-01-08 09:53:11 +01001077 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001078 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001079 }
Michal Vasko086311b2016-01-08 09:53:11 +01001080
Michal Vasko1a38c862016-01-15 15:50:07 +01001081 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001082}
1083
Michal Vasko4c1fb492017-01-30 14:31:07 +01001084static int
1085nc_ssh_bind_add_hostkeys(ssh_bind sbind, const char **hostkeys, uint8_t hostkey_count)
1086{
1087 uint8_t i;
1088 char *privkey_path, *privkey_data;
1089 int privkey_data_rsa, ret;
1090
1091 if (!server_opts.hostkey_clb) {
1092 ERR("Callback for retrieving SSH host keys not set.");
1093 return -1;
1094 }
1095
1096 for (i = 0; i < hostkey_count; ++i) {
1097 privkey_path = privkey_data = NULL;
1098 if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_data_rsa)) {
1099 ERR("Host key callback failed.");
1100 return -1;
1101 }
1102
1103 if (privkey_data) {
1104 privkey_path = base64der_key_to_tmp_file(privkey_data, privkey_data_rsa);
1105 if (!privkey_path) {
1106 ERR("Temporarily storing a host key into a file failed (%s).", strerror(errno));
1107 free(privkey_data);
1108 return -1;
1109 }
1110 }
1111
1112 ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path);
1113
1114 /* cleanup */
1115 if (privkey_data && unlink(privkey_path)) {
1116 WRN("Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno));
1117 }
1118 free(privkey_path);
1119 free(privkey_data);
1120
1121 if (ret != SSH_OK) {
1122 ERR("Failed to set hostkey \"%s\" (%s).", hostkeys[i], ssh_get_error(sbind));
1123 return -1;
1124 }
1125 }
1126
1127 return 0;
1128}
1129
Michal Vasko96164bf2016-01-21 15:41:58 +01001130int
Michal Vasko0190bc32016-03-02 15:47:49 +01001131nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001132{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001133 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001134 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001135 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001136
Michal Vasko2cc4c682016-03-01 09:16:48 +01001137 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001138
Michal Vasko086311b2016-01-08 09:53:11 +01001139 /* other transport-specific data */
1140 session->ti_type = NC_TI_LIBSSH;
1141 session->ti.libssh.session = ssh_new();
1142 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001143 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001144 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001145 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001146 }
1147
Michal Vaskoc61c4492016-01-25 11:13:34 +01001148 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001149 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1150 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001151 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001152 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1153 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001154 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001155 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1156 }
1157 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1158
Michal Vaskoe2713da2016-08-22 16:06:40 +02001159 sbind = ssh_bind_new();
1160 if (!sbind) {
1161 ERR("Failed to create an SSH bind.");
1162 close(sock);
1163 return -1;
1164 }
Michal Vasko4c1fb492017-01-30 14:31:07 +01001165
1166 if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) {
1167 close(sock);
1168 ssh_bind_free(sbind);
1169 return -1;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001170 }
1171 if (opts->banner) {
1172 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1173 }
1174
Michal Vasko086311b2016-01-08 09:53:11 +01001175 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001176 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001177 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001178
Michal Vaskoe2713da2016-08-22 16:06:40 +02001179 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1180 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001181 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001182 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001183 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001184 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001185 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001186
Michal Vasko0190bc32016-03-02 15:47:49 +01001187 ssh_set_blocking(session->ti.libssh.session, 0);
1188
1189 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001190 /* this tends to take longer */
1191 usleep(NC_TIMEOUT_STEP * 20);
1192 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001193 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1194 break;
1195 }
1196 }
1197 if (ret == SSH_AGAIN) {
1198 ERR("SSH key exchange timeout.");
1199 return 0;
1200 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001201 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001202 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001203 }
1204
1205 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001206 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001207 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001208 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001209 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001210 return -1;
1211 }
1212
Michal Vasko086311b2016-01-08 09:53:11 +01001213 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001214 ERR("Failed to receive SSH messages on a session (%s).",
1215 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001216 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001217 }
1218
1219 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1220 break;
1221 }
1222
1223 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001224 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001225 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001226
1227 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1228 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001229 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001230 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001231 }
1232
Michal Vasko086311b2016-01-08 09:53:11 +01001233 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001234 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001235 if (ret < 1) {
1236 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001237 }
1238
Michal Vasko96164bf2016-01-21 15:41:58 +01001239 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001240 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001241}
1242
Michal Vasko71090fc2016-05-24 16:37:28 +02001243API NC_MSG_TYPE
1244nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1245{
1246 NC_MSG_TYPE msgtype;
1247 struct nc_session *new_session = NULL;
1248
1249 if (!orig_session) {
1250 ERRARG("orig_session");
1251 return NC_MSG_ERROR;
1252 } else if (!session) {
1253 ERRARG("session");
1254 return NC_MSG_ERROR;
1255 }
1256
1257 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1258 && orig_session->ti.libssh.next) {
1259 for (new_session = orig_session->ti.libssh.next;
1260 new_session != orig_session;
1261 new_session = new_session->ti.libssh.next) {
1262 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1263 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1264 /* we found our session */
1265 break;
1266 }
1267 }
1268 if (new_session == orig_session) {
1269 new_session = NULL;
1270 }
1271 }
1272
1273 if (!new_session) {
1274 ERR("Session does not have a NETCONF SSH channel ready.");
1275 return NC_MSG_ERROR;
1276 }
1277
1278 /* assign new SID atomically */
1279 pthread_spin_lock(&server_opts.sid_lock);
1280 new_session->id = server_opts.new_session_id++;
1281 pthread_spin_unlock(&server_opts.sid_lock);
1282
1283 /* NETCONF handshake */
1284 msgtype = nc_handshake(new_session);
1285 if (msgtype != NC_MSG_HELLO) {
1286 return msgtype;
1287 }
1288
Michal Vasko2e6defd2016-10-07 15:48:15 +02001289 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001290 new_session->status = NC_STATUS_RUNNING;
1291 *session = new_session;
1292
1293 return msgtype;
1294}
1295
1296API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001297nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001298{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001299 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001300 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001301 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001302 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001303
Michal Vasko45e53ae2016-04-07 11:46:03 +02001304 if (!ps) {
1305 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001306 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001307 } else if (!session) {
1308 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001309 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001310 }
1311
Michal Vasko48a63ed2016-03-01 09:48:21 +01001312 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001313 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001314 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001315 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001316
Michal Vasko96164bf2016-01-21 15:41:58 +01001317 for (i = 0; i < ps->session_count; ++i) {
1318 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1319 && ps->sessions[i]->ti.libssh.next) {
1320 /* an SSH session with more channels */
1321 for (new_session = ps->sessions[i]->ti.libssh.next;
1322 new_session != ps->sessions[i];
1323 new_session = new_session->ti.libssh.next) {
1324 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1325 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1326 /* we found our session */
1327 break;
1328 }
1329 }
1330 if (new_session != ps->sessions[i]) {
1331 break;
1332 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001333
Michal Vasko96164bf2016-01-21 15:41:58 +01001334 new_session = NULL;
1335 }
1336 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001337
Michal Vasko48a63ed2016-03-01 09:48:21 +01001338 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001339 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001340
Michal Vasko96164bf2016-01-21 15:41:58 +01001341 if (!new_session) {
1342 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001343 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001344 }
1345
1346 /* assign new SID atomically */
1347 pthread_spin_lock(&server_opts.sid_lock);
1348 new_session->id = server_opts.new_session_id++;
1349 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001350
Michal Vasko086311b2016-01-08 09:53:11 +01001351 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001352 msgtype = nc_handshake(new_session);
1353 if (msgtype != NC_MSG_HELLO) {
1354 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001355 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001356
Michal Vasko2e6defd2016-10-07 15:48:15 +02001357 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001358 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001359 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001360
Michal Vasko71090fc2016-05-24 16:37:28 +02001361 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001362}