blob: 16fcafd4ece7e3980b66e4708e9db79fa5b41993 [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 *
6 * Copyright (c) 2015 CESNET, z.s.p.o.
7 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vaskoafd416b2016-02-25 14:51:46 +010011 *
Radek Krejci9b81f5b2016-02-24 13:14:49 +010012 * https://opensource.org/licenses/BSD-3-Clause
Michal Vasko086311b2016-01-08 09:53:11 +010013 */
14
15#define _GNU_SOURCE
16
17#include <stdlib.h>
18#include <string.h>
19#include <sys/types.h>
20#include <pwd.h>
21#include <shadow.h>
22#include <crypt.h>
23#include <errno.h>
24
Michal Vasko11d142a2016-01-19 15:58:24 +010025#include "session_server.h"
Michal Vaskoe22c6732016-01-29 11:03:02 +010026#include "session_server_ch.h"
27#include "libnetconf.h"
Michal Vasko086311b2016-01-08 09:53:11 +010028
29extern struct nc_server_opts server_opts;
Michal Vaskob05053d2016-01-22 16:12:06 +010030
Michal Vaskob05053d2016-01-22 16:12:06 +010031static int
Michal Vaskoe2713da2016-08-22 16:06:40 +020032nc_server_ssh_add_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
Michal Vasko086311b2016-01-08 09:53:11 +010033{
Michal Vasko1a38c862016-01-15 15:50:07 +010034 if (!privkey_path) {
Michal Vasko45e53ae2016-04-07 11:46:03 +020035 ERRARG("privkey_path");
Michal Vasko086311b2016-01-08 09:53:11 +010036 return -1;
37 }
38
Michal Vaskoe2713da2016-08-22 16:06:40 +020039 if (eaccess(privkey_path, R_OK)) {
40 ERR("Host key \"%s\" cannot be read (%s).", privkey_path, strerror(errno));
Michal Vasko5fcc7142016-02-02 12:21:10 +010041 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +010042 }
Michal Vaskod45e25a2016-01-08 15:48:44 +010043
Michal Vaskoe2713da2016-08-22 16:06:40 +020044 ++opts->hostkey_count;
45 opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys);
46 if (!opts->hostkeys) {
47 ERRMEM;
48 return -1;
49 }
50 opts->hostkeys[opts->hostkey_count - 1] = lydict_insert(server_opts.ctx, privkey_path, 0);
51
Michal Vasko5fcc7142016-02-02 12:21:10 +010052 return 0;
Michal Vaskob05053d2016-01-22 16:12:06 +010053}
54
55API int
Michal Vaskoe2713da2016-08-22 16:06:40 +020056nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010057{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010058 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +010059 struct nc_endpt *endpt;
60
Michal Vasko51e514d2016-02-02 15:51:52 +010061 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +020062 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +010063 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +010064 return -1;
65 }
Michal Vasko2e6defd2016-10-07 15:48:15 +020066 ret = nc_server_ssh_add_hostkey(privkey_path, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +010067 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010068 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +010069
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010070 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +010071}
72
73API int
Michal Vasko2e6defd2016-10-07 15:48:15 +020074nc_server_ssh_ch_client_add_hostkey(const char *client_name, const char *privkey_path)
Michal Vaskob05053d2016-01-22 16:12:06 +010075{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010076 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +020077 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +010078
Michal Vasko2e6defd2016-10-07 15:48:15 +020079 /* LOCK */
80 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
81 if (!client) {
82 return -1;
83 }
84 ret = nc_server_ssh_add_hostkey(privkey_path, client->opts.ssh);
85 /* UNLOCK */
86 nc_server_ch_client_unlock(client);
Michal Vaskoe2713da2016-08-22 16:06:40 +020087
88 return ret;
89}
90
91static int
92nc_server_ssh_del_hostkey(const char *privkey_path, struct nc_server_ssh_opts *opts)
93{
94 uint8_t i;
95
96 if (!privkey_path) {
97 for (i = 0; i < opts->hostkey_count; ++i) {
98 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
99 }
100 free(opts->hostkeys);
101 opts->hostkeys = NULL;
102 opts->hostkey_count = 0;
103 } else {
104 for (i = 0; i < opts->hostkey_count; ++i) {
105 if (!strcmp(opts->hostkeys[i], privkey_path)) {
106 --opts->hostkey_count;
107 lydict_remove(server_opts.ctx, opts->hostkeys[i]);
108 if (i < opts->hostkey_count - 1) {
109 memmove(opts->hostkeys + i, opts->hostkeys + i + 1, (opts->hostkey_count - i) * sizeof *opts->hostkeys);
110 }
111 return 0;
112 }
113 }
114
115 ERR("Host key \"%s\" not found.", privkey_path);
116 return -1;
117 }
118
119 return 0;
120}
121
122API int
123nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *privkey_path)
124{
125 int ret;
126 struct nc_endpt *endpt;
127
128 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200129 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200130 if (!endpt) {
131 return -1;
132 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200133 ret = nc_server_ssh_del_hostkey(privkey_path, endpt->opts.ssh);
Michal Vaskoe2713da2016-08-22 16:06:40 +0200134 /* UNLOCK */
135 nc_server_endpt_unlock(endpt);
136
137 return ret;
138}
139
140API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200141nc_server_ssh_ch_client_del_hostkey(const char *client_name, const char *privkey_path)
Michal Vaskoe2713da2016-08-22 16:06:40 +0200142{
143 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200144 struct nc_ch_client *client;
Michal Vaskoe2713da2016-08-22 16:06:40 +0200145
Michal Vasko2e6defd2016-10-07 15:48:15 +0200146 /* LOCK */
147 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
148 if (!client) {
149 return -1;
150 }
151 ret = nc_server_ssh_del_hostkey(privkey_path, client->opts.ssh);
152 /* UNLOCK */
153 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100154
155 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100156}
157
158static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100159nc_server_ssh_set_banner(const char *banner, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100160{
Michal Vaskob05053d2016-01-22 16:12:06 +0100161 if (!banner) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200162 ERRARG("banner");
Michal Vaskob05053d2016-01-22 16:12:06 +0100163 return -1;
164 }
165
Michal Vaskoe2713da2016-08-22 16:06:40 +0200166 if (opts->banner) {
167 lydict_remove(server_opts.ctx, opts->banner);
Michal Vaskob05053d2016-01-22 16:12:06 +0100168 }
Michal Vaskoe2713da2016-08-22 16:06:40 +0200169 opts->banner = lydict_insert(server_opts.ctx, banner, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100170 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +0100171}
172
173API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100174nc_server_ssh_endpt_set_banner(const char *endpt_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100175{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100176 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100177 struct nc_endpt *endpt;
178
Michal Vasko51e514d2016-02-02 15:51:52 +0100179 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200180 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100181 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100182 return -1;
183 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200184 ret = nc_server_ssh_set_banner(banner, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100185 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100186 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100187
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100188 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100189}
190
191API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200192nc_server_ssh_ch_client_set_banner(const char *client_name, const char *banner)
Michal Vasko086311b2016-01-08 09:53:11 +0100193{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100194 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200195 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100196
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 }
202 ret = nc_server_ssh_set_banner(banner, client->opts.ssh);
203 /* 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_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100211{
Michal Vasko086311b2016-01-08 09:53:11 +0100212 if (!(auth_methods & NC_SSH_AUTH_PUBLICKEY) && !(auth_methods & NC_SSH_AUTH_PASSWORD)
213 && !(auth_methods & NC_SSH_AUTH_INTERACTIVE)) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200214 ERRARG("auth_methods");
Michal Vasko086311b2016-01-08 09:53:11 +0100215 return -1;
216 }
217
Michal Vaskob05053d2016-01-22 16:12:06 +0100218 opts->auth_methods = auth_methods;
219 return 0;
220}
221
222API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100223nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100224{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100225 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100226 struct nc_endpt *endpt;
227
Michal Vasko51e514d2016-02-02 15:51:52 +0100228 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200229 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100230 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100231 return -1;
232 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200233 ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100234 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100235 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100236
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100237 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100238}
239
240API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200241nc_server_ssh_ch_client_set_auth_methods(const char *client_name, int auth_methods)
Michal Vaskob05053d2016-01-22 16:12:06 +0100242{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100243 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200244 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100245
Michal Vasko2e6defd2016-10-07 15:48:15 +0200246 /* LOCK */
247 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
248 if (!client) {
249 return -1;
250 }
251 ret = nc_server_ssh_set_auth_methods(auth_methods, client->opts.ssh);
252 /* UNLOCK */
253 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100254
255 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100256}
257
258static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100259nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100260{
Michal Vaskob05053d2016-01-22 16:12:06 +0100261 if (!auth_attempts) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200262 ERRARG("auth_attempts");
Michal Vaskob05053d2016-01-22 16:12:06 +0100263 return -1;
264 }
265
Michal Vaskob05053d2016-01-22 16:12:06 +0100266 opts->auth_attempts = auth_attempts;
Michal Vasko086311b2016-01-08 09:53:11 +0100267 return 0;
268}
269
270API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100271nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts)
Michal Vasko086311b2016-01-08 09:53:11 +0100272{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100273 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100274 struct nc_endpt *endpt;
275
Michal Vasko51e514d2016-02-02 15:51:52 +0100276 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200277 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100278 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100279 return -1;
280 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200281 ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100282 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100283 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100284
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100285 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100286}
287
288API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200289nc_server_ssh_set_ch_client_auth_attempts(const char *client_name, uint16_t auth_attempts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100290{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100291 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200292 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100293
Michal Vasko2e6defd2016-10-07 15:48:15 +0200294 /* LOCK */
295 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
296 if (!client) {
297 return -1;
298 }
299 ret = nc_server_ssh_set_auth_attempts(auth_attempts, client->opts.ssh);
300 /* UNLOCK */
301 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100302
303 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100304}
305
306static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100307nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100308{
Michal Vaskob05053d2016-01-22 16:12:06 +0100309 if (!auth_timeout) {
Michal Vasko45e53ae2016-04-07 11:46:03 +0200310 ERRARG("auth_timeout");
Michal Vasko086311b2016-01-08 09:53:11 +0100311 return -1;
312 }
313
Michal Vaskob05053d2016-01-22 16:12:06 +0100314 opts->auth_timeout = auth_timeout;
Michal Vasko086311b2016-01-08 09:53:11 +0100315 return 0;
316}
317
318API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100319nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout)
Michal Vasko086311b2016-01-08 09:53:11 +0100320{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100321 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100322 struct nc_endpt *endpt;
323
Michal Vasko51e514d2016-02-02 15:51:52 +0100324 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200325 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100326 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100327 return -1;
328 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200329 ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100330 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100331 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100332
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100333 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100334}
335
336API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200337nc_server_ssh_ch_client_set_auth_timeout(const char *client_name, uint16_t auth_timeout)
Michal Vaskob05053d2016-01-22 16:12:06 +0100338{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100339 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200340 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100341
Michal Vasko2e6defd2016-10-07 15:48:15 +0200342 /* LOCK */
343 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
344 if (!client) {
345 return -1;
346 }
347 ret = nc_server_ssh_set_auth_timeout(auth_timeout, client->opts.ssh);
348 /* UNLOCK */
349 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100350
351 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100352}
353
354static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100355nc_server_ssh_add_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100356{
Michal Vasko45e53ae2016-04-07 11:46:03 +0200357 if (!pubkey_path) {
358 ERRARG("pubkey_path");
359 return -1;
360 } else if (!username) {
361 ERRARG("username");
Michal Vasko086311b2016-01-08 09:53:11 +0100362 return -1;
363 }
364
Michal Vaskob05053d2016-01-22 16:12:06 +0100365 ++opts->authkey_count;
Michal Vasko4eb3c312016-03-01 14:09:37 +0100366 opts->authkeys = nc_realloc(opts->authkeys, opts->authkey_count * sizeof *opts->authkeys);
367 if (!opts->authkeys) {
368 ERRMEM;
369 return -1;
370 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100371 opts->authkeys[opts->authkey_count - 1].path = lydict_insert(server_opts.ctx, pubkey_path, 0);
372 opts->authkeys[opts->authkey_count - 1].username = lydict_insert(server_opts.ctx, username, 0);
Michal Vaskob05053d2016-01-22 16:12:06 +0100373
Michal Vasko086311b2016-01-08 09:53:11 +0100374 return 0;
375}
376
377API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100378nc_server_ssh_endpt_add_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100379{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100380 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100381 struct nc_endpt *endpt;
382
Michal Vasko51e514d2016-02-02 15:51:52 +0100383 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200384 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100385 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100386 return -1;
387 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200388 ret = nc_server_ssh_add_authkey(pubkey_path, username, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100389 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100390 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100391
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100392 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100393}
394
395API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200396nc_server_ssh_ch_client_add_authkey(const char *client_name, const char *pubkey_path, const char *username)
Michal Vasko086311b2016-01-08 09:53:11 +0100397{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100398 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200399 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100400
Michal Vasko2e6defd2016-10-07 15:48:15 +0200401 /* LOCK */
402 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
403 if (!client) {
404 return -1;
405 }
406 ret = nc_server_ssh_add_authkey(pubkey_path, username, client->opts.ssh);
407 /* UNLOCK */
408 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100409
410 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100411}
412
413static int
Michal Vasko3031aae2016-01-27 16:07:18 +0100414nc_server_ssh_del_authkey(const char *pubkey_path, const char *username, struct nc_server_ssh_opts *opts)
Michal Vaskob05053d2016-01-22 16:12:06 +0100415{
Michal Vasko086311b2016-01-08 09:53:11 +0100416 uint32_t i;
417 int ret = -1;
418
Michal Vasko1a38c862016-01-15 15:50:07 +0100419 if (!pubkey_path && !username) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100420 for (i = 0; i < opts->authkey_count; ++i) {
421 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
422 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko086311b2016-01-08 09:53:11 +0100423
Michal Vasko086311b2016-01-08 09:53:11 +0100424 ret = 0;
425 }
Michal Vaskob05053d2016-01-22 16:12:06 +0100426 free(opts->authkeys);
427 opts->authkeys = NULL;
428 opts->authkey_count = 0;
Michal Vasko1a38c862016-01-15 15:50:07 +0100429 } else {
Michal Vaskob05053d2016-01-22 16:12:06 +0100430 for (i = 0; i < opts->authkey_count; ++i) {
431 if ((!pubkey_path || !strcmp(opts->authkeys[i].path, pubkey_path))
432 && (!username || !strcmp(opts->authkeys[i].username, username))) {
Michal Vaskob05053d2016-01-22 16:12:06 +0100433 lydict_remove(server_opts.ctx, opts->authkeys[i].path);
434 lydict_remove(server_opts.ctx, opts->authkeys[i].username);
Michal Vasko1a38c862016-01-15 15:50:07 +0100435
Michal Vaskob05053d2016-01-22 16:12:06 +0100436 --opts->authkey_count;
Michal Vaskoc0256492016-02-02 12:19:06 +0100437 if (i < opts->authkey_count) {
438 memcpy(&opts->authkeys[i], &opts->authkeys[opts->authkey_count], sizeof *opts->authkeys);
439 } else if (!opts->authkey_count) {
440 free(opts->authkeys);
441 opts->authkeys = NULL;
442 }
Michal Vasko1a38c862016-01-15 15:50:07 +0100443
444 ret = 0;
445 }
446 }
Michal Vasko086311b2016-01-08 09:53:11 +0100447 }
448
449 return ret;
450}
451
Michal Vaskob05053d2016-01-22 16:12:06 +0100452API int
Michal Vasko3031aae2016-01-27 16:07:18 +0100453nc_server_ssh_endpt_del_authkey(const char *endpt_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100454{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100455 int ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100456 struct nc_endpt *endpt;
457
Michal Vasko51e514d2016-02-02 15:51:52 +0100458 /* LOCK */
Michal Vasko2e6defd2016-10-07 15:48:15 +0200459 endpt = nc_server_endpt_lock(endpt_name, NC_TI_LIBSSH, NULL);
Michal Vasko3031aae2016-01-27 16:07:18 +0100460 if (!endpt) {
Michal Vasko3031aae2016-01-27 16:07:18 +0100461 return -1;
462 }
Michal Vasko2e6defd2016-10-07 15:48:15 +0200463 ret = nc_server_ssh_del_authkey(pubkey_path, username, endpt->opts.ssh);
Michal Vasko51e514d2016-02-02 15:51:52 +0100464 /* UNLOCK */
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100465 nc_server_endpt_unlock(endpt);
Michal Vasko3031aae2016-01-27 16:07:18 +0100466
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100467 return ret;
Michal Vaskob05053d2016-01-22 16:12:06 +0100468}
469
470API int
Michal Vasko2e6defd2016-10-07 15:48:15 +0200471nc_server_ssh_ch_client_del_authkey(const char *client_name, const char *pubkey_path, const char *username)
Michal Vaskob05053d2016-01-22 16:12:06 +0100472{
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100473 int ret;
Michal Vasko2e6defd2016-10-07 15:48:15 +0200474 struct nc_ch_client *client;
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100475
Michal Vasko2e6defd2016-10-07 15:48:15 +0200476 /* LOCK */
477 client = nc_server_ch_client_lock(client_name, NC_TI_LIBSSH, NULL);
478 if (!client) {
479 return -1;
480 }
481 ret = nc_server_ssh_del_authkey(pubkey_path, username, client->opts.ssh);
482 /* UNLOCK */
483 nc_server_ch_client_unlock(client);
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100484
485 return ret;
Michal Vasko3031aae2016-01-27 16:07:18 +0100486}
487
488void
Michal Vaskoc6b9c7b2016-01-28 11:10:08 +0100489nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts)
Michal Vasko3031aae2016-01-27 16:07:18 +0100490{
Michal Vaskoe2713da2016-08-22 16:06:40 +0200491 nc_server_ssh_del_hostkey(NULL, opts);
492 if (opts->banner) {
493 lydict_remove(server_opts.ctx, opts->banner);
494 opts->banner = NULL;
Michal Vasko3031aae2016-01-27 16:07:18 +0100495 }
Michal Vasko3031aae2016-01-27 16:07:18 +0100496 nc_server_ssh_del_authkey(NULL, NULL, opts);
Michal Vaskob05053d2016-01-22 16:12:06 +0100497}
498
Michal Vasko086311b2016-01-08 09:53:11 +0100499static char *
500auth_password_get_pwd_hash(const char *username)
501{
502 struct passwd *pwd, pwd_buf;
503 struct spwd *spwd, spwd_buf;
504 char *pass_hash = NULL, buf[256];
505
506 getpwnam_r(username, &pwd_buf, buf, 256, &pwd);
507 if (!pwd) {
Michal Vasko11d142a2016-01-19 15:58:24 +0100508 VRB("User \"%s\" not found locally.", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100509 return NULL;
510 }
511
512 if (!strcmp(pwd->pw_passwd, "x")) {
513 getspnam_r(username, &spwd_buf, buf, 256, &spwd);
514 if (!spwd) {
515 VRB("Failed to retrieve the shadow entry for \"%s\".", username);
516 return NULL;
517 }
518
519 pass_hash = spwd->sp_pwdp;
520 } else {
521 pass_hash = pwd->pw_passwd;
522 }
523
524 if (!pass_hash) {
Michal Vaskod083db62016-01-19 10:31:29 +0100525 ERR("No password could be retrieved for \"%s\".", username);
Michal Vasko086311b2016-01-08 09:53:11 +0100526 return NULL;
527 }
528
529 /* check the hash structure for special meaning */
530 if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) {
531 VRB("User \"%s\" is not allowed to authenticate using a password.", username);
532 return NULL;
533 }
534 if (!strcmp(pass_hash, "*NP*")) {
535 VRB("Retrieving password for \"%s\" from a NIS+ server not supported.", username);
536 return NULL;
537 }
538
539 return strdup(pass_hash);
540}
541
542static int
543auth_password_compare_pwd(const char *pass_hash, const char *pass_clear)
544{
545 char *new_pass_hash;
546 struct crypt_data cdata;
547
548 if (!pass_hash[0]) {
549 if (!pass_clear[0]) {
550 WRN("User authentication successful with an empty password!");
551 return 0;
552 } else {
553 /* the user did now know he does not need any password,
554 * (which should not be used) so deny authentication */
555 return 1;
556 }
557 }
558
559 cdata.initialized = 0;
560 new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata);
561 return strcmp(new_pass_hash, pass_hash);
562}
563
564static void
565nc_sshcb_auth_password(struct nc_session *session, ssh_message msg)
566{
567 char *pass_hash;
568
569 pass_hash = auth_password_get_pwd_hash(session->username);
570 if (pass_hash && !auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg))) {
Michal Vaskod083db62016-01-19 10:31:29 +0100571 VRB("User \"%s\" authenticated.", session->username);
Michal Vasko086311b2016-01-08 09:53:11 +0100572 ssh_message_auth_reply_success(msg, 0);
573 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
574 free(pass_hash);
575 return;
576 }
577
578 free(pass_hash);
Michal Vasko2e6defd2016-10-07 15:48:15 +0200579 ++session->opts.server.ssh_auth_attempts;
580 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100581 ssh_message_reply_default(msg);
582}
583
584static void
585nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg)
586{
587 char *pass_hash;
588
589 if (!ssh_message_auth_kbdint_is_response(msg)) {
590 const char *prompts[] = {"Password: "};
591 char echo[] = {0};
592
593 ssh_message_auth_interactive_request(msg, "Interactive SSH Authentication", "Type your password:", 1, prompts, echo);
594 } else {
595 if (ssh_userauth_kbdint_getnanswers(session->ti.libssh.session) != 1) {
596 ssh_message_reply_default(msg);
597 return;
598 }
599 pass_hash = auth_password_get_pwd_hash(session->username);
600 if (!pass_hash) {
601 ssh_message_reply_default(msg);
602 return;
603 }
604 if (!auth_password_compare_pwd(pass_hash, ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0))) {
605 VRB("User \"%s\" authenticated.", session->username);
606 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
607 ssh_message_auth_reply_success(msg, 0);
608 } else {
Michal Vasko2e6defd2016-10-07 15:48:15 +0200609 ++session->opts.server.ssh_auth_attempts;
610 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100611 ssh_message_reply_default(msg);
612 }
Radek Krejcifb533742016-03-04 15:12:54 +0100613 free(pass_hash);
Michal Vasko086311b2016-01-08 09:53:11 +0100614 }
615}
616
617static const char *
Michal Vasko3031aae2016-01-27 16:07:18 +0100618auth_pubkey_compare_key(struct nc_server_ssh_opts *opts, ssh_key key)
Michal Vasko086311b2016-01-08 09:53:11 +0100619{
620 uint32_t i;
621 ssh_key pub_key;
Michal Vasko76e3a352016-01-18 09:07:00 +0100622 const char *username = NULL;
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200623 int ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100624
Michal Vasko3031aae2016-01-27 16:07:18 +0100625 for (i = 0; i < opts->authkey_count; ++i) {
Michal Vasko7abcdeb2016-05-30 15:27:00 +0200626 ret = ssh_pki_import_pubkey_file(opts->authkeys[i].path, &pub_key);
627 if (ret == SSH_EOF) {
628 WRN("Failed to import the public key \"%s\" (File access problem).", opts->authkeys[i].path);
629 continue;
630 } else if (ret == SSH_ERROR) {
631 WRN("Failed to import the public key \"%s\" (SSH error).", opts->authkeys[i].path);
Michal Vasko086311b2016-01-08 09:53:11 +0100632 continue;
633 }
634
635 if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) {
636 ssh_key_free(pub_key);
637 break;
638 }
639
640 ssh_key_free(pub_key);
641 }
642
Michal Vasko3031aae2016-01-27 16:07:18 +0100643 if (i < opts->authkey_count) {
644 username = opts->authkeys[i].username;
Michal Vasko086311b2016-01-08 09:53:11 +0100645 }
646
647 return username;
648}
649
650static void
651nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg)
652{
653 const char *username;
654 int signature_state;
655
Michal Vaskobd13a932016-09-14 09:00:35 +0200656 if ((username = auth_pubkey_compare_key(session->data, ssh_message_auth_pubkey(msg))) == NULL) {
657 VRB("User \"%s\" tried to use an unknown (unauthorized) public key.", session->username);
658 goto fail;
659 } else if (strcmp(session->username, username)) {
660 VRB("User \"%s\" is not the username identified with the presented public key.", session->username);
Michal Vaskobd13a932016-09-14 09:00:35 +0200661 goto fail;
662 }
Michal Vaskobd13a932016-09-14 09:00:35 +0200663
Michal Vasko086311b2016-01-08 09:53:11 +0100664 signature_state = ssh_message_auth_publickey_state(msg);
665 if (signature_state == SSH_PUBLICKEY_STATE_VALID) {
666 VRB("User \"%s\" authenticated.", session->username);
667 session->flags |= NC_SESSION_SSH_AUTHENTICATED;
668 ssh_message_auth_reply_success(msg, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100669 } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) {
Michal Vaskobd13a932016-09-14 09:00:35 +0200670 /* accepting only the use of a public key */
671 ssh_message_auth_reply_pk_ok_simple(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100672 }
673
Michal Vaskobd13a932016-09-14 09:00:35 +0200674 return;
675
676fail:
Michal Vasko2e6defd2016-10-07 15:48:15 +0200677 ++session->opts.server.ssh_auth_attempts;
678 VRB("Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts);
Michal Vasko086311b2016-01-08 09:53:11 +0100679 ssh_message_reply_default(msg);
680}
681
682static int
Michal Vasko96164bf2016-01-21 15:41:58 +0100683nc_sshcb_channel_open(struct nc_session *session, ssh_message msg)
Michal Vasko086311b2016-01-08 09:53:11 +0100684{
Michal Vasko96164bf2016-01-21 15:41:58 +0100685 ssh_channel chan;
686
687 /* first channel request */
688 if (!session->ti.libssh.channel) {
689 if (session->status != NC_STATUS_STARTING) {
Michal Vasko9e036d52016-01-08 10:49:26 +0100690 ERRINT;
Michal Vasko086311b2016-01-08 09:53:11 +0100691 return -1;
692 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100693 chan = ssh_message_channel_request_open_reply_accept(msg);
694 if (!chan) {
695 ERR("Failed to create a new SSH channel.");
696 return -1;
697 }
698 session->ti.libssh.channel = chan;
Michal Vasko086311b2016-01-08 09:53:11 +0100699
Michal Vasko96164bf2016-01-21 15:41:58 +0100700 /* additional channel request */
701 } else {
702 chan = ssh_message_channel_request_open_reply_accept(msg);
703 if (!chan) {
704 ERR("Session %u: failed to create a new SSH channel.", session->id);
705 return -1;
706 }
707 /* channel was created and libssh stored it internally in the ssh_session structure, good enough */
Michal Vasko086311b2016-01-08 09:53:11 +0100708 }
709
Michal Vasko086311b2016-01-08 09:53:11 +0100710 return 0;
711}
712
713static int
714nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, const char *subsystem)
715{
Michal Vasko96164bf2016-01-21 15:41:58 +0100716 struct nc_session *new_session;
Michal Vasko086311b2016-01-08 09:53:11 +0100717
Michal Vasko96164bf2016-01-21 15:41:58 +0100718 if (strcmp(subsystem, "netconf")) {
719 WRN("Received an unknown subsystem \"%s\" request.", subsystem);
Michal Vasko086311b2016-01-08 09:53:11 +0100720 return -1;
721 }
722
Michal Vasko96164bf2016-01-21 15:41:58 +0100723 if (session->ti.libssh.channel == channel) {
724 /* first channel requested */
725 if (session->ti.libssh.next || (session->status != NC_STATUS_STARTING)) {
726 ERRINT;
727 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +0100728 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100729 if (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) {
730 ERR("Session %u: subsystem \"netconf\" requested for the second time.", session->id);
731 return -1;
732 }
733
734 session->flags |= NC_SESSION_SSH_SUBSYS_NETCONF;
Michal Vasko086311b2016-01-08 09:53:11 +0100735 } else {
Michal Vasko96164bf2016-01-21 15:41:58 +0100736 /* additional channel subsystem request, new session is ready as far as SSH is concerned */
737 new_session = calloc(1, sizeof *new_session);
Michal Vasko4eb3c312016-03-01 14:09:37 +0100738 if (!new_session) {
739 ERRMEM;
740 return -1;
741 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100742
743 /* insert the new session */
744 if (!session->ti.libssh.next) {
745 new_session->ti.libssh.next = session;
746 } else {
747 new_session->ti.libssh.next = session->ti.libssh.next;
748 }
749 session->ti.libssh.next = new_session;
750
751 new_session->status = NC_STATUS_STARTING;
752 new_session->side = NC_SERVER;
753 new_session->ti_type = NC_TI_LIBSSH;
754 new_session->ti_lock = session->ti_lock;
755 new_session->ti.libssh.channel = channel;
756 new_session->ti.libssh.session = session->ti.libssh.session;
757 new_session->username = lydict_insert(server_opts.ctx, session->username, 0);
758 new_session->host = lydict_insert(server_opts.ctx, session->host, 0);
759 new_session->port = session->port;
760 new_session->ctx = server_opts.ctx;
Michal Vaskoc61c4492016-01-25 11:13:34 +0100761 new_session->flags = NC_SESSION_SSH_AUTHENTICATED | NC_SESSION_SSH_SUBSYS_NETCONF | NC_SESSION_SHAREDCTX
762 | (session->flags & NC_SESSION_CALLHOME ? NC_SESSION_CALLHOME : 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100763 }
764
765 return 0;
766}
767
Michal Vasko96164bf2016-01-21 15:41:58 +0100768int
Michal Vaskob48aa812016-01-18 14:13:09 +0100769nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data)
Michal Vasko086311b2016-01-08 09:53:11 +0100770{
771 const char *str_type, *str_subtype = NULL, *username;
772 int subtype, type;
773 struct nc_session *session = (struct nc_session *)data;
Michal Vasko086311b2016-01-08 09:53:11 +0100774
775 type = ssh_message_type(msg);
776 subtype = ssh_message_subtype(msg);
777
778 switch (type) {
779 case SSH_REQUEST_AUTH:
780 str_type = "request-auth";
781 switch (subtype) {
782 case SSH_AUTH_METHOD_NONE:
783 str_subtype = "none";
784 break;
785 case SSH_AUTH_METHOD_PASSWORD:
786 str_subtype = "password";
787 break;
788 case SSH_AUTH_METHOD_PUBLICKEY:
789 str_subtype = "publickey";
790 break;
791 case SSH_AUTH_METHOD_HOSTBASED:
792 str_subtype = "hostbased";
793 break;
794 case SSH_AUTH_METHOD_INTERACTIVE:
795 str_subtype = "interactive";
796 break;
797 case SSH_AUTH_METHOD_GSSAPI_MIC:
798 str_subtype = "gssapi-mic";
799 break;
800 }
801 break;
802
803 case SSH_REQUEST_CHANNEL_OPEN:
804 str_type = "request-channel-open";
805 switch (subtype) {
806 case SSH_CHANNEL_SESSION:
807 str_subtype = "session";
808 break;
809 case SSH_CHANNEL_DIRECT_TCPIP:
810 str_subtype = "direct-tcpip";
811 break;
812 case SSH_CHANNEL_FORWARDED_TCPIP:
813 str_subtype = "forwarded-tcpip";
814 break;
815 case (int)SSH_CHANNEL_X11:
816 str_subtype = "channel-x11";
817 break;
818 case SSH_CHANNEL_UNKNOWN:
819 /* fallthrough */
820 default:
821 str_subtype = "unknown";
822 break;
823 }
824 break;
825
826 case SSH_REQUEST_CHANNEL:
827 str_type = "request-channel";
828 switch (subtype) {
829 case SSH_CHANNEL_REQUEST_PTY:
830 str_subtype = "pty";
831 break;
832 case SSH_CHANNEL_REQUEST_EXEC:
833 str_subtype = "exec";
834 break;
835 case SSH_CHANNEL_REQUEST_SHELL:
836 str_subtype = "shell";
837 break;
838 case SSH_CHANNEL_REQUEST_ENV:
839 str_subtype = "env";
840 break;
841 case SSH_CHANNEL_REQUEST_SUBSYSTEM:
842 str_subtype = "subsystem";
843 break;
844 case SSH_CHANNEL_REQUEST_WINDOW_CHANGE:
845 str_subtype = "window-change";
846 break;
847 case SSH_CHANNEL_REQUEST_X11:
848 str_subtype = "x11";
849 break;
850 case SSH_CHANNEL_REQUEST_UNKNOWN:
851 /* fallthrough */
852 default:
853 str_subtype = "unknown";
854 break;
855 }
856 break;
857
858 case SSH_REQUEST_SERVICE:
859 str_type = "request-service";
860 str_subtype = ssh_message_service_service(msg);
861 break;
862
863 case SSH_REQUEST_GLOBAL:
864 str_type = "request-global";
865 switch (subtype) {
866 case SSH_GLOBAL_REQUEST_TCPIP_FORWARD:
867 str_subtype = "tcpip-forward";
868 break;
869 case SSH_GLOBAL_REQUEST_CANCEL_TCPIP_FORWARD:
870 str_subtype = "cancel-tcpip-forward";
871 break;
872 case SSH_GLOBAL_REQUEST_UNKNOWN:
873 /* fallthrough */
874 default:
875 str_subtype = "unknown";
876 break;
877 }
878 break;
879
880 default:
881 str_type = "unknown";
882 str_subtype = "unknown";
883 break;
884 }
885
886 VRB("Received an SSH message \"%s\" of subtype \"%s\".", str_type, str_subtype);
Michal Vaskoce319162016-02-03 15:33:08 +0100887 if ((session->status == NC_STATUS_CLOSING) || (session->status == NC_STATUS_INVALID)) {
888 /* "valid" situation if, for example, receiving some auth or channel request timeouted,
889 * but we got it now, during session free */
890 VRB("SSH message arrived on a %s session, the request will be denied.",
891 (session->status == NC_STATUS_CLOSING ? "closing" : "invalid"));
892 ssh_message_reply_default(msg);
893 return 0;
894 }
Michal Vasko96164bf2016-01-21 15:41:58 +0100895 session->flags |= NC_SESSION_SSH_NEW_MSG;
Michal Vasko086311b2016-01-08 09:53:11 +0100896
897 /*
898 * process known messages
899 */
900 if (type == SSH_REQUEST_AUTH) {
901 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
902 ERR("User \"%s\" authenticated, but requested another authentication.", session->username);
903 ssh_message_reply_default(msg);
904 return 0;
905 }
906
Michal Vasko2e6defd2016-10-07 15:48:15 +0200907 if (session->opts.server.ssh_auth_attempts >= ((struct nc_server_ssh_opts *)session->data)->auth_attempts) {
Michal Vasko086311b2016-01-08 09:53:11 +0100908 /* too many failed attempts */
909 ssh_message_reply_default(msg);
910 return 0;
911 }
912
913 /* save the username, do not let the client change it */
914 username = ssh_message_auth_user(msg);
915 if (!session->username) {
916 if (!username) {
917 ERR("Denying an auth request without a username.");
918 return 1;
919 }
920
Michal Vasko05ba9df2016-01-13 14:40:27 +0100921 session->username = lydict_insert(server_opts.ctx, username, 0);
Michal Vasko086311b2016-01-08 09:53:11 +0100922 } else if (username) {
923 if (strcmp(username, session->username)) {
924 ERR("User \"%s\" changed its username to \"%s\".", session->username, username);
925 session->status = NC_STATUS_INVALID;
Michal Vasko428087d2016-01-14 16:04:28 +0100926 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko086311b2016-01-08 09:53:11 +0100927 return 1;
928 }
929 }
930
931 if (subtype == SSH_AUTH_METHOD_NONE) {
932 /* libssh will return the supported auth methods */
933 return 1;
934 } else if (subtype == SSH_AUTH_METHOD_PASSWORD) {
935 nc_sshcb_auth_password(session, msg);
936 return 0;
937 } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) {
938 nc_sshcb_auth_pubkey(session, msg);
939 return 0;
940 } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) {
941 nc_sshcb_auth_kbdint(session, msg);
942 return 0;
943 }
944 } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
Michal Vasko0df67562016-01-21 15:50:11 +0100945 if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100946 if (nc_sshcb_channel_open(session, msg)) {
Michal Vasko086311b2016-01-08 09:53:11 +0100947 ssh_message_reply_default(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100948 }
Michal Vasko086311b2016-01-08 09:53:11 +0100949 return 0;
Michal Vasko96164bf2016-01-21 15:41:58 +0100950
Michal Vasko0df67562016-01-21 15:50:11 +0100951 } else if ((type == SSH_REQUEST_CHANNEL) && ((enum ssh_channel_requests_e)subtype == SSH_CHANNEL_REQUEST_SUBSYSTEM)) {
Michal Vasko96164bf2016-01-21 15:41:58 +0100952 if (nc_sshcb_channel_subsystem(session, ssh_message_channel_request_channel(msg),
953 ssh_message_channel_request_subsystem(msg))) {
Michal Vasko086311b2016-01-08 09:53:11 +0100954 ssh_message_reply_default(msg);
Michal Vasko96164bf2016-01-21 15:41:58 +0100955 } else {
956 ssh_message_channel_request_reply_success(msg);
Michal Vasko086311b2016-01-08 09:53:11 +0100957 }
958 return 0;
959 }
960 }
961
962 /* we did not process it */
963 return 1;
964}
965
Michal Vasko1a38c862016-01-15 15:50:07 +0100966/* ret 1 on success, 0 on timeout, -1 on error */
Michal Vasko086311b2016-01-08 09:53:11 +0100967static int
968nc_open_netconf_channel(struct nc_session *session, int timeout)
969{
Michal Vasko62be1ce2016-03-03 13:24:52 +0100970 int elapsed_usec = 0, ret;
Michal Vasko086311b2016-01-08 09:53:11 +0100971
972 /* message callback is executed twice to give chance for the channel to be
973 * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100974 if (!timeout) {
Michal Vasko2a7d4732016-01-15 09:24:46 +0100975 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +0100976 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +0100977 return -1;
978 }
979
Michal vasko50cc94f2016-10-04 13:46:20 +0200980 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100981 if (ret != 1) {
982 return ret;
983 }
984
985 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
986 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +0100987 ERR("Failed to receive SSH messages on a session (%s).",
988 ssh_get_error(session->ti.libssh.session));
Michal Vasko11d142a2016-01-19 15:58:24 +0100989 pthread_mutex_unlock(session->ti_lock);
Michal Vasko086311b2016-01-08 09:53:11 +0100990 return -1;
991 }
992
Michal Vasko7f1c78b2016-01-19 09:52:14 +0100993 if (!session->ti.libssh.channel) {
994 /* we did not receive channel-open, timeout */
995 pthread_mutex_unlock(session->ti_lock);
996 return 0;
997 }
998
999 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1000 if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001001 ERR("Failed to receive SSH messages on a session (%s).",
1002 ssh_get_error(session->ti.libssh.session));
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001003 pthread_mutex_unlock(session->ti_lock);
1004 return -1;
1005 }
1006 pthread_mutex_unlock(session->ti_lock);
1007
1008 if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1009 /* we did not receive subsystem-request, timeout */
1010 return 0;
1011 }
1012
1013 return 1;
1014 }
1015
1016 while (1) {
1017 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001018 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko7f1c78b2016-01-19 09:52:14 +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 Vasko7f1c78b2016-01-19 09:52:14 +01001031 pthread_mutex_unlock(session->ti_lock);
1032 return -1;
1033 }
1034
1035 pthread_mutex_unlock(session->ti_lock);
1036
Michal Vasko086311b2016-01-08 09:53:11 +01001037 if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
Michal Vasko1a38c862016-01-15 15:50:07 +01001038 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001039 }
1040
Michal Vasko105bf272016-02-03 15:34:35 +01001041 if ((timeout != -1) && (elapsed_usec / 1000 >= timeout)) {
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001042 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001043 ERR("Failed to start \"netconf\" SSH subsystem for too long, disconnecting.");
Michal Vasko086311b2016-01-08 09:53:11 +01001044 break;
1045 }
1046
Michal Vasko086311b2016-01-08 09:53:11 +01001047 usleep(NC_TIMEOUT_STEP);
Michal Vasko105bf272016-02-03 15:34:35 +01001048 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vasko7f1c78b2016-01-19 09:52:14 +01001049 }
Michal Vasko086311b2016-01-08 09:53:11 +01001050
Michal Vasko1a38c862016-01-15 15:50:07 +01001051 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001052}
1053
Michal Vasko96164bf2016-01-21 15:41:58 +01001054int
Michal Vasko62be1ce2016-03-03 13:24:52 +01001055nc_ssh_pollin(struct nc_session *session, int timeout)
Michal Vasko96164bf2016-01-21 15:41:58 +01001056{
Michal Vasko62be1ce2016-03-03 13:24:52 +01001057 int ret;
Michal Vasko96164bf2016-01-21 15:41:58 +01001058 struct nc_session *new;
1059
Michal vasko50cc94f2016-10-04 13:46:20 +02001060 ret = nc_timedlock(session->ti_lock, timeout, __func__);
Michal Vasko96164bf2016-01-21 15:41:58 +01001061
Michal Vasko71090fc2016-05-24 16:37:28 +02001062 if (ret < 0) {
1063 return NC_PSPOLL_ERROR;
1064 } else if (!ret) {
1065 return NC_PSPOLL_TIMEOUT;
Michal Vasko96164bf2016-01-21 15:41:58 +01001066 }
1067
1068 ret = ssh_execute_message_callbacks(session->ti.libssh.session);
1069 pthread_mutex_unlock(session->ti_lock);
1070
1071 if (ret != SSH_OK) {
1072 ERR("Session %u: failed to receive SSH messages (%s).", session->id,
1073 ssh_get_error(session->ti.libssh.session));
1074 session->status = NC_STATUS_INVALID;
1075 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001076 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001077 }
1078
1079 /* new SSH message */
1080 if (session->flags & NC_SESSION_SSH_NEW_MSG) {
1081 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
1082 if (session->ti.libssh.next) {
1083 for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) {
1084 if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel
1085 && (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1086 /* new NETCONF SSH channel */
Michal Vasko71090fc2016-05-24 16:37:28 +02001087 return NC_PSPOLL_SSH_CHANNEL;
Michal Vasko96164bf2016-01-21 15:41:58 +01001088 }
1089 }
1090 }
1091
1092 /* just some SSH message */
Michal Vasko71090fc2016-05-24 16:37:28 +02001093 return NC_PSPOLL_SSH_MSG;
Michal Vasko96164bf2016-01-21 15:41:58 +01001094 }
1095
1096 /* no new SSH message, maybe NETCONF data? */
1097 ret = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0);
1098 /* not this one */
1099 if (!ret) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001100 return NC_PSPOLL_PENDING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001101 } else if (ret == SSH_ERROR) {
1102 ERR("Session %u: SSH channel error (%s).", session->id,
1103 ssh_get_error(session->ti.libssh.session));
1104 session->status = NC_STATUS_INVALID;
1105 session->term_reason = NC_SESSION_TERM_OTHER;
Michal Vasko71090fc2016-05-24 16:37:28 +02001106 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001107 } else if (ret == SSH_EOF) {
1108 ERR("Session %u: communication channel unexpectedly closed (libssh).",
1109 session->id);
1110 session->status = NC_STATUS_INVALID;
1111 session->term_reason = NC_SESSION_TERM_DROPPED;
Michal Vasko71090fc2016-05-24 16:37:28 +02001112 return NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001113 }
1114
Michal Vasko71090fc2016-05-24 16:37:28 +02001115 return NC_PSPOLL_RPC;
Michal Vasko96164bf2016-01-21 15:41:58 +01001116}
1117
Michal Vasko3031aae2016-01-27 16:07:18 +01001118int
Michal Vasko0190bc32016-03-02 15:47:49 +01001119nc_accept_ssh_session(struct nc_session *session, int sock, int timeout)
Michal Vasko3031aae2016-01-27 16:07:18 +01001120{
Michal Vaskoe2713da2016-08-22 16:06:40 +02001121 ssh_bind sbind;
Michal Vasko3031aae2016-01-27 16:07:18 +01001122 struct nc_server_ssh_opts *opts;
Michal Vasko72387da2016-02-02 15:52:41 +01001123 int libssh_auth_methods = 0, elapsed_usec = 0, ret;
Michal Vaskoe2713da2016-08-22 16:06:40 +02001124 uint8_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001125
Michal Vasko2cc4c682016-03-01 09:16:48 +01001126 opts = session->data;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001127
Michal Vasko086311b2016-01-08 09:53:11 +01001128 /* other transport-specific data */
1129 session->ti_type = NC_TI_LIBSSH;
1130 session->ti.libssh.session = ssh_new();
1131 if (!session->ti.libssh.session) {
Michal Vaskod083db62016-01-19 10:31:29 +01001132 ERR("Failed to initialize a new SSH session.");
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001133 close(sock);
Michal Vasko9e036d52016-01-08 10:49:26 +01001134 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001135 }
1136
Michal Vaskoc61c4492016-01-25 11:13:34 +01001137 if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) {
Michal Vasko086311b2016-01-08 09:53:11 +01001138 libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY;
1139 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001140 if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) {
Michal Vasko086311b2016-01-08 09:53:11 +01001141 libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD;
1142 }
Michal Vaskoc61c4492016-01-25 11:13:34 +01001143 if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) {
Michal Vasko086311b2016-01-08 09:53:11 +01001144 libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE;
1145 }
1146 ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods);
1147
Michal Vaskoe2713da2016-08-22 16:06:40 +02001148 sbind = ssh_bind_new();
1149 if (!sbind) {
1150 ERR("Failed to create an SSH bind.");
1151 close(sock);
1152 return -1;
1153 }
1154 for (i = 0; i < opts->hostkey_count; ++i) {
1155 if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, opts->hostkeys[i]) != SSH_OK) {
1156 ERR("Failed to set hostkey \"%s\" (%s).", opts->hostkeys[i], ssh_get_error(sbind));
1157 close(sock);
1158 ssh_bind_free(sbind);
1159 return -1;
1160 }
1161 }
1162 if (opts->banner) {
1163 ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_BANNER, opts->banner);
1164 }
1165
Michal Vasko086311b2016-01-08 09:53:11 +01001166 ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session);
Michal Vaskoc61c4492016-01-25 11:13:34 +01001167 /* remember that this session was just set as nc_sshcb_msg() parameter */
Michal Vasko96164bf2016-01-21 15:41:58 +01001168 session->flags |= NC_SESSION_SSH_MSG_CB;
Michal Vasko086311b2016-01-08 09:53:11 +01001169
Michal Vaskoe2713da2016-08-22 16:06:40 +02001170 if (ssh_bind_accept_fd(sbind, session->ti.libssh.session, sock) == SSH_ERROR) {
1171 ERR("SSH failed to accept a new connection (%s).", ssh_get_error(sbind));
Michal Vaskoc14e3c82016-01-11 16:14:30 +01001172 close(sock);
Michal Vaskoe2713da2016-08-22 16:06:40 +02001173 ssh_bind_free(sbind);
Michal Vasko9e036d52016-01-08 10:49:26 +01001174 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001175 }
Michal Vaskoe2713da2016-08-22 16:06:40 +02001176 ssh_bind_free(sbind);
Michal Vasko086311b2016-01-08 09:53:11 +01001177
Michal Vasko0190bc32016-03-02 15:47:49 +01001178 ssh_set_blocking(session->ti.libssh.session, 0);
1179
1180 while ((ret = ssh_handle_key_exchange(session->ti.libssh.session)) == SSH_AGAIN) {
Michal Vaskoe65b4492016-03-03 11:57:51 +01001181 /* this tends to take longer */
1182 usleep(NC_TIMEOUT_STEP * 20);
1183 elapsed_usec += NC_TIMEOUT_STEP * 20;
Michal Vasko0190bc32016-03-02 15:47:49 +01001184 if ((timeout > -1) && (elapsed_usec / 1000 >= timeout)) {
1185 break;
1186 }
1187 }
1188 if (ret == SSH_AGAIN) {
1189 ERR("SSH key exchange timeout.");
1190 return 0;
1191 } else if (ret != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001192 ERR("SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001193 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001194 }
1195
1196 /* authenticate */
Michal Vasko0190bc32016-03-02 15:47:49 +01001197 elapsed_usec = 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001198 do {
Michal Vasko2a7d4732016-01-15 09:24:46 +01001199 if (!nc_session_is_connected(session)) {
Michal Vaskod083db62016-01-19 10:31:29 +01001200 ERR("Communication socket unexpectedly closed (libssh).");
Michal Vasko2a7d4732016-01-15 09:24:46 +01001201 return -1;
1202 }
1203
Michal Vasko086311b2016-01-08 09:53:11 +01001204 if (ssh_execute_message_callbacks(session->ti.libssh.session) != SSH_OK) {
Michal Vaskod083db62016-01-19 10:31:29 +01001205 ERR("Failed to receive SSH messages on a session (%s).",
1206 ssh_get_error(session->ti.libssh.session));
Michal Vasko9e036d52016-01-08 10:49:26 +01001207 return -1;
Michal Vasko086311b2016-01-08 09:53:11 +01001208 }
1209
1210 if (session->flags & NC_SESSION_SSH_AUTHENTICATED) {
1211 break;
1212 }
1213
1214 usleep(NC_TIMEOUT_STEP);
Michal Vasko72387da2016-02-02 15:52:41 +01001215 elapsed_usec += NC_TIMEOUT_STEP;
Michal Vaskoab639942016-02-29 16:23:47 +01001216 } while (!opts->auth_timeout || (elapsed_usec / 1000000 < opts->auth_timeout));
Michal Vasko086311b2016-01-08 09:53:11 +01001217
1218 if (!(session->flags & NC_SESSION_SSH_AUTHENTICATED)) {
1219 /* timeout */
Michal Vasko842522c2016-03-01 09:20:13 +01001220 ERR("Client failed to authenticate for too long, disconnecting.");
Michal Vasko1a38c862016-01-15 15:50:07 +01001221 return 0;
Michal Vasko086311b2016-01-08 09:53:11 +01001222 }
1223
Michal Vasko086311b2016-01-08 09:53:11 +01001224 /* open channel */
Michal Vaskoab639942016-02-29 16:23:47 +01001225 ret = nc_open_netconf_channel(session, opts->auth_timeout ? (opts->auth_timeout * 1000 - elapsed_usec / 1000) : -1);
Michal Vasko1a38c862016-01-15 15:50:07 +01001226 if (ret < 1) {
1227 return ret;
Michal Vasko086311b2016-01-08 09:53:11 +01001228 }
1229
Michal Vasko96164bf2016-01-21 15:41:58 +01001230 session->flags &= ~NC_SESSION_SSH_NEW_MSG;
Michal Vasko1a38c862016-01-15 15:50:07 +01001231 return 1;
Michal Vasko086311b2016-01-08 09:53:11 +01001232}
1233
Michal Vasko71090fc2016-05-24 16:37:28 +02001234API NC_MSG_TYPE
1235nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session)
1236{
1237 NC_MSG_TYPE msgtype;
1238 struct nc_session *new_session = NULL;
1239
1240 if (!orig_session) {
1241 ERRARG("orig_session");
1242 return NC_MSG_ERROR;
1243 } else if (!session) {
1244 ERRARG("session");
1245 return NC_MSG_ERROR;
1246 }
1247
1248 if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH)
1249 && orig_session->ti.libssh.next) {
1250 for (new_session = orig_session->ti.libssh.next;
1251 new_session != orig_session;
1252 new_session = new_session->ti.libssh.next) {
1253 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1254 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1255 /* we found our session */
1256 break;
1257 }
1258 }
1259 if (new_session == orig_session) {
1260 new_session = NULL;
1261 }
1262 }
1263
1264 if (!new_session) {
1265 ERR("Session does not have a NETCONF SSH channel ready.");
1266 return NC_MSG_ERROR;
1267 }
1268
1269 /* assign new SID atomically */
1270 pthread_spin_lock(&server_opts.sid_lock);
1271 new_session->id = server_opts.new_session_id++;
1272 pthread_spin_unlock(&server_opts.sid_lock);
1273
1274 /* NETCONF handshake */
1275 msgtype = nc_handshake(new_session);
1276 if (msgtype != NC_MSG_HELLO) {
1277 return msgtype;
1278 }
1279
Michal Vasko2e6defd2016-10-07 15:48:15 +02001280 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko71090fc2016-05-24 16:37:28 +02001281 new_session->status = NC_STATUS_RUNNING;
1282 *session = new_session;
1283
1284 return msgtype;
1285}
1286
1287API NC_MSG_TYPE
Michal Vasko96164bf2016-01-21 15:41:58 +01001288nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session)
Michal Vasko086311b2016-01-08 09:53:11 +01001289{
Michal Vaskobdcf2362016-07-26 11:35:43 +02001290 uint8_t q_id;
Michal Vasko71090fc2016-05-24 16:37:28 +02001291 NC_MSG_TYPE msgtype;
Michal Vasko96164bf2016-01-21 15:41:58 +01001292 struct nc_session *new_session = NULL;
Michal Vaskoc61c4492016-01-25 11:13:34 +01001293 uint16_t i;
Michal Vasko086311b2016-01-08 09:53:11 +01001294
Michal Vasko45e53ae2016-04-07 11:46:03 +02001295 if (!ps) {
1296 ERRARG("ps");
Michal Vasko71090fc2016-05-24 16:37:28 +02001297 return NC_MSG_ERROR;
Michal Vasko45e53ae2016-04-07 11:46:03 +02001298 } else if (!session) {
1299 ERRARG("session");
Michal Vasko71090fc2016-05-24 16:37:28 +02001300 return NC_MSG_ERROR;
Michal Vasko086311b2016-01-08 09:53:11 +01001301 }
1302
Michal Vasko48a63ed2016-03-01 09:48:21 +01001303 /* LOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001304 if (nc_ps_lock(ps, &q_id, __func__)) {
Michal Vasko71090fc2016-05-24 16:37:28 +02001305 return NC_MSG_ERROR;
Michal Vaskof04a52a2016-04-07 10:52:10 +02001306 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001307
Michal Vasko96164bf2016-01-21 15:41:58 +01001308 for (i = 0; i < ps->session_count; ++i) {
1309 if ((ps->sessions[i]->status == NC_STATUS_RUNNING) && (ps->sessions[i]->ti_type == NC_TI_LIBSSH)
1310 && ps->sessions[i]->ti.libssh.next) {
1311 /* an SSH session with more channels */
1312 for (new_session = ps->sessions[i]->ti.libssh.next;
1313 new_session != ps->sessions[i];
1314 new_session = new_session->ti.libssh.next) {
1315 if ((new_session->status == NC_STATUS_STARTING) && new_session->ti.libssh.channel
1316 && (new_session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) {
1317 /* we found our session */
1318 break;
1319 }
1320 }
1321 if (new_session != ps->sessions[i]) {
1322 break;
1323 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001324
Michal Vasko96164bf2016-01-21 15:41:58 +01001325 new_session = NULL;
1326 }
1327 }
Michal Vaskofb89d772016-01-08 12:25:35 +01001328
Michal Vasko48a63ed2016-03-01 09:48:21 +01001329 /* UNLOCK */
Michal Vasko227f8ff2016-07-26 14:08:59 +02001330 nc_ps_unlock(ps, q_id, __func__);
Michal Vasko48a63ed2016-03-01 09:48:21 +01001331
Michal Vasko96164bf2016-01-21 15:41:58 +01001332 if (!new_session) {
1333 ERR("No session with a NETCONF SSH channel ready was found.");
Michal Vasko71090fc2016-05-24 16:37:28 +02001334 return NC_MSG_ERROR;
Michal Vasko96164bf2016-01-21 15:41:58 +01001335 }
1336
1337 /* assign new SID atomically */
1338 pthread_spin_lock(&server_opts.sid_lock);
1339 new_session->id = server_opts.new_session_id++;
1340 pthread_spin_unlock(&server_opts.sid_lock);
Michal Vaskofb89d772016-01-08 12:25:35 +01001341
Michal Vasko086311b2016-01-08 09:53:11 +01001342 /* NETCONF handshake */
Michal Vasko71090fc2016-05-24 16:37:28 +02001343 msgtype = nc_handshake(new_session);
1344 if (msgtype != NC_MSG_HELLO) {
1345 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001346 }
Michal Vasko48a63ed2016-03-01 09:48:21 +01001347
Michal Vasko2e6defd2016-10-07 15:48:15 +02001348 new_session->opts.server.session_start = new_session->opts.server.last_rpc = time(NULL);
Michal Vasko086311b2016-01-08 09:53:11 +01001349 new_session->status = NC_STATUS_RUNNING;
Michal Vasko96164bf2016-01-21 15:41:58 +01001350 *session = new_session;
Michal Vasko086311b2016-01-08 09:53:11 +01001351
Michal Vasko71090fc2016-05-24 16:37:28 +02001352 return msgtype;
Michal Vasko086311b2016-01-08 09:53:11 +01001353}